-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathpyprio.py
executable file
·146 lines (126 loc) · 4.11 KB
/
pyprio.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#!/usr/bin/env python3
# SPDX-License-Identifier: WTFPL
import argparse
import ast
import locale
import os
import re
import sys
op_to_str = {
ast.And: "and",
ast.Or: "or",
ast.Add: "+",
ast.Sub: "-",
ast.Mult: "*",
ast.Pow: "**",
ast.MatMult: "@",
ast.Div: "/",
ast.FloorDiv: "//",
ast.Mod: "%",
ast.LShift: "<<",
ast.RShift: ">>",
ast.BitOr: "|",
ast.BitXor: "^",
ast.BitAnd: "&",
ast.UAdd: "+",
ast.USub: "-",
ast.Not: "not",
ast.Invert: "~",
ast.Is: "is",
ast.IsNot: "is not",
ast.In: "in",
ast.NotIn: "not in",
ast.Eq: "==",
ast.Lt: "<",
ast.LtE: "<=",
ast.Gt: ">",
ast.GtE: ">=",
ast.NotEq: "!=",
}
def has_colors(fp=sys.stdout):
if os.environ.get("NO_COLOR"):
return False
if os.environ.get("CLICOLOR_FORCE") or os.environ.get("FORCE_COLOR"):
return True
return fp.isatty()
def unparse(node):
match node:
case ast.BoolOp():
return "(" + f" {op_to_str[type(node.op)]} ".join(unparse(sub) for sub in node.values) + ")"
case ast.BinOp():
return f"({unparse(node.left)} {op_to_str[type(node.op)]} {unparse(node.right)})"
case ast.UnaryOp():
return f"({op_to_str[type(node.op)]} {unparse(node.operand)})"
case ast.Starred():
return f"*{unparse(node.value)}"
case ast.Compare():
return f"({unparse(node.left)} " + " ".join(f"{op_to_str[type(op)]} {unparse(val)}" for op, val in zip(node.ops, node.comparators)) + ")"
case ast.Attribute():
return f"{unparse(node.value)}.{node.attr}"
#return f"({unparse(node.value, no_par=True)}.{node.attr})"
case ast.Constant() | ast.Name():
return ast.unparse(node)
case ast.IfExp():
return f"({unparse(node.body)} if {unparse(node.test)} else {unparse(node.orelse)})"
case ast.NamedExpr():
return f"({unparse(node.target)} := {unparse(node.value)})"
case ast.Call():
args = [unparse(sub) for sub in node.args]
for sub in node.keywords:
if sub.arg:
args.append(f"{sub.arg}={unparse(sub.value)}")
else:
args.append(f"**{unparse(sub.value)}")
return f"({unparse(node.func)}({', '.join(args)}))"
case ast.List():
return f"([{', '.join(unparse(sub) for sub in node.elts)}])"
case ast.Tuple():
if not node.elts:
return "()"
return f"({', '.join(unparse(sub) for sub in node.elts)},)"
case ast.Dict():
args = [
f"{unparse(key)}: {unparse(value)}"
if key
else f"**{unparse(value)}"
for key, value in zip(node.keys, node.values)
]
return "({" + ", ".join(args) + "})"
raise NotImplementedError(type(node).__name__)
try:
import colorama
except ImportError:
def color_par(line):
return line
else:
def color_par(line):
if not has_colors():
return line
COLORS = [
colorama.Fore.LIGHTRED_EX, colorama.Fore.LIGHTGREEN_EX,
colorama.Fore.LIGHTBLUE_EX, colorama.Fore.LIGHTCYAN_EX,
colorama.Fore.LIGHTMAGENTA_EX, colorama.Fore.LIGHTYELLOW_EX,
]
result = ""
level = -1
while True:
m = re.search("[()]", line)
if not m:
return result + line
if m[0] == "(":
level += 1
result += line[:m.start()] + COLORS[level] + m[0] + colorama.Fore.RESET
line = line[m.end():]
if m[0] == ")":
level -= 1
def main():
locale.setlocale(locale.LC_ALL, "")
parser = argparse.ArgumentParser()
parser.add_argument("expr", help="expression on which to make priorities explicit")
args = parser.parse_args()
root = ast.parse(args.expr, mode="eval")
result = unparse(root.body).removeprefix("(").removesuffix(")")
result = color_par(result)
print(result)
if __name__ == "__main__":
main()