-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathparse.py
More file actions
96 lines (80 loc) · 2.56 KB
/
Copy pathparse.py
File metadata and controls
96 lines (80 loc) · 2.56 KB
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
class Command(object):
def __init__(self, args=[], rin=None, rout=None, rapp=None):
self.args = args
self.rin = rin
self.rout = rout
self.rapp = rapp
def __repr__(self):
return ' '.join(self.args)
class ParseError(Exception):
def __init__(self, pos, ch):
self.pos = pos
self.ch = ch
super().__init__(self.__str__())
def __str__(self):
return f'Parse error: \'{self.ch}\' at {self.pos}'
class AST(object):
"""
Attributes:
root: list[tuple[list[Command], bool]] - bg/fg pipes
"""
def __init__(self, text):
self.root = AST.parse(text)
def __repr__(self):
return str(self.root)
@staticmethod
def clear_split(text, delimiter, allow_delimiter_end):
# seg delim seg delim [seg]
if not text:
return []
segs = [] # (seg, ends_with_delimiter)
seg_start = 0
text = text.strip()
for seg_end, ch in enumerate(text):
if ch == delimiter:
seg = text[seg_start:seg_end].strip()
if not seg:
raise ParseError(seg_end, ch)
segs.append((seg, True))
seg_start = seg_end + 1
if seg_start > seg_end:
# The text ends with the delimiter.
if not allow_delimiter_end:
raise ParseError(seg_end, delimiter)
else:
# The text ends with a segment.
segs.append((text[seg_start:].strip(), False))
return segs
@staticmethod
def parse(text):
pipes = AST.clear_split(text, '&', True)
return [(AST.parse_pipe(pipe), bg) for pipe, bg in pipes]
@staticmethod
def parse_pipe(text):
commands = AST.clear_split(text, '|', False)
return [AST.parse_command(command) for command, _ in commands]
@staticmethod
def parse_command(text):
tokens = text.split()
args = []
rin, rout, rapp = None, None, None
fin, fout, fapp = False, False, False
for token in tokens:
if token == '<':
fin = True
elif fin:
rin = token
fin = False
elif token == '>':
fout = True
elif fout:
rout = token
fout = False
elif token == '>>':
fapp = True
elif fapp:
rapp = token
fapp = False
else:
args.append(token)
return Command(args, rin, rout, rapp)