import tango.utils as utils from tango.value import TangoExpr, TangoIdent from tango.scope import RootTangoScope, ChildTangoScope from tango.parser import StringReader, TangoParser class TangoVM: def __init__(self): self.root_scope = RootTangoScope() def eval_string(self, s): r = StringReader(s) p = TangoParser(r) e = p.parse() return self.eval(e) def eval(self, expr): if utils.is_expr(expr): return self.eval_in_scope(self.root_scope, expr) else: raise ValueError('Root was not Tango Expression') def def_builtin(self, ident, builtin): if isinstance(ident, str): ident = TangoIdent(ident) if utils.is_ident(ident): self.root_scope.set_def(ident, builtin) else: raise ValueError('Not ident') def eval_in_scope(self, scope, val): if utils.is_expr(val): if val.is_unit: return None elif val.is_block: return self._do_block(scope, val) elif val.is_call: return self._do_call(scope, val) if utils.is_ident(val): return scope.get_def(val) return val def multi_eval_in_scope(self, scope, vals): return [self.eval_in_scope(scope, val) for val in vals] def _do_eval_child(self, scope, expr, args = TangoExpr()): child_scope = ChildTangoScope(scope) child_scope.set_def('$', args) return self.eval_in_scope(child_scope, expr) def _do_block(self, scope, block_expr): for expr in block_expr: res = self.eval_in_scope(scope, expr) return res def _do_call(self, scope, call_expr): fn_ident, *fn_args = call_expr fn_args = TangoExpr(fn_args) fn_body = scope.get_def(fn_ident) if fn_body is None: TangoVM._rt_error('ident `{}` is undefined'.format(fn_ident)) if utils.is_callable(fn_body): return fn_body(self, scope, *fn_args) if utils.is_expr(fn_body): if fn_body.is_unit: return None if fn_body.is_block: return self._do_eval_child(scope, fn_body, fn_args) elif fn_body.is_call: fn_body = self._do_call(scope, fn_body) return self._do_eval_child(scope, fn_body, fn_args) TangoVM._rt_error('fatal error') @staticmethod def _rt_error(msg): raise RuntimeError(msg)