Compare commits

..

No commits in common. "6575b077fee557b8d1acda4181646e5c2dbbce99" and "121774005b6aea6e877e38802bafb3686a15b9c8" have entirely different histories.

6 changed files with 168 additions and 396 deletions

View File

@ -2,16 +2,11 @@ package parser
import "fmt" import "fmt"
// Position represents source location information // Note: Type definitions moved to types.go for proper separation of concerns
type Position struct {
Line int
Column int
}
// Node represents any node in the AST // Node represents any node in the AST
type Node interface { type Node interface {
String() string String() string
Pos() Position
} }
// Statement represents statement nodes that can appear at the top level or in blocks // Statement represents statement nodes that can appear at the top level or in blocks
@ -24,14 +19,14 @@ type Statement interface {
type Expression interface { type Expression interface {
Node Node
expressionNode() expressionNode()
TypeInfo() TypeInfo TypeInfo() TypeInfo // Returns type by value, not pointer
} }
// Program represents the root of the AST containing all top-level statements. // Program represents the root of the AST containing all top-level statements.
// Tracks exit code for script termination and owns the statement list.
type Program struct { type Program struct {
Statements []Statement Statements []Statement
ExitCode int ExitCode int
Position Position
} }
func (p *Program) String() string { func (p *Program) String() string {
@ -41,26 +36,24 @@ func (p *Program) String() string {
} }
return result return result
} }
func (p *Program) Pos() Position { return p.Position }
// StructField represents a field definition within a struct. // StructField represents a field definition within a struct.
// Contains field name and required type annotation for compile-time checking.
type StructField struct { type StructField struct {
Name string Name string
TypeHint TypeInfo TypeHint TypeInfo // Required for struct fields, embeds directly
Position Position
} }
func (sf *StructField) String() string { func (sf *StructField) String() string {
return fmt.Sprintf("%s: %s", sf.Name, typeToString(sf.TypeHint)) return fmt.Sprintf("%s: %s", sf.Name, typeToString(sf.TypeHint))
} }
func (sf *StructField) Pos() Position { return sf.Position }
// StructStatement represents struct type definitions with named fields. // StructStatement represents struct type definitions with named fields.
// Defines new types that can be instantiated and used for type checking.
type StructStatement struct { type StructStatement struct {
Name string Name string
Fields []StructField Fields []StructField
ID uint16 ID uint16 // Unique identifier for fast lookup
Position Position
} }
func (ss *StructStatement) statementNode() {} func (ss *StructStatement) statementNode() {}
@ -74,28 +67,26 @@ func (ss *StructStatement) String() string {
} }
return fmt.Sprintf("struct %s {\n\t%s\n}", ss.Name, fields) return fmt.Sprintf("struct %s {\n\t%s\n}", ss.Name, fields)
} }
func (ss *StructStatement) Pos() Position { return ss.Position }
// MethodDefinition represents method definitions attached to struct types. // MethodDefinition represents method definitions attached to struct types.
// Links a function implementation to a specific struct via struct ID.
type MethodDefinition struct { type MethodDefinition struct {
StructID uint16 StructID uint16 // Index into struct table for fast lookup
MethodName string MethodName string
Function *FunctionLiteral Function *FunctionLiteral
Position Position
} }
func (md *MethodDefinition) statementNode() {} func (md *MethodDefinition) statementNode() {}
func (md *MethodDefinition) String() string { func (md *MethodDefinition) String() string {
return fmt.Sprintf("fn <struct>.%s%s", md.MethodName, md.Function.String()[2:]) return fmt.Sprintf("fn <struct>.%s%s", md.MethodName, md.Function.String()[2:])
} }
func (md *MethodDefinition) Pos() Position { return md.Position }
// StructConstructor represents struct instantiation with field initialization. // StructConstructor represents struct instantiation with field initialization.
// Uses struct ID for fast type resolution and validation during parsing.
type StructConstructor struct { type StructConstructor struct {
StructID uint16 StructID uint16 // Index into struct table
Fields []TablePair Fields []TablePair // Reuses table pair structure for field assignments
typeInfo TypeInfo typeInfo TypeInfo // Cached type info for this constructor
Position Position
} }
func (sc *StructConstructor) expressionNode() {} func (sc *StructConstructor) expressionNode() {}
@ -107,16 +98,15 @@ func (sc *StructConstructor) String() string {
return fmt.Sprintf("<struct>{%s}", joinStrings(pairs, ", ")) return fmt.Sprintf("<struct>{%s}", joinStrings(pairs, ", "))
} }
func (sc *StructConstructor) TypeInfo() TypeInfo { return sc.typeInfo } func (sc *StructConstructor) TypeInfo() TypeInfo { return sc.typeInfo }
func (sc *StructConstructor) Pos() Position { return sc.Position }
// Assignment represents both variable assignment statements and assignment expressions. // Assignment represents both variable assignment statements and assignment expressions.
// Unified design reduces AST node count and simplifies type checking logic.
type Assignment struct { type Assignment struct {
Target Expression Target Expression // Target (identifier, dot, or index expression)
Value Expression Value Expression // Value being assigned
TypeHint TypeInfo TypeHint TypeInfo // Optional explicit type hint, embeds directly
IsDeclaration bool IsDeclaration bool // True if declaring new variable in current scope
IsExpression bool IsExpression bool // True if used as expression (wrapped in parentheses)
Position Position
} }
func (a *Assignment) statementNode() {} func (a *Assignment) statementNode() {}
@ -141,45 +131,40 @@ func (a *Assignment) String() string {
return result return result
} }
func (a *Assignment) TypeInfo() TypeInfo { return a.Value.TypeInfo() } func (a *Assignment) TypeInfo() TypeInfo { return a.Value.TypeInfo() }
func (a *Assignment) Pos() Position { return a.Position }
// ExpressionStatement wraps expressions used as statements. // ExpressionStatement wraps expressions used as statements.
// Allows function calls and other expressions at statement level.
type ExpressionStatement struct { type ExpressionStatement struct {
Expression Expression Expression Expression
Position Position
} }
func (es *ExpressionStatement) statementNode() {} func (es *ExpressionStatement) statementNode() {}
func (es *ExpressionStatement) String() string { func (es *ExpressionStatement) String() string {
return es.Expression.String() return es.Expression.String()
} }
func (es *ExpressionStatement) Pos() Position { return es.Position }
// EchoStatement represents output statements for displaying values. // EchoStatement represents output statements for displaying values.
// Simple debugging and output mechanism built into the language.
type EchoStatement struct { type EchoStatement struct {
Value Expression Value Expression
Position Position
} }
func (es *EchoStatement) statementNode() {} func (es *EchoStatement) statementNode() {}
func (es *EchoStatement) String() string { func (es *EchoStatement) String() string {
return fmt.Sprintf("echo %s", es.Value.String()) return fmt.Sprintf("echo %s", es.Value.String())
} }
func (es *EchoStatement) Pos() Position { return es.Position }
// BreakStatement represents loop exit statements. // BreakStatement represents loop exit statements.
type BreakStatement struct { // Simple marker node with no additional data needed.
Position Position type BreakStatement struct{}
}
func (bs *BreakStatement) statementNode() {} func (bs *BreakStatement) statementNode() {}
func (bs *BreakStatement) String() string { return "break" } func (bs *BreakStatement) String() string { return "break" }
func (bs *BreakStatement) Pos() Position { return bs.Position }
// ExitStatement represents script termination with optional exit code. // ExitStatement represents script termination with optional exit code.
// Value expression is nil for plain "exit", non-nil for "exit <code>".
type ExitStatement struct { type ExitStatement struct {
Value Expression Value Expression // Optional exit code expression
Position Position
} }
func (es *ExitStatement) statementNode() {} func (es *ExitStatement) statementNode() {}
@ -189,12 +174,11 @@ func (es *ExitStatement) String() string {
} }
return fmt.Sprintf("exit %s", es.Value.String()) return fmt.Sprintf("exit %s", es.Value.String())
} }
func (es *ExitStatement) Pos() Position { return es.Position }
// ReturnStatement represents function return with optional value. // ReturnStatement represents function return with optional value.
// Value expression is nil for plain "return", non-nil for "return <value>".
type ReturnStatement struct { type ReturnStatement struct {
Value Expression Value Expression // Optional return value expression
Position Position
} }
func (rs *ReturnStatement) statementNode() {} func (rs *ReturnStatement) statementNode() {}
@ -204,13 +188,12 @@ func (rs *ReturnStatement) String() string {
} }
return fmt.Sprintf("return %s", rs.Value.String()) return fmt.Sprintf("return %s", rs.Value.String())
} }
func (rs *ReturnStatement) Pos() Position { return rs.Position }
// ElseIfClause represents conditional branches in if statements. // ElseIfClause represents conditional branches in if statements.
// Contains condition expression and body statements for this branch.
type ElseIfClause struct { type ElseIfClause struct {
Condition Expression Condition Expression
Body []Statement Body []Statement
Position Position
} }
func (eic *ElseIfClause) String() string { func (eic *ElseIfClause) String() string {
@ -220,15 +203,14 @@ func (eic *ElseIfClause) String() string {
} }
return fmt.Sprintf("elseif %s then\n%s", eic.Condition.String(), body) return fmt.Sprintf("elseif %s then\n%s", eic.Condition.String(), body)
} }
func (eic *ElseIfClause) Pos() Position { return eic.Position }
// IfStatement represents conditional execution with optional elseif and else branches. // IfStatement represents conditional execution with optional elseif and else branches.
// Supports multiple elseif clauses and an optional final else clause.
type IfStatement struct { type IfStatement struct {
Condition Expression Condition Expression // Main condition
Body []Statement Body []Statement // Statements to execute if condition is true
ElseIfs []ElseIfClause ElseIfs []ElseIfClause // Optional elseif branches
Else []Statement Else []Statement // Optional else branch
Position Position
} }
func (is *IfStatement) statementNode() {} func (is *IfStatement) statementNode() {}
@ -254,13 +236,12 @@ func (is *IfStatement) String() string {
result += "end" result += "end"
return result return result
} }
func (is *IfStatement) Pos() Position { return is.Position }
// WhileStatement represents condition-based loops that execute while condition is true. // WhileStatement represents condition-based loops that execute while condition is true.
// Contains condition expression and body statements to repeat.
type WhileStatement struct { type WhileStatement struct {
Condition Expression Condition Expression
Body []Statement Body []Statement
Position Position
} }
func (ws *WhileStatement) statementNode() {} func (ws *WhileStatement) statementNode() {}
@ -275,16 +256,15 @@ func (ws *WhileStatement) String() string {
result += "end" result += "end"
return result return result
} }
func (ws *WhileStatement) Pos() Position { return ws.Position }
// ForStatement represents numeric for loops with start, end, and optional step. // ForStatement represents numeric for loops with start, end, and optional step.
// Variable is automatically scoped to the loop body.
type ForStatement struct { type ForStatement struct {
Variable *Identifier Variable *Identifier // Loop variable (automatically number type)
Start Expression Start Expression // Starting value expression
End Expression End Expression // Ending value expression
Step Expression Step Expression // Optional step expression (nil means step of 1)
Body []Statement Body []Statement // Loop body statements
Position Position
} }
func (fs *ForStatement) statementNode() {} func (fs *ForStatement) statementNode() {}
@ -305,15 +285,14 @@ func (fs *ForStatement) String() string {
result += "end" result += "end"
return result return result
} }
func (fs *ForStatement) Pos() Position { return fs.Position }
// ForInStatement represents iterator-based loops over tables, arrays, or other iterables. // ForInStatement represents iterator-based loops over tables, arrays, or other iterables.
// Supports both single variable (for v in iter) and key-value (for k,v in iter) forms.
type ForInStatement struct { type ForInStatement struct {
Key *Identifier Key *Identifier // Optional key variable (nil for single variable iteration)
Value *Identifier Value *Identifier // Value variable (required)
Iterable Expression Iterable Expression // Expression to iterate over
Body []Statement Body []Statement // Loop body statements
Position Position
} }
func (fis *ForInStatement) statementNode() {} func (fis *ForInStatement) statementNode() {}
@ -334,13 +313,12 @@ func (fis *ForInStatement) String() string {
result += "end" result += "end"
return result return result
} }
func (fis *ForInStatement) Pos() Position { return fis.Position }
// FunctionParameter represents a parameter in function definitions. // FunctionParameter represents a parameter in function definitions.
// Contains parameter name and optional type hint for type checking.
type FunctionParameter struct { type FunctionParameter struct {
Name string Name string
TypeHint TypeInfo TypeHint TypeInfo // Optional type constraint, embeds directly
Position Position
} }
func (fp *FunctionParameter) String() string { func (fp *FunctionParameter) String() string {
@ -349,15 +327,12 @@ func (fp *FunctionParameter) String() string {
} }
return fp.Name return fp.Name
} }
func (fp *FunctionParameter) Pos() Position { return fp.Position }
// Identifier represents variable references and names. // Identifier represents variable references and names.
// Stores resolved type information for efficient type checking.
type Identifier struct { type Identifier struct {
Value string Value string
ScopeDepth int // 0 = global, 1+ = local depth typeInfo TypeInfo // Resolved type, embeds directly
SlotIndex int // register/stack slot (-1 = unresolved)
typeInfo TypeInfo
Position Position
} }
func (i *Identifier) expressionNode() {} func (i *Identifier) expressionNode() {}
@ -368,35 +343,31 @@ func (i *Identifier) TypeInfo() TypeInfo {
} }
return i.typeInfo return i.typeInfo
} }
func (i *Identifier) Pos() Position { return i.Position }
func (i *Identifier) IsResolved() bool { return i.SlotIndex >= 0 }
// NumberLiteral represents numeric constants including integers, floats, hex, and binary. // NumberLiteral represents numeric constants including integers, floats, hex, and binary.
// Always has number type, so no additional type storage needed.
type NumberLiteral struct { type NumberLiteral struct {
Value float64 Value float64 // All numbers stored as float64 for simplicity
Position Position
} }
func (nl *NumberLiteral) expressionNode() {} func (nl *NumberLiteral) expressionNode() {}
func (nl *NumberLiteral) String() string { return fmt.Sprintf("%.2f", nl.Value) } func (nl *NumberLiteral) String() string { return fmt.Sprintf("%.2f", nl.Value) }
func (nl *NumberLiteral) TypeInfo() TypeInfo { return NumberType } func (nl *NumberLiteral) TypeInfo() TypeInfo { return NumberType }
func (nl *NumberLiteral) Pos() Position { return nl.Position }
// StringLiteral represents string constants and multiline strings. // StringLiteral represents string constants and multiline strings.
// Always has string type, so no additional type storage needed.
type StringLiteral struct { type StringLiteral struct {
Value string Value string // String content without quotes
Position Position
} }
func (sl *StringLiteral) expressionNode() {} func (sl *StringLiteral) expressionNode() {}
func (sl *StringLiteral) String() string { return fmt.Sprintf(`"%s"`, sl.Value) } func (sl *StringLiteral) String() string { return fmt.Sprintf(`"%s"`, sl.Value) }
func (sl *StringLiteral) TypeInfo() TypeInfo { return StringType } func (sl *StringLiteral) TypeInfo() TypeInfo { return StringType }
func (sl *StringLiteral) Pos() Position { return sl.Position }
// BooleanLiteral represents true and false constants. // BooleanLiteral represents true and false constants.
// Always has bool type, so no additional type storage needed.
type BooleanLiteral struct { type BooleanLiteral struct {
Value bool Value bool
Position Position
} }
func (bl *BooleanLiteral) expressionNode() {} func (bl *BooleanLiteral) expressionNode() {}
@ -407,28 +378,22 @@ func (bl *BooleanLiteral) String() string {
return "false" return "false"
} }
func (bl *BooleanLiteral) TypeInfo() TypeInfo { return BoolType } func (bl *BooleanLiteral) TypeInfo() TypeInfo { return BoolType }
func (bl *BooleanLiteral) Pos() Position { return bl.Position }
// NilLiteral represents the nil constant value. // NilLiteral represents the nil constant value.
type NilLiteral struct { // Always has nil type, so no additional type storage needed.
Position Position type NilLiteral struct{}
}
func (nl *NilLiteral) expressionNode() {} func (nl *NilLiteral) expressionNode() {}
func (nl *NilLiteral) String() string { return "nil" } func (nl *NilLiteral) String() string { return "nil" }
func (nl *NilLiteral) TypeInfo() TypeInfo { return NilType } func (nl *NilLiteral) TypeInfo() TypeInfo { return NilType }
func (nl *NilLiteral) Pos() Position { return nl.Position }
// FunctionLiteral represents function definitions with parameters, body, and optional return type. // FunctionLiteral represents function definitions with parameters, body, and optional return type.
// Always has function type, stores additional return type information separately.
type FunctionLiteral struct { type FunctionLiteral struct {
Parameters []FunctionParameter Parameters []FunctionParameter // Function parameters with optional types
Body []Statement Body []Statement // Function body statements
ReturnType TypeInfo ReturnType TypeInfo // Optional return type hint, embeds directly
Variadic bool Variadic bool // True if function accepts variable arguments
LocalCount int // Pre-computed local variable count
UpvalueCount int // Number of captured variables
MaxStackDepth int // Maximum expression evaluation depth
Position Position
} }
func (fl *FunctionLiteral) expressionNode() {} func (fl *FunctionLiteral) expressionNode() {}
@ -460,14 +425,13 @@ func (fl *FunctionLiteral) String() string {
return result return result
} }
func (fl *FunctionLiteral) TypeInfo() TypeInfo { return FunctionType } func (fl *FunctionLiteral) TypeInfo() TypeInfo { return FunctionType }
func (fl *FunctionLiteral) Pos() Position { return fl.Position }
// CallExpression represents function calls with arguments. // CallExpression represents function calls with arguments.
// Stores inferred return type from function signature analysis.
type CallExpression struct { type CallExpression struct {
Function Expression Function Expression // Function expression to call
Arguments []Expression Arguments []Expression // Argument expressions
typeInfo TypeInfo typeInfo TypeInfo // Inferred return type, embeds directly
Position Position
} }
func (ce *CallExpression) expressionNode() {} func (ce *CallExpression) expressionNode() {}
@ -479,14 +443,13 @@ func (ce *CallExpression) String() string {
return fmt.Sprintf("%s(%s)", ce.Function.String(), joinStrings(args, ", ")) return fmt.Sprintf("%s(%s)", ce.Function.String(), joinStrings(args, ", "))
} }
func (ce *CallExpression) TypeInfo() TypeInfo { return ce.typeInfo } func (ce *CallExpression) TypeInfo() TypeInfo { return ce.typeInfo }
func (ce *CallExpression) Pos() Position { return ce.Position }
// PrefixExpression represents unary operations like negation and logical not. // PrefixExpression represents unary operations like negation and logical not.
// Stores result type based on operator and operand type analysis.
type PrefixExpression struct { type PrefixExpression struct {
Operator string Operator string // Operator symbol ("-", "not")
Right Expression Right Expression // Operand expression
typeInfo TypeInfo typeInfo TypeInfo // Result type, embeds directly
Position Position
} }
func (pe *PrefixExpression) expressionNode() {} func (pe *PrefixExpression) expressionNode() {}
@ -497,15 +460,14 @@ func (pe *PrefixExpression) String() string {
return fmt.Sprintf("(%s%s)", pe.Operator, pe.Right.String()) return fmt.Sprintf("(%s%s)", pe.Operator, pe.Right.String())
} }
func (pe *PrefixExpression) TypeInfo() TypeInfo { return pe.typeInfo } func (pe *PrefixExpression) TypeInfo() TypeInfo { return pe.typeInfo }
func (pe *PrefixExpression) Pos() Position { return pe.Position }
// InfixExpression represents binary operations between two expressions. // InfixExpression represents binary operations between two expressions.
// Stores result type based on operator and operand type compatibility.
type InfixExpression struct { type InfixExpression struct {
Left Expression Left Expression // Left operand
Right Expression Right Expression // Right operand
Operator string Operator string // Operator symbol ("+", "-", "==", "and", etc.)
typeInfo TypeInfo typeInfo TypeInfo // Result type, embeds directly
Position Position
} }
func (ie *InfixExpression) expressionNode() {} func (ie *InfixExpression) expressionNode() {}
@ -513,14 +475,13 @@ func (ie *InfixExpression) String() string {
return fmt.Sprintf("(%s %s %s)", ie.Left.String(), ie.Operator, ie.Right.String()) return fmt.Sprintf("(%s %s %s)", ie.Left.String(), ie.Operator, ie.Right.String())
} }
func (ie *InfixExpression) TypeInfo() TypeInfo { return ie.typeInfo } func (ie *InfixExpression) TypeInfo() TypeInfo { return ie.typeInfo }
func (ie *InfixExpression) Pos() Position { return ie.Position }
// IndexExpression represents bracket-based member access (table[key]). // IndexExpression represents bracket-based member access (table[key]).
// Stores inferred element type based on container type analysis.
type IndexExpression struct { type IndexExpression struct {
Left Expression Left Expression // Container expression
Index Expression Index Expression // Index/key expression
typeInfo TypeInfo typeInfo TypeInfo // Element type, embeds directly
Position Position
} }
func (ie *IndexExpression) expressionNode() {} func (ie *IndexExpression) expressionNode() {}
@ -528,14 +489,13 @@ func (ie *IndexExpression) String() string {
return fmt.Sprintf("%s[%s]", ie.Left.String(), ie.Index.String()) return fmt.Sprintf("%s[%s]", ie.Left.String(), ie.Index.String())
} }
func (ie *IndexExpression) TypeInfo() TypeInfo { return ie.typeInfo } func (ie *IndexExpression) TypeInfo() TypeInfo { return ie.typeInfo }
func (ie *IndexExpression) Pos() Position { return ie.Position }
// DotExpression represents dot-based member access (table.key). // DotExpression represents dot-based member access (table.key).
// Stores inferred member type based on container type and field analysis.
type DotExpression struct { type DotExpression struct {
Left Expression Left Expression // Container expression
Key string Key string // Member name
typeInfo TypeInfo typeInfo TypeInfo // Member type, embeds directly
Position Position
} }
func (de *DotExpression) expressionNode() {} func (de *DotExpression) expressionNode() {}
@ -543,13 +503,12 @@ func (de *DotExpression) String() string {
return fmt.Sprintf("%s.%s", de.Left.String(), de.Key) return fmt.Sprintf("%s.%s", de.Left.String(), de.Key)
} }
func (de *DotExpression) TypeInfo() TypeInfo { return de.typeInfo } func (de *DotExpression) TypeInfo() TypeInfo { return de.typeInfo }
func (de *DotExpression) Pos() Position { return de.Position }
// TablePair represents key-value pairs in table literals and struct constructors. // TablePair represents key-value pairs in table literals and struct constructors.
// Key is nil for array-style elements, non-nil for object-style elements.
type TablePair struct { type TablePair struct {
Key Expression Key Expression // Key expression (nil for array elements)
Value Expression Value Expression // Value expression
Position Position
} }
func (tp *TablePair) String() string { func (tp *TablePair) String() string {
@ -558,12 +517,11 @@ func (tp *TablePair) String() string {
} }
return fmt.Sprintf("%s = %s", tp.Key.String(), tp.Value.String()) return fmt.Sprintf("%s = %s", tp.Key.String(), tp.Value.String())
} }
func (tp *TablePair) Pos() Position { return tp.Position }
// TableLiteral represents table/array/object literals with key-value pairs. // TableLiteral represents table/array/object literals with key-value pairs.
// Always has table type, provides methods to check if it's array-style.
type TableLiteral struct { type TableLiteral struct {
Pairs []TablePair Pairs []TablePair // Key-value pairs (key nil for array elements)
Position Position
} }
func (tl *TableLiteral) expressionNode() {} func (tl *TableLiteral) expressionNode() {}
@ -575,7 +533,6 @@ func (tl *TableLiteral) String() string {
return fmt.Sprintf("{%s}", joinStrings(pairs, ", ")) return fmt.Sprintf("{%s}", joinStrings(pairs, ", "))
} }
func (tl *TableLiteral) TypeInfo() TypeInfo { return TableType } func (tl *TableLiteral) TypeInfo() TypeInfo { return TableType }
func (tl *TableLiteral) Pos() Position { return tl.Position }
// IsArray returns true if this table contains only array-style elements (no explicit keys) // IsArray returns true if this table contains only array-style elements (no explicit keys)
func (tl *TableLiteral) IsArray() bool { func (tl *TableLiteral) IsArray() bool {

View File

@ -257,8 +257,6 @@ func (l *Lexer) NextToken() Token {
tok = Token{Type: STAR, Literal: string(l.ch), Line: l.line, Column: l.column} tok = Token{Type: STAR, Literal: string(l.ch), Line: l.line, Column: l.column}
case '/': case '/':
tok = Token{Type: SLASH, Literal: string(l.ch), Line: l.line, Column: l.column} tok = Token{Type: SLASH, Literal: string(l.ch), Line: l.line, Column: l.column}
case '%':
tok = Token{Type: MOD, Literal: string(l.ch), Line: l.line, Column: l.column}
case ':': case ':':
tok = Token{Type: COLON, Literal: string(l.ch), Line: l.line, Column: l.column} tok = Token{Type: COLON, Literal: string(l.ch), Line: l.line, Column: l.column}
case '.': case '.':

View File

@ -31,11 +31,9 @@ type Parser struct {
errors []ParseError errors []ParseError
// Scope tracking with slot management // Scope tracking
scopes []map[string]*Variable scopes []map[string]bool
scopeTypes []string scopeTypes []string
scopeSlots []int // Next available slot per scope
currentDepth int
// Struct tracking with ID mapping // Struct tracking with ID mapping
structs map[string]*StructStatement structs map[string]*StructStatement
@ -43,26 +41,16 @@ type Parser struct {
nextID uint16 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 // NewParser creates a new parser instance
func NewParser(lexer *Lexer) *Parser { func NewParser(lexer *Lexer) *Parser {
p := &Parser{ p := &Parser{
lexer: lexer, lexer: lexer,
errors: []ParseError{}, errors: []ParseError{},
scopes: []map[string]*Variable{make(map[string]*Variable)}, scopes: []map[string]bool{make(map[string]bool)},
scopeTypes: []string{"global"}, scopeTypes: []string{"global"},
scopeSlots: []int{0}, structs: make(map[string]*StructStatement),
currentDepth: 0, structIDs: make(map[uint16]*StructStatement),
structs: make(map[string]*StructStatement), nextID: 1, // 0 reserved for non-struct types
structIDs: make(map[uint16]*StructStatement),
nextID: 1, // 0 reserved for non-struct types
} }
p.prefixParseFns = make(map[TokenType]func() Expression) p.prefixParseFns = make(map[TokenType]func() Expression)
@ -83,7 +71,6 @@ func NewParser(lexer *Lexer) *Parser {
p.registerInfix(MINUS, p.parseInfixExpression) p.registerInfix(MINUS, p.parseInfixExpression)
p.registerInfix(SLASH, p.parseInfixExpression) p.registerInfix(SLASH, p.parseInfixExpression)
p.registerInfix(STAR, p.parseInfixExpression) p.registerInfix(STAR, p.parseInfixExpression)
p.registerInfix(MOD, p.parseInfixExpression)
p.registerInfix(EQ, p.parseInfixExpression) p.registerInfix(EQ, p.parseInfixExpression)
p.registerInfix(NOT_EQ, p.parseInfixExpression) p.registerInfix(NOT_EQ, p.parseInfixExpression)
p.registerInfix(LT, p.parseInfixExpression) p.registerInfix(LT, p.parseInfixExpression)
@ -103,11 +90,6 @@ func NewParser(lexer *Lexer) *Parser {
return p 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 // Struct management
func (p *Parser) registerStruct(stmt *StructStatement) { func (p *Parser) registerStruct(stmt *StructStatement) {
stmt.ID = p.nextID stmt.ID = p.nextID
@ -125,24 +107,20 @@ func (p *Parser) isStructDefined(name string) bool {
return exists return exists
} }
// Scope management with slot allocation // Scope management
func (p *Parser) enterScope(scopeType string) { func (p *Parser) enterScope(scopeType string) {
p.scopes = append(p.scopes, make(map[string]*Variable)) p.scopes = append(p.scopes, make(map[string]bool))
p.scopeTypes = append(p.scopeTypes, scopeType) p.scopeTypes = append(p.scopeTypes, scopeType)
p.scopeSlots = append(p.scopeSlots, 0)
p.currentDepth++
} }
func (p *Parser) exitScope() { func (p *Parser) exitScope() {
if len(p.scopes) > 1 { if len(p.scopes) > 1 {
p.scopes = p.scopes[:len(p.scopes)-1] p.scopes = p.scopes[:len(p.scopes)-1]
p.scopeTypes = p.scopeTypes[:len(p.scopeTypes)-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 { func (p *Parser) currentVariableScope() map[string]bool {
if len(p.scopeTypes) > 1 && p.scopeTypes[len(p.scopeTypes)-1] == "loop" { 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)-2]
} }
@ -151,55 +129,19 @@ func (p *Parser) currentVariableScope() map[string]*Variable {
func (p *Parser) isVariableDeclared(name string) bool { func (p *Parser) isVariableDeclared(name string) bool {
for i := len(p.scopes) - 1; i >= 0; i-- { for i := len(p.scopes) - 1; i >= 0; i-- {
if _, exists := p.scopes[i][name]; exists { if p.scopes[i][name] {
return true return true
} }
} }
return false return false
} }
func (p *Parser) declareVariable(name string, typeHint TypeInfo) *Variable { func (p *Parser) declareVariable(name string) {
scope := p.currentVariableScope() p.currentVariableScope()[name] = true
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 { func (p *Parser) declareLoopVariable(name string) {
scope := p.scopes[len(p.scopes)-1] p.scopes[len(p.scopes)-1][name] = true
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 // parseTypeHint parses optional type hint after colon, returns by value
@ -259,7 +201,7 @@ func (p *Parser) nextToken() {
// ParseProgram parses the entire program // ParseProgram parses the entire program
func (p *Parser) ParseProgram() *Program { func (p *Parser) ParseProgram() *Program {
program := &Program{Position: p.pos()} program := &Program{}
program.Statements = []Statement{} program.Statements = []Statement{}
for !p.curTokenIs(EOF) { for !p.curTokenIs(EOF) {
@ -312,7 +254,7 @@ func (p *Parser) parseStatement() Statement {
// parseStructStatement parses struct definitions // parseStructStatement parses struct definitions
func (p *Parser) parseStructStatement() *StructStatement { func (p *Parser) parseStructStatement() *StructStatement {
stmt := &StructStatement{Position: p.pos()} stmt := &StructStatement{}
if !p.expectPeek(IDENT) { if !p.expectPeek(IDENT) {
p.addError("expected struct name") p.addError("expected struct name")
@ -347,7 +289,7 @@ func (p *Parser) parseStructStatement() *StructStatement {
return nil return nil
} }
field := StructField{Name: p.curToken.Literal, Position: p.pos()} field := StructField{Name: p.curToken.Literal}
// Parse required type hint // Parse required type hint
field.TypeHint = p.parseTypeHint() field.TypeHint = p.parseTypeHint()
@ -385,8 +327,6 @@ func (p *Parser) parseStructStatement() *StructStatement {
// parseFunctionStatement handles both regular functions and methods // parseFunctionStatement handles both regular functions and methods
func (p *Parser) parseFunctionStatement() Statement { func (p *Parser) parseFunctionStatement() Statement {
pos := p.pos()
if !p.expectPeek(IDENT) { if !p.expectPeek(IDENT) {
p.addError("expected function name") p.addError("expected function name")
return nil return nil
@ -418,7 +358,7 @@ func (p *Parser) parseFunctionStatement() Statement {
} }
// Parse the function literal // Parse the function literal
funcLit := &FunctionLiteral{Position: p.pos()} funcLit := &FunctionLiteral{}
funcLit.Parameters, funcLit.Variadic = p.parseFunctionParameters() funcLit.Parameters, funcLit.Variadic = p.parseFunctionParameters()
if !p.expectPeek(RPAREN) { if !p.expectPeek(RPAREN) {
@ -433,7 +373,7 @@ func (p *Parser) parseFunctionStatement() Statement {
p.enterScope("function") p.enterScope("function")
for _, param := range funcLit.Parameters { for _, param := range funcLit.Parameters {
p.declareVariable(param.Name, param.TypeHint) p.declareVariable(param.Name)
} }
funcLit.Body = p.parseBlockStatements(END) funcLit.Body = p.parseBlockStatements(END)
p.exitScope() p.exitScope()
@ -447,7 +387,6 @@ func (p *Parser) parseFunctionStatement() Statement {
StructID: structDef.ID, StructID: structDef.ID,
MethodName: methodName, MethodName: methodName,
Function: funcLit, Function: funcLit,
Position: pos,
} }
} }
@ -457,13 +396,11 @@ func (p *Parser) parseFunctionStatement() Statement {
return nil return nil
} }
return &ExpressionStatement{Expression: funcLit, Position: pos} return &ExpressionStatement{Expression: funcLit}
} }
// parseIdentifierStatement handles assignments and expression statements // parseIdentifierStatement handles assignments and expression statements
func (p *Parser) parseIdentifierStatement() Statement { func (p *Parser) parseIdentifierStatement() Statement {
pos := p.pos()
// Parse the left-hand side expression first // Parse the left-hand side expression first
expr := p.ParseExpression(LOWEST) expr := p.ParseExpression(LOWEST)
if expr == nil { if expr == nil {
@ -482,23 +419,14 @@ func (p *Parser) parseIdentifierStatement() Statement {
assignment := &Assignment{ assignment := &Assignment{
Target: expr, Target: expr,
TypeHint: typeHint, TypeHint: typeHint,
Position: pos,
} }
// Validate assignment target and check if it's a declaration // Validate assignment target and check if it's a declaration
switch target := expr.(type) { switch target := expr.(type) {
case *Identifier: case *Identifier:
if variable := p.lookupVariable(target.Value); variable != nil { assignment.IsDeclaration = !p.isVariableDeclared(target.Value)
// Existing variable - resolve it if assignment.IsDeclaration {
target.ScopeDepth = variable.ScopeDepth p.declareVariable(target.Value)
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: case *DotExpression, *IndexExpression:
assignment.IsDeclaration = false assignment.IsDeclaration = false
@ -522,13 +450,13 @@ func (p *Parser) parseIdentifierStatement() Statement {
return assignment return assignment
} else { } else {
// This is an expression statement // This is an expression statement
return &ExpressionStatement{Expression: expr, Position: pos} return &ExpressionStatement{Expression: expr}
} }
} }
// parseEchoStatement parses echo statements // parseEchoStatement parses echo statements
func (p *Parser) parseEchoStatement() *EchoStatement { func (p *Parser) parseEchoStatement() *EchoStatement {
stmt := &EchoStatement{Position: p.pos()} stmt := &EchoStatement{}
p.nextToken() // move past 'echo' p.nextToken() // move past 'echo'
@ -547,11 +475,11 @@ func (p *Parser) parseBreakStatement() *BreakStatement {
p.addError("unexpected identifier") p.addError("unexpected identifier")
return nil return nil
} }
return &BreakStatement{Position: p.pos()} return &BreakStatement{}
} }
func (p *Parser) parseExitStatement() *ExitStatement { func (p *Parser) parseExitStatement() *ExitStatement {
stmt := &ExitStatement{Position: p.pos()} stmt := &ExitStatement{}
if p.canStartExpression(p.peekToken.Type) { if p.canStartExpression(p.peekToken.Type) {
p.nextToken() p.nextToken()
@ -566,7 +494,7 @@ func (p *Parser) parseExitStatement() *ExitStatement {
} }
func (p *Parser) parseReturnStatement() *ReturnStatement { func (p *Parser) parseReturnStatement() *ReturnStatement {
stmt := &ReturnStatement{Position: p.pos()} stmt := &ReturnStatement{}
if p.canStartExpression(p.peekToken.Type) { if p.canStartExpression(p.peekToken.Type) {
p.nextToken() p.nextToken()
@ -591,7 +519,7 @@ func (p *Parser) canStartExpression(tokenType TokenType) bool {
// Loop statement parsers // Loop statement parsers
func (p *Parser) parseWhileStatement() *WhileStatement { func (p *Parser) parseWhileStatement() *WhileStatement {
stmt := &WhileStatement{Position: p.pos()} stmt := &WhileStatement{}
p.nextToken() p.nextToken()
@ -619,7 +547,6 @@ func (p *Parser) parseWhileStatement() *WhileStatement {
} }
func (p *Parser) parseForStatement() Statement { func (p *Parser) parseForStatement() Statement {
pos := p.pos()
p.nextToken() p.nextToken()
if !p.curTokenIs(IDENT) { if !p.curTokenIs(IDENT) {
@ -627,20 +554,20 @@ func (p *Parser) parseForStatement() Statement {
return nil return nil
} }
firstVar := &Identifier{Value: p.curToken.Literal, Position: p.pos()} firstVar := &Identifier{Value: p.curToken.Literal}
if p.peekTokenIs(ASSIGN) { if p.peekTokenIs(ASSIGN) {
return p.parseNumericForStatement(firstVar, pos) return p.parseNumericForStatement(firstVar)
} else if p.peekTokenIs(COMMA) || p.peekTokenIs(IN) { } else if p.peekTokenIs(COMMA) || p.peekTokenIs(IN) {
return p.parseForInStatement(firstVar, pos) return p.parseForInStatement(firstVar)
} else { } else {
p.addError("expected '=', ',' or 'in' after for loop variable") p.addError("expected '=', ',' or 'in' after for loop variable")
return nil return nil
} }
} }
func (p *Parser) parseNumericForStatement(variable *Identifier, pos Position) *ForStatement { func (p *Parser) parseNumericForStatement(variable *Identifier) *ForStatement {
stmt := &ForStatement{Variable: variable, Position: pos} stmt := &ForStatement{Variable: variable}
if !p.expectPeek(ASSIGN) { if !p.expectPeek(ASSIGN) {
return nil return nil
@ -686,12 +613,7 @@ func (p *Parser) parseNumericForStatement(variable *Identifier, pos Position) *F
p.nextToken() p.nextToken()
p.enterScope("loop") p.enterScope("loop")
// Declare and resolve loop variable p.declareLoopVariable(variable.Value)
loopVar := p.declareLoopVariable(variable.Value, NumberType)
variable.ScopeDepth = loopVar.ScopeDepth
variable.SlotIndex = loopVar.SlotIndex
variable.typeInfo = NumberType
stmt.Body = p.parseBlockStatements(END) stmt.Body = p.parseBlockStatements(END)
p.exitScope() p.exitScope()
@ -703,8 +625,8 @@ func (p *Parser) parseNumericForStatement(variable *Identifier, pos Position) *F
return stmt return stmt
} }
func (p *Parser) parseForInStatement(firstVar *Identifier, pos Position) *ForInStatement { func (p *Parser) parseForInStatement(firstVar *Identifier) *ForInStatement {
stmt := &ForInStatement{Position: pos} stmt := &ForInStatement{}
if p.peekTokenIs(COMMA) { if p.peekTokenIs(COMMA) {
stmt.Key = firstVar stmt.Key = firstVar
@ -716,7 +638,7 @@ func (p *Parser) parseForInStatement(firstVar *Identifier, pos Position) *ForInS
return nil return nil
} }
stmt.Value = &Identifier{Value: p.curToken.Literal, Position: p.pos()} stmt.Value = &Identifier{Value: p.curToken.Literal}
} else { } else {
stmt.Value = firstVar stmt.Value = firstVar
} }
@ -742,20 +664,10 @@ func (p *Parser) parseForInStatement(firstVar *Identifier, pos Position) *ForInS
p.nextToken() p.nextToken()
p.enterScope("loop") p.enterScope("loop")
// Declare and resolve loop variables
if stmt.Key != nil { if stmt.Key != nil {
keyVar := p.declareLoopVariable(stmt.Key.Value, AnyType) p.declareLoopVariable(stmt.Key.Value)
stmt.Key.ScopeDepth = keyVar.ScopeDepth
stmt.Key.SlotIndex = keyVar.SlotIndex
stmt.Key.typeInfo = AnyType
} }
p.declareLoopVariable(stmt.Value.Value)
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) stmt.Body = p.parseBlockStatements(END)
p.exitScope() p.exitScope()
@ -769,7 +681,7 @@ func (p *Parser) parseForInStatement(firstVar *Identifier, pos Position) *ForInS
// parseIfStatement parses if statements // parseIfStatement parses if statements
func (p *Parser) parseIfStatement() *IfStatement { func (p *Parser) parseIfStatement() *IfStatement {
stmt := &IfStatement{Position: p.pos()} stmt := &IfStatement{}
p.nextToken() p.nextToken()
@ -793,7 +705,7 @@ func (p *Parser) parseIfStatement() *IfStatement {
stmt.Body = p.parseBlockStatements(ELSEIF, ELSE, END) stmt.Body = p.parseBlockStatements(ELSEIF, ELSE, END)
for p.curTokenIs(ELSEIF) { for p.curTokenIs(ELSEIF) {
elseif := ElseIfClause{Position: p.pos()} elseif := ElseIfClause{}
p.nextToken() p.nextToken()
@ -881,25 +793,11 @@ func (p *Parser) ParseExpression(precedence Precedence) Expression {
// Expression parsing functions // Expression parsing functions
func (p *Parser) parseIdentifier() Expression { func (p *Parser) parseIdentifier() Expression {
ident := &Identifier{ return &Identifier{Value: p.curToken.Literal}
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 { func (p *Parser) parseNumberLiteral() Expression {
lit := &NumberLiteral{Position: p.pos()} lit := &NumberLiteral{}
literal := p.curToken.Literal literal := p.curToken.Literal
var value float64 var value float64
@ -955,21 +853,20 @@ func (p *Parser) parseNumberLiteral() Expression {
} }
func (p *Parser) parseStringLiteral() Expression { func (p *Parser) parseStringLiteral() Expression {
return &StringLiteral{Value: p.curToken.Literal, Position: p.pos()} return &StringLiteral{Value: p.curToken.Literal}
} }
func (p *Parser) parseBooleanLiteral() Expression { func (p *Parser) parseBooleanLiteral() Expression {
return &BooleanLiteral{Value: p.curTokenIs(TRUE), Position: p.pos()} return &BooleanLiteral{Value: p.curTokenIs(TRUE)}
} }
func (p *Parser) parseNilLiteral() Expression { func (p *Parser) parseNilLiteral() Expression {
return &NilLiteral{Position: p.pos()} return &NilLiteral{}
} }
func (p *Parser) parsePrefixExpression() Expression { func (p *Parser) parsePrefixExpression() Expression {
expression := &PrefixExpression{ expression := &PrefixExpression{
Operator: p.curToken.Literal, Operator: p.curToken.Literal,
Position: p.pos(),
} }
p.nextToken() p.nextToken()
@ -1007,7 +904,6 @@ func (p *Parser) parseGroupedExpression() Expression {
// parseParenthesizedAssignment parses assignment expressions in parentheses // parseParenthesizedAssignment parses assignment expressions in parentheses
func (p *Parser) parseParenthesizedAssignment() Expression { func (p *Parser) parseParenthesizedAssignment() Expression {
pos := p.pos()
target := p.parseIdentifier() target := p.parseIdentifier()
if !p.expectPeek(ASSIGN) { if !p.expectPeek(ASSIGN) {
@ -1031,22 +927,13 @@ func (p *Parser) parseParenthesizedAssignment() Expression {
Target: target, Target: target,
Value: value, Value: value,
IsExpression: true, IsExpression: true,
Position: pos,
} }
// Handle variable declaration for assignment expressions // Handle variable declaration for assignment expressions
if ident, ok := target.(*Identifier); ok { if ident, ok := target.(*Identifier); ok {
if variable := p.lookupVariable(ident.Value); variable != nil { assignExpr.IsDeclaration = !p.isVariableDeclared(ident.Value)
// Existing variable if assignExpr.IsDeclaration {
ident.ScopeDepth = variable.ScopeDepth p.declareVariable(ident.Value)
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
} }
} }
@ -1054,7 +941,7 @@ func (p *Parser) parseParenthesizedAssignment() Expression {
} }
func (p *Parser) parseFunctionLiteral() Expression { func (p *Parser) parseFunctionLiteral() Expression {
fn := &FunctionLiteral{Position: p.pos()} fn := &FunctionLiteral{}
if !p.expectPeek(LPAREN) { if !p.expectPeek(LPAREN) {
p.addError("expected '(' after 'fn'") p.addError("expected '(' after 'fn'")
@ -1074,21 +961,10 @@ func (p *Parser) parseFunctionLiteral() Expression {
p.nextToken() p.nextToken()
p.enterScope("function") p.enterScope("function")
// Declare function parameters as resolved variables
for _, param := range fn.Parameters { for _, param := range fn.Parameters {
p.declareVariable(param.Name, param.TypeHint) p.declareVariable(param.Name)
param.Position = Position{Line: p.curToken.Line, Column: p.curToken.Column}
} }
fn.Body = p.parseBlockStatements(END) 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() p.exitScope()
if !p.curTokenIs(END) { if !p.curTokenIs(END) {
@ -1120,7 +996,7 @@ func (p *Parser) parseFunctionParameters() ([]FunctionParameter, bool) {
return nil, false return nil, false
} }
param := FunctionParameter{Name: p.curToken.Literal, Position: p.pos()} param := FunctionParameter{Name: p.curToken.Literal}
// Check for type hint // Check for type hint
param.TypeHint = p.parseTypeHint() param.TypeHint = p.parseTypeHint()
@ -1144,7 +1020,7 @@ func (p *Parser) parseFunctionParameters() ([]FunctionParameter, bool) {
} }
func (p *Parser) parseTableLiteral() Expression { func (p *Parser) parseTableLiteral() Expression {
table := &TableLiteral{Position: p.pos()} table := &TableLiteral{}
table.Pairs = make([]TablePair, 0, 4) // Pre-allocate table.Pairs = make([]TablePair, 0, 4) // Pre-allocate
if p.peekTokenIs(RBRACE) { if p.peekTokenIs(RBRACE) {
@ -1160,13 +1036,13 @@ func (p *Parser) parseTableLiteral() Expression {
return nil return nil
} }
pair := TablePair{Position: p.pos()} pair := TablePair{}
if (p.curTokenIs(IDENT) || p.curTokenIs(STRING)) && p.peekTokenIs(ASSIGN) { if (p.curTokenIs(IDENT) || p.curTokenIs(STRING)) && p.peekTokenIs(ASSIGN) {
if p.curTokenIs(IDENT) { if p.curTokenIs(IDENT) {
pair.Key = &Identifier{Value: p.curToken.Literal, Position: p.pos()} pair.Key = &Identifier{Value: p.curToken.Literal}
} else { } else {
pair.Key = &StringLiteral{Value: p.curToken.Literal, Position: p.pos()} pair.Key = &StringLiteral{Value: p.curToken.Literal}
} }
p.nextToken() p.nextToken()
p.nextToken() p.nextToken()
@ -1213,8 +1089,6 @@ func (p *Parser) parseTableLiteral() Expression {
// parseStructConstructor handles struct constructor calls // parseStructConstructor handles struct constructor calls
func (p *Parser) parseStructConstructor(left Expression) Expression { func (p *Parser) parseStructConstructor(left Expression) Expression {
pos := p.pos()
ident, ok := left.(*Identifier) ident, ok := left.(*Identifier)
if !ok { if !ok {
return p.parseTableLiteralFromBrace() return p.parseTableLiteralFromBrace()
@ -1231,7 +1105,6 @@ func (p *Parser) parseStructConstructor(left Expression) Expression {
StructID: structDef.ID, StructID: structDef.ID,
Fields: make([]TablePair, 0, 4), Fields: make([]TablePair, 0, 4),
typeInfo: TypeInfo{Type: TypeStruct, StructID: structDef.ID, Inferred: true}, typeInfo: TypeInfo{Type: TypeStruct, StructID: structDef.ID, Inferred: true},
Position: pos,
} }
if p.peekTokenIs(RBRACE) { if p.peekTokenIs(RBRACE) {
@ -1247,13 +1120,13 @@ func (p *Parser) parseStructConstructor(left Expression) Expression {
return nil return nil
} }
pair := TablePair{Position: p.pos()} pair := TablePair{}
if (p.curTokenIs(IDENT) || p.curTokenIs(STRING)) && p.peekTokenIs(ASSIGN) { if (p.curTokenIs(IDENT) || p.curTokenIs(STRING)) && p.peekTokenIs(ASSIGN) {
if p.curTokenIs(IDENT) { if p.curTokenIs(IDENT) {
pair.Key = &Identifier{Value: p.curToken.Literal, Position: p.pos()} pair.Key = &Identifier{Value: p.curToken.Literal}
} else { } else {
pair.Key = &StringLiteral{Value: p.curToken.Literal, Position: p.pos()} pair.Key = &StringLiteral{Value: p.curToken.Literal}
} }
p.nextToken() p.nextToken()
p.nextToken() p.nextToken()
@ -1299,7 +1172,7 @@ func (p *Parser) parseStructConstructor(left Expression) Expression {
} }
func (p *Parser) parseTableLiteralFromBrace() Expression { func (p *Parser) parseTableLiteralFromBrace() Expression {
table := &TableLiteral{Position: p.pos()} table := &TableLiteral{}
table.Pairs = make([]TablePair, 0, 4) table.Pairs = make([]TablePair, 0, 4)
if p.peekTokenIs(RBRACE) { if p.peekTokenIs(RBRACE) {
@ -1315,13 +1188,13 @@ func (p *Parser) parseTableLiteralFromBrace() Expression {
return nil return nil
} }
pair := TablePair{Position: p.pos()} pair := TablePair{}
if (p.curTokenIs(IDENT) || p.curTokenIs(STRING)) && p.peekTokenIs(ASSIGN) { if (p.curTokenIs(IDENT) || p.curTokenIs(STRING)) && p.peekTokenIs(ASSIGN) {
if p.curTokenIs(IDENT) { if p.curTokenIs(IDENT) {
pair.Key = &Identifier{Value: p.curToken.Literal, Position: p.pos()} pair.Key = &Identifier{Value: p.curToken.Literal}
} else { } else {
pair.Key = &StringLiteral{Value: p.curToken.Literal, Position: p.pos()} pair.Key = &StringLiteral{Value: p.curToken.Literal}
} }
p.nextToken() p.nextToken()
p.nextToken() p.nextToken()
@ -1370,7 +1243,6 @@ func (p *Parser) parseInfixExpression(left Expression) Expression {
expression := &InfixExpression{ expression := &InfixExpression{
Left: left, Left: left,
Operator: p.curToken.Literal, Operator: p.curToken.Literal,
Position: p.pos(),
} }
precedence := p.curPrecedence() precedence := p.curPrecedence()
@ -1386,22 +1258,19 @@ func (p *Parser) parseInfixExpression(left Expression) Expression {
} }
func (p *Parser) parseDotExpression(left Expression) Expression { func (p *Parser) parseDotExpression(left Expression) Expression {
pos := p.pos()
if !p.expectPeekIdent() { if !p.expectPeekIdent() {
p.addError("expected identifier after '.'") p.addError("expected identifier after '.'")
return nil return nil
} }
return &DotExpression{ return &DotExpression{
Left: left, Left: left,
Key: p.curToken.Literal, Key: p.curToken.Literal,
Position: pos,
} }
} }
func (p *Parser) parseCallExpression(fn Expression) Expression { func (p *Parser) parseCallExpression(fn Expression) Expression {
call := &CallExpression{Function: fn, Position: p.pos()} call := &CallExpression{Function: fn}
call.Arguments = p.parseExpressionList(RPAREN) call.Arguments = p.parseExpressionList(RPAREN)
return call return call
} }
@ -1431,7 +1300,6 @@ func (p *Parser) parseExpressionList(end TokenType) []Expression {
} }
func (p *Parser) parseIndexExpression(left Expression) Expression { func (p *Parser) parseIndexExpression(left Expression) Expression {
pos := p.pos()
p.nextToken() p.nextToken()
index := p.ParseExpression(LOWEST) index := p.ParseExpression(LOWEST)
@ -1446,9 +1314,8 @@ func (p *Parser) parseIndexExpression(left Expression) Expression {
} }
return &IndexExpression{ return &IndexExpression{
Left: left, Left: left,
Index: index, Index: index,
Position: pos,
} }
} }
@ -1514,7 +1381,7 @@ func (p *Parser) noPrefixParseFnError(t TokenType) {
switch t { switch t {
case ASSIGN: case ASSIGN:
message = "unexpected assignment operator, missing left-hand side identifier" message = "unexpected assignment operator, missing left-hand side identifier"
case PLUS, MINUS, STAR, SLASH, MOD: case PLUS, MINUS, STAR, SLASH:
message = fmt.Sprintf("unexpected operator '%s', missing left operand", tokenTypeString(t)) message = fmt.Sprintf("unexpected operator '%s', missing left operand", tokenTypeString(t))
case RPAREN: case RPAREN:
message = "unexpected closing parenthesis" message = "unexpected closing parenthesis"
@ -1579,8 +1446,6 @@ func tokenTypeString(t TokenType) string {
return "*" return "*"
case SLASH: case SLASH:
return "/" return "/"
case MOD:
return "%"
case DOT: case DOT:
return "." return "."
case COLON: case COLON:

View File

@ -325,46 +325,3 @@ echo {result = x}`
t.Fatalf("expected TableLiteral in echo, got %T", echo2.Value) t.Fatalf("expected TableLiteral in echo, got %T", echo2.Value)
} }
} }
func TestModuloOperator(t *testing.T) {
input := `result = 10 % 3`
l := parser.NewLexer(input)
p := parser.NewParser(l)
program := p.ParseProgram()
checkParserErrors(t, p)
if len(program.Statements) != 1 {
t.Fatalf("expected 1 statement, got %d", len(program.Statements))
}
stmt, ok := program.Statements[0].(*parser.Assignment)
if !ok {
t.Fatalf("expected AssignStatement, got %T", program.Statements[0])
}
infix, ok := stmt.Value.(*parser.InfixExpression)
if !ok {
t.Fatalf("expected InfixExpression, got %T", stmt.Value)
}
if infix.Operator != "%" {
t.Errorf("expected operator '%%', got %s", infix.Operator)
}
leftLit, ok := infix.Left.(*parser.NumberLiteral)
if !ok {
t.Fatalf("expected NumberLiteral on left, got %T", infix.Left)
}
if leftLit.Value != 10.0 {
t.Errorf("expected left value 10, got %f", leftLit.Value)
}
rightLit, ok := infix.Right.(*parser.NumberLiteral)
if !ok {
t.Fatalf("expected NumberLiteral on right, got %T", infix.Right)
}
if rightLit.Value != 3.0 {
t.Errorf("expected right value 3, got %f", rightLit.Value)
}
}

View File

@ -18,7 +18,6 @@ const (
MINUS // - MINUS // -
STAR // * STAR // *
SLASH // / SLASH // /
MOD // %
DOT // . DOT // .
// Comparison operators // Comparison operators
@ -86,7 +85,7 @@ const (
EQUALS // ==, != EQUALS // ==, !=
LESSGREATER // >, <, >=, <= LESSGREATER // >, <, >=, <=
SUM // +, - SUM // +, -
PRODUCT // *, /, % PRODUCT // *, /
PREFIX // -x, not x PREFIX // -x, not x
MEMBER // table[key], table.key MEMBER // table[key], table.key
CALL // function() CALL // function()
@ -106,7 +105,6 @@ var precedences = map[TokenType]Precedence{
MINUS: SUM, MINUS: SUM,
SLASH: PRODUCT, SLASH: PRODUCT,
STAR: PRODUCT, STAR: PRODUCT,
MOD: PRODUCT,
DOT: MEMBER, DOT: MEMBER,
LBRACKET: MEMBER, LBRACKET: MEMBER,
LPAREN: CALL, LPAREN: CALL,

View File

@ -521,7 +521,7 @@ func (ti *TypeInferrer) inferInfixExpression(infix *InfixExpression) TypeInfo {
var resultType TypeInfo var resultType TypeInfo
switch infix.Operator { switch infix.Operator {
case "+", "-", "*", "/", "%": case "+", "-", "*", "/":
if !ti.isNumericType(leftType) || !ti.isNumericType(rightType) { if !ti.isNumericType(leftType) || !ti.isNumericType(rightType) {
ti.addError(fmt.Sprintf("arithmetic operator '%s' requires numeric operands", infix.Operator), infix) ti.addError(fmt.Sprintf("arithmetic operator '%s' requires numeric operands", infix.Operator), infix)
} }
@ -636,11 +636,8 @@ func (ti *TypeInferrer) exitScope() {
} }
func (ti *TypeInferrer) addError(message string, node Node) { func (ti *TypeInferrer) addError(message string, node Node) {
pos := node.Pos()
ti.errors = append(ti.errors, TypeError{ ti.errors = append(ti.errors, TypeError{
Message: message, Message: message,
Line: pos.Line,
Column: pos.Column,
Node: node, Node: node,
}) })
} }