Ejemplo n.º 1
0
def iter_macroexpand(env, source_obj, position=None):
    if env:
        compiler = env.get("__compiler__") or current()
    elif env is None:
        compiler = current()
        if compiler:
            env = compiler.env

    if env is None:
        raise CompilerException("macroexpand requires non-None env when"
                                " no compiler is active")

    if position is None and is_pair(source_obj):
        position = source_obj.get_position()

    # expander = env_get_expander(env, source_obj)
    expander = compiler.find_expander(source_obj, env)

    while expander:
        expanded = expander()
        yield expanded

        # expander = env_get_expander(env, expanded)
        expander = compiler.find_expander(expanded, env)
        if is_pair(expanded):
            fill_position(expanded, position)
Ejemplo n.º 2
0
def iter_macroexpand(env, source_obj, position=None):
    if env:
        compiler = env.get("__compiler__") or current()
    elif env is None:
        compiler = current()
        if compiler:
            env = compiler.env

    if env is None:
        raise CompilerException("macroexpand requires non-None env when"
                                " no compiler is active")

    if position is None and is_pair(source_obj):
        position = source_obj.get_position()

    # expander = env_get_expander(env, source_obj)
    expander = compiler.find_expander(source_obj, env)

    while expander:
        expanded = expander()
        yield expanded

        # expander = env_get_expander(env, expanded)
        expander = compiler.find_expander(expanded, env)
        if is_pair(expanded):
            fill_position(expanded, position)
Ejemplo n.º 3
0
    def error(self, message, source):
        """
        Create a CompilerSyntaxError based on the message and source
        object. Will attempt to find position and line text based
        on the compiler's state.
        """

        text = None

        pos = None
        if is_pair(source) and source is not nil:
            pos = source.get_position()

        if pos and exists(self.filename):
            with open(self.filename, "rt") as fin:
                for text, _lineno in zip(fin, range(0, pos[0])):
                    # print(" ...", text)
                    pass

        if not text:
            text = str(source)
            if pos:
                pos = (pos[0], 0)

        return CompilerSyntaxError(message, pos, text=text,
                                   filename=self.filename)
Ejemplo n.º 4
0
    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())
Ejemplo n.º 5
0
    def compile(self, source_obj, tc, cont):
        """
        Compile a supported source object into an expression.

        pair, symbol, keyword, and the pythonic constant types are
        valid source obj types.
        """

        self.require_active()

        tc = self.tco_enabled and tc

        if is_pair(source_obj):
            dispatch = self.compile_pair
        elif is_symbol(source_obj) or is_lazygensym(source_obj):
            dispatch = self.compile_symbol
        elif is_keyword(source_obj):
            dispatch = self.compile_keyword
        elif isinstance(source_obj, CONST_TYPES):
            dispatch = self.compile_constant
        else:
            msg = "Unsupported source object %r" % source_obj
            raise CompilerException(msg)

        try:
            return dispatch(source_obj, tc, cont or self._compile_cont)
        except (CompilerException, SibilantSyntaxError):
            # these two should be propogated unchanged
            raise
        except Exception as ex:
            raise UncaughtCompilerException(ex, source_obj)
Ejemplo n.º 6
0
    def error(self, message, source):
        """
        Create a CompilerSyntaxError based on the message and source
        object. Will attempt to find position and line text based
        on the compiler's state.
        """

        text = None

        pos = None
        if is_pair(source) and source is not nil:
            pos = source.get_position()

        if pos and exists(self.filename):
            with open(self.filename, "rt") as fin:
                for text, _lineno in zip(fin, range(0, pos[0])):
                    # print(" ...", text)
                    pass

        if not text:
            text = str(source)
            if pos:
                pos = (pos[0], 0)

        return CompilerSyntaxError(message,
                                   pos,
                                   text=text,
                                   filename=self.filename)
Ejemplo n.º 7
0
    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)
Ejemplo n.º 8
0
    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())
Ejemplo n.º 9
0
    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)
Ejemplo n.º 10
0
    def compile(self, compiler, source_obj, tc, cont):
        expanded = self.expand()
        expanded = _symbol_None if expanded is None else expanded

        if is_pair(source_obj):
            called_by, source = source_obj
            res = cons(expanded, source)
            fill_position(res, source_obj.get_position())
            expanded = res

        return tcf(cont, expanded, tc)
Ejemplo n.º 11
0
    def compile(self, compiler, source_obj, tc, cont):
        expanded = self.expand()
        expanded = _symbol_None if expanded is None else expanded

        if is_pair(source_obj):
            called_by, source = source_obj
            res = cons(expanded, source)
            fill_position(res, source_obj.get_position())
            expanded = res

        return tcf(cont, expanded, tc)
