def try_block(ctx, list_w): _, w_body, w_catch, w_finally = list_w fn_code = fn(ctx, unwrap(w_body)) if w_catch is w_nil: catch_code = [] else: catch_code = fn(ctx, unwrap(w_catch)) + [ops.INVOKE, 1] finally_id = fn(ctx, unwrap(w_finally))[1] if w_finally is not w_nil else -1 return fn_code + [ops.TRY, finally_id, 1 + len(catch_code)] + catch_code
def emit_quasiquoted_list(ctx, w_val, recur): list_w = unwrap(w_val) if is_unquote(list_w): code = emit(ctx, list_w[1]) return code else: code = [] list = unwrap(w_val) for w_elem in list: code += recur(ctx, w_elem) + [ops.PUSH] return code + [ops.SYM, ctx.st().add_sym("list"), ops.INVOKE, len(list)]
def mkfn(ctx, w_args, body_w): args = [] rest_args_id = -1 arg_vec_w = unwrap(cast(w_args, W_Seq)) idx = 0 st = ctx.st() while idx < len(arg_vec_w): w_arg = cast(arg_vec_w[idx], W_Sym) if w_arg.val == '&': assert idx == len(arg_vec_w) - 2 w_last = cast(arg_vec_w[idx + 1], W_Sym) rest_args_id = st.add_sym(w_last.val) break else: args.append(w_arg.val) idx += 1 ids = [] for arg in args: id = st.add_sym(arg) ids.append(id) if len(ids) > 0 or rest_args_id >= 0: recur_bindings = (ids, rest_args_id) else: recur_bindings = empty_recur_bindings code = [] for w_elem in body_w: code += emit(ctx, w_elem, recur_bindings) return st.add_fn(W_Fun(code, ids, rest_args_id))
def invoke(self, args, ctx): form = args[0] if not isinstance(form, space.W_List): return form head = form.first() if not isinstance(head, space.W_Sym): return form st = ctx.st() val = head.val if not st.has_macro(val): return form macro_fn = st.get_fn(st.get_macro(val)) return eval.invoke_fn(macro_fn, space.unwrap(form.rest()), ctx)
def apply(ip, env, code, r1, stack, sp, ctx): ip += 1 assert code[ip] == 2 new_sp = sp - 2 assert new_sp >= 0 (fn, w_args) = stack[new_sp : sp] sp = new_sp args = space.unwrap(w_args) argc = len(args) if isinstance(fn, space.W_Fun): arg_ids_len = len(fn.arg_ids) if fn.env.on_stack: new_env = Env([], fn.env) else: new_env = fn.env try: new_env.mark_used() if (argc < arg_ids_len or (argc > arg_ids_len and not fn.got_rest_args())): raise space.ArityException(argc, arg_ids_len) if argc > arg_ids_len and fn.got_rest_args(): rest_args = [args.pop() for _ in range(argc - arg_ids_len)] rest_args.reverse() new_env.set(fn.rest_args_id, space.wrap(rest_args)) elif fn.got_rest_args(): new_env.set(fn.rest_args_id, space.w_empty_list) idx = 0 for arg_id in fn.arg_ids: new_env.set(arg_id, args[idx]) idx += 1 return eval(ctx, new_env, fn.code) finally: new_env.mark_free() else: ret = fn.invoke(args, ctx) if ret is None: ret = space.w_nil return ret
def emit_list(ctx, node, recur_bindings): st = ctx.st() list_w = unwrap(node) if len(list_w) == 0: return [ops.SYM, st.add_sym("list"), ops.INVOKE, 0] w_head = list_w[0] args_w = list_w[1:] if isinstance(w_head, W_Sym): head = w_head.val if head == "if": cond_code = emit(ctx, list_w[1]) true_code = emit(ctx, list_w[2], recur_bindings) false_code = emit(ctx, list_w[3], recur_bindings) jmp_code = [ops.RELJMP, len(false_code)] code = (cond_code + [ops.IF, len(true_code) + len(jmp_code)] + true_code + jmp_code + false_code) return code elif head == "let*": sym = cast(list_w[1], W_Sym) binding_code = emit(ctx, list_w[2]) id = st.add_sym(sym.val) inner_code = emit(ctx, list_w[3], recur_bindings) code = binding_code + [ops.PUSH_ENV, id] + inner_code + [ops.POP_ENV] return code elif head == "do": body_code = [] for step in list_w[1:]: body_code += emit(ctx, step, recur_bindings) return body_code elif head == "quote": return emit_quote(ctx, list_w[1]) elif head == "qquote*": return emit_quasiquote(ctx, list_w[1]) elif head == "fn*": return fn(ctx, list_w) elif head == "defmacro*": return defmacro(ctx, list_w) elif head == "def*": var_sym = cast(list_w[1], W_Sym) id = st.add_sym(var_sym.val) val_code = emit(ctx, list_w[2]) return val_code + [ops.DEF, id] elif head == "try*": return try_block(ctx, list_w) elif head == "recur": args = list_w[1:] args_code = [] (ids, rest_id) = recur_bindings if len(args) < len(ids): msg = "Invalid number of recur arguments: %s given, %s expected" raise CompilationException(msg % (len(args), len(ids))) for arg in args: c = emit(ctx, arg) args_code += c + [ops.PUSH] if recur_bindings == no_recur_bindings: raise CompilationException("Not a recur point :o") bindings_ids = [x for x in reversed(ids)] + \ [rest_id if rest_id >= 0 else -1] return args_code + [ops.RECUR, len(args)] + bindings_ids elif st.has_macro(head): return expand_macro(ctx, st.get_macro(head), list_w[1:], recur_bindings) elif head == "apply": args_code = [] for arg in args_w: c = emit(ctx, arg) args_code += c + [ops.PUSH] return args_code + [ops.APPLY, len(args_w)] args_code = [] for arg in args_w: c = emit(ctx, arg) args_code += c + [ops.PUSH] fn_code = emit(ctx, w_head) return args_code + fn_code + [ops.INVOKE, len(args_w)]
def emit_quoted_list(ctx, w_val, recur): code = [] list = unwrap(w_val) for w_elem in list: code += recur(ctx, w_elem) + [ops.PUSH] return code + [ops.SYM, ctx.st().add_sym("list"), ops.INVOKE, len(list)]
def try_block(ctx, list_w): _, w_body, w_catch = list_w fn_code = fn(ctx, unwrap(w_body)) catch_code = fn(ctx, unwrap(w_catch)) return fn_code + [ops.TRY, 3 + len(catch_code)] + catch_code + [ops.INVOKE, 1]