예제 #1
0
    def exec_(self, instructions: t.Iterable[Instruction]):
        stack = []

        for instruction in instructions:
            operator = instruction.operator
            location = instruction.location
            args = instruction.args

            if operator == Operator.push:
                value, = args

                if not isinstance(value, Symbol):
                    stack.append(value)
                    continue

                content = value.content

                try:
                    bound_value = self.env[content]
                except KeyError:
                    raise LispError(f'undefined symbol "{content}"', location)

                stack.append(bound_value)
            elif operator == Operator.call:
                proc = stack.pop()

                if not callable(proc):
                    raise LispError(
                        f'head of procedure call expression is not a procedure',
                        location)

                arg_count, = args
                assert arg_count <= len(stack), "The virtual machine "\
                "encountered a stack underflow."
                args = [stack.pop() for _ in range(arg_count)]
                args.reverse()
                stack.append(proc(*args))
            elif operator == Operator.def_:
                symbol, = args
                assert stack, "The virtual machine encountered a stack "\
                "underflow."
                value = stack.pop()
                self.env[symbol.content] = value
            else:
                assert False, "The virtual machine encountered an invalid "\
                "operator."

        return stack
예제 #2
0
    def eval_real_literal(location: Location, c: str, *, sign, value, base,
                          denominator):
        if c is None:
            return sign * value

        try:
            digit = basedigit(c, base)
        except ValueError:
            pass
        else:
            return partial(
                eval_real_literal,
                sign=sign,
                value=value + Fraction(digit, denominator),
                base=base,
                denominator=denominator * base,
            )

        if c == '_':
            return partial(
                eval_real_literal,
                sign=sign,
                value=value,
                base=base,
                denominator=denominator,
            )

        raise LispError(
            f'Invalid character in base {base} real literal',
            location,
        )
예제 #3
0
    def scan_char_code_with_base(self, location: Location, c: str,
    *, fragment: LexemeFragment, delimiter: str, base: int, code: int)\
    -> t.Iterator[ScannerYield]:
        yield from ()

        try:
            digit = basedigit(c, base)
        except ValueError:
            pass
        else:
            return partial(
                self.scan_char_code_with_base,
                fragment=fragment,
                delimiter=delimiter,
                base=base,
                code=base * code + digit,
            )

        if c == ')':
            fragment.content.append(chr(code))
            return partial(self.scan_string,
                fragment=fragment,
                delimiter=delimiter,
            )
        else:
            raise LispError('Invalid character in character code', location)
예제 #4
0
    def scan_char_code(self, location: Location, c: str,
    *, fragment: LexemeFragment, delimiter: str, code: int)\
    -> t.Iterator[ScannerYield]:
        yield from ()

        if c.isdigit():
            return partial(self.scan_char_code,
                fragment=fragment,
                delimiter=delimiter,
                code=10 * code + (ord(c) - ord('0')),
            )
        elif c == '#':
            return partial(self.scan_char_code_with_base,
                fragment=fragment,
                delimiter=delimiter,
                base=code,
                code=0,
            )
        elif c == ')':
            fragment.content.append(chr(code))
            return partial(self.scan_string,
                fragment=fragment,
                delimiter=delimiter,
            )
        else:
            raise LispError('Invalid character code', location)
예제 #5
0
    def end(self) -> Expr:
        fragment = self.expr_stack.pop()

        if self.expr_stack:
            raise LispError(
                'Unmatched opening parentheses',
                self.expr_stack[-1].location,
            )

        return ComplexExpr(fragment.location, tuple(fragment.subexprs))
예제 #6
0
    def compile_definition(location, tail):
        try:
            name_expr, value_expr = tail
        except ValueError:
            raise LispError(
                f'Invalid definition; got {len(tail)} arguments but'
                ' definitions must have exactly 2 arguments',
                location) from None

        if isinstance(name_expr, Token):
            symbol = name_expr.content

            if isinstance(symbol, Symbol):
                expr_stack.append(
                    Instruction(Operator.def_, location, [name_expr.content]))
                expr_stack.append(value_expr)
                return

        raise LispError('Invalid name in definition; it must be a symbol',
                        name_expr.location)
