- commit
- 68512ded080190cc589b5e36ba61f0d701362742
- parent
- 49085514ed4e60b80b4481456ea33fbd7fc258c3
- Author
- Tobias Bengfort <tobias.bengfort@posteo.de>
- Date
- 2022-08-26 10:17
create renderer
Diffstat
| A | xipd/renderer.py | 134 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 files changed, 134 insertions, 0 deletions
diff --git a/xipd/renderer.py b/xipd/renderer.py
@@ -0,0 +1,134 @@
-1 1 import sys
-1 2
-1 3 from .parser import Parser
-1 4
-1 5
-1 6 class Scope:
-1 7 def __init__(self, parent=None):
-1 8 self.parent = parent
-1 9 self._node_count = 0
-1 10 self._refs = {}
-1 11 self._funcs = {}
-1 12
-1 13 @property
-1 14 def root(self):
-1 15 return self.parent.root if self.parent else self
-1 16
-1 17 def create_node(self):
-1 18 self.root._node_count += 1
-1 19 return self.root._node_count - 1
-1 20
-1 21 def add_ref(self, name, ref):
-1 22 self._refs[name] = ref
-1 23
-1 24 def get_ref(self, name):
-1 25 if name in self._refs:
-1 26 return self._refs[name]
-1 27 elif self.parent:
-1 28 return self.parent.get_ref(name)
-1 29 else:
-1 30 raise KeyError(name)
-1 31
-1 32 def add_func(self, name, value):
-1 33 self._funcs[name] = value
-1 34
-1 35 def get_func(self, name):
-1 36 if name in self._funcs:
-1 37 value = self._funcs[name]
-1 38 return *value, self
-1 39 elif self.parent:
-1 40 return self.parent.get_func(name)
-1 41 else:
-1 42 raise KeyError(name)
-1 43
-1 44
-1 45 class Renderer:
-1 46 def __init__(self):
-1 47 self.parser = Parser()
-1 48
-1 49 def _print(self, s):
-1 50 print('#%s;' % s, end='\r\n')
-1 51
-1 52 def call(self, name, args, scope):
-1 53 params, body, lexical_scope = scope.get_func(name)
-1 54 if len(args) != len(params):
-1 55 raise SyntaxError(f'wrong number of argumtnes for function {name}')
-1 56
-1 57 subscope = Scope(lexical_scope)
-1 58 for param, arg in zip(params, args):
-1 59 subscope.add_ref(param, self.expr_to_ref(arg, scope))
-1 60 value = self.render_with_scope(body, subscope)
-1 61 if value is None:
-1 62 raise SyntaxError(f'missing return in function {name}')
-1 63 return value
-1 64
-1 65 def expr_to_ref(self, expr, scope):
-1 66 if expr[0] == 'op':
-1 67 _, op, left, right = expr
-1 68 fn = 'op_' if op.endswith('~') else 'op'
-1 69 return self.call(fn, [('raw', op), left, right], scope)
-1 70 elif expr[0] == 'parens':
-1 71 return self.expr_to_ref(expr[1], scope)
-1 72 elif expr[0] == 'call':
-1 73 _, name, args = expr
-1 74 return self.call(name, args, scope)
-1 75 elif expr[0] == 'ref':
-1 76 _, name, port = expr
-1 77 index, default_port = scope.get_ref(name)
-1 78 if port is None:
-1 79 port = default_port or 0
-1 80 return index, port
-1 81 elif expr[0] == 'raw':
-1 82 self._print(f'X obj 0 0 {expr[1]}')
-1 83 index = scope.create_node()
-1 84 return index, 0
-1 85 elif expr[0] in ['str', 'int', 'float']:
-1 86 self._print(f'X msg 0 0 {expr[1]}')
-1 87 index = scope.create_node()
-1 88 self._print('X connect %i %i %i %i' % (
-1 89 *scope.get_ref('!loadbang'),
-1 90 index, 0
-1 91 ))
-1 92 return index, 0
-1 93 else:
-1 94 raise SyntaxError('invalid expression', expr)
-1 95
-1 96 def render_with_scope(self, ast, scope):
-1 97 for stmt in ast:
-1 98 if stmt[0] == 'include':
-1 99 with open(stmt[1]) as fh:
-1 100 ast = self.parser.parse_file(fh)
-1 101 self.render_with_scope(ast, scope)
-1 102 elif stmt[0] == 'return':
-1 103 _, expr = stmt
-1 104 return self.expr_to_ref(expr, scope)
-1 105 elif stmt[0] == 'connect':
-1 106 _, left, right = stmt
-1 107 self._print('X connect %i %i %i %i' % (
-1 108 *self.expr_to_ref(left, scope),
-1 109 *self.expr_to_ref(right, scope),
-1 110 ))
-1 111 elif stmt[0] == 'assign':
-1 112 _, name, expr = stmt
-1 113 ref = self.expr_to_ref(expr, scope)
-1 114 scope.add_ref(name, ref)
-1 115 elif stmt[0] == 'func':
-1 116 _, name, params, body = stmt
-1 117 scope.add_func(name, (params, body))
-1 118 else:
-1 119 raise SyntaxError('invalid statement', stmt)
-1 120
-1 121 def render(self, fh):
-1 122 scope = Scope()
-1 123 ast = self.parser.parse_file(fh)
-1 124 self._print('N canvas')
-1 125 self.render_with_scope([
-1 126 ('assign', '!loadbang', ('raw', 'loadbang'))
-1 127 ], scope)
-1 128 self.render_with_scope(ast, scope)
-1 129
-1 130
-1 131 if __name__ == '__main__':
-1 132 renderer = Renderer()
-1 133 with open(sys.argv[1]) as fh:
-1 134 renderer.render(fh)