/
space_verse
/
LambdaLanguage
Обзор
Документация
Войти
/
space_verse
/
LambdaLanguage
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
src/parser/parser.py
318 строк
11 KB
spacer
FIXED: bug with extra par;
19 авг 2025, 21:57
19 авг 2025, 21:57
813b69d
Код
Авторство
О чём код?
from dataclasses import dataclass from typing import Literal from parser.ast.branches import IfNode from parser.ast.codespace import AbstractNode from parser.ast.codespace import UnknownNode from parser.ast.codespace import NoneNode from parser.ast.codespace import DeclareAssignNode from parser.ast.codespace import VariableNode from parser.ast.bin_op_nodes import AssignDeductOpNode from parser.ast.bin_op_nodes import AssignDivOpNode from parser.ast.bin_op_nodes import AssignMulOpNode from parser.ast.bin_op_nodes import AssignOpNode from parser.ast.bin_op_nodes import AssignSumOpNode from parser.ast.bin_op_nodes import BinOperationNode from parser.ast.bin_op_nodes import BinDeductOpNode from parser.ast.bin_op_nodes import BinDivOpNode from parser.ast.bin_op_nodes import BinMulOpNode from parser.ast.bin_op_nodes import BinSumOpNode from parser.ast.bin_op_nodes import EqualOpNode from parser.ast.bin_op_nodes import GEqualOpNode from parser.ast.bin_op_nodes import GreatOpNode from parser.ast.bin_op_nodes import LEqualOpNode from parser.ast.bin_op_nodes import LessOpNode from parser.ast.bin_op_nodes import NotEqualOpNode from parser.ast.literal import LiteralNode from parser.ast.stdlib.console import ConsoleOutNode from parser.ast.stdlib.utils import GetTypeNode from parser.ast.uno_op_nodes import UnoOperationNode from parser.ast.uno_op_nodes import AssignSumUnoOpNode from parser.ast.uno_op_nodes import AssignDeductUnoOpNode from tokenizer.tokens import STOP_STATEMENT, Token, TYPES def expr_op_bind_pow(op: str) -> int: if op in ("=", "+=", "-=", "*=", "/="): return 0 if op == ("<", "<=", ">", ">=", "==", "!="): return 1 if op == "and": return 2 if op == "or": return 3 if op in "+-": return 4 if op in "*/": return 5 if op in ("++", "--"): return 6 raise SyntaxError("Unknown operation: %r" % op) @dataclass class ExprMember: node: AbstractNode bind: Literal["left", "right"] @dataclass class ExprOperation: value: str class Parser: def __init__(self, tokens: list[Token]) -> None: self.tokens = tokens self.position = 0 def peek(self) -> Token | None: if self.position < len(self.tokens): return self.tokens[self.position] def expect(self, *token_types: str) -> Token: token = self.peek() if token: if token.type in token_types: self.advance() return token if token: raise SyntaxError("Expected %s, got %s at file::%d.%d" % ( ' or '.join(token_types), token.type, token.line, token.column, )) raise SyntaxError("Expected %s, got %s at file" % ( ' or '.join(token_types), None, )) def advance(self) -> None: self.position += 1 def parse(self, *stop_token_types: str) -> list[AbstractNode]: statements: list[AbstractNode] = [] while ( (token := self.peek()) and token.type not in stop_token_types ): if statement := self.statement(*stop_token_types): statements.append(statement) token = self.peek() if token is not None: if token.type in stop_token_types: break self.advance() return statements def statement(self, *stop_token_types: str) -> AbstractNode | None: stop_token_types += tuple(STOP_STATEMENT) if (token := self.peek()) is not None: if token.value in TYPES: return self.declare_assignment(*stop_token_types) elif token.type == "SEMICOLON": raise SyntaxError("Unknown semicolon char at file::%d.%d" % (token.line, token.column)) elif token.type == "IF": return self.if_node() elif token.type == "COUT": return self.cout(*stop_token_types) elif token.type == "TYPE": return self.type(*stop_token_types) elif token.type in stop_token_types: return None return self.expr(stop_token_types=stop_token_types) return None def declare_assignment(self, *stop_token_types: str) -> DeclareAssignNode: stop_token_types += ("ENDL", "SEMICOLON") var_type = self.expect(*[t.upper() for t in TYPES]).value var_name = self.expect("ID").value expression = UnknownNode() token = self.peek() if token is not None: if token.type == "OP" and token.value == "=": self.advance() expression = self.expr() elif token.type not in stop_token_types: raise SyntaxError("Invalid assignment %r at file::%d.%d" % (var_name, token.line, token.column)) return DeclareAssignNode(var_type, var_name, expression) def expr( self, stop_token_types: tuple[str, ...] = ("ENDL", "SEMICOLON"), left_member_node: AbstractNode | None = None, ) -> AbstractNode: if left_member_node is None: left_member_node = self.expr_member( stop_token_types=stop_token_types).node op = self.peek_expr_op_token(*stop_token_types) if op is not None: self.advance() if self.is_uno_op(op.value): uno_op_node_type = self.expr_uno_op_node_type(op) left_member_node = uno_op_node_type(left_member_node) return self.expr( stop_token_types=stop_token_types, left_member_node=left_member_node) op_node_type = self.expr_op_node_type(op) right_member = self.expr_member( left_op=op.value, stop_token_types=stop_token_types) if right_member.bind == "right": return op_node_type( left=left_member_node, right=self.expr( stop_token_types=stop_token_types, left_member_node=right_member.node )) else: # right_member.bind == "left" left_member_node = op_node_type( left=left_member_node, right=right_member.node) return self.expr( stop_token_types=stop_token_types, left_member_node=left_member_node, ) else: return left_member_node def peek_expr_op_token(self, *stop_token_types: str) -> Token | None: while token := self.peek(): if token.type == "OP": return token elif token.type in stop_token_types: break elif token.type == "RPAR": raise SyntaxError("Extra right par at file::%d.%d" % (token.line, token.column)) self.advance() return None def expr_member_node(self, token: Token) -> AbstractNode: match token.type: case "ID": return VariableNode(name=token.value) case "FLOAT_NUMBER": return LiteralNode(type="float", value=float(token.value)) case "INT_NUMBER": return LiteralNode(type="int", value=int(token.value)) case "STRING": return LiteralNode(type="str", value=token.value) case "TRUE": return LiteralNode(type="bool", value=True) case "FALSE": return LiteralNode(type="bool", value=False) case "LPAR": node = self.expr(stop_token_types=("RPAR",)) self.expect("RPAR") return node case _: return NoneNode() def is_uno_op(self, op: str) -> bool: return op in ("--", "++") def expr_uno_op_node_type(self, token: Token) -> type[UnoOperationNode]: maps = { "++": AssignSumUnoOpNode, "--": AssignDeductUnoOpNode, } if token.value in maps: return maps[token.value] raise SyntaxError("Unknown operation: %r" % token.value) def expr_op_node_type(self, token: Token) -> type[BinOperationNode]: maps = { "+": BinSumOpNode, "-": BinDeductOpNode, "*": BinMulOpNode, "/": BinDivOpNode, "=": AssignOpNode, "+=": AssignSumOpNode, "-=": AssignDeductOpNode, "*=": AssignMulOpNode, "/=": AssignDivOpNode, "==": EqualOpNode, "!=": NotEqualOpNode, ">": GreatOpNode, ">=": GEqualOpNode, "<": LessOpNode, "<=": LEqualOpNode, } if token.value in maps: return maps[token.value] raise SyntaxError("Unknown operation: %r" % token.value) def expr_member( self, left_op: str | None = None, stop_token_types: tuple[str, ...] = ("ENDL", "SEMICOLON"), ) -> ExprMember: token = self.expect( "ID", "FLOAT_NUMBER", "INT_NUMBER", "STRING", "TRUE", "FALSE", "LPAR", ) node = self.expr_member_node(token=token) op_token = self.peek_expr_op_token(*stop_token_types) bind = "left" if op_token is not None: if left_op is None: bind = "right" else: right_bind_power = expr_op_bind_pow(op_token.value) left_bind_power = expr_op_bind_pow(left_op) if left_bind_power < right_bind_power: bind = "right" return ExprMember( node=node, bind=bind, ) def cout(self, *stop_token_types: str) -> ConsoleOutNode: self.expect("COUT") expressions = [] while self.peek(): expressions.append(self.expr(stop_token_types=stop_token_types)) token = self.peek() if token is not None: if token.type == "COMMA": self.advance() elif token.type in stop_token_types: break return ConsoleOutNode(expressions) def type(self, *stop_token_types: str) -> GetTypeNode: self.expect("TYPE") expression = self.expr(stop_token_types=stop_token_types) return GetTypeNode(expression) def if_node(self) -> IfNode: self.expect("IF") self.expect("LPAR") conditions = self.expr(stop_token_types=("RPAR",)) self.expect("RPAR") while (token := self.peek()) and token.type == "ENDL": self.advance() self.expect("LCPAR") body = self.parse("RCPAR") self.expect("RCPAR") return IfNode(conditions, body)