bin/ 230 CLI tools (ask-*, br-*, agent-*, roadid, carpool) scripts/ 99 automation scripts fleet/ Node configs and deployment workers/ Cloudflare Worker sources (roadpay, road-search, squad webhooks) roadc/ RoadC programming language roadnet/ Mesh network (5 APs, WireGuard) operator/ Memory system scripts config/ System configs dotfiles/ Shell configs docs/ Documentation BlackRoad OS — Pave Tomorrow. RoadChain-SHA2048: d1a24f55318d338b RoadChain-Identity: alexa@sovereign RoadChain-Full: d1a24f55318d338b24b60bad7be39286379c76ae5470817482100cb0ddbbcb97e147d07ac7243da0a9f0363e4e5c833d612b9c0df3a3cd20802465420278ef74875a5b77f55af6fe42a931b8b635b3d0d0b6bde9abf33dc42eea52bc03c951406d8cbe49f1a3d29b26a94dade05e9477f34a7d4d4c6ec4005c3c2ac54e73a68440c512c8e83fd9b1fe234750b898ef8f4032c23db173961fe225e67a0432b5293a9714f76c5c57ed5fdf35b9fb40fd73c03ebf88b7253c6a0575f5afb6a6b49b3bda310602fb1ef676859962dad2aebbb2875814b30eee0a8ba195e482d4cbc91d8819e7f38f6db53e8063401649c77bb994371473cabfb917fb53e8cbe73d60
827 lines
29 KiB
Python
827 lines
29 KiB
Python
"""
|
|
BlackRoad OS Language Parser
|
|
Transforms tokens into an Abstract Syntax Tree (AST)
|
|
"""
|
|
|
|
from typing import List, Optional, Union
|
|
from lexer import Token, TokenType, lex
|
|
from ast_nodes import *
|
|
|
|
class Parser:
|
|
"""Recursive descent parser for BlackRoad language"""
|
|
|
|
def __init__(self, tokens: List[Token]):
|
|
self.tokens = tokens
|
|
self.pos = 0
|
|
|
|
def current_token(self) -> Token:
|
|
"""Get current token"""
|
|
if self.pos >= len(self.tokens):
|
|
return self.tokens[-1] # EOF
|
|
return self.tokens[self.pos]
|
|
|
|
def peek_token(self, offset: int = 1) -> Token:
|
|
"""Look ahead at token"""
|
|
pos = self.pos + offset
|
|
if pos >= len(self.tokens):
|
|
return self.tokens[-1] # EOF
|
|
return self.tokens[pos]
|
|
|
|
def advance(self) -> Token:
|
|
"""Move to next token"""
|
|
token = self.current_token()
|
|
if token.type != TokenType.EOF:
|
|
self.pos += 1
|
|
return token
|
|
|
|
def expect(self, token_type: TokenType) -> Token:
|
|
"""Expect a specific token type"""
|
|
token = self.current_token()
|
|
if token.type != token_type:
|
|
raise SyntaxError(f"Expected {token_type.name}, got {token.type.name} at {token.line}:{token.column}")
|
|
return self.advance()
|
|
|
|
def match(self, *token_types: TokenType) -> bool:
|
|
"""Check if current token matches any of the given types"""
|
|
return self.current_token().type in token_types
|
|
|
|
def skip_newlines(self):
|
|
"""Skip NEWLINE tokens"""
|
|
while self.match(TokenType.NEWLINE):
|
|
self.advance()
|
|
|
|
# ========================================================================
|
|
# Program & Statements
|
|
# ========================================================================
|
|
|
|
def parse_program(self) -> Program:
|
|
"""Parse entire program"""
|
|
statements = []
|
|
self.skip_newlines()
|
|
|
|
while not self.match(TokenType.EOF):
|
|
stmt = self.parse_statement()
|
|
if stmt:
|
|
statements.append(stmt)
|
|
self.skip_newlines()
|
|
|
|
token = self.current_token()
|
|
return Program(statements, line=token.line, column=token.column)
|
|
|
|
def parse_statement(self) -> Optional[Statement]:
|
|
"""Parse a statement"""
|
|
self.skip_newlines()
|
|
|
|
# Module declaration
|
|
if self.match(TokenType.MODULE):
|
|
return self.parse_module_declaration()
|
|
|
|
# Import statement
|
|
if self.match(TokenType.IMPORT, TokenType.FROM):
|
|
return self.parse_import_statement()
|
|
|
|
# Function definition
|
|
if self.match(TokenType.FUN, TokenType.ASYNC):
|
|
return self.parse_function_definition()
|
|
|
|
# Type definition
|
|
if self.match(TokenType.TYPE):
|
|
return self.parse_type_definition()
|
|
|
|
# Space definition (3D)
|
|
if self.match(TokenType.SPACE):
|
|
return self.parse_space_definition()
|
|
|
|
# Variable declaration
|
|
if self.match(TokenType.LET, TokenType.VAR, TokenType.CONST):
|
|
return self.parse_variable_declaration()
|
|
|
|
# Control flow
|
|
if self.match(TokenType.IF):
|
|
return self.parse_if_statement()
|
|
|
|
if self.match(TokenType.MATCH):
|
|
return self.parse_match_statement()
|
|
|
|
if self.match(TokenType.FOR):
|
|
return self.parse_for_loop()
|
|
|
|
if self.match(TokenType.WHILE):
|
|
return self.parse_while_loop()
|
|
|
|
# Return, break, continue
|
|
if self.match(TokenType.RETURN):
|
|
return self.parse_return_statement()
|
|
|
|
if self.match(TokenType.BREAK):
|
|
token = self.advance()
|
|
return BreakStatement(line=token.line, column=token.column)
|
|
|
|
if self.match(TokenType.CONTINUE):
|
|
token = self.advance()
|
|
return ContinueStatement(line=token.line, column=token.column)
|
|
|
|
# Spawn (concurrency)
|
|
if self.match(TokenType.SPAWN):
|
|
return self.parse_spawn_statement()
|
|
|
|
# Export
|
|
if self.match(TokenType.EXPORT):
|
|
token = self.advance()
|
|
stmt = self.parse_statement()
|
|
return ExportStatement(stmt, line=token.line, column=token.column)
|
|
|
|
# Expression statement or assignment
|
|
return self.parse_expression_or_assignment()
|
|
|
|
def parse_module_declaration(self) -> ModuleDeclaration:
|
|
"""Parse module declaration: module math"""
|
|
token = self.expect(TokenType.MODULE)
|
|
name_token = self.expect(TokenType.IDENTIFIER)
|
|
return ModuleDeclaration(name_token.value, line=token.line, column=token.column)
|
|
|
|
def parse_import_statement(self) -> ImportStatement:
|
|
"""Parse import statement"""
|
|
token = self.current_token()
|
|
|
|
if self.match(TokenType.FROM):
|
|
# from math import factorial, fibonacci
|
|
self.advance()
|
|
module_token = self.expect(TokenType.IDENTIFIER)
|
|
self.expect(TokenType.IMPORT)
|
|
|
|
items = []
|
|
items.append(self.expect(TokenType.IDENTIFIER).value)
|
|
while self.match(TokenType.COMMA):
|
|
self.advance()
|
|
items.append(self.expect(TokenType.IDENTIFIER).value)
|
|
|
|
return ImportStatement(module_token.value, None, items, line=token.line, column=token.column)
|
|
else:
|
|
# import math or import http as network
|
|
self.expect(TokenType.IMPORT)
|
|
module_token = self.expect(TokenType.IDENTIFIER)
|
|
|
|
alias = None
|
|
if self.match(TokenType.IDENTIFIER) and self.current_token().value == 'as':
|
|
self.advance()
|
|
alias = self.expect(TokenType.IDENTIFIER).value
|
|
|
|
return ImportStatement(module_token.value, alias, None, line=token.line, column=token.column)
|
|
|
|
def parse_variable_declaration(self) -> VariableDeclaration:
|
|
"""Parse variable declaration: let x: int = 42"""
|
|
token = self.current_token()
|
|
is_const = self.match(TokenType.CONST)
|
|
is_mutable = self.match(TokenType.VAR)
|
|
self.advance() # let, var, or const
|
|
|
|
name_token = self.expect(TokenType.IDENTIFIER)
|
|
name = name_token.value
|
|
|
|
# Optional type annotation
|
|
type_annotation = None
|
|
if self.match(TokenType.COLON):
|
|
self.advance()
|
|
type_annotation = self.parse_type()
|
|
|
|
# Optional initializer
|
|
initializer = None
|
|
if self.match(TokenType.ASSIGN):
|
|
self.advance()
|
|
initializer = self.parse_expression()
|
|
|
|
return VariableDeclaration(
|
|
name, type_annotation, initializer,
|
|
is_mutable, is_const,
|
|
line=token.line, column=token.column
|
|
)
|
|
|
|
def parse_function_definition(self) -> FunctionDefinition:
|
|
"""Parse function definition"""
|
|
token = self.current_token()
|
|
is_async = False
|
|
|
|
if self.match(TokenType.ASYNC):
|
|
is_async = True
|
|
self.advance()
|
|
|
|
self.expect(TokenType.FUN)
|
|
name_token = self.expect(TokenType.IDENTIFIER)
|
|
name = name_token.value
|
|
|
|
# Parameters
|
|
self.expect(TokenType.LPAREN)
|
|
parameters = self.parse_parameters()
|
|
self.expect(TokenType.RPAREN)
|
|
|
|
# Return type
|
|
return_type = None
|
|
if self.match(TokenType.ARROW):
|
|
self.advance()
|
|
return_type = self.parse_type()
|
|
|
|
# Body
|
|
self.expect(TokenType.COLON)
|
|
self.skip_newlines()
|
|
self.expect(TokenType.INDENT)
|
|
body = self.parse_block()
|
|
self.expect(TokenType.DEDENT)
|
|
|
|
return FunctionDefinition(
|
|
name, parameters, return_type, body, is_async, False,
|
|
line=token.line, column=token.column
|
|
)
|
|
|
|
def parse_parameters(self) -> List[Parameter]:
|
|
"""Parse function parameters"""
|
|
parameters = []
|
|
|
|
if self.match(TokenType.RPAREN):
|
|
return parameters
|
|
|
|
while True:
|
|
param_token = self.current_token()
|
|
|
|
# Check for variadic parameter
|
|
is_variadic = False
|
|
if self.match(TokenType.TRIPLE_DOT):
|
|
is_variadic = True
|
|
self.advance()
|
|
|
|
name = self.expect(TokenType.IDENTIFIER).value
|
|
|
|
# Optional type annotation
|
|
type_annotation = None
|
|
if self.match(TokenType.COLON):
|
|
self.advance()
|
|
type_annotation = self.parse_type()
|
|
|
|
# Optional default value
|
|
default_value = None
|
|
if self.match(TokenType.ASSIGN):
|
|
self.advance()
|
|
default_value = self.parse_expression()
|
|
|
|
parameters.append(Parameter(
|
|
name, type_annotation, default_value, is_variadic,
|
|
line=param_token.line, column=param_token.column
|
|
))
|
|
|
|
if not self.match(TokenType.COMMA):
|
|
break
|
|
self.advance()
|
|
|
|
return parameters
|
|
|
|
def parse_block(self) -> List[Statement]:
|
|
"""Parse a block of statements"""
|
|
statements = []
|
|
self.skip_newlines()
|
|
|
|
while not self.match(TokenType.DEDENT, TokenType.EOF):
|
|
stmt = self.parse_statement()
|
|
if stmt:
|
|
statements.append(stmt)
|
|
self.skip_newlines()
|
|
|
|
return statements
|
|
|
|
def parse_if_statement(self) -> IfStatement:
|
|
"""Parse if statement"""
|
|
token = self.expect(TokenType.IF)
|
|
condition = self.parse_expression()
|
|
self.expect(TokenType.COLON)
|
|
self.skip_newlines()
|
|
self.expect(TokenType.INDENT)
|
|
then_block = self.parse_block()
|
|
self.expect(TokenType.DEDENT)
|
|
|
|
# Elif blocks
|
|
elif_blocks = []
|
|
while self.match(TokenType.ELIF):
|
|
self.advance()
|
|
elif_condition = self.parse_expression()
|
|
self.expect(TokenType.COLON)
|
|
self.skip_newlines()
|
|
self.expect(TokenType.INDENT)
|
|
elif_block = self.parse_block()
|
|
self.expect(TokenType.DEDENT)
|
|
elif_blocks.append((elif_condition, elif_block))
|
|
|
|
# Else block
|
|
else_block = None
|
|
if self.match(TokenType.ELSE):
|
|
self.advance()
|
|
self.expect(TokenType.COLON)
|
|
self.skip_newlines()
|
|
self.expect(TokenType.INDENT)
|
|
else_block = self.parse_block()
|
|
self.expect(TokenType.DEDENT)
|
|
|
|
return IfStatement(condition, then_block, elif_blocks, else_block, line=token.line, column=token.column)
|
|
|
|
def parse_for_loop(self) -> ForLoop:
|
|
"""Parse for loop"""
|
|
token = self.expect(TokenType.FOR)
|
|
variable = self.expect(TokenType.IDENTIFIER).value
|
|
|
|
# Check for 'in' keyword
|
|
if not (self.match(TokenType.IDENTIFIER) and self.current_token().value == 'in'):
|
|
raise SyntaxError(f"Expected 'in' in for loop at {self.current_token().line}:{self.current_token().column}")
|
|
self.advance()
|
|
|
|
iterable = self.parse_expression()
|
|
self.expect(TokenType.COLON)
|
|
self.skip_newlines()
|
|
self.expect(TokenType.INDENT)
|
|
body = self.parse_block()
|
|
self.expect(TokenType.DEDENT)
|
|
|
|
return ForLoop(variable, iterable, body, line=token.line, column=token.column)
|
|
|
|
def parse_while_loop(self) -> WhileLoop:
|
|
"""Parse while loop"""
|
|
token = self.expect(TokenType.WHILE)
|
|
condition = self.parse_expression()
|
|
self.expect(TokenType.COLON)
|
|
self.skip_newlines()
|
|
self.expect(TokenType.INDENT)
|
|
body = self.parse_block()
|
|
self.expect(TokenType.DEDENT)
|
|
|
|
return WhileLoop(condition, body, line=token.line, column=token.column)
|
|
|
|
def parse_return_statement(self) -> ReturnStatement:
|
|
"""Parse return statement"""
|
|
token = self.expect(TokenType.RETURN)
|
|
|
|
value = None
|
|
if not self.match(TokenType.NEWLINE, TokenType.EOF, TokenType.DEDENT):
|
|
value = self.parse_expression()
|
|
|
|
return ReturnStatement(value, line=token.line, column=token.column)
|
|
|
|
def parse_expression_or_assignment(self) -> Union[ExpressionStatement, Assignment, CompoundAssignment]:
|
|
"""Parse expression statement or assignment"""
|
|
expr = self.parse_expression()
|
|
|
|
# Check for assignment
|
|
if self.match(TokenType.ASSIGN):
|
|
token = self.advance()
|
|
value = self.parse_expression()
|
|
return Assignment(expr, value, line=token.line, column=token.column)
|
|
|
|
# Check for compound assignment
|
|
if self.match(TokenType.PLUS_ASSIGN, TokenType.MINUS_ASSIGN,
|
|
TokenType.STAR_ASSIGN, TokenType.SLASH_ASSIGN):
|
|
token = self.advance()
|
|
value = self.parse_expression()
|
|
return CompoundAssignment(expr, token.value, value, line=token.line, column=token.column)
|
|
|
|
return ExpressionStatement(expr, line=expr.line, column=expr.column)
|
|
|
|
# ========================================================================
|
|
# Type Parsing
|
|
# ========================================================================
|
|
|
|
def parse_type(self) -> TypeNode:
|
|
"""Parse type annotation"""
|
|
token = self.current_token()
|
|
|
|
# Primitive types
|
|
if self.match(TokenType.INT, TokenType.FLOAT_TYPE, TokenType.STRING_TYPE,
|
|
TokenType.BOOL_TYPE, TokenType.BYTE, TokenType.CHAR,
|
|
TokenType.COLOR_TYPE, TokenType.ANY):
|
|
name = self.advance().value
|
|
return PrimitiveType(name, line=token.line, column=token.column)
|
|
|
|
# Vector types
|
|
if self.match(TokenType.VEC2):
|
|
self.advance()
|
|
return VectorType(2, line=token.line, column=token.column)
|
|
if self.match(TokenType.VEC3):
|
|
self.advance()
|
|
return VectorType(3, line=token.line, column=token.column)
|
|
if self.match(TokenType.VEC4):
|
|
self.advance()
|
|
return VectorType(4, line=token.line, column=token.column)
|
|
|
|
# Collection types
|
|
if self.match(TokenType.LIST):
|
|
self.advance()
|
|
self.expect(TokenType.LBRACKET)
|
|
element_type = self.parse_type()
|
|
self.expect(TokenType.RBRACKET)
|
|
return ListType(element_type, line=token.line, column=token.column)
|
|
|
|
if self.match(TokenType.DICT):
|
|
self.advance()
|
|
self.expect(TokenType.LBRACKET)
|
|
key_type = self.parse_type()
|
|
self.expect(TokenType.COMMA)
|
|
value_type = self.parse_type()
|
|
self.expect(TokenType.RBRACKET)
|
|
return DictType(key_type, value_type, line=token.line, column=token.column)
|
|
|
|
if self.match(TokenType.SET):
|
|
self.advance()
|
|
self.expect(TokenType.LBRACKET)
|
|
element_type = self.parse_type()
|
|
self.expect(TokenType.RBRACKET)
|
|
return SetType(element_type, line=token.line, column=token.column)
|
|
|
|
# Custom type
|
|
if self.match(TokenType.IDENTIFIER):
|
|
name = self.advance().value
|
|
return CustomType(name, line=token.line, column=token.column)
|
|
|
|
raise SyntaxError(f"Expected type at {token.line}:{token.column}")
|
|
|
|
# ========================================================================
|
|
# Expression Parsing (Operator Precedence)
|
|
# ========================================================================
|
|
|
|
def parse_expression(self) -> Expression:
|
|
"""Parse expression (top level - lowest precedence)"""
|
|
return self.parse_or_expression()
|
|
|
|
def parse_or_expression(self) -> Expression:
|
|
"""Parse logical OR expression"""
|
|
left = self.parse_and_expression()
|
|
|
|
while self.match(TokenType.OR):
|
|
op_token = self.advance()
|
|
right = self.parse_and_expression()
|
|
left = BinaryOp(left, 'or', right, line=op_token.line, column=op_token.column)
|
|
|
|
return left
|
|
|
|
def parse_and_expression(self) -> Expression:
|
|
"""Parse logical AND expression"""
|
|
left = self.parse_not_expression()
|
|
|
|
while self.match(TokenType.AND):
|
|
op_token = self.advance()
|
|
right = self.parse_not_expression()
|
|
left = BinaryOp(left, 'and', right, line=op_token.line, column=op_token.column)
|
|
|
|
return left
|
|
|
|
def parse_not_expression(self) -> Expression:
|
|
"""Parse logical NOT expression"""
|
|
if self.match(TokenType.NOT):
|
|
op_token = self.advance()
|
|
operand = self.parse_not_expression()
|
|
return UnaryOp('not', operand, line=op_token.line, column=op_token.column)
|
|
|
|
return self.parse_comparison_expression()
|
|
|
|
def parse_comparison_expression(self) -> Expression:
|
|
"""Parse comparison expression"""
|
|
left = self.parse_additive_expression()
|
|
|
|
while self.match(TokenType.EQ, TokenType.NE, TokenType.LT,
|
|
TokenType.GT, TokenType.LE, TokenType.GE):
|
|
op_token = self.advance()
|
|
right = self.parse_additive_expression()
|
|
left = BinaryOp(left, op_token.value, right, line=op_token.line, column=op_token.column)
|
|
|
|
return left
|
|
|
|
def parse_additive_expression(self) -> Expression:
|
|
"""Parse addition/subtraction expression"""
|
|
left = self.parse_multiplicative_expression()
|
|
|
|
while self.match(TokenType.PLUS, TokenType.MINUS):
|
|
op_token = self.advance()
|
|
right = self.parse_multiplicative_expression()
|
|
left = BinaryOp(left, op_token.value, right, line=op_token.line, column=op_token.column)
|
|
|
|
return left
|
|
|
|
def parse_multiplicative_expression(self) -> Expression:
|
|
"""Parse multiplication/division expression"""
|
|
left = self.parse_power_expression()
|
|
|
|
while self.match(TokenType.STAR, TokenType.SLASH, TokenType.PERCENT):
|
|
op_token = self.advance()
|
|
right = self.parse_power_expression()
|
|
left = BinaryOp(left, op_token.value, right, line=op_token.line, column=op_token.column)
|
|
|
|
return left
|
|
|
|
def parse_power_expression(self) -> Expression:
|
|
"""Parse exponentiation (right-associative)"""
|
|
base = self.parse_unary_expression()
|
|
|
|
if self.match(TokenType.POWER):
|
|
op_token = self.advance()
|
|
exponent = self.parse_power_expression() # right-associative
|
|
return BinaryOp(base, op_token.value, exponent, line=op_token.line, column=op_token.column)
|
|
|
|
return base
|
|
|
|
def parse_unary_expression(self) -> Expression:
|
|
"""Parse unary expression"""
|
|
if self.match(TokenType.MINUS, TokenType.PLUS):
|
|
op_token = self.advance()
|
|
operand = self.parse_unary_expression()
|
|
return UnaryOp(op_token.value, operand, line=op_token.line, column=op_token.column)
|
|
|
|
# Await expression
|
|
if self.match(TokenType.AWAIT):
|
|
op_token = self.advance()
|
|
operand = self.parse_unary_expression()
|
|
return AwaitExpression(operand, line=op_token.line, column=op_token.column)
|
|
|
|
return self.parse_postfix_expression()
|
|
|
|
def parse_postfix_expression(self) -> Expression:
|
|
"""Parse postfix expression (function call, member access, indexing)"""
|
|
expr = self.parse_primary_expression()
|
|
|
|
while True:
|
|
# Function call
|
|
if self.match(TokenType.LPAREN):
|
|
token = self.advance()
|
|
arguments = self.parse_arguments()
|
|
self.expect(TokenType.RPAREN)
|
|
expr = FunctionCall(expr, arguments, line=token.line, column=token.column)
|
|
|
|
# Member access
|
|
elif self.match(TokenType.DOT):
|
|
token = self.advance()
|
|
member = self.expect(TokenType.IDENTIFIER).value
|
|
expr = MemberAccess(expr, member, line=token.line, column=token.column)
|
|
|
|
# Index access
|
|
elif self.match(TokenType.LBRACKET):
|
|
token = self.advance()
|
|
index = self.parse_expression()
|
|
self.expect(TokenType.RBRACKET)
|
|
expr = IndexAccess(expr, index, line=token.line, column=token.column)
|
|
|
|
else:
|
|
break
|
|
|
|
return expr
|
|
|
|
def parse_arguments(self) -> List[Expression]:
|
|
"""Parse function call arguments"""
|
|
arguments = []
|
|
|
|
if self.match(TokenType.RPAREN):
|
|
return arguments
|
|
|
|
while True:
|
|
arguments.append(self.parse_expression())
|
|
|
|
if not self.match(TokenType.COMMA):
|
|
break
|
|
self.advance()
|
|
|
|
return arguments
|
|
|
|
def parse_primary_expression(self) -> Expression:
|
|
"""Parse primary expression (literals, identifiers, parenthesized)"""
|
|
token = self.current_token()
|
|
|
|
# Integer literal
|
|
if self.match(TokenType.INTEGER):
|
|
self.advance()
|
|
return IntegerLiteral(token.value, line=token.line, column=token.column)
|
|
|
|
# Float literal
|
|
if self.match(TokenType.FLOAT):
|
|
self.advance()
|
|
return FloatLiteral(token.value, line=token.line, column=token.column)
|
|
|
|
# String literal
|
|
if self.match(TokenType.STRING):
|
|
self.advance()
|
|
return StringLiteral(token.value, line=token.line, column=token.column)
|
|
|
|
# Boolean literal
|
|
if self.match(TokenType.BOOLEAN):
|
|
self.advance()
|
|
return BooleanLiteral(token.value, line=token.line, column=token.column)
|
|
|
|
# Color literal
|
|
if self.match(TokenType.COLOR):
|
|
self.advance()
|
|
return ColorLiteral(token.value, line=token.line, column=token.column)
|
|
|
|
# Vector constructor: vec3(1, 2, 3)
|
|
if self.match(TokenType.VEC2, TokenType.VEC3, TokenType.VEC4):
|
|
vec_token = self.advance()
|
|
dimension = {'vec2': 2, 'vec3': 3, 'vec4': 4}[vec_token.value]
|
|
self.expect(TokenType.LPAREN)
|
|
components = []
|
|
while not self.match(TokenType.RPAREN):
|
|
components.append(self.parse_expression())
|
|
if self.match(TokenType.COMMA):
|
|
self.advance()
|
|
self.expect(TokenType.RPAREN)
|
|
return VectorLiteral(dimension, components, line=vec_token.line, column=vec_token.column)
|
|
|
|
# List literal: [1, 2, 3]
|
|
if self.match(TokenType.LBRACKET):
|
|
self.advance()
|
|
elements = []
|
|
while not self.match(TokenType.RBRACKET):
|
|
elements.append(self.parse_expression())
|
|
if self.match(TokenType.COMMA):
|
|
self.advance()
|
|
self.expect(TokenType.RBRACKET)
|
|
return ListLiteral(elements, line=token.line, column=token.column)
|
|
|
|
# Dict literal: {key: value} or Set literal: {1, 2, 3}
|
|
if self.match(TokenType.LBRACE):
|
|
self.advance()
|
|
|
|
# Empty dict
|
|
if self.match(TokenType.RBRACE):
|
|
self.advance()
|
|
return DictLiteral([], line=token.line, column=token.column)
|
|
|
|
# Check first element
|
|
first_expr = self.parse_expression()
|
|
|
|
# Dictionary (has colon)
|
|
if self.match(TokenType.COLON):
|
|
self.advance()
|
|
first_value = self.parse_expression()
|
|
pairs = [(first_expr, first_value)]
|
|
|
|
while self.match(TokenType.COMMA):
|
|
self.advance()
|
|
key = self.parse_expression()
|
|
self.expect(TokenType.COLON)
|
|
value = self.parse_expression()
|
|
pairs.append((key, value))
|
|
|
|
self.expect(TokenType.RBRACE)
|
|
return DictLiteral(pairs, line=token.line, column=token.column)
|
|
|
|
# Set (no colon)
|
|
else:
|
|
elements = [first_expr]
|
|
while self.match(TokenType.COMMA):
|
|
self.advance()
|
|
elements.append(self.parse_expression())
|
|
|
|
self.expect(TokenType.RBRACE)
|
|
return SetLiteral(elements, line=token.line, column=token.column)
|
|
|
|
# Parenthesized expression or tuple: (expr) or (1, 2, 3)
|
|
if self.match(TokenType.LPAREN):
|
|
self.advance()
|
|
|
|
# Empty tuple
|
|
if self.match(TokenType.RPAREN):
|
|
self.advance()
|
|
return TupleLiteral([], line=token.line, column=token.column)
|
|
|
|
first_expr = self.parse_expression()
|
|
|
|
# Tuple (has comma)
|
|
if self.match(TokenType.COMMA):
|
|
elements = [first_expr]
|
|
while self.match(TokenType.COMMA):
|
|
self.advance()
|
|
if not self.match(TokenType.RPAREN):
|
|
elements.append(self.parse_expression())
|
|
self.expect(TokenType.RPAREN)
|
|
return TupleLiteral(elements, line=token.line, column=token.column)
|
|
|
|
# Just parenthesized expression
|
|
self.expect(TokenType.RPAREN)
|
|
return first_expr
|
|
|
|
# Range: 0..10
|
|
if self.match(TokenType.INTEGER):
|
|
start = IntegerLiteral(token.value, line=token.line, column=token.column)
|
|
self.advance()
|
|
if self.match(TokenType.DOUBLE_DOT):
|
|
self.advance()
|
|
end_token = self.expect(TokenType.INTEGER)
|
|
end = IntegerLiteral(end_token.value, line=end_token.line, column=end_token.column)
|
|
return RangeExpression(start, end, line=token.line, column=token.column)
|
|
return start
|
|
|
|
# Identifier
|
|
if self.match(TokenType.IDENTIFIER):
|
|
self.advance()
|
|
return Identifier(token.value, line=token.line, column=token.column)
|
|
|
|
raise SyntaxError(f"Unexpected token {token.type.name} at {token.line}:{token.column}")
|
|
|
|
# ========================================================================
|
|
# 3D/Spatial Parsing
|
|
# ========================================================================
|
|
|
|
def parse_space_definition(self) -> SpaceDefinition:
|
|
"""Parse 3D space definition"""
|
|
token = self.expect(TokenType.SPACE)
|
|
name = self.expect(TokenType.IDENTIFIER).value
|
|
self.expect(TokenType.COLON)
|
|
self.skip_newlines()
|
|
self.expect(TokenType.INDENT)
|
|
|
|
objects = []
|
|
while not self.match(TokenType.DEDENT, TokenType.EOF):
|
|
self.skip_newlines()
|
|
if self.match(TokenType.CUBE, TokenType.SPHERE, TokenType.PLANE,
|
|
TokenType.LIGHT, TokenType.CAMERA):
|
|
objects.append(self.parse_3d_object())
|
|
else:
|
|
break
|
|
|
|
self.expect(TokenType.DEDENT)
|
|
return SpaceDefinition(name, objects, line=token.line, column=token.column)
|
|
|
|
def parse_3d_object(self) -> Object3D:
|
|
"""Parse 3D object"""
|
|
token = self.current_token()
|
|
obj_type = self.advance().type
|
|
name = self.expect(TokenType.IDENTIFIER).value
|
|
self.expect(TokenType.COLON)
|
|
self.skip_newlines()
|
|
self.expect(TokenType.INDENT)
|
|
|
|
properties = []
|
|
while not self.match(TokenType.DEDENT, TokenType.EOF):
|
|
self.skip_newlines()
|
|
# Accept identifiers or type keywords as property names
|
|
if self.match(TokenType.IDENTIFIER, TokenType.COLOR_TYPE, TokenType.INT,
|
|
TokenType.FLOAT_TYPE, TokenType.STRING_TYPE, TokenType.BOOL_TYPE,
|
|
TokenType.VEC2, TokenType.VEC3, TokenType.VEC4):
|
|
prop_name_token = self.advance()
|
|
prop_name = prop_name_token.value
|
|
self.expect(TokenType.COLON)
|
|
prop_value = self.parse_expression()
|
|
properties.append(Property3D(prop_name, prop_value, line=prop_name_token.line, column=prop_name_token.column))
|
|
self.skip_newlines()
|
|
else:
|
|
break
|
|
|
|
self.expect(TokenType.DEDENT)
|
|
|
|
# Create appropriate object type
|
|
if obj_type == TokenType.CUBE:
|
|
return CubeObject(name, properties, line=token.line, column=token.column)
|
|
elif obj_type == TokenType.SPHERE:
|
|
return SphereObject(name, properties, line=token.line, column=token.column)
|
|
elif obj_type == TokenType.PLANE:
|
|
return PlaneObject(name, properties, line=token.line, column=token.column)
|
|
elif obj_type == TokenType.LIGHT:
|
|
return LightObject(name, properties, line=token.line, column=token.column)
|
|
elif obj_type == TokenType.CAMERA:
|
|
return CameraObject(name, properties, line=token.line, column=token.column)
|
|
|
|
# Stubs for other complex features
|
|
def parse_type_definition(self) -> TypeDefinition:
|
|
"""Parse type definition (stub)"""
|
|
# TODO: Implement full type definition parsing
|
|
pass
|
|
|
|
def parse_match_statement(self) -> MatchStatement:
|
|
"""Parse match statement (stub)"""
|
|
# TODO: Implement match statement parsing
|
|
pass
|
|
|
|
def parse_spawn_statement(self) -> SpawnStatement:
|
|
"""Parse spawn statement (stub)"""
|
|
# TODO: Implement spawn statement parsing
|
|
pass
|
|
|
|
|
|
def parse(source: str, filename: str = "<stdin>") -> Program:
|
|
"""Convenience function to parse source code"""
|
|
tokens = lex(source, filename)
|
|
parser = Parser(tokens)
|
|
return parser.parse_program()
|
|
|
|
|
|
# Example usage
|
|
if __name__ == "__main__":
|
|
test_code = '''
|
|
let x: int = 42
|
|
let name = "BlackRoad"
|
|
|
|
fun greet(name: string) -> string:
|
|
return "Hello, {name}!"
|
|
|
|
space MyScene:
|
|
cube Box1:
|
|
position: vec3(0, 0, 0)
|
|
color: #FF1D6C
|
|
'''
|
|
|
|
ast = parse(test_code)
|
|
print("AST generated successfully!")
|
|
print(f"Program has {len(ast.statements)} statements")
|
|
for i, stmt in enumerate(ast.statements):
|
|
print(f"{i+1}. {stmt.__class__.__name__}")
|