modulo operator
This commit is contained in:
parent
121774005b
commit
3d07d010dc
211
parser/ast.go
211
parser/ast.go
@ -2,11 +2,16 @@ package parser
|
|||||||
|
|
||||||
import "fmt"
|
import "fmt"
|
||||||
|
|
||||||
// Note: Type definitions moved to types.go for proper separation of concerns
|
// Position represents source location information
|
||||||
|
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
|
||||||
@ -19,14 +24,14 @@ type Statement interface {
|
|||||||
type Expression interface {
|
type Expression interface {
|
||||||
Node
|
Node
|
||||||
expressionNode()
|
expressionNode()
|
||||||
TypeInfo() TypeInfo // Returns type by value, not pointer
|
TypeInfo() TypeInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
@ -36,24 +41,26 @@ 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 // Required for struct fields, embeds directly
|
TypeHint TypeInfo
|
||||||
|
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 // Unique identifier for fast lookup
|
ID uint16
|
||||||
|
Position Position
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *StructStatement) statementNode() {}
|
func (ss *StructStatement) statementNode() {}
|
||||||
@ -67,26 +74,28 @@ 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 // Index into struct table for fast lookup
|
StructID uint16
|
||||||
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 // Index into struct table
|
StructID uint16
|
||||||
Fields []TablePair // Reuses table pair structure for field assignments
|
Fields []TablePair
|
||||||
typeInfo TypeInfo // Cached type info for this constructor
|
typeInfo TypeInfo
|
||||||
|
Position Position
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sc *StructConstructor) expressionNode() {}
|
func (sc *StructConstructor) expressionNode() {}
|
||||||
@ -98,15 +107,16 @@ 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 (identifier, dot, or index expression)
|
Target Expression
|
||||||
Value Expression // Value being assigned
|
Value Expression
|
||||||
TypeHint TypeInfo // Optional explicit type hint, embeds directly
|
TypeHint TypeInfo
|
||||||
IsDeclaration bool // True if declaring new variable in current scope
|
IsDeclaration bool
|
||||||
IsExpression bool // True if used as expression (wrapped in parentheses)
|
IsExpression bool
|
||||||
|
Position Position
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *Assignment) statementNode() {}
|
func (a *Assignment) statementNode() {}
|
||||||
@ -131,40 +141,45 @@ 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.
|
||||||
// Simple marker node with no additional data needed.
|
type BreakStatement struct {
|
||||||
type BreakStatement struct{}
|
Position Position
|
||||||
|
}
|
||||||
|
|
||||||
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 // Optional exit code expression
|
Value Expression
|
||||||
|
Position Position
|
||||||
}
|
}
|
||||||
|
|
||||||
func (es *ExitStatement) statementNode() {}
|
func (es *ExitStatement) statementNode() {}
|
||||||
@ -174,11 +189,12 @@ 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 // Optional return value expression
|
Value Expression
|
||||||
|
Position Position
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rs *ReturnStatement) statementNode() {}
|
func (rs *ReturnStatement) statementNode() {}
|
||||||
@ -188,12 +204,13 @@ 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 {
|
||||||
@ -203,14 +220,15 @@ 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 // Main condition
|
Condition Expression
|
||||||
Body []Statement // Statements to execute if condition is true
|
Body []Statement
|
||||||
ElseIfs []ElseIfClause // Optional elseif branches
|
ElseIfs []ElseIfClause
|
||||||
Else []Statement // Optional else branch
|
Else []Statement
|
||||||
|
Position Position
|
||||||
}
|
}
|
||||||
|
|
||||||
func (is *IfStatement) statementNode() {}
|
func (is *IfStatement) statementNode() {}
|
||||||
@ -236,12 +254,13 @@ 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() {}
|
||||||
@ -256,15 +275,16 @@ 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 // Loop variable (automatically number type)
|
Variable *Identifier
|
||||||
Start Expression // Starting value expression
|
Start Expression
|
||||||
End Expression // Ending value expression
|
End Expression
|
||||||
Step Expression // Optional step expression (nil means step of 1)
|
Step Expression
|
||||||
Body []Statement // Loop body statements
|
Body []Statement
|
||||||
|
Position Position
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fs *ForStatement) statementNode() {}
|
func (fs *ForStatement) statementNode() {}
|
||||||
@ -285,14 +305,15 @@ 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 // Optional key variable (nil for single variable iteration)
|
Key *Identifier
|
||||||
Value *Identifier // Value variable (required)
|
Value *Identifier
|
||||||
Iterable Expression // Expression to iterate over
|
Iterable Expression
|
||||||
Body []Statement // Loop body statements
|
Body []Statement
|
||||||
|
Position Position
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fis *ForInStatement) statementNode() {}
|
func (fis *ForInStatement) statementNode() {}
|
||||||
@ -313,12 +334,13 @@ 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 // Optional type constraint, embeds directly
|
TypeHint TypeInfo
|
||||||
|
Position Position
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fp *FunctionParameter) String() string {
|
func (fp *FunctionParameter) String() string {
|
||||||
@ -327,12 +349,13 @@ 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
|
||||||
typeInfo TypeInfo // Resolved type, embeds directly
|
typeInfo TypeInfo
|
||||||
|
Position Position
|
||||||
}
|
}
|
||||||
|
|
||||||
func (i *Identifier) expressionNode() {}
|
func (i *Identifier) expressionNode() {}
|
||||||
@ -343,31 +366,34 @@ func (i *Identifier) TypeInfo() TypeInfo {
|
|||||||
}
|
}
|
||||||
return i.typeInfo
|
return i.typeInfo
|
||||||
}
|
}
|
||||||
|
func (i *Identifier) Pos() Position { return i.Position }
|
||||||
|
|
||||||
// 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 // All numbers stored as float64 for simplicity
|
Value float64
|
||||||
|
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 // String content without quotes
|
Value string
|
||||||
|
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() {}
|
||||||
@ -378,22 +404,25 @@ 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.
|
||||||
// Always has nil type, so no additional type storage needed.
|
type NilLiteral struct {
|
||||||
type NilLiteral struct{}
|
Position Position
|
||||||
|
}
|
||||||
|
|
||||||
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 // Function parameters with optional types
|
Parameters []FunctionParameter
|
||||||
Body []Statement // Function body statements
|
Body []Statement
|
||||||
ReturnType TypeInfo // Optional return type hint, embeds directly
|
ReturnType TypeInfo
|
||||||
Variadic bool // True if function accepts variable arguments
|
Variadic bool
|
||||||
|
Position Position
|
||||||
}
|
}
|
||||||
|
|
||||||
func (fl *FunctionLiteral) expressionNode() {}
|
func (fl *FunctionLiteral) expressionNode() {}
|
||||||
@ -425,13 +454,14 @@ 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 to call
|
Function Expression
|
||||||
Arguments []Expression // Argument expressions
|
Arguments []Expression
|
||||||
typeInfo TypeInfo // Inferred return type, embeds directly
|
typeInfo TypeInfo
|
||||||
|
Position Position
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ce *CallExpression) expressionNode() {}
|
func (ce *CallExpression) expressionNode() {}
|
||||||
@ -443,13 +473,14 @@ 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 symbol ("-", "not")
|
Operator string
|
||||||
Right Expression // Operand expression
|
Right Expression
|
||||||
typeInfo TypeInfo // Result type, embeds directly
|
typeInfo TypeInfo
|
||||||
|
Position Position
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pe *PrefixExpression) expressionNode() {}
|
func (pe *PrefixExpression) expressionNode() {}
|
||||||
@ -460,14 +491,15 @@ 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 operand
|
Left Expression
|
||||||
Right Expression // Right operand
|
Right Expression
|
||||||
Operator string // Operator symbol ("+", "-", "==", "and", etc.)
|
Operator string
|
||||||
typeInfo TypeInfo // Result type, embeds directly
|
typeInfo TypeInfo
|
||||||
|
Position Position
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ie *InfixExpression) expressionNode() {}
|
func (ie *InfixExpression) expressionNode() {}
|
||||||
@ -475,13 +507,14 @@ 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 // Container expression
|
Left Expression
|
||||||
Index Expression // Index/key expression
|
Index Expression
|
||||||
typeInfo TypeInfo // Element type, embeds directly
|
typeInfo TypeInfo
|
||||||
|
Position Position
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ie *IndexExpression) expressionNode() {}
|
func (ie *IndexExpression) expressionNode() {}
|
||||||
@ -489,13 +522,14 @@ 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 // Container expression
|
Left Expression
|
||||||
Key string // Member name
|
Key string
|
||||||
typeInfo TypeInfo // Member type, embeds directly
|
typeInfo TypeInfo
|
||||||
|
Position Position
|
||||||
}
|
}
|
||||||
|
|
||||||
func (de *DotExpression) expressionNode() {}
|
func (de *DotExpression) expressionNode() {}
|
||||||
@ -503,12 +537,13 @@ 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 (nil for array elements)
|
Key Expression
|
||||||
Value Expression // Value expression
|
Value Expression
|
||||||
|
Position Position
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tp *TablePair) String() string {
|
func (tp *TablePair) String() string {
|
||||||
@ -517,11 +552,12 @@ 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 // Key-value pairs (key nil for array elements)
|
Pairs []TablePair
|
||||||
|
Position Position
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tl *TableLiteral) expressionNode() {}
|
func (tl *TableLiteral) expressionNode() {}
|
||||||
@ -533,6 +569,7 @@ 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 {
|
||||||
|
@ -257,6 +257,8 @@ 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 '.':
|
||||||
|
109
parser/parser.go
109
parser/parser.go
@ -71,6 +71,7 @@ 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)
|
||||||
@ -90,6 +91,11 @@ 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
|
||||||
@ -201,7 +207,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{}
|
program := &Program{Position: p.pos()}
|
||||||
program.Statements = []Statement{}
|
program.Statements = []Statement{}
|
||||||
|
|
||||||
for !p.curTokenIs(EOF) {
|
for !p.curTokenIs(EOF) {
|
||||||
@ -254,7 +260,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{}
|
stmt := &StructStatement{Position: p.pos()}
|
||||||
|
|
||||||
if !p.expectPeek(IDENT) {
|
if !p.expectPeek(IDENT) {
|
||||||
p.addError("expected struct name")
|
p.addError("expected struct name")
|
||||||
@ -289,7 +295,7 @@ func (p *Parser) parseStructStatement() *StructStatement {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
field := StructField{Name: p.curToken.Literal}
|
field := StructField{Name: p.curToken.Literal, Position: p.pos()}
|
||||||
|
|
||||||
// Parse required type hint
|
// Parse required type hint
|
||||||
field.TypeHint = p.parseTypeHint()
|
field.TypeHint = p.parseTypeHint()
|
||||||
@ -327,6 +333,8 @@ 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
|
||||||
@ -358,7 +366,7 @@ func (p *Parser) parseFunctionStatement() Statement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parse the function literal
|
// Parse the function literal
|
||||||
funcLit := &FunctionLiteral{}
|
funcLit := &FunctionLiteral{Position: p.pos()}
|
||||||
funcLit.Parameters, funcLit.Variadic = p.parseFunctionParameters()
|
funcLit.Parameters, funcLit.Variadic = p.parseFunctionParameters()
|
||||||
|
|
||||||
if !p.expectPeek(RPAREN) {
|
if !p.expectPeek(RPAREN) {
|
||||||
@ -387,6 +395,7 @@ func (p *Parser) parseFunctionStatement() Statement {
|
|||||||
StructID: structDef.ID,
|
StructID: structDef.ID,
|
||||||
MethodName: methodName,
|
MethodName: methodName,
|
||||||
Function: funcLit,
|
Function: funcLit,
|
||||||
|
Position: pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,11 +405,13 @@ func (p *Parser) parseFunctionStatement() Statement {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ExpressionStatement{Expression: funcLit}
|
return &ExpressionStatement{Expression: funcLit, Position: pos}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 {
|
||||||
@ -419,6 +430,7 @@ 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
|
||||||
@ -450,13 +462,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}
|
return &ExpressionStatement{Expression: expr, Position: pos}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseEchoStatement parses echo statements
|
// parseEchoStatement parses echo statements
|
||||||
func (p *Parser) parseEchoStatement() *EchoStatement {
|
func (p *Parser) parseEchoStatement() *EchoStatement {
|
||||||
stmt := &EchoStatement{}
|
stmt := &EchoStatement{Position: p.pos()}
|
||||||
|
|
||||||
p.nextToken() // move past 'echo'
|
p.nextToken() // move past 'echo'
|
||||||
|
|
||||||
@ -475,11 +487,11 @@ func (p *Parser) parseBreakStatement() *BreakStatement {
|
|||||||
p.addError("unexpected identifier")
|
p.addError("unexpected identifier")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return &BreakStatement{}
|
return &BreakStatement{Position: p.pos()}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) parseExitStatement() *ExitStatement {
|
func (p *Parser) parseExitStatement() *ExitStatement {
|
||||||
stmt := &ExitStatement{}
|
stmt := &ExitStatement{Position: p.pos()}
|
||||||
|
|
||||||
if p.canStartExpression(p.peekToken.Type) {
|
if p.canStartExpression(p.peekToken.Type) {
|
||||||
p.nextToken()
|
p.nextToken()
|
||||||
@ -494,7 +506,7 @@ func (p *Parser) parseExitStatement() *ExitStatement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) parseReturnStatement() *ReturnStatement {
|
func (p *Parser) parseReturnStatement() *ReturnStatement {
|
||||||
stmt := &ReturnStatement{}
|
stmt := &ReturnStatement{Position: p.pos()}
|
||||||
|
|
||||||
if p.canStartExpression(p.peekToken.Type) {
|
if p.canStartExpression(p.peekToken.Type) {
|
||||||
p.nextToken()
|
p.nextToken()
|
||||||
@ -519,7 +531,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{}
|
stmt := &WhileStatement{Position: p.pos()}
|
||||||
|
|
||||||
p.nextToken()
|
p.nextToken()
|
||||||
|
|
||||||
@ -547,6 +559,7 @@ 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) {
|
||||||
@ -554,20 +567,20 @@ func (p *Parser) parseForStatement() Statement {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
firstVar := &Identifier{Value: p.curToken.Literal}
|
firstVar := &Identifier{Value: p.curToken.Literal, Position: p.pos()}
|
||||||
|
|
||||||
if p.peekTokenIs(ASSIGN) {
|
if p.peekTokenIs(ASSIGN) {
|
||||||
return p.parseNumericForStatement(firstVar)
|
return p.parseNumericForStatement(firstVar, pos)
|
||||||
} else if p.peekTokenIs(COMMA) || p.peekTokenIs(IN) {
|
} else if p.peekTokenIs(COMMA) || p.peekTokenIs(IN) {
|
||||||
return p.parseForInStatement(firstVar)
|
return p.parseForInStatement(firstVar, pos)
|
||||||
} 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) *ForStatement {
|
func (p *Parser) parseNumericForStatement(variable *Identifier, pos Position) *ForStatement {
|
||||||
stmt := &ForStatement{Variable: variable}
|
stmt := &ForStatement{Variable: variable, Position: pos}
|
||||||
|
|
||||||
if !p.expectPeek(ASSIGN) {
|
if !p.expectPeek(ASSIGN) {
|
||||||
return nil
|
return nil
|
||||||
@ -625,8 +638,8 @@ func (p *Parser) parseNumericForStatement(variable *Identifier) *ForStatement {
|
|||||||
return stmt
|
return stmt
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) parseForInStatement(firstVar *Identifier) *ForInStatement {
|
func (p *Parser) parseForInStatement(firstVar *Identifier, pos Position) *ForInStatement {
|
||||||
stmt := &ForInStatement{}
|
stmt := &ForInStatement{Position: pos}
|
||||||
|
|
||||||
if p.peekTokenIs(COMMA) {
|
if p.peekTokenIs(COMMA) {
|
||||||
stmt.Key = firstVar
|
stmt.Key = firstVar
|
||||||
@ -638,7 +651,7 @@ func (p *Parser) parseForInStatement(firstVar *Identifier) *ForInStatement {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt.Value = &Identifier{Value: p.curToken.Literal}
|
stmt.Value = &Identifier{Value: p.curToken.Literal, Position: p.pos()}
|
||||||
} else {
|
} else {
|
||||||
stmt.Value = firstVar
|
stmt.Value = firstVar
|
||||||
}
|
}
|
||||||
@ -681,7 +694,7 @@ func (p *Parser) parseForInStatement(firstVar *Identifier) *ForInStatement {
|
|||||||
|
|
||||||
// parseIfStatement parses if statements
|
// parseIfStatement parses if statements
|
||||||
func (p *Parser) parseIfStatement() *IfStatement {
|
func (p *Parser) parseIfStatement() *IfStatement {
|
||||||
stmt := &IfStatement{}
|
stmt := &IfStatement{Position: p.pos()}
|
||||||
|
|
||||||
p.nextToken()
|
p.nextToken()
|
||||||
|
|
||||||
@ -705,7 +718,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{}
|
elseif := ElseIfClause{Position: p.pos()}
|
||||||
|
|
||||||
p.nextToken()
|
p.nextToken()
|
||||||
|
|
||||||
@ -793,11 +806,11 @@ func (p *Parser) ParseExpression(precedence Precedence) Expression {
|
|||||||
|
|
||||||
// Expression parsing functions
|
// Expression parsing functions
|
||||||
func (p *Parser) parseIdentifier() Expression {
|
func (p *Parser) parseIdentifier() Expression {
|
||||||
return &Identifier{Value: p.curToken.Literal}
|
return &Identifier{Value: p.curToken.Literal, Position: p.pos()}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) parseNumberLiteral() Expression {
|
func (p *Parser) parseNumberLiteral() Expression {
|
||||||
lit := &NumberLiteral{}
|
lit := &NumberLiteral{Position: p.pos()}
|
||||||
literal := p.curToken.Literal
|
literal := p.curToken.Literal
|
||||||
|
|
||||||
var value float64
|
var value float64
|
||||||
@ -853,20 +866,21 @@ func (p *Parser) parseNumberLiteral() Expression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) parseStringLiteral() Expression {
|
func (p *Parser) parseStringLiteral() Expression {
|
||||||
return &StringLiteral{Value: p.curToken.Literal}
|
return &StringLiteral{Value: p.curToken.Literal, Position: p.pos()}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) parseBooleanLiteral() Expression {
|
func (p *Parser) parseBooleanLiteral() Expression {
|
||||||
return &BooleanLiteral{Value: p.curTokenIs(TRUE)}
|
return &BooleanLiteral{Value: p.curTokenIs(TRUE), Position: p.pos()}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) parseNilLiteral() Expression {
|
func (p *Parser) parseNilLiteral() Expression {
|
||||||
return &NilLiteral{}
|
return &NilLiteral{Position: p.pos()}
|
||||||
}
|
}
|
||||||
|
|
||||||
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()
|
||||||
@ -904,6 +918,7 @@ 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) {
|
||||||
@ -927,6 +942,7 @@ 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
|
||||||
@ -941,7 +957,7 @@ func (p *Parser) parseParenthesizedAssignment() Expression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) parseFunctionLiteral() Expression {
|
func (p *Parser) parseFunctionLiteral() Expression {
|
||||||
fn := &FunctionLiteral{}
|
fn := &FunctionLiteral{Position: p.pos()}
|
||||||
|
|
||||||
if !p.expectPeek(LPAREN) {
|
if !p.expectPeek(LPAREN) {
|
||||||
p.addError("expected '(' after 'fn'")
|
p.addError("expected '(' after 'fn'")
|
||||||
@ -996,7 +1012,7 @@ func (p *Parser) parseFunctionParameters() ([]FunctionParameter, bool) {
|
|||||||
return nil, false
|
return nil, false
|
||||||
}
|
}
|
||||||
|
|
||||||
param := FunctionParameter{Name: p.curToken.Literal}
|
param := FunctionParameter{Name: p.curToken.Literal, Position: p.pos()}
|
||||||
|
|
||||||
// Check for type hint
|
// Check for type hint
|
||||||
param.TypeHint = p.parseTypeHint()
|
param.TypeHint = p.parseTypeHint()
|
||||||
@ -1020,7 +1036,7 @@ func (p *Parser) parseFunctionParameters() ([]FunctionParameter, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) parseTableLiteral() Expression {
|
func (p *Parser) parseTableLiteral() Expression {
|
||||||
table := &TableLiteral{}
|
table := &TableLiteral{Position: p.pos()}
|
||||||
table.Pairs = make([]TablePair, 0, 4) // Pre-allocate
|
table.Pairs = make([]TablePair, 0, 4) // Pre-allocate
|
||||||
|
|
||||||
if p.peekTokenIs(RBRACE) {
|
if p.peekTokenIs(RBRACE) {
|
||||||
@ -1036,13 +1052,13 @@ func (p *Parser) parseTableLiteral() Expression {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
pair := TablePair{}
|
pair := TablePair{Position: p.pos()}
|
||||||
|
|
||||||
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}
|
pair.Key = &Identifier{Value: p.curToken.Literal, Position: p.pos()}
|
||||||
} else {
|
} else {
|
||||||
pair.Key = &StringLiteral{Value: p.curToken.Literal}
|
pair.Key = &StringLiteral{Value: p.curToken.Literal, Position: p.pos()}
|
||||||
}
|
}
|
||||||
p.nextToken()
|
p.nextToken()
|
||||||
p.nextToken()
|
p.nextToken()
|
||||||
@ -1089,6 +1105,8 @@ 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()
|
||||||
@ -1105,6 +1123,7 @@ 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) {
|
||||||
@ -1120,13 +1139,13 @@ func (p *Parser) parseStructConstructor(left Expression) Expression {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
pair := TablePair{}
|
pair := TablePair{Position: p.pos()}
|
||||||
|
|
||||||
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}
|
pair.Key = &Identifier{Value: p.curToken.Literal, Position: p.pos()}
|
||||||
} else {
|
} else {
|
||||||
pair.Key = &StringLiteral{Value: p.curToken.Literal}
|
pair.Key = &StringLiteral{Value: p.curToken.Literal, Position: p.pos()}
|
||||||
}
|
}
|
||||||
p.nextToken()
|
p.nextToken()
|
||||||
p.nextToken()
|
p.nextToken()
|
||||||
@ -1172,7 +1191,7 @@ func (p *Parser) parseStructConstructor(left Expression) Expression {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *Parser) parseTableLiteralFromBrace() Expression {
|
func (p *Parser) parseTableLiteralFromBrace() Expression {
|
||||||
table := &TableLiteral{}
|
table := &TableLiteral{Position: p.pos()}
|
||||||
table.Pairs = make([]TablePair, 0, 4)
|
table.Pairs = make([]TablePair, 0, 4)
|
||||||
|
|
||||||
if p.peekTokenIs(RBRACE) {
|
if p.peekTokenIs(RBRACE) {
|
||||||
@ -1188,13 +1207,13 @@ func (p *Parser) parseTableLiteralFromBrace() Expression {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
pair := TablePair{}
|
pair := TablePair{Position: p.pos()}
|
||||||
|
|
||||||
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}
|
pair.Key = &Identifier{Value: p.curToken.Literal, Position: p.pos()}
|
||||||
} else {
|
} else {
|
||||||
pair.Key = &StringLiteral{Value: p.curToken.Literal}
|
pair.Key = &StringLiteral{Value: p.curToken.Literal, Position: p.pos()}
|
||||||
}
|
}
|
||||||
p.nextToken()
|
p.nextToken()
|
||||||
p.nextToken()
|
p.nextToken()
|
||||||
@ -1243,6 +1262,7 @@ 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()
|
||||||
@ -1258,6 +1278,8 @@ 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
|
||||||
@ -1266,11 +1288,12 @@ func (p *Parser) parseDotExpression(left Expression) Expression {
|
|||||||
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}
|
call := &CallExpression{Function: fn, Position: p.pos()}
|
||||||
call.Arguments = p.parseExpressionList(RPAREN)
|
call.Arguments = p.parseExpressionList(RPAREN)
|
||||||
return call
|
return call
|
||||||
}
|
}
|
||||||
@ -1300,6 +1323,7 @@ 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)
|
||||||
@ -1316,6 +1340,7 @@ func (p *Parser) parseIndexExpression(left Expression) Expression {
|
|||||||
return &IndexExpression{
|
return &IndexExpression{
|
||||||
Left: left,
|
Left: left,
|
||||||
Index: index,
|
Index: index,
|
||||||
|
Position: pos,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1381,7 +1406,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:
|
case PLUS, MINUS, STAR, SLASH, MOD:
|
||||||
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"
|
||||||
@ -1446,6 +1471,8 @@ 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:
|
||||||
|
@ -325,3 +325,46 @@ 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -18,6 +18,7 @@ const (
|
|||||||
MINUS // -
|
MINUS // -
|
||||||
STAR // *
|
STAR // *
|
||||||
SLASH // /
|
SLASH // /
|
||||||
|
MOD // %
|
||||||
DOT // .
|
DOT // .
|
||||||
|
|
||||||
// Comparison operators
|
// Comparison operators
|
||||||
@ -85,7 +86,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()
|
||||||
@ -105,6 +106,7 @@ 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,
|
||||||
|
@ -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,8 +636,11 @@ 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,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user