package parser import ( "fmt" "strconv" "strings" ) // ParseError represents a parsing error with location information type ParseError struct { Message string Line int Column int Token Token } func (pe ParseError) Error() string { return fmt.Sprintf("Parse error at line %d, column %d: %s (near '%s')", pe.Line, pe.Column, pe.Message, pe.Token.Literal) } // Parser implements a recursive descent Pratt parser with optimized AST generation type Parser struct { lexer *Lexer curToken Token peekToken Token prefixParseFns map[TokenType]func() Expression infixParseFns map[TokenType]func(Expression) Expression errors []ParseError // Scope tracking with slot management scopes []map[string]*Variable scopeTypes []string scopeSlots []int // Next available slot per scope currentDepth int // Struct tracking with ID mapping structs map[string]*StructStatement structIDs map[uint16]*StructStatement nextID uint16 } // Variable represents a resolved variable in scope type Variable struct { Name string ScopeDepth int SlotIndex int TypeHint TypeInfo } // NewParser creates a new parser instance func NewParser(lexer *Lexer) *Parser { p := &Parser{ lexer: lexer, errors: []ParseError{}, scopes: []map[string]*Variable{make(map[string]*Variable)}, scopeTypes: []string{"global"}, scopeSlots: []int{0}, currentDepth: 0, structs: make(map[string]*StructStatement), structIDs: make(map[uint16]*StructStatement), nextID: 1, // 0 reserved for non-struct types } p.prefixParseFns = make(map[TokenType]func() Expression) p.registerPrefix(IDENT, p.parseIdentifier) p.registerPrefix(NUMBER, p.parseNumberLiteral) p.registerPrefix(STRING, p.parseStringLiteral) p.registerPrefix(TRUE, p.parseBooleanLiteral) p.registerPrefix(FALSE, p.parseBooleanLiteral) p.registerPrefix(NIL, p.parseNilLiteral) p.registerPrefix(LPAREN, p.parseGroupedExpression) p.registerPrefix(LBRACE, p.parseTableLiteral) p.registerPrefix(MINUS, p.parsePrefixExpression) p.registerPrefix(NOT, p.parsePrefixExpression) p.registerPrefix(FN, p.parseFunctionLiteral) p.infixParseFns = make(map[TokenType]func(Expression) Expression) p.registerInfix(PLUS, p.parseInfixExpression) p.registerInfix(MINUS, p.parseInfixExpression) p.registerInfix(SLASH, p.parseInfixExpression) p.registerInfix(STAR, p.parseInfixExpression) p.registerInfix(MOD, p.parseInfixExpression) p.registerInfix(EQ, p.parseInfixExpression) p.registerInfix(NOT_EQ, p.parseInfixExpression) p.registerInfix(LT, p.parseInfixExpression) p.registerInfix(GT, p.parseInfixExpression) p.registerInfix(LT_EQ, p.parseInfixExpression) p.registerInfix(GT_EQ, p.parseInfixExpression) p.registerInfix(AND, p.parseInfixExpression) p.registerInfix(OR, p.parseInfixExpression) p.registerInfix(DOT, p.parseDotExpression) p.registerInfix(LBRACKET, p.parseIndexExpression) p.registerInfix(LPAREN, p.parseCallExpression) p.registerInfix(LBRACE, p.parseStructConstructor) p.nextToken() p.nextToken() return p } // Helper to create position from token func (p *Parser) pos() Position { return Position{Line: p.curToken.Line, Column: p.curToken.Column} } // Struct management func (p *Parser) registerStruct(stmt *StructStatement) { stmt.ID = p.nextID p.nextID++ p.structs[stmt.Name] = stmt p.structIDs[stmt.ID] = stmt } func (p *Parser) getStructByName(name string) *StructStatement { return p.structs[name] } func (p *Parser) isStructDefined(name string) bool { _, exists := p.structs[name] return exists } // Scope management with slot allocation func (p *Parser) enterScope(scopeType string) { p.scopes = append(p.scopes, make(map[string]*Variable)) p.scopeTypes = append(p.scopeTypes, scopeType) p.scopeSlots = append(p.scopeSlots, 0) p.currentDepth++ } func (p *Parser) exitScope() { if len(p.scopes) > 1 { p.scopes = p.scopes[:len(p.scopes)-1] p.scopeTypes = p.scopeTypes[:len(p.scopeTypes)-1] p.scopeSlots = p.scopeSlots[:len(p.scopeSlots)-1] p.currentDepth-- } } func (p *Parser) currentVariableScope() map[string]*Variable { if len(p.scopeTypes) > 1 && p.scopeTypes[len(p.scopeTypes)-1] == "loop" { return p.scopes[len(p.scopes)-2] } return p.scopes[len(p.scopes)-1] } func (p *Parser) isVariableDeclared(name string) bool { for i := len(p.scopes) - 1; i >= 0; i-- { if _, exists := p.scopes[i][name]; exists { return true } } return false } func (p *Parser) declareVariable(name string, typeHint TypeInfo) *Variable { scope := p.currentVariableScope() scopeIdx := len(p.scopes) - 1 if len(p.scopeTypes) > 1 && p.scopeTypes[len(p.scopeTypes)-1] == "loop" { scopeIdx-- } variable := &Variable{ Name: name, ScopeDepth: p.currentDepth, SlotIndex: p.scopeSlots[scopeIdx], TypeHint: typeHint, } scope[name] = variable p.scopeSlots[scopeIdx]++ return variable } func (p *Parser) declareLoopVariable(name string, typeHint TypeInfo) *Variable { scope := p.scopes[len(p.scopes)-1] scopeIdx := len(p.scopes) - 1 variable := &Variable{ Name: name, ScopeDepth: p.currentDepth, SlotIndex: p.scopeSlots[scopeIdx], TypeHint: typeHint, } scope[name] = variable p.scopeSlots[scopeIdx]++ return variable } func (p *Parser) lookupVariable(name string) *Variable { for i := len(p.scopes) - 1; i >= 0; i-- { if variable, exists := p.scopes[i][name]; exists { return variable } } return nil } // parseTypeHint parses optional type hint after colon, returns by value func (p *Parser) parseTypeHint() TypeInfo { if !p.peekTokenIs(COLON) { return UnknownType } p.nextToken() // consume ':' if !p.expectPeekIdent() { p.addError("expected type name after ':'") return UnknownType } typeName := p.curToken.Literal // Check built-in types switch typeName { case "number": return TypeInfo{Type: TypeNumber, Inferred: false} case "string": return TypeInfo{Type: TypeString, Inferred: false} case "bool": return TypeInfo{Type: TypeBool, Inferred: false} case "nil": return TypeInfo{Type: TypeNil, Inferred: false} case "table": return TypeInfo{Type: TypeTable, Inferred: false} case "function": return TypeInfo{Type: TypeFunction, Inferred: false} case "any": return TypeInfo{Type: TypeAny, Inferred: false} default: // Check if it's a struct type if structDef := p.getStructByName(typeName); structDef != nil { return TypeInfo{Type: TypeStruct, StructID: structDef.ID, Inferred: false} } p.addError(fmt.Sprintf("invalid type name '%s'", typeName)) return UnknownType } } // registerPrefix/registerInfix func (p *Parser) registerPrefix(tokenType TokenType, fn func() Expression) { p.prefixParseFns[tokenType] = fn } func (p *Parser) registerInfix(tokenType TokenType, fn func(Expression) Expression) { p.infixParseFns[tokenType] = fn } func (p *Parser) nextToken() { p.curToken = p.peekToken p.peekToken = p.lexer.NextToken() } // ParseProgram parses the entire program func (p *Parser) ParseProgram() *Program { program := &Program{Position: p.pos()} program.Statements = []Statement{} for !p.curTokenIs(EOF) { stmt := p.parseStatement() if stmt != nil { program.Statements = append(program.Statements, stmt) } p.nextToken() } return program } // parseStatement parses a statement func (p *Parser) parseStatement() Statement { switch p.curToken.Type { case STRUCT: return p.parseStructStatement() case FN: return p.parseFunctionStatement() case IDENT: return p.parseIdentifierStatement() case IF: return p.parseIfStatement() case FOR: return p.parseForStatement() case WHILE: return p.parseWhileStatement() case ECHO: return p.parseEchoStatement() case BREAK: return p.parseBreakStatement() case EXIT: return p.parseExitStatement() case RETURN: return p.parseReturnStatement() case ASSIGN: p.addError("assignment operator '=' without left-hand side identifier") return nil case ILLEGAL: p.addError(fmt.Sprintf("unexpected token '%s'", p.curToken.Literal)) return nil case EOF: return nil default: p.addError(fmt.Sprintf("unexpected token '%s', expected statement", p.curToken.Literal)) return nil } } // parseStructStatement parses struct definitions func (p *Parser) parseStructStatement() *StructStatement { stmt := &StructStatement{Position: p.pos()} if !p.expectPeek(IDENT) { p.addError("expected struct name") return nil } stmt.Name = p.curToken.Literal if !p.expectPeek(LBRACE) { p.addError("expected '{' after struct name") return nil } stmt.Fields = []StructField{} if p.peekTokenIs(RBRACE) { p.nextToken() p.registerStruct(stmt) return stmt } p.nextToken() for { if p.curTokenIs(EOF) { p.addError("unexpected end of input, expected }") return nil } if !p.curTokenIs(IDENT) { p.addError("expected field name") return nil } field := StructField{Name: p.curToken.Literal, Position: p.pos()} // Parse required type hint field.TypeHint = p.parseTypeHint() if field.TypeHint.Type == TypeUnknown { p.addError("struct fields require type annotation") return nil } stmt.Fields = append(stmt.Fields, field) if !p.peekTokenIs(COMMA) { break } p.nextToken() // consume comma p.nextToken() // move to next field if p.curTokenIs(RBRACE) { break } if p.curTokenIs(EOF) { p.addError("expected next token to be }") return nil } } if !p.expectPeek(RBRACE) { return nil } p.registerStruct(stmt) return stmt } // parseFunctionStatement handles both regular functions and methods func (p *Parser) parseFunctionStatement() Statement { pos := p.pos() if !p.expectPeek(IDENT) { p.addError("expected function name") return nil } funcName := p.curToken.Literal // Check if this is a method definition (struct.method) if p.peekTokenIs(DOT) { p.nextToken() // consume '.' if !p.expectPeek(IDENT) { p.addError("expected method name after '.'") return nil } methodName := p.curToken.Literal // Get struct ID structDef := p.getStructByName(funcName) if structDef == nil { p.addError(fmt.Sprintf("method defined on undefined struct '%s'", funcName)) return nil } if !p.expectPeek(LPAREN) { p.addError("expected '(' after method name") return nil } // Parse the function literal funcLit := &FunctionLiteral{Position: p.pos()} funcLit.Parameters, funcLit.Variadic = p.parseFunctionParameters() if !p.expectPeek(RPAREN) { p.addError("expected ')' after function parameters") return nil } // Check for return type hint funcLit.ReturnType = p.parseTypeHint() p.nextToken() p.enterScope("function") for _, param := range funcLit.Parameters { p.declareVariable(param.Name, param.TypeHint) } funcLit.Body = p.parseBlockStatements(END) p.exitScope() if !p.curTokenIs(END) { p.addError("expected 'end' to close function") return nil } return &MethodDefinition{ StructID: structDef.ID, MethodName: methodName, Function: funcLit, Position: pos, } } // Regular function - handle as function literal expression statement funcLit := p.parseFunctionLiteral() if funcLit == nil { return nil } return &ExpressionStatement{Expression: funcLit, Position: pos} } // parseIdentifierStatement handles assignments and expression statements func (p *Parser) parseIdentifierStatement() Statement { pos := p.pos() // Parse the left-hand side expression first expr := p.ParseExpression(LOWEST) if expr == nil { return nil } // Check for type hint (only valid on simple identifiers) var typeHint TypeInfo = UnknownType if _, ok := expr.(*Identifier); ok { typeHint = p.parseTypeHint() } // Check if this is an assignment if p.peekTokenIs(ASSIGN) { // Create unified assignment assignment := &Assignment{ Target: expr, TypeHint: typeHint, Position: pos, } // Validate assignment target and check if it's a declaration switch target := expr.(type) { case *Identifier: if variable := p.lookupVariable(target.Value); variable != nil { // Existing variable - resolve it target.ScopeDepth = variable.ScopeDepth target.SlotIndex = variable.SlotIndex assignment.IsDeclaration = false } else { // New variable declaration - create and resolve variable := p.declareVariable(target.Value, typeHint) target.ScopeDepth = variable.ScopeDepth target.SlotIndex = variable.SlotIndex assignment.IsDeclaration = true } case *DotExpression, *IndexExpression: assignment.IsDeclaration = false default: p.addError("invalid assignment target") return nil } if !p.expectPeek(ASSIGN) { return nil } p.nextToken() assignment.Value = p.ParseExpression(LOWEST) if assignment.Value == nil { p.addError("expected expression after assignment operator") return nil } return assignment } else { // This is an expression statement return &ExpressionStatement{Expression: expr, Position: pos} } } // parseEchoStatement parses echo statements func (p *Parser) parseEchoStatement() *EchoStatement { stmt := &EchoStatement{Position: p.pos()} p.nextToken() // move past 'echo' stmt.Value = p.ParseExpression(LOWEST) if stmt.Value == nil { p.addError("expected expression after 'echo'") return nil } return stmt } // Simple statement parsers func (p *Parser) parseBreakStatement() *BreakStatement { if p.peekTokenIs(IDENT) { p.addError("unexpected identifier") return nil } return &BreakStatement{Position: p.pos()} } func (p *Parser) parseExitStatement() *ExitStatement { stmt := &ExitStatement{Position: p.pos()} if p.canStartExpression(p.peekToken.Type) { p.nextToken() stmt.Value = p.ParseExpression(LOWEST) if stmt.Value == nil { p.addError("expected expression after 'exit'") return nil } } return stmt } func (p *Parser) parseReturnStatement() *ReturnStatement { stmt := &ReturnStatement{Position: p.pos()} if p.canStartExpression(p.peekToken.Type) { p.nextToken() stmt.Value = p.ParseExpression(LOWEST) if stmt.Value == nil { p.addError("expected expression after 'return'") return nil } } return stmt } func (p *Parser) canStartExpression(tokenType TokenType) bool { switch tokenType { case IDENT, NUMBER, STRING, TRUE, FALSE, NIL, LPAREN, LBRACE, MINUS, NOT, FN: return true default: return false } } // Loop statement parsers func (p *Parser) parseWhileStatement() *WhileStatement { stmt := &WhileStatement{Position: p.pos()} p.nextToken() stmt.Condition = p.ParseExpression(LOWEST) if stmt.Condition == nil { p.addError("expected condition after 'while'") return nil } if !p.expectPeek(DO) { p.addError("expected 'do' after while condition") return nil } p.nextToken() stmt.Body = p.parseBlockStatements(END) if !p.curTokenIs(END) { p.addError("expected 'end' to close while loop") return nil } return stmt } func (p *Parser) parseForStatement() Statement { pos := p.pos() p.nextToken() if !p.curTokenIs(IDENT) { p.addError("expected identifier after 'for'") return nil } firstVar := &Identifier{Value: p.curToken.Literal, Position: p.pos()} if p.peekTokenIs(ASSIGN) { return p.parseNumericForStatement(firstVar, pos) } else if p.peekTokenIs(COMMA) || p.peekTokenIs(IN) { return p.parseForInStatement(firstVar, pos) } else { p.addError("expected '=', ',' or 'in' after for loop variable") return nil } } func (p *Parser) parseNumericForStatement(variable *Identifier, pos Position) *ForStatement { stmt := &ForStatement{Variable: variable, Position: pos} if !p.expectPeek(ASSIGN) { return nil } p.nextToken() stmt.Start = p.ParseExpression(LOWEST) if stmt.Start == nil { p.addError("expected start expression in for loop") return nil } if !p.expectPeek(COMMA) { p.addError("expected ',' after start expression in for loop") return nil } p.nextToken() stmt.End = p.ParseExpression(LOWEST) if stmt.End == nil { p.addError("expected end expression in for loop") return nil } if p.peekTokenIs(COMMA) { p.nextToken() p.nextToken() stmt.Step = p.ParseExpression(LOWEST) if stmt.Step == nil { p.addError("expected step expression in for loop") return nil } } if !p.expectPeek(DO) { p.addError("expected 'do' after for loop header") return nil } p.nextToken() p.enterScope("loop") // Declare and resolve loop variable loopVar := p.declareLoopVariable(variable.Value, NumberType) variable.ScopeDepth = loopVar.ScopeDepth variable.SlotIndex = loopVar.SlotIndex variable.typeInfo = NumberType stmt.Body = p.parseBlockStatements(END) p.exitScope() if !p.curTokenIs(END) { p.addError("expected 'end' to close for loop") return nil } return stmt } func (p *Parser) parseForInStatement(firstVar *Identifier, pos Position) *ForInStatement { stmt := &ForInStatement{Position: pos} if p.peekTokenIs(COMMA) { stmt.Key = firstVar p.nextToken() p.nextToken() if !p.curTokenIs(IDENT) { p.addError("expected identifier after ',' in for loop") return nil } stmt.Value = &Identifier{Value: p.curToken.Literal, Position: p.pos()} } else { stmt.Value = firstVar } if !p.expectPeek(IN) { p.addError("expected 'in' in for loop") return nil } p.nextToken() stmt.Iterable = p.ParseExpression(LOWEST) if stmt.Iterable == nil { p.addError("expected expression after 'in' in for loop") return nil } if !p.expectPeek(DO) { p.addError("expected 'do' after for loop header") return nil } p.nextToken() p.enterScope("loop") // Declare and resolve loop variables if stmt.Key != nil { keyVar := p.declareLoopVariable(stmt.Key.Value, AnyType) stmt.Key.ScopeDepth = keyVar.ScopeDepth stmt.Key.SlotIndex = keyVar.SlotIndex stmt.Key.typeInfo = AnyType } valueVar := p.declareLoopVariable(stmt.Value.Value, AnyType) stmt.Value.ScopeDepth = valueVar.ScopeDepth stmt.Value.SlotIndex = valueVar.SlotIndex stmt.Value.typeInfo = AnyType stmt.Body = p.parseBlockStatements(END) p.exitScope() if !p.curTokenIs(END) { p.addError("expected 'end' to close for loop") return nil } return stmt } // parseIfStatement parses if statements func (p *Parser) parseIfStatement() *IfStatement { stmt := &IfStatement{Position: p.pos()} p.nextToken() stmt.Condition = p.ParseExpression(LOWEST) if stmt.Condition == nil { p.addError("expected condition after 'if'") return nil } if p.peekTokenIs(THEN) { p.nextToken() } p.nextToken() if p.curTokenIs(END) { p.addError("expected 'end' to close if statement") return nil } stmt.Body = p.parseBlockStatements(ELSEIF, ELSE, END) for p.curTokenIs(ELSEIF) { elseif := ElseIfClause{Position: p.pos()} p.nextToken() elseif.Condition = p.ParseExpression(LOWEST) if elseif.Condition == nil { p.addError("expected condition after 'elseif'") return nil } if p.peekTokenIs(THEN) { p.nextToken() } p.nextToken() elseif.Body = p.parseBlockStatements(ELSEIF, ELSE, END) stmt.ElseIfs = append(stmt.ElseIfs, elseif) } if p.curTokenIs(ELSE) { p.nextToken() stmt.Else = p.parseBlockStatements(END) } if !p.curTokenIs(END) { p.addError("expected 'end' to close if statement") return nil } return stmt } // parseBlockStatements parses statements until terminators func (p *Parser) parseBlockStatements(terminators ...TokenType) []Statement { statements := make([]Statement, 0, 8) // Pre-allocate for performance for !p.curTokenIs(EOF) && !p.isTerminator(terminators...) { stmt := p.parseStatement() if stmt != nil { statements = append(statements, stmt) } p.nextToken() } return statements } func (p *Parser) isTerminator(terminators ...TokenType) bool { for _, terminator := range terminators { if p.curTokenIs(terminator) { return true } } return false } // ParseExpression parses expressions using Pratt parsing func (p *Parser) ParseExpression(precedence Precedence) Expression { prefix := p.prefixParseFns[p.curToken.Type] if prefix == nil { p.noPrefixParseFnError(p.curToken.Type) return nil } leftExp := prefix() if leftExp == nil { return nil } for !p.peekTokenIs(EOF) && precedence < p.peekPrecedence() { infix := p.infixParseFns[p.peekToken.Type] if infix == nil { return leftExp } p.nextToken() leftExp = infix(leftExp) if leftExp == nil { return nil } } return leftExp } // Expression parsing functions func (p *Parser) parseIdentifier() Expression { ident := &Identifier{ Value: p.curToken.Literal, ScopeDepth: -1, SlotIndex: -1, Position: p.pos(), } // Resolve variable if it exists if variable := p.lookupVariable(ident.Value); variable != nil { ident.ScopeDepth = variable.ScopeDepth ident.SlotIndex = variable.SlotIndex ident.typeInfo = variable.TypeHint } return ident } func (p *Parser) parseNumberLiteral() Expression { lit := &NumberLiteral{Position: p.pos()} literal := p.curToken.Literal var value float64 var err error if strings.HasPrefix(literal, "0x") || strings.HasPrefix(literal, "0X") { if len(literal) <= 2 { p.addError(fmt.Sprintf("could not parse '%s' as hexadecimal number", literal)) return nil } hexPart := literal[2:] for _, ch := range hexPart { if !((ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F')) { p.addError(fmt.Sprintf("could not parse '%s' as hexadecimal number", literal)) return nil } } intVal, parseErr := strconv.ParseInt(literal, 0, 64) if parseErr != nil { p.addError(fmt.Sprintf("could not parse '%s' as hexadecimal number", literal)) return nil } value = float64(intVal) } else if strings.HasPrefix(literal, "0b") || strings.HasPrefix(literal, "0B") { if len(literal) <= 2 { p.addError(fmt.Sprintf("could not parse '%s' as binary number", literal)) return nil } binaryPart := literal[2:] for _, ch := range binaryPart { if ch != '0' && ch != '1' { p.addError(fmt.Sprintf("could not parse '%s' as binary number", literal)) return nil } } binaryStr := literal[2:] intVal, parseErr := strconv.ParseInt(binaryStr, 2, 64) if parseErr != nil { p.addError(fmt.Sprintf("could not parse '%s' as binary number", literal)) return nil } value = float64(intVal) } else { value, err = strconv.ParseFloat(literal, 64) if err != nil { p.addError(fmt.Sprintf("could not parse '%s' as number", literal)) return nil } } lit.Value = value return lit } func (p *Parser) parseStringLiteral() Expression { return &StringLiteral{Value: p.curToken.Literal, Position: p.pos()} } func (p *Parser) parseBooleanLiteral() Expression { return &BooleanLiteral{Value: p.curTokenIs(TRUE), Position: p.pos()} } func (p *Parser) parseNilLiteral() Expression { return &NilLiteral{Position: p.pos()} } func (p *Parser) parsePrefixExpression() Expression { expression := &PrefixExpression{ Operator: p.curToken.Literal, Position: p.pos(), } p.nextToken() expression.Right = p.ParseExpression(PREFIX) if expression.Right == nil { p.addError(fmt.Sprintf("expected expression after prefix operator '%s'", expression.Operator)) return nil } return expression } // parseGroupedExpression handles parentheses and assignment expressions func (p *Parser) parseGroupedExpression() Expression { p.nextToken() // Check if this is an assignment expression inside parentheses if p.curTokenIs(IDENT) && p.peekTokenIs(ASSIGN) { return p.parseParenthesizedAssignment() } // Regular grouped expression exp := p.ParseExpression(LOWEST) if exp == nil { return nil } if !p.expectPeek(RPAREN) { return nil } return exp } // parseParenthesizedAssignment parses assignment expressions in parentheses func (p *Parser) parseParenthesizedAssignment() Expression { pos := p.pos() target := p.parseIdentifier() if !p.expectPeek(ASSIGN) { return nil } p.nextToken() // move past = value := p.ParseExpression(LOWEST) if value == nil { p.addError("expected expression after assignment operator") return nil } if !p.expectPeek(RPAREN) { return nil } // Create assignment expression assignExpr := &Assignment{ Target: target, Value: value, IsExpression: true, Position: pos, } // Handle variable declaration for assignment expressions if ident, ok := target.(*Identifier); ok { if variable := p.lookupVariable(ident.Value); variable != nil { // Existing variable ident.ScopeDepth = variable.ScopeDepth ident.SlotIndex = variable.SlotIndex assignExpr.IsDeclaration = false } else { // New variable declaration newVar := p.declareVariable(ident.Value, UnknownType) ident.ScopeDepth = newVar.ScopeDepth ident.SlotIndex = newVar.SlotIndex assignExpr.IsDeclaration = true } } return assignExpr } func (p *Parser) parseFunctionLiteral() Expression { fn := &FunctionLiteral{Position: p.pos()} if !p.expectPeek(LPAREN) { p.addError("expected '(' after 'fn'") return nil } fn.Parameters, fn.Variadic = p.parseFunctionParameters() if !p.expectPeek(RPAREN) { p.addError("expected ')' after function parameters") return nil } // Check for return type hint fn.ReturnType = p.parseTypeHint() p.nextToken() p.enterScope("function") // Declare function parameters as resolved variables for _, param := range fn.Parameters { p.declareVariable(param.Name, param.TypeHint) param.Position = Position{Line: p.curToken.Line, Column: p.curToken.Column} } fn.Body = p.parseBlockStatements(END) // Calculate function metadata before exiting scope scopeIdx := len(p.scopes) - 1 fn.LocalCount = p.scopeSlots[scopeIdx] fn.UpvalueCount = 0 // TODO: Calculate captured variables fn.MaxStackDepth = 0 // TODO: Calculate max expression depth p.exitScope() if !p.curTokenIs(END) { p.addError("expected 'end' to close function") return nil } return fn } func (p *Parser) parseFunctionParameters() ([]FunctionParameter, bool) { var params []FunctionParameter var variadic bool if p.peekTokenIs(RPAREN) { return params, false } p.nextToken() for { if p.curTokenIs(ELLIPSIS) { variadic = true break } if !p.curTokenIs(IDENT) { p.addError("expected parameter name") return nil, false } param := FunctionParameter{Name: p.curToken.Literal, Position: p.pos()} // Check for type hint param.TypeHint = p.parseTypeHint() params = append(params, param) if !p.peekTokenIs(COMMA) { break } p.nextToken() p.nextToken() if p.curTokenIs(ELLIPSIS) { variadic = true break } } return params, variadic } func (p *Parser) parseTableLiteral() Expression { table := &TableLiteral{Position: p.pos()} table.Pairs = make([]TablePair, 0, 4) // Pre-allocate if p.peekTokenIs(RBRACE) { p.nextToken() return table } p.nextToken() for { if p.curTokenIs(EOF) { p.addError("unexpected end of input, expected }") return nil } pair := TablePair{Position: p.pos()} if (p.curTokenIs(IDENT) || p.curTokenIs(STRING)) && p.peekTokenIs(ASSIGN) { if p.curTokenIs(IDENT) { pair.Key = &Identifier{Value: p.curToken.Literal, Position: p.pos()} } else { pair.Key = &StringLiteral{Value: p.curToken.Literal, Position: p.pos()} } p.nextToken() p.nextToken() if p.curTokenIs(EOF) { p.addError("expected expression after assignment operator") return nil } pair.Value = p.ParseExpression(LOWEST) } else { pair.Value = p.ParseExpression(LOWEST) } if pair.Value == nil { return nil } table.Pairs = append(table.Pairs, pair) if !p.peekTokenIs(COMMA) { break } p.nextToken() p.nextToken() if p.curTokenIs(RBRACE) { break } if p.curTokenIs(EOF) { p.addError("expected next token to be }") return nil } } if !p.expectPeek(RBRACE) { return nil } return table } // parseStructConstructor handles struct constructor calls func (p *Parser) parseStructConstructor(left Expression) Expression { pos := p.pos() ident, ok := left.(*Identifier) if !ok { return p.parseTableLiteralFromBrace() } structName := ident.Value structDef := p.getStructByName(structName) if structDef == nil { // Not a struct, parse as table literal return p.parseTableLiteralFromBrace() } constructor := &StructConstructor{ StructID: structDef.ID, Fields: make([]TablePair, 0, 4), typeInfo: TypeInfo{Type: TypeStruct, StructID: structDef.ID, Inferred: true}, Position: pos, } if p.peekTokenIs(RBRACE) { p.nextToken() return constructor } p.nextToken() for { if p.curTokenIs(EOF) { p.addError("unexpected end of input, expected }") return nil } pair := TablePair{Position: p.pos()} if (p.curTokenIs(IDENT) || p.curTokenIs(STRING)) && p.peekTokenIs(ASSIGN) { if p.curTokenIs(IDENT) { pair.Key = &Identifier{Value: p.curToken.Literal, Position: p.pos()} } else { pair.Key = &StringLiteral{Value: p.curToken.Literal, Position: p.pos()} } p.nextToken() p.nextToken() if p.curTokenIs(EOF) { p.addError("expected expression after assignment operator") return nil } pair.Value = p.ParseExpression(LOWEST) } else { pair.Value = p.ParseExpression(LOWEST) } if pair.Value == nil { return nil } constructor.Fields = append(constructor.Fields, pair) if !p.peekTokenIs(COMMA) { break } p.nextToken() p.nextToken() if p.curTokenIs(RBRACE) { break } if p.curTokenIs(EOF) { p.addError("expected next token to be }") return nil } } if !p.expectPeek(RBRACE) { return nil } return constructor } func (p *Parser) parseTableLiteralFromBrace() Expression { table := &TableLiteral{Position: p.pos()} table.Pairs = make([]TablePair, 0, 4) if p.peekTokenIs(RBRACE) { p.nextToken() return table } p.nextToken() for { if p.curTokenIs(EOF) { p.addError("unexpected end of input, expected }") return nil } pair := TablePair{Position: p.pos()} if (p.curTokenIs(IDENT) || p.curTokenIs(STRING)) && p.peekTokenIs(ASSIGN) { if p.curTokenIs(IDENT) { pair.Key = &Identifier{Value: p.curToken.Literal, Position: p.pos()} } else { pair.Key = &StringLiteral{Value: p.curToken.Literal, Position: p.pos()} } p.nextToken() p.nextToken() if p.curTokenIs(EOF) { p.addError("expected expression after assignment operator") return nil } pair.Value = p.ParseExpression(LOWEST) } else { pair.Value = p.ParseExpression(LOWEST) } if pair.Value == nil { return nil } table.Pairs = append(table.Pairs, pair) if !p.peekTokenIs(COMMA) { break } p.nextToken() p.nextToken() if p.curTokenIs(RBRACE) { break } if p.curTokenIs(EOF) { p.addError("expected next token to be }") return nil } } if !p.expectPeek(RBRACE) { return nil } return table } func (p *Parser) parseInfixExpression(left Expression) Expression { expression := &InfixExpression{ Left: left, Operator: p.curToken.Literal, Position: p.pos(), } precedence := p.curPrecedence() p.nextToken() expression.Right = p.ParseExpression(precedence) if expression.Right == nil { p.addError(fmt.Sprintf("expected expression after operator '%s'", expression.Operator)) return nil } return expression } func (p *Parser) parseDotExpression(left Expression) Expression { pos := p.pos() if !p.expectPeekIdent() { p.addError("expected identifier after '.'") return nil } return &DotExpression{ Left: left, Key: p.curToken.Literal, Position: pos, } } func (p *Parser) parseCallExpression(fn Expression) Expression { call := &CallExpression{Function: fn, Position: p.pos()} call.Arguments = p.parseExpressionList(RPAREN) return call } func (p *Parser) parseExpressionList(end TokenType) []Expression { var args []Expression if p.peekTokenIs(end) { p.nextToken() return args } p.nextToken() args = append(args, p.ParseExpression(LOWEST)) for p.peekTokenIs(COMMA) { p.nextToken() p.nextToken() args = append(args, p.ParseExpression(LOWEST)) } if !p.expectPeek(end) { return nil } return args } func (p *Parser) parseIndexExpression(left Expression) Expression { pos := p.pos() p.nextToken() index := p.ParseExpression(LOWEST) if index == nil { p.addError("expected expression inside brackets") return nil } if !p.expectPeek(RBRACKET) { p.addError("expected ']' after index expression") return nil } return &IndexExpression{ Left: left, Index: index, Position: pos, } } // Helper methods func (p *Parser) curTokenIs(t TokenType) bool { return p.curToken.Type == t } func (p *Parser) peekTokenIs(t TokenType) bool { return p.peekToken.Type == t } func (p *Parser) expectPeek(t TokenType) bool { if p.peekTokenIs(t) { p.nextToken() return true } p.peekError(t) return false } func (p *Parser) expectPeekIdent() bool { if p.peekTokenIs(IDENT) || p.isKeyword(p.peekToken.Type) { p.nextToken() return true } p.peekError(IDENT) return false } func (p *Parser) isKeyword(t TokenType) bool { switch t { case TRUE, FALSE, NIL, AND, OR, NOT, IF, THEN, ELSEIF, ELSE, END, ECHO, FOR, WHILE, IN, DO, BREAK, EXIT, FN, RETURN, STRUCT: return true default: return false } } // Error handling func (p *Parser) addError(message string) { p.errors = append(p.errors, ParseError{ Message: message, Line: p.curToken.Line, Column: p.curToken.Column, Token: p.curToken, }) } func (p *Parser) peekError(t TokenType) { message := fmt.Sprintf("expected next token to be %s, got %s instead", tokenTypeString(t), tokenTypeString(p.peekToken.Type)) p.errors = append(p.errors, ParseError{ Message: message, Line: p.peekToken.Line, Column: p.peekToken.Column, Token: p.peekToken, }) } func (p *Parser) noPrefixParseFnError(t TokenType) { var message string switch t { case ASSIGN: message = "unexpected assignment operator, missing left-hand side identifier" case PLUS, MINUS, STAR, SLASH, MOD: message = fmt.Sprintf("unexpected operator '%s', missing left operand", tokenTypeString(t)) case RPAREN: message = "unexpected closing parenthesis" case RBRACE: message = "unexpected closing brace" case RBRACKET: message = "unexpected closing bracket" case EOF: message = "unexpected end of input" default: message = fmt.Sprintf("unexpected token '%s'", tokenTypeString(t)) } p.addError(message) } func (p *Parser) peekPrecedence() Precedence { if p, ok := precedences[p.peekToken.Type]; ok { return p } return LOWEST } func (p *Parser) curPrecedence() Precedence { if p, ok := precedences[p.curToken.Type]; ok { return p } return LOWEST } // Error reporting func (p *Parser) Errors() []ParseError { return p.errors } func (p *Parser) HasErrors() bool { return len(p.errors) > 0 } func (p *Parser) ErrorStrings() []string { result := make([]string, len(p.errors)) for i, err := range p.errors { result[i] = err.Error() } return result } // tokenTypeString returns human-readable string for token types func tokenTypeString(t TokenType) string { switch t { case IDENT: return "identifier" case NUMBER: return "number" case STRING: return "string" case TRUE, FALSE: return "boolean" case NIL: return "nil" case ASSIGN: return "=" case PLUS: return "+" case MINUS: return "-" case STAR: return "*" case SLASH: return "/" case MOD: return "%" case DOT: return "." case COLON: return ":" case EQ: return "==" case NOT_EQ: return "!=" case LT: return "<" case GT: return ">" case LT_EQ: return "<=" case GT_EQ: return ">=" case AND: return "and" case OR: return "or" case NOT: return "not" case LPAREN: return "(" case RPAREN: return ")" case LBRACE: return "{" case RBRACE: return "}" case LBRACKET: return "[" case RBRACKET: return "]" case COMMA: return "," case ELLIPSIS: return "..." case IF: return "if" case THEN: return "then" case ELSEIF: return "elseif" case ELSE: return "else" case END: return "end" case ECHO: return "echo" case FOR: return "for" case WHILE: return "while" case IN: return "in" case DO: return "do" case BREAK: return "break" case EXIT: return "exit" case FN: return "fn" case RETURN: return "return" case STRUCT: return "struct" case EOF: return "end of file" case ILLEGAL: return "illegal token" default: return "unknown" } }