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
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
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
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)
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
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
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
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
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