- commit
- ee5c52c73ecdcdfc002ea5e12bd93f9abbed31f7
- parent
- efd21a6a6564e2fc336b2dd8fb2094f5ef0af1f5
- Author
- Tobias Bengfort <tobias.bengfort@posteo.de>
- Date
- 2022-08-26 10:13
create parser
Diffstat
| A | xipd/parser.py | 179 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 files changed, 179 insertions, 0 deletions
diff --git a/xipd/parser.py b/xipd/parser.py
@@ -0,0 +1,179 @@
-1 1 import re
-1 2 import sys
-1 3
-1 4
-1 5 class SyntaxError(TypeError):
-1 6 pass
-1 7
-1 8
-1 9 class Parser:
-1 10 def parse_re(self, s, pattern):
-1 11 m = re.match(pattern, s)
-1 12 if m:
-1 13 head = s[:m.end()]
-1 14 tail = s[m.end():]
-1 15 return head, tail
-1 16 else:
-1 17 raise SyntaxError('expected "%s"' % pattern, s)
-1 18
-1 19 def parse_any(self, s, parsers, parse_to_end=False):
-1 20 errors = []
-1 21 for parser in parsers:
-1 22 try:
-1 23 value, s = parser(s)
-1 24 if parse_to_end and s:
-1 25 raise SyntaxError('tail', s)
-1 26 return value, s
-1 27 except SyntaxError as e:
-1 28 errors.append(e)
-1 29 pass
-1 30 raise SyntaxError(errors)
-1 31
-1 32 def parse_list(self, s, parse_item):
-1 33 _, s = self.parse_re(s, r'\(')
-1 34 items = []
-1 35 while not s.startswith(')'):
-1 36 if items:
-1 37 _, s = self.parse_re(s, r', *')
-1 38 item, s = parse_item(s)
-1 39 items.append(item)
-1 40 _, s = self.parse_re(s, r'\)')
-1 41 return items, s
-1 42
-1 43 def parse_name(self, s):
-1 44 return self.parse_re(s, r'[a-zA-Z_][a-zA-Z0-9_]*')
-1 45
-1 46 def parse_port(self, s):
-1 47 port, s = self.parse_re(s, r':[0-9]+')
-1 48 return int(port[1:]), s
-1 49
-1 50 def parse_str(self, s):
-1 51 value, s = self.parse_re(s, r'"[^"]*"')
-1 52 return ('str', value[1:-1]), s
-1 53
-1 54 def parse_int(self, s):
-1 55 value, s = self.parse_re(s, r'[0-9]+')
-1 56 return ('int', int(value)), s
-1 57
-1 58 def parse_float(self, s):
-1 59 value, s = self.parse_re(s, r'[0-9]+(\.[0-9]+)')
-1 60 return ('float', float(value)), s
-1 61
-1 62 def parse_raw(self, s):
-1 63 value, s = self.parse_re(s, r'`[^`]*`')
-1 64 return ('raw', value[1:-1]), s
-1 65
-1 66 def parse_ref(self, s):
-1 67 name, s = self.parse_name(s)
-1 68 if s.startswith(':'):
-1 69 port, s = self.parse_port(s)
-1 70 else:
-1 71 port = None
-1 72 return ('ref', name, port), s
-1 73
-1 74 def parse_connect(self, s):
-1 75 left, s = self.parse_expr(s)
-1 76 _, s = self.parse_re(s, r' *-> *')
-1 77 right, s = self.parse_expr(s)
-1 78 return ('connect', left, right), s
-1 79
-1 80 def parse_expr_no_op(self, s):
-1 81 return self.parse_any(s, [
-1 82 self.parse_parens,
-1 83 self.parse_call,
-1 84 self.parse_ref,
-1 85 self.parse_str,
-1 86 self.parse_float,
-1 87 self.parse_int,
-1 88 self.parse_raw,
-1 89 ])
-1 90
-1 91 def parse_expr(self, s):
-1 92 return self.parse_any(s, [
-1 93 self.parse_op,
-1 94 self.parse_expr_no_op,
-1 95 ])
-1 96
-1 97 def parse_parens(self, s):
-1 98 _, s = self.parse_re(s, r'\(')
-1 99 expr, s = self.parse_expr(s)
-1 100 _, s = self.parse_re(s, r'\)')
-1 101 return expr, s
-1 102
-1 103 def parse_op(self, s):
-1 104 left, s = self.parse_expr_no_op(s)
-1 105 op, s = self.parse_re(s, r' *(\+|-|\*|/)~? *')
-1 106 right, s = self.parse_expr(s)
-1 107 return ('op', op.strip(), left, right), s
-1 108
-1 109 def parse_assign(self, s):
-1 110 name, s = self.parse_name(s)
-1 111 _, s = self.parse_re(s, r' *= *')
-1 112 expr, s = self.parse_expr(s)
-1 113 return ('assign', name, expr), s
-1 114
-1 115 def parse_startfunc(self, s):
-1 116 name, s = self.parse_name(s)
-1 117 args, s = self.parse_list(s, self.parse_name)
-1 118 _, s = self.parse_re(s, ' *{')
-1 119 return ('startfunc', name, args), s
-1 120
-1 121 def parse_endfunc(self, s):
-1 122 _, s = self.parse_re(s, '}')
-1 123 return ('endfunc',), s
-1 124
-1 125 def parse_call(self, s):
-1 126 name, s = self.parse_name(s)
-1 127 args, s = self.parse_list(s, self.parse_expr)
-1 128 return ('call', name, args), s
-1 129
-1 130 def parse_include(self, s):
-1 131 _, s = self.parse_re(s, r'include *')
-1 132 path, s = self.parse_str(s)
-1 133 return ('include', path[1]), s
-1 134
-1 135 def parse_return(self, s):
-1 136 _, s = self.parse_re(s, r'return *')
-1 137 expr, s = self.parse_expr(s)
-1 138 return ('return', expr), s
-1 139
-1 140 def parse_line(self, s):
-1 141 ast, s = self.parse_any(s, [
-1 142 self.parse_include,
-1 143 self.parse_return,
-1 144 self.parse_connect,
-1 145 self.parse_assign,
-1 146 self.parse_startfunc,
-1 147 self.parse_endfunc,
-1 148 ], parse_to_end=True)
-1 149 return ast
-1 150
-1 151 def parse_file(self, fh):
-1 152 stack = [[]]
-1 153 for lineno, line in enumerate(fh):
-1 154 line = line.strip()
-1 155 if not line or line.startswith('#'):
-1 156 continue
-1 157 parsed = self.parse_line(line)
-1 158
-1 159 if parsed[0] == 'startfunc':
-1 160 stack[-1].append(parsed)
-1 161 stack.append([])
-1 162 elif parsed[0] == 'endfunc':
-1 163 body = stack.pop()
-1 164 startfunc = stack[-1].pop()
-1 165 stack[-1].append(('func', *startfunc[1:], body))
-1 166 else:
-1 167 stack[-1].append(parsed)
-1 168 if not len(stack) == 1:
-1 169 raise SyntaxError('unbalanced blocks')
-1 170 return stack[0]
-1 171
-1 172
-1 173 if __name__ == '__main__':
-1 174 parser = Parser()
-1 175 with open(sys.argv[1]) as fh:
-1 176 ast = parser.parse_file(fh)
-1 177
-1 178 from pprint import pprint
-1 179 pprint(ast)