diff --git a/README.md b/README.md index 525544d..c5a19b8 100644 --- a/README.md +++ b/README.md @@ -41,4 +41,14 @@ elseif otherExpression() then else // final thing end + +// Table order is preserved, unlike Lua +table = { + anything = "foo", + 123 = "arg" +} +echo table["anything"] // outputs foo +echo table.anything // outputs foo +table.rawr = "mega" +table["mega"] = "rawr" ``` diff --git a/types/ast.go b/types/ast.go index 0c721c1..4f6aaf1 100644 --- a/types/ast.go +++ b/types/ast.go @@ -91,9 +91,10 @@ func (a AssignStmt) String() string { // FunctionStmt represents a function declaration type FunctionStmt struct { - Name Token - Params []Token - Body []Statement + Name Token + Params []Token + IsVariadic bool + Body []Statement } func (f FunctionStmt) statementNode() {} @@ -105,9 +106,46 @@ func (f FunctionStmt) String() string { } params += param.Lexeme } + + if f.IsVariadic { + if len(f.Params) > 0 { + params += ", ..." + } else { + params += "..." + } + } + return fmt.Sprintf("fn %s(%s) ... end", f.Name.Lexeme, params) } +// FunctionStmt represents a function expression (anonymous function) +type FunctionExpr struct { + Params []Token + IsVariadic bool + Body []Statement +} + +func (f FunctionExpr) expressionNode() {} +func (f FunctionExpr) String() string { + params := "" + for i, param := range f.Params { + if i > 0 { + params += ", " + } + params += param.Lexeme + } + + if f.IsVariadic { + if len(f.Params) > 0 { + params += ", ..." + } else { + params += "..." + } + } + + return fmt.Sprintf("fn(%s) ... end", params) +} + // ReturnStmt represents a return statement type ReturnStmt struct { Keyword Token diff --git a/types/bytecode.go b/types/bytecode.go index f97dc8d..b1c0b7f 100644 --- a/types/bytecode.go +++ b/types/bytecode.go @@ -34,6 +34,10 @@ const ( OP_JUMP_IF_FALSE OP_CALL OP_RETURN + OP_CLOSURE + OP_GET_UPVALUE + OP_SET_UPVALUE + OP_CLOSE_UPVALUE ) // String returns the string representation of an OpCode @@ -44,7 +48,8 @@ func (op OpCode) String() string { "OP_SET_GLOBAL", "OP_EQUAL", "OP_GREATER", "OP_LESS", "OP_ADD", "OP_SUBTRACT", "OP_MULTIPLY", "OP_DIVIDE", "OP_NOT", "OP_NEGATE", "OP_PRINT", "OP_JUMP", - "OP_JUMP_IF_FALSE", "OP_CALL", "OP_RETURN", + "OP_JUMP_IF_FALSE", "OP_CALL", "OP_RETURN", "OP_CLOSURE", + "OP_GET_UPVALUE", "OP_SET_UPVALUE", "OP_CLOSE_UPVALUE", } return names[op] } @@ -66,11 +71,18 @@ type Chunk struct { type Function struct { Name string Arity int + IsVariadic bool Chunk *Chunk + Upvalues []Upvalue LocalCount int UpvalueCount int } +type Closure struct { + Function *Function + Upvalues []*Upvalue +} + // Environment represents a variable scope type Environment struct { Values map[string]Value @@ -84,3 +96,9 @@ func NewEnvironment(enclosing *Environment) *Environment { Enclosing: enclosing, } } + +// Upvalue support for closures +type Upvalue struct { + Index uint8 + IsLocal bool +} diff --git a/types/token.go b/types/token.go index d7e59d6..c2cd606 100644 --- a/types/token.go +++ b/types/token.go @@ -55,6 +55,7 @@ const ( LEFT_PAREN // ( RIGHT_PAREN // ) COMMA // , + ELLIPSIS // ... ) // String returns the string representation of the token type @@ -96,4 +97,5 @@ var tokenNames = [...]string{ LEFT_PAREN: "LEFT_PAREN", RIGHT_PAREN: "RIGHT_PAREN", COMMA: "COMMA", + ELLIPSIS: "ELLIPSIS", } diff --git a/types/value.go b/types/value.go index 1b914bf..2fcad4b 100644 --- a/types/value.go +++ b/types/value.go @@ -12,6 +12,7 @@ const ( STRING_TYPE FUNCTION_TYPE TABLE_TYPE + CLOSURE_TYPE ) // Value interface represents any value in the language at runtime @@ -104,3 +105,18 @@ func (t TableValue) Type() ValueType { func (t TableValue) String() string { return fmt.Sprintf("", &t) } + +type ClosureValue struct { + Closure *Closure +} + +func (c ClosureValue) Type() ValueType { + return CLOSURE_TYPE +} + +func (c ClosureValue) String() string { + if c.Closure.Function.Name == "" { + return "" + } + return fmt.Sprintf("", c.Closure.Function.Name) +}