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 _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 c_struct(name, size, spec): d = {} for x in range(rt.count(spec)): row = rt.nth(spec, rt.wrap(x)) nm = rt.nth(row, rt.wrap(0)) tp = rt.nth(row, rt.wrap(1)) offset = rt.nth(row, rt.wrap(2)) affirm(isinstance(nm, Keyword), u"c-struct field names must be keywords") if not isinstance(tp, CType): runtime_error(u"c-struct field types must be c types, got: " + rt.name(rt.str(tp))) d[nm] = (tp, offset.int_val()) return CStructType(rt.name(name), size.int_val(), d)
def _compare(self, frm, to): if isinstance(to, tuple): assert isinstance(frm, Cons) for x in to: self._compare(frm.first(), x) frm = frm.next() assert frm == nil elif isinstance(to, int): assert isinstance(frm, Integer) assert frm._int_val == to elif isinstance(to, Symbol): assert isinstance(frm, Symbol) assert frm._str == to._str elif isinstance(to, list): assert isinstance(frm, PersistentVector) for x in range(max(len(to), rt._count(frm)._int_val)): self._compare(rt.nth(frm, rt.wrap(x)), to[x]) elif isinstance(to, dict): assert isinstance(frm, PersistentHashMap) for key in dict.keys(to): self._compare(frm.val_at(rt.wrap(key), ""), to[key]) assert rt._count(frm)._int_val == len(dict.keys(to)) elif isinstance(to, Character): assert isinstance(frm, Character) assert to._char_val == frm._char_val else: raise Exception("Don't know how to handle " + str(type(to)))
def load_lib(self): if not self._is_inited: load_paths = rt.deref(rt.deref(rt.load_paths)) for x in range(rt.count(load_paths)): s = rffi.str2charp( str(rt.name(rt.nth(load_paths, rt.wrap(x)))) + "/" + str(self._name)) try: self._dyn_lib = dynload.dlopen(s) self._is_inited = True except dynload.DLOpenError as ex: continue finally: rffi.free_charp(s) break if not self._is_inited: s = rffi.str2charp(str(self._name)) try: self._dyn_lib = dynload.dlopen(s) self._is_inited = True except dynload.DLOpenError as ex: raise object.runtime_error( u"Couldn't Load Library: " + self._name, u"pixie.stdlib/LibraryNotFoundException") finally: rffi.free_charp(s)
def load_ns(filename): import pixie.vm.string as string import pixie.vm.symbol as symbol import os.path as path 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 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 load_lib(self): if not self._is_inited: load_paths = rt.deref(rt.deref(rt.load_paths)) for x in range(rt.count(load_paths)): s = rffi.str2charp(str(rt.name(rt.nth(load_paths, rt.wrap(x)))) + "/" + str(self._name)) try: self._dyn_lib = dynload.dlopen(s) self._is_inited = True except dynload.DLOpenError as ex: continue finally: rffi.free_charp(s) break if not self._is_inited: s = rffi.str2charp(str(self._name)) try: self._dyn_lib = dynload.dlopen(s) self._is_inited = True except dynload.DLOpenError as ex: raise object.runtime_error(u"Couldn't Load Library: " + self._name, u"pixie.stdlib/LibraryNotFoundException") finally: rffi.free_charp(s)
def _ffi_fn__args(args): affirm(len(args) >= 4, u"ffi-fn requires at least 4 arguments") lib, nm, arg_types, ret_type = args[:4] affirm(isinstance(lib, ExternalLib), u"First argument must be an ExternalLib") affirm(isinstance(ret_type, object.Type), u"Ret type must be a type") affirm(rt.namespace(nm) is None, u"Name must not be namespaced") cnt = rt.count(arg_types) new_args = [None] * cnt for x in range(cnt): t = rt.nth(arg_types, rt.wrap(x)) affirm(isinstance(t, object.Type), u"Arg defs must be types") new_args[x] = t kwargs = args[4:] affirm(len(kwargs) & 0x1 == 0, u"ffi-fn requires even number of options") is_variadic = False for i in range(0, len(kwargs)/2, 2): key = kwargs[i] val = kwargs[i+1] affirm(isinstance(key, Keyword), u"ffi-fn options should be keyword/bool pairs") affirm(val is true or val is false, u"ffi-fn options should be keyword/bool pairs") k = rt.name(key) if k == u"variadic?": is_variadic = True if val is true else False else: affirm(False, u"unknown ffi-fn option: :" + k) f = FFIFn(lib, rt.name(nm), new_args, ret_type, is_variadic) return f
def compile_form(form, ctx): if form is nil: ctx.push_const(nil) return if rt.instance_QMARK_(rt.ISeq.deref(), form) and form is not nil: return compile_cons(form, ctx) if isinstance(form, numbers.Integer): ctx.push_const(form) return if isinstance(form, symbol.Symbol): name = form._str loc = resolve_local(ctx, name) if loc is None: var = resolve_var(ctx, form) if var is None: var = NS_VAR.deref().intern_or_make(name) ctx.push_const(var) ctx.bytecode.append(code.DEREF_VAR) return loc.emit(ctx) return if isinstance(form, Bool) or form is nil: ctx.push_const(form) return if isinstance(form, Keyword): ctx.push_const(form) return if isinstance(form, PersistentVector): vector_var = rt.vector() size = rt.count(form).int_val() #assert rt.count(form).int_val() == 0 ctx.push_const(code.intern_var(u"pixie.stdlib", u"vector")) for x in range(size): compile_form(rt.nth(form, numbers.Integer(x)), ctx) ctx.bytecode.append(code.INVOKE) ctx.bytecode.append(r_uint(size + 1)) ctx.sub_sp(size) return if rt.instance_QMARK_(rt.IMap.deref(), form): compile_map_literal(form, ctx) return if isinstance(form, String): ctx.push_const(form) return raise Exception("Can't compile ")
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 compile_local_macro(form, ctx): form = rt.next(form) binding = rt.first(form) body = rt.next(form) sym = rt.nth(binding, rt.wrap(0)) bind_form = rt.nth(binding, rt.wrap(1)) ctx.add_local(rt.name(sym), LocalMacro(bind_form)) while True: compile_form(rt.first(body), ctx) body = rt.next(body) if body is nil: break else: ctx.pop() ctx.pop_locals()
def c_struct(name, size, spec): """(c-struct name size spec) Creates a CStruct named name, of size size, with the given spec. Spec is a vector of vectors. Each row of the format [field-name type offset]""" d = {} for x in range(rt.count(spec)): row = rt.nth(spec, rt.wrap(x)) nm = rt.nth(row, rt.wrap(0)) tp = rt.nth(row, rt.wrap(1)) offset = rt.nth(row, rt.wrap(2)) affirm(isinstance(nm, Keyword), u"c-struct field names must be keywords") #if not isinstance(tp, CType): # runtime_error(u"c-struct field types must be c types, got: " + rt.name(rt.str(tp))) d[nm] = (tp, offset.int_val()) tp = CStructType(rt.name(name), size.int_val(), d) proto._dispose_BANG_.extend(tp, _dispose_cstruct) return tp
def compile_loop(form, ctx): form = rt.next(form) bindings = rt.first(form) affirm(isinstance(bindings, PersistentVector), u"Loop bindings must be a vector") body = rt.next(form) ctx.enable_tail_call() ctc = ctx.can_tail_call ctx.disable_tail_call() binding_count = 0 for i in range(0, rt.count(bindings), 2): binding_count += 1 name = rt.nth(bindings, rt.wrap(i)) affirm(isinstance(name, symbol.Symbol), u"Loop bindings must be symbols") bind = rt.nth(bindings, rt.wrap(i + 1)) compile_form(bind, ctx) ctx.add_local(rt.name(name), LetBinding(ctx.sp())) if ctc: ctx.enable_tail_call() ctx.push_recur_point(LoopRecurPoint(binding_count, ctx)) while True: compile_form(rt.first(body), ctx) body = rt.next(body) if body is nil: break else: ctx.pop() ctx.pop_recur_point() ctx.bytecode.append(code.POP_UP_N) ctx.sub_sp(binding_count) ctx.bytecode.append(binding_count) ctx.pop_locals(binding_count)
def add_args(args, ctx): required_args = -1 local_idx = 0 for x in range(rt.count(args).int_val()): arg = rt.nth(args, numbers.Integer(x)) affirm(isinstance(arg, symbol.Symbol), u"Argument names must be symbols") if arg._str == u"&": required_args = x continue ctx.add_local(arg._str, Arg(local_idx)) local_idx += 1 return required_args
def compile_loop(form, ctx): form = rt.next(form) bindings = rt.first(form) affirm(isinstance(bindings, PersistentVector), u"Loop bindings must be a vector") body = rt.next(form) ctc = ctx.can_tail_call ctx.disable_tail_call() binding_count = 0 for i in range(0, rt.count(bindings), 2): binding_count += 1 name = rt.nth(bindings, rt.wrap(i)) affirm(isinstance(name, symbol.Symbol), u"Loop must bindings must be symbols") bind = rt.nth(bindings, rt.wrap(i + 1)) compile_form(bind, ctx) ctx.add_local(rt.name(name), LetBinding(ctx.sp())) if ctc: ctx.enable_tail_call() ctx.push_recur_point(LoopRecurPoint(binding_count, ctx)) while True: compile_form(rt.first(body), ctx) body = rt.next(body) if body is nil: break else: ctx.pop() ctx.pop_recur_point() ctx.bytecode.append(code.POP_UP_N) ctx.sub_sp(binding_count) ctx.bytecode.append(binding_count) ctx.pop_locals(binding_count)
def _ffi_fn(lib, nm, args, ret_type): affirm(isinstance(lib, ExternalLib), u"First argument must be an ExternalLib") affirm(isinstance(ret_type, object.Type), u"Ret type must be a type") affirm(rt.namespace(nm) is None, u"Name must not be namespaced") cnt = rt.count(args) new_args = [None] * cnt for x in range(cnt): t = rt.nth(args, rt.wrap(x)) affirm(isinstance(t, object.Type), u"Arg defs must be types") new_args[x] = t f = FFIFn(lib, rt.name(nm), new_args, ret_type) return f
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 arg._str == u"&": required_args = intmask(x) continue ctx.add_local(arg._str, Arg(local_idx)) local_idx += 1 return required_args
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 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 load_file(filename): import pixie.vm.reader as reader import pixie.vm.compiler as compiler import pixie.vm.string as string import pixie.vm.symbol as symbol import os.path as path 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".lisp" 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 = open(str(full_path)) break if f is None: affirm(False, u"File does not exist in any directory found in load-paths") else: data = f.read() f.close() if data.startswith("#!"): newline_pos = data.find("\n") if newline_pos > 0: data = data[newline_pos:] rdr = reader.StringReader(unicode(data)) with compiler.with_ns(u"user"): while True: form = reader.read(rdr, False) if form is reader.eof: return nil result = compiler.compile(form).invoke([]) return nil
def ffi_callback(args, ret_type): """(ffi-callback args ret-type) Creates a ffi callback type. Args is a vector of CType args. Ret-type is the CType return type of the callback. Returns a ffi callback type that can be used with ffi-prep-callback.""" args_w = [None] * rt.count(args) for x in range(rt.count(args)): arg = rt.nth(args, rt.wrap(x)) if not isinstance(arg, object.Type): runtime_error(u"Expected type, got " + rt.name(rt.str(arg))) args_w[x] = arg if not isinstance(ret_type, object.Type): runtime_error(u"Expected type, got " + rt.name(rt.str(ret_type))) return CFunctionType(args_w, ret_type)
def _ffi_fn__args(args): affirm(len(args) >= 4, u"ffi-fn requires at least 4 arguments") lib, nm, arg_types, ret_type = args[:4] affirm(isinstance(lib, ExternalLib), u"First argument must be an ExternalLib") affirm(isinstance(ret_type, object.Type), u"Ret type must be a type") affirm(rt.namespace(nm) is None, u"Name must not be namespaced") cnt = rt.count(arg_types) new_args = [None] * cnt for x in range(cnt): t = rt.nth(arg_types, rt.wrap(x)) affirm(isinstance(t, object.Type), u"Arg defs must be types") new_args[x] = t kwargs = args[4:] affirm(len(kwargs) & 0x1 == 0, u"ffi-fn requires even number of options") is_variadic = False for i in range(0, len(kwargs) / 2, 2): key = kwargs[i] val = kwargs[i + 1] affirm(isinstance(key, Keyword), u"ffi-fn options should be keyword/bool pairs") affirm(val is true or val is false, u"ffi-fn options should be keyword/bool pairs") k = rt.name(key) if k == u"variadic?": is_variadic = True if val is true else False else: affirm(False, u"unknown ffi-fn option: :" + k) tp = CFunctionType(new_args, ret_type, is_variadic) nm = rt.name(nm) f = FFIFn(nm, lib.get_fn_ptr(nm), tp) return f
def _compare(self, frm, to): if isinstance(to, tuple): assert isinstance(frm, Cons) for x in to: self._compare(frm.first(), x) frm = frm.next() elif isinstance(to, int): assert isinstance(frm, Integer) assert frm._int_val == to elif isinstance(to, Symbol): assert isinstance(frm, Symbol) assert frm._str == to._str elif isinstance(to, list): assert isinstance(frm, PersistentVector) for x in range(len(to)): self._compare(rt.nth(frm, rt.wrap(x)), to[x]) else: raise Exception("Don't know how to handle " + str(type(to)))
def compile_form(form, ctx): if form is nil: ctx.push_const(nil) return if rt._satisfies_QMARK_(rt.ISeq.deref(), form) and form is not nil: form = maybe_oop_invoke(form) return compile_cons(form, ctx) if isinstance(form, numbers.Integer): ctx.push_const(form) return if isinstance(form, numbers.BigInteger): ctx.push_const(form) return if isinstance(form, numbers.Float): ctx.push_const(form) return if isinstance(form, numbers.Ratio): ctx.push_const(form) return if isinstance(form, symbol.Symbol): name = rt.name(form) ns = rt.namespace(form) loc = resolve_local(ctx, name) var = resolve_var(form) if var is None and loc: loc.emit(ctx) return if var and loc and ns is None: loc.emit(ctx) return if var is None: name = rt.name(form) var = NS_VAR.deref().intern_or_make(name) ctx.push_const(var) meta = rt.meta(form) if meta is not nil: ctx.debug_points[len( ctx.bytecode)] = rt.interpreter_code_info(meta) ctx.bytecode.append(code.DEREF_VAR) return if isinstance(form, Bool) or form is nil: ctx.push_const(form) return if isinstance(form, Keyword): ctx.push_const(form) return if isinstance(form, PersistentVector): size = rt.count(form) #assert rt.count(form).int_val() == 0 ctx.push_const(code.intern_var(u"pixie.stdlib", u"vector")) for x in range(size): compile_form(rt.nth(form, rt.wrap(x)), ctx) ctx.bytecode.append(code.INVOKE) ctx.bytecode.append(r_uint(size + 1)) ctx.sub_sp(size) compile_meta(rt.meta(form), ctx) return if isinstance(form, PersistentHashSet): compile_set_literal(form, ctx) return if rt._satisfies_QMARK_(rt.IMap.deref(), form): compile_map_literal(form, ctx) return if isinstance(form, String): ctx.push_const(form) return if isinstance(form, Character): ctx.push_const(form) return raise Exception("Can't compile ")
def reduce(self, f, init): for x in range(self._idx, rt.count(self._w_array)): if rt.reduced_QMARK_(init): return rt.deref(init) init = f.invoke([init, rt.nth(self._w_array, rt.wrap(x))]) return init
def first(self): return rt.nth(self._w_array, rt.wrap(self._idx))
def compile_form(form, ctx): if form is nil: ctx.push_const(nil) return if rt._satisfies_QMARK_(rt.ISeq.deref(), form) and form is not nil: form = macroexpand(form) return compile_cons(form, ctx) if isinstance(form, numbers.Integer): ctx.push_const(form) return if isinstance(form, numbers.BigInteger): ctx.push_const(form) return if isinstance(form, numbers.Float): ctx.push_const(form) return if isinstance(form, numbers.Ratio): ctx.push_const(form) return if isinstance(form, symbol.Symbol): name = rt.name(form) ns = rt.namespace(form) loc = resolve_local(ctx, name) var = resolve_var(ctx, form) if var is None and loc: loc.emit(ctx) return if var and loc and ns is None: loc.emit(ctx) return if var is None: name = rt.name(form) var = NS_VAR.deref().intern_or_make(name) ctx.push_const(var) meta = rt.meta(form) if meta is not nil: ctx.debug_points[len(ctx.bytecode)] = rt.interpreter_code_info(meta) ctx.bytecode.append(code.DEREF_VAR) return if isinstance(form, Bool) or form is nil: ctx.push_const(form) return if isinstance(form, Keyword): ctx.push_const(form) return if isinstance(form, PersistentVector): vector_var = rt.vector() size = rt.count(form) #assert rt.count(form).int_val() == 0 ctx.push_const(code.intern_var(u"pixie.stdlib", u"vector")) for x in range(size): compile_form(rt.nth(form, rt.wrap(x)), ctx) ctx.bytecode.append(code.INVOKE) ctx.bytecode.append(r_uint(size + 1)) ctx.sub_sp(size) compile_meta(rt.meta(form), ctx) return if isinstance(form, PersistentHashSet): compile_set_literal(form, ctx) return if rt._satisfies_QMARK_(rt.IMap.deref(), form): compile_map_literal(form, ctx) return if isinstance(form, String): ctx.push_const(form) return if isinstance(form, Character): ctx.push_const(form) return raise Exception("Can't compile ")
def ratio_read(obj): return Ratio( rt.nth(obj, rt.wrap(0)).int_val(), rt.nth(obj, rt.wrap(1)).int_val())
def ratio_read(obj): return Ratio(rt.nth(obj, rt.wrap(0)).int_val(), rt.nth(obj, rt.wrap(1)).int_val())