예제 #7
0
def compile_expr(expr: Expr) -> t.Iterator[Instruction]:
    expr_stack = [expr]

    def compile_definition(location, tail):
        try:
            name_expr, value_expr = tail
        except ValueError:
            raise LispError(
                f'Invalid definition; got {len(tail)} arguments but'
                ' definitions must have exactly 2 arguments',
                location) from None

        if isinstance(name_expr, Token):
            symbol = name_expr.content

            if isinstance(symbol, Symbol):
                expr_stack.append(
                    Instruction(Operator.def_, location, [name_expr.content]))
                expr_stack.append(value_expr)
                return

        raise LispError('Invalid name in definition; it must be a symbol',
                        name_expr.location)

    while expr_stack:
        expr = expr_stack.pop()
        location = expr.location

        if isinstance(expr, Instruction):
            yield expr
        elif isinstance(expr, Token):
            yield Instruction(Operator.push, location, [expr.content])
        elif not expr.subexprs:
            raise LispError('empty procedure call expression', location)
        else:
            head = expr.subexprs[0]
            tail = expr.subexprs[1:]

            if isinstance(head, Token):
                value = head.content

                if isinstance(value, Symbol):
                    if value.content == 'def':
                        compile_definition(location, tail)
                        continue

            expr_stack.append(Instruction(Operator.call, location,
                                          [len(tail)]))
            expr_stack.append(head)
            expr_stack.extend(reversed(tail))
예제 #8
0
    def parse(self, tokens: t.Iterable[Token]) -> None:
        for token in tokens:
            content = token.content

            if isinstance(content, ParserDirective):
                if content.content == '(':
                    self.expr_stack.append(ExprFragment(token.location, []))
                elif content.content == ')':
                    if len(self.expr_stack) <= 1:
                        raise LispError('Unmatched closing parenthesis',
                                        token.location)

                    fragment = self.expr_stack.pop()
                    expr = ComplexExpr(fragment.location,
                                       tuple(fragment.subexprs))
                    self.expr_stack[-1].subexprs.append(expr)
                else:
                    assert False, "The parser received an invalid directive."
            else:
                self.expr_stack[-1].subexprs.append(token)
예제 #9
0
    def scan_escape_sequence(self, location: Location, c: str,
    *, fragment: LexemeFragment, delimiter: str) -> t.Iterator[ScannerYield]:
        yield from ()

        if c == '(':
            return partial(self.scan_char_code,
                fragment=fragment,
                delimiter=delimiter,
                code=0,
            )

        try:
            escaped_c = self.SIMPLE_ESCAPE_SEQUENCES[c]
        except KeyError:
            raise LispError('Invalid escape sequence', location)
        
        fragment.content.append(escaped_c)
        return partial(self.scan_string,
            fragment=fragment,
            delimiter=delimiter,
        )
예제 #10
0
    def eval_integer_literal_with_base(location: Location, c: str, *,
                                       sign: int, value: int, base: int):
        if c is None:
            return sign * value

        try:
            digit = basedigit(c, base)
        except ValueError:
            pass
        else:
            return partial(
                eval_integer_literal_with_base,
                sign=sign,
                value=base * value + digit,
                base=base,
            )

        if c == '_':
            return partial(
                eval_integer_literal_with_base,
                sign=sign,
                value=value,
                base=base,
            )

        if c == '.':
            return partial(
                eval_real_literal,
                sign=sign,
                value=Fraction(value),
                base=base,
                denominator=base,
            )

        raise LispError(
            f'Invalid character in base {base} integer literal',
            location,
        )
예제 #11
0
    def eval_integer_literal(location: Location, c: str, *, sign: int,
                             value: int):
        if c is None:
            return sign * value

        if c.isdigit():
            return partial(
                eval_integer_literal,
                sign=sign,
                value=10 * value + (ord(c) - ord('0')),
            )

        if c == '_':
            return partial(eval_integer_literal, sign=sign, value=value)

        if c == '#':
            return partial(
                eval_integer_literal_with_base,
                sign=sign,
                value=0,
                base=value,
            )

        if c == '.':
            return partial(
                eval_real_literal,
                sign=sign,
                value=Fraction(value),
                base=10,
                denominator=10,
            )

        raise LispError(
            'Invalid character in base 10 integer literal',
            location,
        )