Ejemplo n.º 12
0
    def compile_apply(self, source_obj: pair, tc, cont):
        """
        Compile a runtime function apply expression.
        """

        tc = tc and self.tco_enabled and not self.generator

        head, tail = source_obj

        if tc and self.self_ref and \
           is_symbol(head) and (str(head) == self.name):
            return tcf(self.compile_tcr_apply, source_obj, tc, cont)

        pos = source_obj.get_position()

        if is_pair(head):
            # @trampoline
            def ccp(new_head, tc):
                # Continue Compiling Pair. This is how we finish
                # compiling a function invocation after first
                # compiling the head

                if new_head is None:
                    # the original head pair compiled down to a None,
                    # which means it pushed bytecode and left a value
                    # on the stack. Complete the apply based on that.
                    return tcf(self.complete_apply, tail, pos, tc, cont)
                else:
                    # the original head pair was transformed, so now
                    # we need to start over in a new compile_pair call
                    # using a newly assembled expression.
                    expr = pair(new_head, tail)
                    expr.set_position(pos)
                    return tcf(self.compile_pair, expr, tc, cont)

            # we need to compile the head first, to figure out if it
            # expands into a symbolic reference or something. We'll
            # use ccp as a temporary continuation. Note that the
            # evaluation of the head of the pair is never a tailcall
            # itself, even if it would be a tailcall to apply it as a
            # function afterwards.
            return tcf(self.compile_pair, head, False, ccp)

        elif tc:
            self.declare_tailcall()
            self.pseudop_get_global(_symbol_tailcall_full)
            return tcf(self.complete_apply, source_obj, pos, False, cont)

        else:
            self.add_expression(head)
            return tcf(self.complete_apply, tail, pos, tc, cont)
Ejemplo n.º 13
0
    def compile_apply(self, source_obj: pair, tc, cont):
        """
        Compile a runtime function apply expression.
        """

        tc = tc and self.tco_enabled and not self.generator

        head, tail = source_obj

        if tc and self.self_ref and \
           is_symbol(head) and (str(head) == self.name):
            return tcf(self.compile_tcr_apply, source_obj, tc, cont)

        pos = source_obj.get_position()

        if is_pair(head):
            # @trampoline
            def ccp(new_head, tc):
                # Continue Compiling Pair. This is how we finish
                # compiling a function invocation after first
                # compiling the head

                if new_head is None:
                    # the original head pair compiled down to a None,
                    # which means it pushed bytecode and left a value
                    # on the stack. Complete the apply based on that.
                    return tcf(self.complete_apply, tail, pos, tc, cont)
                else:
                    # the original head pair was transformed, so now
                    # we need to start over in a new compile_pair call
                    # using a newly assembled expression.
                    expr = pair(new_head, tail)
                    expr.set_position(pos)
                    return tcf(self.compile_pair, expr, tc, cont)

            # we need to compile the head first, to figure out if it
            # expands into a symbolic reference or something. We'll
            # use ccp as a temporary continuation. Note that the
            # evaluation of the head of the pair is never a tailcall
            # itself, even if it would be a tailcall to apply it as a
            # function afterwards.
            return tcf(self.compile_pair, head, False, ccp)

        elif tc:
            self.declare_tailcall()
            self.pseudop_get_global(_symbol_tailcall_full)
            return tcf(self.complete_apply, source_obj, pos, False, cont)

        else:
            self.add_expression(head)
            return tcf(self.complete_apply, tail, pos, tc, cont)
Ejemplo n.º 14
0
    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))
Ejemplo n.º 15
0
    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))
Ejemplo n.º 16
0
    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)
Ejemplo n.º 17
0
    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)
Ejemplo n.º 18
0
    def compile(self, source_obj, tc, cont):
        """
        Compile a supported source object into an expression.

        pair, symbol, keyword, and the pythonic constant types are
        valid source obj types.
        """

        tc = self.tco_enabled and tc

        if is_pair(source_obj):
            dispatch = self.compile_pair
        elif is_symbol(source_obj) or is_lazygensym(source_obj):
            dispatch = self.compile_symbol
        elif is_keyword(source_obj):
            dispatch = self.compile_keyword
        elif isinstance(source_obj, CONST_TYPES):
            dispatch = self.compile_constant
        else:
            msg = "Unsupported source object %r" % source_obj
            raise self.error(msg, source_obj)

        return tcf(dispatch, source_obj, tc, cont or self._compile_cont)
Ejemplo n.º 19
0
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)
Ejemplo n.º 20
0
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)
Ejemplo n.º 21
0
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)
Ejemplo n.º 22
0
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)