Esempio n. 1
0
def require(stream: TokenStream, category: TokenCat, desc: str = "", consume=False):
    """Requires the next token in the stream to match a specified category.
    Consumes and discards it if consume==True.
    """
    if stream.peek().kind != category:
        raise InputError(f"Expecting {desc or category}, but saw {stream.peek()} instead")
    if consume:
        stream.take()
    return
Esempio n. 2
0
def _stmt(stream: TokenStream) -> expr.Expr:
    """
    stmt ::= exp ['=' exp]
    """
    left = _expr(stream)
    if stream.peek().kind is TokenCat.EQUALS:
        stream.take()
        right = _expr(stream)
        return expr.Equals(left, right)
    else:
        return left
Esempio n. 3
0
def _stmt(stream: TokenStream) -> expr.Expr:
    """
    stmt ::= exp ['=' exp]
    """
    left = _expr(stream)
    if stream.peek().kind is TokenCat.ASSIGN:
        if not isinstance(left, expr.Var):
            raise InputError("Can only assign to a variable")
        stream.take()
        right = _expr(stream)
        return expr.Assign(left, right)
    else:
        return left
Esempio n. 4
0
def _term(stream: TokenStream) -> expr.Expr:
    """term ::= primary { ('*'|'/')  primary }"""
    left = _secondary(stream)
    log.debug(f"term starts with {left}")
    while stream.peek().value in ["*", "/"]:
        op = stream.take()
        right = _secondary(stream)
        if op.value == "*":
            left = expr.Times(left, right)
        elif op.value == "/":
            left = expr.Div(left, right)
        else:
            raise InputError(f"Expecting multiplicative op, got {op}")
    return left
Esempio n. 5
0
def _secondary(stream: TokenStream) -> expr.Expr:
    """term ::= secondary { ('^'|'|')  secondary }"""
    left = _primary(stream)
    log.debug(f"term starts with {left}")
    while stream.peek().value in ["^", "|"]:
        op = stream.take()
        right = _primary(stream)
        if op.value == "^":
            left = expr.Raise(left, right)
        elif op.value == "|":
            left = expr.Root(left, right)
        else:
            raise InputError(f"Expecting Exponential op, got {op}")
    return left
Esempio n. 6
0
def _block(stream: TokenStream) -> expr.Expr:
    """
    block ::= { stmt }
    """
    log.debug(f"Parsing block from token {stream.peek()}")
    if stream.peek().kind not in first["stmt"]:
        return expr.Pass()
    left = _stmt(stream)
    log.debug(f"Starting block with {left}")
    while stream.peek().kind in first["stmt"]:
        right = _stmt(stream)
        log.debug(f"Adding statement to block: {right}")
        left = expr.Seq(left, right)
    return left
Esempio n. 7
0
def _primary(stream: TokenStream) -> expr.Expr:
    """Unary operations, Constants, Variables,
    input, and parenthesized expressions"""
    log.debug(f"Parsing primary with starting token {stream.peek()}")
    token = stream.take()
    if token.kind is TokenCat.INT:
        log.debug(f"Returning IntConst node from token {token}")
        return expr.IntConst(int(token.value))
    elif token.kind is TokenCat.VAR:
        log.debug(f"Variable {token.value}")
        return expr.Var(token.value)
    elif token.kind is TokenCat.READ:
        log.debug("Read")
        return expr.Read()
    elif token.kind is TokenCat.ABS:
        operand = _primary(stream)
        return expr.Abs(operand)
    elif token.kind is TokenCat.NEG:
        operand = _primary(stream)
        return expr.Neg(operand)
    elif token.kind is TokenCat.LPAREN:
        nested = _expr(stream)
        require(stream, TokenCat.RPAREN, consume=True)
        return nested
    else:
        raise InputError(f"Confused about {token} in expression")
Esempio n. 8
0
def _expr(stream: TokenStream) -> expr.Expr:
    """
    expr ::= term { ('+'|'-') term }
    """
    log.debug(f"parsing sum starting from token {stream.peek()}")
    left = _term(stream)
    log.debug(f"sum begins with {left}")
    while stream.peek().value in ["+", "-"]:
        op = stream.take()
        log.debug(f"expr addition op {op}")
        right = _term(stream)
        if op.value == "+":
            left = expr.Plus(left, right)
        elif op.value == "-":
            left = expr.Minus(left, right)
        else:
            raise InputError(f"What's that op? {op}")
    return left
Esempio n. 9
0
def _rel(stream: TokenStream) -> expr.Comparison:
    left = _expr(stream)
    op = stream.take()
    right = _expr(stream)
    if op.kind in COMPARISONS:
        clazz = COMPARISONS[op.kind]
        return clazz(left, right)
    else:
        raise InputError(f"Expecting comparison, saw '{op.value}' instead")
Esempio n. 10
0
def _if(stream: TokenStream) -> expr.If:
    require(stream, TokenCat.IF, consume=True)
    cond = _rel(stream)
    require(stream, TokenCat.THEN, consume=True)
    then_block = _block(stream)
    if stream.peek().kind == TokenCat.ELSE:
        require(stream, TokenCat.ELSE, consume=True)
        else_block = _block(stream)
        result = expr.If(cond, then_block, else_block)
    else:
        result = expr.If(cond, then_block, elsepart=expr.Pass())
    require(stream, TokenCat.FI, consume=True)
    return result
Esempio n. 11
0
def parse(srcfile: TextIO) -> expr.Expr:
    """Interface function to LL parser"""
    stream = TokenStream(srcfile)
    return _program(stream)
Esempio n. 12
0
def _stmt(stream: TokenStream) -> expr.Expr:
    """
    #  stmt ::=  assign | loop | ifstmt | printstmt
    assignment ::= IDENT '=' expression ';'
    """
    if stream.peek().kind is TokenCat.WHILE:
        return _while(stream)
    if stream.peek().kind is TokenCat.IF:
        return _if(stream)
    if stream.peek().kind is TokenCat.PRINT:
        return _print(stream)
    if stream.peek().kind is not TokenCat.VAR:
        raise InputError(
            f"Expecting identifier at beginning of assignment, got {stream.peek()}"
        )
    target = expr.Var(stream.take().value)
    if stream.peek().kind is not TokenCat.ASSIGN:
        raise InputError(f"Expecting assignment symbol, got {stream.peek()}")
    stream.take()  # Discard token
    value = _expr(stream)
    if stream.peek().kind is not TokenCat.SEMI:
        raise InputError(
            f"Expecting semicolon after assignment, got {stream.peek()}")
    stream.take()  # Discard token
    return expr.Assign(target, value)