def _merge__args(args): affirm(len(args) > 0, u"Merge takes at least one arg") x = 1 acc = args[0] for x in range(1, len(args)): acc = rt._reduce(acc, merge_fn, args[x]) return acc
def wrapped_fn(self, args): affirm(len(args) == 2, u"Expected 2 arguments to " + fn_name) try: return fn(args[0], args[1]) except object.WrappedException as ex: ex._ex._trace.append(object.NativeCodeInfo(fn_name)) raise
def pop(self): self.ensure_editable() affirm(self._cnt != 0, u"Can't pop an empty vector") i = self._cnt - 1 if i & 0x01f > 0: self._cnt -= 1 return self new_tail = self.editable_array_for(self._cnt - 1) new_root = self.pop_tail(self._shift, self._root) new_shift = self._shift root = self._root assert isinstance(root, Node) if new_root is None: new_root = Node(root._edit) if self._shift > 5 and new_root._array[1] is None: new_root = self.ensure_node_editable(new_root._array[0]) new_shift -= 5 self._root = new_root self._shift = new_shift self._cnt -= 1 self._tail = new_tail return self
def compile_if(form, ctx): form = form.next() affirm(2 <= rt.count(form) <= 3, u"If must have either 2 or 3 forms") test = rt.first(form) form = rt.next(form) then = rt.first(form) form = rt.next(form) els = rt.first(form) ctx.disable_tail_call() compile_form(test, ctx) ctx.bytecode.append(code.COND_BR) ctx.sub_sp(1) sp1 = ctx.sp() cond_lbl = ctx.label() ctx.enable_tail_call() compile_form(then, ctx) ctx.bytecode.append(code.JMP) ctx.sub_sp(1) affirm(ctx.sp() == sp1, u"If branches stacks are unequal " + unicode(str(ctx.sp())) + u", " + unicode(str(sp1))) else_lbl = ctx.label() ctx.mark(cond_lbl) compile_form(els, ctx) ctx.mark(else_lbl)
def get_fn(self, arity): f = self._arities.get(arity, None) if f is not None: return f if self._rest_fn is not None and arity >= self._required_arity: return self._rest_fn affirm(False, u"Wrong number of args to fn")
def deref(self): if self.is_dynamic(self._rev): return self.get_dynamic_value() else: val = self.get_root(self._rev) affirm(val is not undefined, u"Var " + self._name + u" is undefined") return val
def compile_let(form, ctx): form = next(form) bindings = rt.first(form) affirm(isinstance(bindings, PersistentVector), u"Bindings must be a vector") body = next(form) ctc = ctx.can_tail_call ctx.disable_tail_call() binding_count = 0 for i in range(0, rt.count(bindings).int_val(), 2): binding_count += 1 name = rt.nth(bindings, numbers.Integer(i)) affirm(isinstance(name, symbol.Symbol), u"Let locals must be symbols") bind = rt.nth(bindings, numbers.Integer(i + 1)) compile_form(bind, ctx) ctx.add_local(name._str, LetBinding(ctx.sp())) if ctc: ctx.enable_tail_call() while True: compile_form(rt.first(body), ctx) body = rt.next(body) if body is nil: break else: ctx.pop() ctx.bytecode.append(code.POP_UP_N) ctx.sub_sp(binding_count) ctx.bytecode.append(binding_count)
def _nth(self, idx): assert isinstance(self, Array) ival = idx.int_val() if ival < len(self.list()): return self.list()[ival] else: affirm(False, u"Index out of Range")
def resolve(self, s, use_refers=True): import pixie.vm.symbol as symbol affirm(isinstance(s, symbol.Symbol), u"Must resolve symbols") ns = rt.namespace(s) name = rt.name(s) if ns is not None: refer = self._refers.get(ns, None) resolved_ns = None if refer is not None: resolved_ns = refer._namespace if resolved_ns is None: resolved_ns = _ns_registry.get(ns, None) if resolved_ns is None: affirm(False, u"Unable to resolve namespace: " + ns + u" inside namespace " + self._name) else: resolved_ns = self assert isinstance(resolved_ns, Namespace) var = resolved_ns._registry.get(name, None) if var is None and use_refers: for refer_nm in self._refers: refer = self._refers[refer_nm] if name in refer._refer_syms or refer._refer_all: var = refer._namespace.resolve(symbol.Symbol(name), False) if var is not None: return var return None return var
def syntax_quote(form): if isinstance(form, Symbol) and compiler.is_compiler_special(form): ret = rt.list(QUOTE, form) elif isinstance(form, Symbol): if rt.namespace(form) is None and rt.name(form).endswith("#"): gmap = rt.deref(GEN_SYM_ENV) affirm(gmap is not nil, u"Gensym literal used outside a syntax quote") gs = rt.get(gmap, form) if gs is nil: gs = rt.symbol(rt.str(form, rt.wrap(u"__"), rt.gensym())) GEN_SYM_ENV.set_value(rt.assoc(gmap, form, gs)) form = gs else: var = rt.resolve_in(compiler.NS_VAR.deref(), form) if var is nil: form = rt.symbol(rt.str(rt.wrap(rt.name(rt.deref(compiler.NS_VAR))), rt.wrap(u"/"), form)) else: form = rt.symbol(rt.str(rt.wrap(rt.namespace(var)), rt.wrap(u"/"), rt.str(rt.wrap(rt.name(var))))) ret = rt.list(QUOTE, form) elif is_unquote(form): ret = rt.first(rt.next(form)) elif is_unquote_splicing(form): raise Exception("Unquote splicing not used inside list") elif rt.vector_QMARK_(form) is true: ret = rt.list(APPLY, CONCAT, SyntaxQuoteReader.expand_list(form)) elif rt.seq_QMARK_(form) is true: ret = rt.list(APPLY, LIST, rt.cons(CONCAT, SyntaxQuoteReader.expand_list(rt.seq(form)))) else: ret = rt.list(QUOTE, form) return ret
def index_of3(a, sep, start): affirm(isinstance(start, Integer), u"Third argument must be an integer") start = start.int_val() if start >= 0: return rt.wrap(rt.name(a).find(rt.name(sep), start)) else: runtime_error(u"Third argument must be a non-negative integer")
def _extend(proto_fn, tp, fn): if not isinstance(proto_fn, PolymorphicFn): runtime_error(u"Fist argument to extend should be a PolymorphicFn not a " + proto_fn.type().name()) affirm(isinstance(tp, Type) or isinstance(tp, Protocol), u"Second argument to extend must be a Type or Protocol") affirm(isinstance(fn, BaseCode), u"Last argument to extend must be a function") proto_fn.extend(tp, fn) return nil
def intern_or_make(self, name): assert name is not None affirm(isinstance(name, unicode), u"Var names must be unicode") v = self._registry.get(name, None) if v is None: v = Var(self._name, name) self._registry[name] = v return v
def index_of4(a, sep, start, end): affirm(isinstance(start, Integer) and isinstance(end, Integer), u"Third and fourth argument must be integers") start = start.int_val() end = end.int_val() if start >= 0 and end >= 0: return rt.wrap(rt.name(a).find(rt.name(sep), start, end)) else: runtime_error(u"Third and fourth argument must be non-negative integers")
def _nth(self, idx): assert isinstance(self, ByteArray) affirm(isinstance(idx, Integer), u"Index must be an integer") ival = idx.r_uint_val() if 0 <= ival < self._cnt: return rt.wrap(ord(self._buffer[ival])) return nil
def wrap(x): if isinstance(x, int): return numbers.Integer(x) if isinstance(x, unicode): return String(x) #if isinstance(x, str): # return String(unicode(x)) affirm(False, u"Bad wrap")
def new_handler(h, _): fn = global_state._val global_state._val = None h = global_state._th.switch(h) val = global_state._val fn.invoke([StackletHandle(h), val]) affirm(False, u"TODO: What do we do now?") return h
def substring3(a, start, end): affirm(isinstance(a, String), u"First argument must be a string") affirm(isinstance(start, Integer) and isinstance(end, Integer), u"Second and third argument must be integers") start = start.int_val() end = end.int_val() if start >= 0 and end >= 0: return rt.wrap(rt.name(a)[start:end]) else: runtime_error(u"Second and third argument must be non-negative integers")
def nth(self, i, not_found=None): if 0 <= i < self._cnt: node = self.array_for(r_uint(i)) return node[i & 0x01f] if not_found is None: affirm(False, u"Index out of Range") else: return not_found
def resize_list(lst, new_size): """'Resizes' a list, via reallocation and copy""" affirm(len(lst) < new_size, u"New list must be larger than old list") new_list = [None] * new_size i = r_uint(0) while i < len(lst): new_list[i] = lst[i] i += 1 return new_list
def aslice(self, offset): affirm(isinstance(self, Array), u"aset expects an Array as the first argument") affirm(isinstance(offset, Integer), u"aset expects an Integer as the second argument") offset = offset.int_val() if offset >= 0: return Array(self.list()[offset:]) else: rt.throw(rt.wrap(u"offset must be an Integer >= 0"))
def _invoke(self, args): affirm(len(args) == 1, u"Only one arg to continuation allowed") global_state._from = global_state._to global_state._to = self global_state._op = OP_SWITCH global_state._val = args[0] global_state._h = global_state._th.switch(global_state._h) return global_state._val
def invoke(self, args): affirm(not self._used, u"Can only call a given stacklet handle once.") affirm(len(args) == 1, u"Only one arg should be handed to a stacklet handle") self._used = True global_state._val = args[0] new_h = StackletHandle(global_state._th.switch(self._stacklet_handle)) val = global_state._val global_state._val = None return rt.vector(new_h, val)
def invoke(self, args): affirm(len(args) >= 1, u"Wrong number of args") a = args[0].type() fn = self.get_protocol_fn(a, self._rev) try: return fn.invoke(args) except object.WrappedException as ex: ex._ex._trace.append(object.PolymorphicCodeInfo(self._name, args[0].type())) raise
def _run_blocking__args(args): affirm(len(args) > 0, u"At least one arg must be supplied to blocking-call") fn = args[0] argc = len(args) - 1 new_args = [None] * argc for x in range(argc): new_args[x] = args[x + 1] from pixie.vm.stacklet import execute_uv_func return execute_uv_func(RunFFIFunc(fn, new_args))
def create_type(type_name, fields): affirm(isinstance(type_name, Keyword), u"Type name must be a keyword") acc = {} for i in range(rt.count(fields)): val = rt.nth(fields, rt.wrap(i)) affirm(isinstance(val, Keyword), u"Field names must be keywords") acc[val] = i return CustomType(rt.name(type_name), acc)
def invoke(self, args): affirm(len(args) >= 1, u"Wrong number of args") a = args[0].type() fn = self.get_protocol_fn(a, self._rev) try: return fn.invoke(args) except object.WrappedException as ex: ex._ex._trace.append( object.PolymorphicCodeInfo(self._name, args[0].type())) raise
def resolve_ns(self, ns_alias): refer = self._refers.get(ns_alias, None) resolved_ns = None if refer is not None: resolved_ns = refer._namespace if resolved_ns is None: resolved_ns = _ns_registry.get(ns_alias, None) if resolved_ns is None: affirm(False, u"Unable to resolve namespace: " + ns_alias + u" inside namespace " + self._name) return resolved_ns
def type_satisfies(proto, type): affirm(isinstance(type, Type), u"type must be a Type") if proto.satisfies(type): return true elif type == Object._type: # top level type do not recurse return false elif type.parent(): return type_satisfies(proto, type.parent()) else: return false
def index_of4(a, sep, start, end): affirm( isinstance(start, Integer) and isinstance(end, Integer), u"Third and fourth argument must be integers") start = start.int_val() end = end.int_val() if start >= 0 and end >= 0: return rt.wrap(rt.name(a).find(rt.name(sep), start, end)) else: runtime_error( u"Third and fourth argument must be non-negative integers")
def compile_platform_eq(form, ctx): form = form.next() affirm(rt.count(form) == 2, u"TODO: REMOVE") while form is not nil: compile_form(form.first(), ctx) form = form.next() ctx.bytecode.append(code.EQ) ctx.sub_sp(1) return ctx
def invoke(self, args): affirm(not self._used, u"Can only call a given stacklet handle once.") affirm( len(args) == 1, u"Only one arg should be handed to a stacklet handle") self._used = True global_state._val = args[0] new_h = StackletHandle(global_state._th.switch(self._stacklet_handle)) val = global_state._val global_state._val = None return rt.vector(new_h, val)
def compile_ns(form, ctx): affirm(rt.count(form).int_val() == 2, u"ns only takes one argument, a symbol") nm = rt.first(rt.next(form)) affirm(isinstance(nm, symbol.Symbol), u"Namespace name must be a symbol") str_name = rt.name(nm) NS_VAR.set_value(code._ns_registry.find_or_make(str_name)) ctx.push_const(nil)
def invoke(self, args): tp = args[0].type() assert isinstance(tp, object.Type) pfn = self._pfn if isinstance(pfn, PolymorphicFn): protocol = pfn._protocol elif isinstance(pfn, DoublePolymorphicFn): protocol = pfn._protocol else: assert False assert isinstance(protocol, Protocol) affirm(False, u"No override for " + tp._name + u" on " + self._pfn._name + u" in protocol " + protocol._name)
def substring3(a, start, end): affirm(isinstance(a, String), u"First argument must be a string") affirm( isinstance(start, Integer) and isinstance(end, Integer), u"Second and third argument must be integers") start = start.int_val() end = end.int_val() if start >= 0 and end >= 0: return rt.wrap(rt.name(a)[start:end]) else: runtime_error( u"Second and third argument must be non-negative integers")
def compile_var(form, ctx): form = rt.next(form) name = rt.first(form) affirm(isinstance(name, symbol.Symbol), u"var name must be a symbol") if rt.namespace(name) is not None: var = code._ns_registry.find_or_make(rt.namespace(name)) else: var = NS_VAR.deref().intern_or_make(rt.name(name)) ctx.push_const(var)
def compile_ns(form, ctx): affirm(rt.count(form) == 2, u"ns only takes one argument, a symbol") nm = rt.first(rt.next(form)) affirm(isinstance(nm, symbol.Symbol), u"Namespace name must be a symbol") str_name = rt.name(nm) NS_VAR.set_value(code._ns_registry.find_or_make(str_name)) NS_VAR.deref().include_stdlib() ctx.push_const(nil)
def editable_array_for(self, i): if i >= 0 and i < self._cnt: if i >= self.tailoff(): return self._tail node = self._root level = self._shift while level > 0: node = self.ensure_node_editable(node._array[(i >> level) & 0x1f]) level -= 5 return node._array affirm(False, u"Index out of bounds")
def _extend(proto_fn, tp, fn): if not isinstance(proto_fn, PolymorphicFn): runtime_error( u"Fist argument to extend should be a PolymorphicFn not a " + proto_fn.type().name()) affirm( isinstance(tp, Type) or isinstance(tp, Protocol), u"Second argument to extend must be a Type or Protocol") affirm(isinstance(fn, BaseCode), u"Last argument to extend must be a function") proto_fn.extend(tp, fn) return nil
def load_ns(filename): import pixie.vm.string as string import pixie.vm.symbol as symbol if isinstance(filename, symbol.Symbol): affirm( rt.namespace(filename) is None, u"load-file takes a un-namespaced symbol") filename_str = rt.name(filename).replace(u".", u"/") + u".pxi" loaded_ns = code._ns_registry.get(rt.name(filename), None) if loaded_ns is not None: return loaded_ns else: affirm(isinstance(filename, string.String), u"Filename must be string") filename_str = rt.name(filename) paths = rt.deref(rt.deref(rt.load_paths)) f = None for x in range(rt.count(paths)): path_x = rt.nth(paths, rt.wrap(x)) affirm(isinstance(path_x, string.String), u"Contents of load-paths must be strings") full_path = path.join(str(rt.name(path_x)), str(filename_str)) if path.isfile(full_path): f = full_path break if f is None: affirm( False, u"File '" + rt.name(filename) + u"' does not exist in any directory found in load-paths") else: rt.load_file(rt.wrap(f)) return nil
def _throw(ex): from pixie.vm.keyword import keyword if isinstance(ex, RuntimeException): raise WrappedException(ex) if rt._satisfies_QMARK_(IVector, ex): data = rt.nth(ex, rt.wrap(0)) msg = rt.nth(ex, rt.wrap(1)) elif rt._satisfies_QMARK_(ILookup, ex): data = rt._val_at(ex, keyword(u"data"), nil) msg = rt._val_at(ex, keyword(u"msg"), nil) else: affirm(False, u"Can only throw vectors, maps and exceptions") return nil raise WrappedException(RuntimeException(msg, data))
def array_for(self, i): if 0 <= i < self._cnt: if i >= self.tailoff(): return self._tail node = self._root level = self._shift while level > 0: assert isinstance(node, Node) node = node._array[(i >> level) & 0x01f] level -= 5 return node._array affirm(False, u"Index out of Range")
def deref(self): if self.is_dynamic(): if we_are_translated(): return self.get_dynamic_value() else: ## NOT RPYTHON if globals().has_key("_dynamic_vars"): return self.get_dynamic_value() else: return self.get_root(self._rev) else: val = self.get_root(self._rev) affirm(val is not undefined, u"Var " + self._name + u" is undefined") return val
def compile_fn_body(name, args, body, ctx): new_ctx = Context(rt.name(name), rt.count(args), ctx) required_args = add_args(rt.name(name), args, new_ctx) bc = 0 if name is not None: affirm(isinstance(name, symbol.Symbol), u"Function names must be symbols") #new_ctx.add_local(name._str, Self()) arg_syms = EMPTY for x in range(rt.count(args)): sym = rt.nth(args, rt.wrap(x)) if not rt.name(sym) == u"&": arg_syms = rt.conj(rt.conj(arg_syms, sym), sym) body = rt.list(rt.cons(LOOP, rt.cons(arg_syms, body))) #new_ctx.push_recur_point(FunctionRecurPoint()) new_ctx.disable_tail_call() if body is nil: compile_form(body, new_ctx) else: while body is not nil: if rt.next(body) is nil: new_ctx.enable_tail_call() compile_form(rt.first(body), new_ctx) body = rt.next(body) if body is not nil: new_ctx.pop() new_ctx.bytecode.append(code.RETURN) closed_overs = new_ctx.closed_overs if len(closed_overs) == 0: ctx.push_const(new_ctx.to_code(required_args)) else: ctx.push_const(new_ctx.to_code(required_args)) for x in closed_overs: x.emit(ctx) ctx.bytecode.append(code.MAKE_CLOSURE) ctx.bytecode.append(r_uint(len(closed_overs))) ctx.sub_sp(len(closed_overs)) if required_args >= 0: ctx.bytecode.append(code.MAKE_VARIADIC) ctx.bytecode.append(r_uint(required_args)) return required_args, intmask(rt.count(args))
def hashmap__args(args): affirm(len(args) & 0x1 == 0, u"hashmap requires even number of args") idx = 0 acc = EMPTY while idx < len(args): key = args[idx] val = args[idx + 1] acc = acc.assoc(key, val) idx += 2 return acc
def make_multi_arity(frame, argc): d = {} required_arity = 0 rest_fn = None for i in range(argc): a = frame.get_inst() if a & 256: affirm(rest_fn is None, u"Can't have multiple rest_fns") required_arity = a & 0xFF rest_fn = frame.pop() else: fn = frame.pop() d[a] = fn return code.MultiArityFn(d, required_arity, rest_fn)
def add_args(name, args, ctx): required_args = -1 local_idx = 0 ctx.add_local(name, Self()) for x in range(rt.count(args)): arg = rt.nth(args, rt.wrap(x)) affirm(isinstance(arg, symbol.Symbol), u"Argument names must be symbols") if rt.name(arg) == u"&": required_args = intmask(x) continue ctx.add_local(rt.name(arg), Arg(local_idx)) local_idx += 1 return required_args
def invoke_with(self, args, self_fn): from pixie.vm.array import array argc = len(args) if self._required_arity == 0: return self._code.invoke_with([array(args)], self_fn) if argc == self._required_arity: new_args = resize_list(args, len(args) + 1) new_args[len(args)] = array([]) return self._code.invoke_with(new_args, self_fn) elif argc > self._required_arity: start = slice_from_start(args, self._required_arity, 1) rest = slice_to_end(args, self._required_arity) start[self._required_arity] = array(rest) return self._code.invoke_with(start, self_fn) affirm(False, u"Got " + unicode(str(argc)) + u" arg(s) need at least " + unicode(str(self._required_arity)))
def get_fn(self, arity): f = self._arities.get(arity, None) if f is not None: return f if self._rest_fn is not None and arity >= self._required_arity: return self._rest_fn acc = [] for x in self._arities: acc.append(unicode(str(x))) if self._rest_fn: acc.append(u" or more") affirm( False, u"Wrong number of args to fn: got " + unicode(str(arity)) + u" expected " + u",".join(acc))
def _load_file(filename, compile=False): from pixie.vm.string import String from pixie.vm.util import unicode_from_utf8 import pixie.vm.reader as reader import pixie.vm.libs.pxic.writer as pxic_writer affirm(isinstance(filename, String), u"filename must be a string") filename = str(rt.name(filename)) if filename.endswith(".pxic"): load_pxic_file(filename) return nil if path.isfile(filename + "c") and not compile: load_pxic_file(filename + "c") return nil affirm(path.isfile(filename), unicode(filename) + u" does not exist") f = open(filename) data = f.read() f.close() if data.startswith("#!"): newline_pos = data.find("\n") if newline_pos > 0: data = data[newline_pos:] if compile: pxic_f = open(filename + "c", "wb") wtr = pxic_writer.Writer(pxic_f, True) with code.bindings(PXIC_WRITER, pxic_writer.WriterBox(wtr)): rt.load_reader( reader.MetaDataReader( reader.StringReader(unicode_from_utf8(data)), unicode(filename))) wtr.finish() pxic_f.close() else: with code.bindings(PXIC_WRITER, nil): rt.load_reader( reader.MetaDataReader( reader.StringReader(unicode_from_utf8(data)), unicode(filename))) return nil
def compile_def(form, ctx): form = rt.next(form) name = rt.first(form) form = rt.next(form) val = rt.first(form) affirm(isinstance(name, symbol.Symbol), u"Def'd name must be a symbol") var = NS_VAR.deref().intern_or_make(rt.name(name)) if rt._val_at(rt.meta(name), DYNAMIC_KW, nil) is true: assert isinstance(var, code.Var) var.set_dynamic() ctx.push_const(var) compile_form(val, ctx) ctx.bytecode.append(code.SET_VAR) ctx.sub_sp(1)
def invoke(self, rdr, ch): nms = u"" ch = rdr.read() if ch == u":": itm = read_inner(rdr, True) nms = rt.name(rt.ns.deref()) else: rdr.unread() itm = read_inner(rdr, True) affirm(isinstance(itm, Symbol), u"Can't keyword quote a non-symbol") if nms: affirm( rt.namespace(itm) is None, u"Kewyword cannot have two namespaces") return keyword(rt.name(itm), nms) else: return keyword(rt.name(itm), rt.namespace(itm))
def ns_aliases(ns): from pixie.vm.symbol import Symbol affirm( isinstance(ns, Namespace) or isinstance(ns, Symbol), u"ns must be a symbol or a namespace") if isinstance(ns, Symbol): ns = rt.the_ns(ns) if ns is nil: return nil if isinstance(ns, Namespace): m = rt.hashmap() for alias in ns._refers: refered_ns = ns._refers[alias]._namespace m = rt.assoc(m, rt.symbol(rt.wrap(alias)), refered_ns) return m return nil
def ns_map(ns): from pixie.vm.symbol import Symbol affirm( isinstance(ns, Namespace) or isinstance(ns, Symbol), u"ns must be a symbol or a namespace") if isinstance(ns, Symbol): ns = rt.the_ns(ns) if ns is nil: return nil if isinstance(ns, Namespace): m = rt.hashmap() for name in ns._registry: var = ns._registry.get(name, nil) m = rt.assoc(m, rt.symbol(rt.wrap(name)), var) return m return nil
def _div(n, d): assert isinstance(n, Integer) and isinstance(d, Integer) nv = n.int_val() dv = d.int_val() object.affirm(dv != 0, u"Divide by zero") g = gcd(nv, dv) if g == 0: return rt.wrap(0) nv = nv / g dv = dv / g if dv == 1: return rt.wrap(nv) elif dv == -1: return rt.wrap(-1 * nv) else: if dv < 0: nv = nv * -1 dv = dv * -1 return Ratio(nv, dv)