tango-py/tango/vm.py

84 lines
2.5 KiB
Python

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)