84 lines
2.5 KiB
Python
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)
|
||
|
|