def to_class(tokens: tc.TokenSeq) -> tp.Union[stmt.ClassStmt, stmt.ErrorStmt]: if mu.get(tokens, 1, tc.sentinel_token).type is not tt.IDENTIFIER: error(tokens[0].line, "Expect class name") return stmt.ErrorStmt() if mu.get(tokens, 2, tc.sentinel_token).type is tt.LESS: token_s_class = mu.get(tokens, 3, tc.sentinel_token) if token_s_class.type is not tt.IDENTIFIER: error(tokens[2].line, "Expect superclass name") stmt.ErrorStmt() super_class: tp.Optional[expr.Variable] = expr.Variable(token_s_class) first_brace = 4 else: super_class = None first_brace = 2 if mu.get(tokens, first_brace, tc.sentinel_token).type is not tt.LEFT_BRACE: error(tokens[2].line, 'Expect "{" before class body') # This should be the right brace of the last method declaration or # the left_brace starting the class if the class is empty. elif (tokens[-1].type is not tt.RIGHT_BRACE and mu.get( tokens, first_brace, tc.sentinel_token) is not tokens[-1]): error(tokens[-1].line, 'Expect "}" after class body') else: methods = filter_methods(to_methods(tokens[first_brace + 1:-1])) if methods is not None: return stmt.ClassStmt.from_params( tokens[1], super_class, methods[lc.MethodType.BOUND], methods[lc.MethodType.STATIC], methods[lc.MethodType.PROPERTY], ) return stmt.ErrorStmt()
def first_for_clause(clause: tp.Optional[tc.TokenSeq], error_line: int) -> FirstClauseType: if clause is None: error(error_line, "Need for loop clauses.") return stmt.ErrorStmt() if not clause: return stmt.NullStmt() first_type = clause[0].type if first_type is tt.VAR: return to_var(clause) if first_type not in PROGRAM_FUNCS: return to_expr(clause) error(clause[-1].line, "Invalid statement in for loop initializer") return stmt.ErrorStmt()
def to_while(tokens: tc.TokenSeq) -> tp.Union[stmt.WhileStmt, stmt.ErrorStmt]: right_paren = next(tu.token_find_index(tokens, {tt.RIGHT_PAREN}), len(tokens)) condition = ep.conditional(tokens[:right_paren + 1]) if condition.has_error(): return stmt.ErrorStmt() body = to_meta_dec(tokens[right_paren + 1:]) return stmt.WhileStmt(condition, body)
def to_var( tokens: tp.Sequence[tc.Token] ) -> tp.Union[stmt.ErrorStmt, stmt.VarStmt]: relevant_tokens = tu.token_find_index(tokens, {tt.IDENTIFIER, tt.EQUAL}) index = next(relevant_tokens, None) name = tokens[index] if index is not None else tc.sentinel_token if name.type is not tt.IDENTIFIER: error(name.line, "Expect variable name.") return stmt.ErrorStmt() equal_index = next(relevant_tokens, None) if equal_index is None and index != len(tokens) - 1: error(name.line, "Expect nothing between the variable name and the equals sign.") return stmt.ErrorStmt() initializer = (ep.expression(tokens[equal_index + 1:]) if equal_index is not None else None) return stmt.VarStmt(name, initializer)
def to_if(tokens: tc.TokenSeq) -> tp.Union[stmt.IfStmt, stmt.ErrorStmt]: paren = next(tu.token_find_index(tokens, {tt.RIGHT_PAREN}), len(tokens)) condition = ep.conditional(tokens[:paren + 1]) if condition.has_error(): return stmt.ErrorStmt() else_index = next(tu.token_find_index(tokens, {tt.ELSE}), len(tokens)) then_stmt = to_meta_dec(tokens[paren + 1:else_index]) else_stmt = to_meta_dec( tokens[else_index:]) if tokens[else_index:] else None return stmt.IfStmt(condition, then_stmt, else_stmt)
def to_fun( tokens: tc.TokenSeq, kind: str, ) -> tp.Union[stmt.ErrorStmt, stmt.FunctionStmt]: name = tokens[1] if name.type is not tt.IDENTIFIER: error(name.line, f"Expect {kind} name") return stmt.ErrorStmt() i_tokens = iter(tokens[1:]) parens = tuple(sp.parens(i_tokens)) if not tu.paren_check(parens): return stmt.ErrorStmt() arguments = tuple(ep.function_def_args(parens[2:-1])) if any(arg is None for arg in arguments): return stmt.ErrorStmt() body = to_meta_dec(tuple(i_tokens)) if not isinstance(body, stmt.BraceStmt): error(name.line, "Expect braced statement after function") return stmt.ErrorStmt() body.function_scope = True return stmt.FunctionStmt.from_params(arguments, body, name) # type: ignore
def from_tokens(tokens: tp.Iterable[tc.Token]) -> tp.Iterator[ae.Stmt]: """ Returns an iterator of declarations from a list of tokens.""" # if not tu.check_matching(tokens): # yield stmt.ErrorStmt() # yield from from_tokens(tuple(tu.synchronize(tokens))) # return for stmt_tokens in sp.split_tokens(tokens): result = to_meta_dec(tuple(stmt_tokens)) if result.has_error(): yield stmt.ErrorStmt() yield from from_tokens(tu.synchronize(stmt_tokens)) else: yield result
def to_for(tokens: tp.Iterable[tc.Token]) -> ae.Stmt: iter_tokens = iter(tokens) clauses = for_clauses(tuple(sp.parens(iter_tokens))) if clauses.has_error(): return stmt.ErrorStmt() body = to_meta_dec(tuple(iter_tokens)) if clauses.increment is not None: # Not a function, but function_scope is true to prevent the # creation of unnecessary scopes. body = stmt.BraceStmt((body, stmt.ExprStmt(clauses.increment)), function_scope=True) body = stmt.WhileStmt(clauses.condition or expr.Literal(True), body) body = stmt.BraceStmt((clauses.initializer, body)) return body
def third_for_clause(clause: tp.Optional[tc.TokenSeq]) -> tp.Optional[ae.Expr]: return ep.expression(clause) if clause else None class ForClausesNT(tp.NamedTuple): initializer: FirstClauseType condition: tp.Optional[ae.Expr] increment: tp.Optional[ae.Expr] def has_error(self) -> bool: return any(clause is not None and clause.has_error() for clause in self) error_for_clauses = ForClausesNT(stmt.ErrorStmt(), expr.ErrorExpr(), expr.ErrorExpr()) def for_clauses(clauses: tc.TokenSeq) -> ForClausesNT: """ Helper function to parse the clauses of a for loop. Assumes the parentheses are for token is included. """ if not tu.paren_check(clauses): return error_for_clauses split_tokens = ( tuple(x) for x in tu.specified_token_split(clauses[2:-1], tt.SEMICOLON)) first_clause = first_for_clause(next(split_tokens, None), clauses[-1].line) second_clause = second_for_clause(next(split_tokens, None),