xipd

programming language for audio processing that compiles to PureData
git clone https://git.ce9e.org/xipd.git

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)