def test_recursive_cons(self): a = cons(1, 2, 3, recursive=True) self.assertTrue(a.is_proper()) self.assertTrue(a.is_recursive()) self.assertTrue(is_pair(a)) self.assertTrue(is_proper(a)) self.assertEqual(a.length(), 3) self.assertEqual(car(a), car(cdr(cdr(cdr(a))))) self.assertEqual(str(a), "(1 2 3 ...)") self.assertEqual(repr(a), "cons(1, 2, 3, recursive=True)") b = pair(0, a) c = pair(0, a) self.assertEqual(b, c) self.assertNotEqual(a, b) self.assertNotEqual(a, c) z = cons(1, cons(2, cons(3, nil))) setcdr(cdr(cdr(z)), z) self.assertEqual(a, z) self.assertEqual(z, a)
def test_nil(self): # singleton nil check Nil = type(nil) self.assertEqual(id(nil), id(Nil())) self.assertEqual(id(Nil()), id(Nil())) self.assertTrue(nil is Nil()) # behavior self.assertIsInstance(nil, pair) self.assertTrue(is_pair(nil)) self.assertTrue(is_nil(nil)) self.assertTrue(is_proper(nil)) self.assertFalse(nil) self.assertTrue(not nil) self.assertEqual(str(nil), "nil") self.assertEqual(repr(nil), "nil") self.assertEqual(nil, nil) self.assertNotEqual(nil, cons(1, nil)) self.assertNotEqual(cons(1, nil), nil) with self.assertRaises(TypeError): car(nil) with self.assertRaises(TypeError): cdr(nil) self.assertEqual(list(nil), list()) self.assertEqual(tuple(nil), tuple())
def test_improper_cons(self): z = cons(1, 2) self.assertEqual(z.length(), 2) self.assertFalse(z.is_proper()) self.assertEqual(car(z), 1) self.assertEqual(cdr(z), 2) self.assertEqual(str(z), "(1 . 2)") self.assertEqual(repr(z), "cons(1, 2)") self.assertTrue(is_pair(z)) self.assertFalse(is_proper(z)) self.assertNotEqual(z, None) self.assertNotEqual(z, nil) self.assertNotEqual(z, cons(1, nil)) self.assertNotEqual(z, cons(1, 3)) self.assertNotEqual(z, cons(1, 3, nil))
def find_expander(self, source_obj, env=None): """ Find an expander function for the given source obj in the specified environment. """ env = self.env if env is None else env if not isinstance(env, Mapping): env = vars(env) expander = None if source_obj is nil: return None elif is_symbol(source_obj): namesym = source_obj found = self.find_compiled(namesym, env) if is_alias(found): expander = found.expand elif is_proper(source_obj): namesym, params = source_obj if is_symbol(namesym): found = self.find_compiled(namesym, env) if is_alias(found): def expander(): expanded = cons(found.expand(), params) expanded.set_position(source_obj.get_position()) return expanded elif is_macro(found): if found._proper: # FIXME: this is some garbage right here. we need # to make sure macros aren't being invoked this # way with a variadic. position = params.get_position() args, kwargs = simple_parameters(params, position) expander = partial(found.expand, *args, **kwargs) else: expander = partial(found.expand, *params.unpack()) return expander
def compile_pair(self, source_obj: pair, tc, cont): """ Compile a pair expression. This will become either a literal nil, a macro expansion, a special invocation, or a runtime function application. """ if is_nil(source_obj): return tcf(self.compile_nil, source_obj, tc, cont) if not is_proper(source_obj): # print("** WUT", self, source_obj, tc, cont) msg = "cannot evaluate improper lists as expressions" raise self.error(msg, source_obj) self.pseudop_position_of(source_obj) head, tail = source_obj if is_symbol(head) or is_lazygensym(head): comp = self.find_compiled(head) if comp: # the head of the pair is a symbolic reference which # resolved to a compile-time object. Invoke that. return tcf(comp.compile, self, source_obj, tc, cont) else: return tcf(self.compile_apply, source_obj, tc, cont) elif is_pair(head): return tcf(self.compile_apply, source_obj, tc, cont) else: # TODO: should this be a compile-time error? If we have # something that isn't a symbolic reference or isn't a # pair, then WTF else would it be? Let's just let it break # at runtime, for now. return tcf(self.compile_apply, source_obj, tc, cont)
def gather_formals(args, declared_at=None, filename=None): """ parses formals pair args into five values: (positional, keywords, defaults, stararg, starstararg) - positional is a list of symbols defining positional arguments - defaults is a list of keywords and expr pairs defining keyword arguments and their default value expression - kwonly is a list of keywords and expr pairs which are keyword-only arguments and theid default value expression - stararg is a symbol for variadic positional arguments - starstararg is a symbol for variadic keyword arguments """ undefined = object() err = partial(SibilantSyntaxError, location=declared_at, filename=filename) if is_symbol(args): return ((), (), (), args, None) elif isinstance(args, (list, tuple)): improper = False args = cons(*args, nil) elif is_proper(args): improper = False elif is_pair(args): improper = True else: raise err("formals must be symbol or pair, not %r" % args) positional = [] iargs = iter(args.unpack()) for arg in iargs: if is_keyword(arg): if improper: raise err("cannot mix improper formal with keywords") else: break elif is_symbol(arg) or is_lazygensym(arg): positional.append(arg) else: raise err("positional formals must be symbols, not %r" % arg) else: # handled all of args, done deal. if improper: return (positional[:-1], (), (), positional[-1], None) else: return (positional, (), (), None, None) defaults = [] kwonly = [] while arg not in (_keyword_star, _keyword_starstar): value = next(iargs, undefined) if value is undefined: raise err("missing value for keyword formal %s" % args) else: defaults.append((arg, value)) arg = next(iargs, undefined) if arg is undefined: break elif is_keyword(arg): continue else: raise err("keyword formals must be alternating keywords and" " values, not %r" % arg) star = None starstar = None if arg is undefined: return (positional, defaults, kwonly, None, None) if arg is _keyword_star: star = next(iargs, undefined) if star is undefined: raise err("* keyword requires symbol binding") elif star is nil: # nil means an ignored star arg, this is allowed. pass elif not (is_symbol(star) or is_lazygensym(star)): raise err("* keyword requires symbol binding, not %r" % star) arg = next(iargs, undefined) if arg is undefined: return (positional, defaults, kwonly, star, starstar) # while is_symbol(arg): # kwonly.append(arg) # arg = next(iargs, undefined) # # if arg is undefined: # return (positional, defaults, kwonly, star, starstar) if not is_keyword(arg): raise err("expected keyword in formals, got %r" % arg) # keyword formals after *: are considered keyword-only while arg not in (_keyword_star, _keyword_starstar): value = next(iargs, undefined) if value is undefined: raise err("missing value for keyword-only formal %s" % arg) else: kwonly.append((arg, value)) arg = next(iargs, undefined) if arg is undefined: break elif is_keyword(arg): continue else: raise err("keyword-only formals must be alternating keywords" " and values, not %r" % arg) if arg is _keyword_starstar: starstar = next(iargs, undefined) if starstar is undefined: raise err("** keyword requires symbol binding") elif not (is_symbol(starstar) or is_lazygensym(starstar)): raise err("** keyword requires symbol binding, not %r" % star) arg = next(iargs, undefined) if arg is not undefined: raise err("leftover formals %r" % arg) return (positional, defaults, kwonly, star, starstar)
def gather_parameters(args, declared_at=None, filename=None): """ parses parameter args into five values: (positional, keywords, values, stararg, starstararg) - positional is a list of expressions for positional arguments - keywords is a list of keywords defining keyword arguments - values is a list of expressions defining values for keywords - stararg is a symbol for variadic positional expression - starstararg is a symbol for variadic keyword expression """ undefined = object() def err(msg): return SibilantSyntaxError(msg, location=declared_at, filename=filename) if is_symbol(args) or is_lazygensym(args): return ((), (), (), args, None) elif isinstance(args, (list, tuple)): improper = False args = cons(*args, nil) if args else nil elif is_proper(args): improper = False elif is_pair(args): improper = True else: raise err("parameters must be symbol or pair, not %r" % args) positional = [] iargs = iter(args.unpack()) for arg in iargs: if is_keyword(arg): break else: positional.append(arg) else: # handled all of args, done deal. if improper: return (positional[:-1], (), (), positional[-1], None) else: return (positional, (), (), None, None) keywords = [] defaults = [] while arg not in (_keyword_star, _keyword_starstar): keywords.append(arg) value = next(iargs, undefined) if value is undefined: raise err("missing value for keyword parameter %s" % arg) else: defaults.append(value) arg = next(iargs, undefined) if arg is undefined: break elif is_keyword(arg): continue else: raise err("keyword parameters must be alternating keywords and" " values, not %r" % arg) star = None starstar = None if arg is undefined: return (positional, keywords, defaults, None, None) if arg is _keyword_star: star = next(iargs, undefined) if star is undefined: raise err("* keyword parameter needs value") arg = next(iargs, undefined) if arg is _keyword_starstar: starstar = next(iargs, undefined) if starstar is undefined: raise err("** keyword parameter needs value") arg = next(iargs, undefined) if arg is not undefined: raise err("leftover parameters %r" % arg) return (positional, keywords, defaults, star, starstar)
def gather_formals(args, declared_at=None, filename=None): """ parses formals pair args into five values: (positional, keywords, defaults, stararg, starstararg) - positional is a list of symbols defining positional arguments - defaults is a list of keywords and expr pairs defining keyword arguments and their default value expression - kwonly is a list of keywords and expr pairs which are keyword-only arguments and theid default value expression - stararg is a symbol for variadic positional arguments - starstararg is a symbol for variadic keyword arguments """ undefined = object() err = partial(SibilantSyntaxError, location=declared_at, filename=filename) if is_symbol(args): return ((), (), (), args, None) elif isinstance(args, (list, tuple)): improper = False args = cons(*args, nil) elif is_proper(args): improper = False elif is_pair(args): improper = True else: raise err("formals must be symbol or pair, not %r" % args) positional = [] iargs = iter(args.unpack()) for arg in iargs: if is_keyword(arg): if improper: raise err("cannot mix improper formal with keywords") else: break elif is_symbol(arg) or is_lazygensym(arg): positional.append(arg) else: raise err("positional formals must be symbols, nor %r" % arg) else: # handled all of args, done deal. if improper: return (positional[:-1], (), (), positional[-1], None) else: return (positional, (), (), None, None) defaults = [] kwonly = [] while arg not in (_keyword_star, _keyword_starstar): value = next(iargs, undefined) if value is undefined: raise err("missing value for keyword formal %s" % args) else: defaults.append((arg, value)) arg = next(iargs, undefined) if arg is undefined: break elif is_keyword(arg): continue else: raise err("keyword formals must be alternating keywords and" " values, not %r" % arg) star = None starstar = None if arg is undefined: return (positional, defaults, kwonly, None, None) if arg is _keyword_star: star = next(iargs, undefined) if star is undefined: raise err("* keyword requires symbol binding") elif star is nil: # nil means an ignored star arg, this is allowed. pass elif not (is_symbol(star) or is_lazygensym(star)): raise err("* keyword requires symbol binding, not %r" % star) arg = next(iargs, undefined) if arg is undefined: return (positional, defaults, kwonly, star, starstar) # while is_symbol(arg): # kwonly.append(arg) # arg = next(iargs, undefined) # # if arg is undefined: # return (positional, defaults, kwonly, star, starstar) if not is_keyword(arg): raise err("expected keyword in formals, got %r" % arg) # keyword formals after *: are considered keyword-only while arg not in (_keyword_star, _keyword_starstar): value = next(iargs, undefined) if value is undefined: raise err("missing value for keyword-only formal %s" % arg) else: kwonly.append((arg, value)) arg = next(iargs, undefined) if arg is undefined: break elif is_keyword(arg): continue else: raise err("keyword-only formals must be alternating keywords" " and values, not %r" % arg) if arg is _keyword_starstar: starstar = next(iargs, undefined) if starstar is undefined: raise err("** keyword requires symbol binding") elif not (is_symbol(starstar) or is_lazygensym(starstar)): raise err("** keyword requires symbol binding, not %r" % star) arg = next(iargs, undefined) if arg is not undefined: raise err("leftover formals %r" % arg) return (positional, defaults, kwonly, star, starstar)