def _process_quasiquote_list_item(obj): # TODO: could we invoke (list) directly? if is_list(obj) and first(obj) == Symbol('unquote'): return List([Symbol('list', ns='kaa.core'), first(rest(obj))]) if is_list(obj) and first(obj) == Symbol('unquote-splice'): return first(rest(obj)) return List([Symbol('list', ns='kaa.core'), _process_quasiquote(obj)])
def parse(cls, L): if not (is_list(L) and all(is_symbol(p) for p in L)): _err('params must be list of symbols', L) required_names = [sym.name for sym in cls._parse_required(L)] optional_names = [sym.name for sym in cls._parse_optional(L)] rest = cls._parse_rest(L) rest_name = rest and rest.name or None return cls(required_names, optional_names, rest_name)
def parse_params(form): check( is_list(form) and all(is_symbol(p) for p in form), 'params must be a list of symbols', form) required = _parse_required_params(form) optional = _parse_optional_params(form) rest = _parse_rest_param(form) return Params(required, optional, rest)
def parse(expr): if is_list(expr) and expr and is_symbol(expr[0]): try: f = SPECIAL_FORMS[expr[0]] except KeyError: pass else: expr = f(expr) return expr
def _process_quasiquote(obj): # `a -> 'a if not is_list(obj) or empty(obj): return List([Symbol('quote'), obj]) # `~a -> a if first(obj) == Symbol('unquote'): return obj # `(a ~b ~@c) -> (concat (list 'a) (list b) c) return List([Symbol('concat')] + [_process_quasiquote_list_item(o) for o in obj])
def _process_quasiquote(obj): # `a -> 'a if not is_list(obj) or not obj: return List([Symbol('quote'), obj]) # `~a -> a if first(obj) == Symbol('unquote'): return obj # `(a ~b ~@c) -> (concat (list 'a) (list b) c) # TODO: could we invoke (concat) directly? return List([Symbol('concat', ns='kaa.core')] + [_process_quasiquote_list_item(o) for o in obj])
def eval__Import(self, node): # pylint: disable=invalid-name symbols = '*' if is_symbol(node.names) and node.names.name == '*' \ else tuple(sym.name for sym in node.names) if is_list(node.names) \ else () alias = node.alias.name if node.alias else None if node.source.ns == 'py': self.ns.import_module(self.ns.load_module(node.source.name), symbols, alias) else: self.ns.import_ns(self.ns.load_ns(node.source.name), symbols, alias)
def evaluate(self, expr): "Recursively parse and evaluate some s-expression." expr = parse(expr) # Function invocation if is_list(expr) and expr: return self.call(expr) evaluator = getattr(self, f'eval__{type(expr).__name__}', None) if evaluator: return evaluator(expr) return expr
def parse_import(form): check(len(form) >= 2, '`import` requires 1+ args', form) form = form[1:] if is_list(form[0]): symbols = form.pop(0) check(all(is_symbol(x) for x in symbols), 'imported names must be symbols', symbols) form.pop(0) else: symbols = None source = form.pop(0) check(is_symbol(source), 'import source must be a symbol', source) alias = form[1] if form else None if alias: check(is_symbol(alias), 'import alias must be a symbol', alias) return Import(source, symbols, alias)
def loop(self): self.ns[self.last_result_symbol] = None while True: try: exprs = self.read_exprs() if exprs and exprs[0] and is_list(exprs[0]) and \ exprs[0][0] == Symbol('debug', '__kaa__'): exprs = exprs[0][1:] pdb.set_trace() result = Evaluator(self.ns).evaluate_all(exprs) except KeyboardInterrupt: # Ctrl-C; user wants to abandon current input print() continue except EOFError: # Ctrl-D; user wants to quit print() break except Exception: # pylint: disable=broad-except traceback.print_exc() continue if result is not None: self.ns[self.last_result_symbol] = result print(serialize(result))
def _parse_handler(cls, L): if not (is_list(L) and len(L) == 3 and L[0] == Symbol('except')): _err('invalid except form', L) return L[1:3]
def _parse_except(form): check( is_list(form) and len(form) == 3 and form[0] == Symbol('except'), 'invalid except form', form) return form[1:3]