├── .gitignore ├── README.md ├── STATS.md ├── ast ├── ast.go ├── ast_test.go └── nodes.go ├── cmd └── main.go ├── code ├── code.go └── code_test.go ├── compiler ├── compiler.go ├── compiler_test.go └── symbol_table.go ├── eval ├── builtin.go ├── eval.go └── eval_test.go ├── go.mod ├── go.sum ├── lexer ├── lexer.go └── lexer_test.go ├── object ├── array.go ├── boolean.go ├── builtinfn.go ├── closure.go ├── env.go ├── error.go ├── function.go ├── hashmap.go ├── integer.go ├── null.go ├── object.go ├── return.go └── string.go ├── parser ├── parser.go └── parser_test.go ├── repl └── repl.go ├── token ├── keywords.go ├── token.go └── types.go └── vm ├── frame.go ├── vm.go └── vm_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by https://www.gitignore.io/api/go,code,jetbrains 2 | # Edit at https://www.gitignore.io/?templates=go,code,jetbrains 3 | 4 | ### Code ### 5 | .vscode/* 6 | !.vscode/settings.json 7 | !.vscode/tasks.json 8 | !.vscode/launch.json 9 | !.vscode/extensions.json 10 | 11 | ### Go ### 12 | # Binaries for programs and plugins 13 | *.exe 14 | *.exe~ 15 | *.dll 16 | *.so 17 | *.dylib 18 | 19 | # Test binary, built with `go test -c` 20 | *.test 21 | 22 | # Output of the go coverage tool, specifically when used with LiteIDE 23 | *.out 24 | 25 | # Dependency directories (remove the comment below to include it) 26 | # vendor/ 27 | 28 | ### Go Patch ### 29 | /vendor/ 30 | /Godeps/ 31 | 32 | ### JetBrains ### 33 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 34 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 35 | 36 | # User-specific stuff 37 | .idea/**/workspace.xml 38 | .idea/**/tasks.xml 39 | .idea/**/usage.statistics.xml 40 | .idea/**/dictionaries 41 | .idea/**/shelf 42 | 43 | # Generated files 44 | .idea/**/contentModel.xml 45 | 46 | # Sensitive or high-churn files 47 | .idea/**/dataSources/ 48 | .idea/**/dataSources.ids 49 | .idea/**/dataSources.local.xml 50 | .idea/**/sqlDataSources.xml 51 | .idea/**/dynamic.xml 52 | .idea/**/uiDesigner.xml 53 | .idea/**/dbnavigator.xml 54 | 55 | # Gradle 56 | .idea/**/gradle.xml 57 | .idea/**/libraries 58 | 59 | # Gradle and Maven with auto-import 60 | # When using Gradle or Maven with auto-import, you should exclude module files, 61 | # since they will be recreated, and may cause churn. Uncomment if using 62 | # auto-import. 63 | # .idea/modules.xml 64 | # .idea/*.iml 65 | # .idea/modules 66 | # *.iml 67 | # *.ipr 68 | 69 | # CMake 70 | cmake-build-*/ 71 | 72 | # Mongo Explorer plugin 73 | .idea/**/mongoSettings.xml 74 | 75 | # File-based project format 76 | *.iws 77 | 78 | # IntelliJ 79 | out/ 80 | 81 | # mpeltonen/sbt-idea plugin 82 | .idea_modules/ 83 | 84 | # JIRA plugin 85 | atlassian-ide-plugin.xml 86 | 87 | # Cursive Clojure plugin 88 | .idea/replstate.xml 89 | 90 | # Crashlytics plugin (for Android Studio and IntelliJ) 91 | com_crashlytics_export_strings.xml 92 | crashlytics.properties 93 | crashlytics-build.properties 94 | fabric.properties 95 | 96 | # Editor-based Rest Client 97 | .idea/httpRequests 98 | 99 | # Android studio 3.1+ serialized cache file 100 | .idea/caches/build_file_checksums.ser 101 | 102 | ### JetBrains Patch ### 103 | # Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 104 | 105 | # *.iml 106 | # modules.xml 107 | # .idea/misc.xml 108 | # *.ipr 109 | 110 | # Sonarlint plugin 111 | .idea/**/sonarlint/ 112 | 113 | # SonarQube Plugin 114 | .idea/**/sonarIssues.xml 115 | 116 | # Markdown Navigator plugin 117 | .idea/**/markdown-navigator.xml 118 | .idea/**/markdown-navigator/ 119 | 120 | # End of https://www.gitignore.io/api/go,code,jetbrains 121 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # monkey-giggle 2 | 3 | monkey-giggle is a toy based language with a similar syntax to Javascript, 4 | the language is both interpreted and compiled. 5 | 6 | monkey-giggle compiles to [bytecode](code/code.go) that runs inside a stack based 7 | [virtual machine](vm/vm.go). 8 | 9 | ## Build 10 | 11 | ```sh 12 | 13 | user@box:$ go build cmd/ 14 | 15 | ``` 16 | 17 | ## Tour 18 | 19 | Monkey is a functional language that support closures, conditionals and the usual 20 | package. 21 | 22 | - Bindings and Functions : 23 | 24 | ```javascript 25 | 26 | let a = 5; 27 | let b = 10; 28 | let c = 120; 29 | 30 | let add = fn(x,y,y){x + y + z}; 31 | ``` 32 | 33 | - Conditionals 34 | 35 | ```javascript 36 | 37 | let a = 10; 38 | let b = -10; 39 | 40 | let c = if((a+b) == 0){ 41 | 5; 42 | } else { 43 | 10; 44 | } 45 | 46 | ``` 47 | 48 | - Builin Functions 49 | 50 | ```javascript 51 | 52 | giggle>> len("Hello World") 53 | 11 54 | giggle>> len([1,2,3,4,5]) 55 | 5 56 | giggle>> tail([1,2,3,4,5]) 57 | [2, 3, 4, 5] 58 | giggle>> head([1,2,3,4,5]) 59 | 1 60 | giggle>> append([1,2,3,4,5],6) 61 | [1, 2, 3, 4, 5, 6] 62 | 63 | ``` 64 | 65 | - Types 66 | 67 | ```javascript 68 | 69 | giggle>> "Hello World" 70 | Hello World 71 | giggle>> 2555 72 | 2555 73 | giggle>> true 74 | true 75 | giggle>> {"one":1,"two":2,"three":3} 76 | {one: 1, two: 2, three: 3} 77 | giggle>> [1,2,3,4,5] 78 | [1, 2, 3, 4, 5] 79 | giggle>> let map = {"one":1,"two":2,"three":3} 80 | giggle>> map["one"] 81 | 82 | 83 | 1 84 | ``` 85 | 86 | - Functions 87 | 88 | ```javascript 89 | 90 | giggle>> let map = fn(arr,f){ 91 | let iter = fn(arr,acc){ 92 | if (len(arr) == 0){ 93 | acc 94 | } else { 95 | iter(tail(arr),append(acc,f(head(arr)))); 96 | } 97 | }; 98 | iter(arr,[]); 99 | }; 100 | giggle>> map([1,2,3],square) 101 | [1, 4, 9] 102 | giggle>> let cube = fn(x){ x*x*x} 103 | giggle>> cube(3) 104 | 27 105 | giggle>> map([1,2,3],cube) 106 | [1, 8, 27] 107 | 108 | giggle >> let toSort = [9,8,7,6,5,4,3,2,1]; 109 | let insert = fn(arr,elem){ 110 | if (len(arr) == 0){ 111 | return [elem]; 112 | } else { 113 | if (elem < head(arr)){ 114 | return concat(concat([elem],[head(arr)]),tail(arr)); 115 | } else { 116 | return concat([head(arr)],insert(tail(arr),elem)); 117 | } 118 | } 119 | }; 120 | let sortByInsert = fn(arr){ 121 | if (len(arr) == 0){ 122 | return []; 123 | } else { 124 | insert(sortByInsert(tail(arr)),head(arr)); 125 | } 126 | }; 127 | 128 | giggle >> sortByInsert(toSort) 129 | giggle >> [1,2,3,4,5,6,7,8,9] 130 | 131 | giggle >> let fib = fn(x){ 132 | if (x == 0){ 133 | return 0; 134 | } else { 135 | if (x == 1){ 136 | return 1; 137 | } else { 138 | fib(x - 1) + fib(x - 2); 139 | } 140 | } 141 | }; 142 | giggle >> fib(15); 143 | giggle >> 610 144 | ``` 145 | 146 | - REPL 147 | 148 | ```javascript 149 | Make me giggle ! 150 | giggle>> let add_mod = fn(x,y,z){ (x + y) % z}; 151 | giggle>> add_mod(15,16,3) 152 | 1 153 | giggle>> add_mod(252,2343,13) 154 | 8 155 | giggle>> exit 156 | Ohhh you are leaving already !⏎ 157 | 158 | ```` 159 | 160 | Return statements are not needed the language is expression oriented. 161 | 162 | The tests contain further code examples you can run. 163 | -------------------------------------------------------------------------------- /STATS.md: -------------------------------------------------------------------------------- 1 | # Codebase Statistics 2 | 3 | ─────────────────────────────────────────────────────────────────────────────── 4 | Language Files Lines Code Comments Blanks Complexity 5 | ─────────────────────────────────────────────────────────────────────────────── 6 | Go 35 7204 5957 402 845 654 7 | Markdown 2 162 125 0 37 0 8 | gitignore 1 120 46 46 28 0 9 | ─────────────────────────────────────────────────────────────────────────────── 10 | Total 38 7486 6128 448 910 654 11 | ─────────────────────────────────────────────────────────────────────────────── 12 | Estimated Cost to Develop $181,250 13 | Estimated Schedule Effort 8.017467 months 14 | Estimated People Required 2.677919 15 | ─────────────────────────────────────────────────────────────────────────────── 16 | -------------------------------------------------------------------------------- /ast/ast.go: -------------------------------------------------------------------------------- 1 | // Package ast implement an abstract syntax tree api, the tree is built 2 | // using recursive descent parsing. 3 | package ast 4 | 5 | import ( 6 | "bytes" 7 | 8 | "github.com/actuallyachraf/monkey-giggle/token" 9 | ) 10 | 11 | // Node describes a node in the ast. 12 | type Node interface { 13 | TokenLiteral() token.Literal 14 | String() string 15 | } 16 | 17 | // Statement describes a statement node in the ast, statements are declarations 18 | // and don't produce values. 19 | type Statement interface { 20 | Node 21 | statementNode() 22 | } 23 | 24 | // Expression describes an expression node in the ast, expressions are value 25 | // producing declarations. 26 | type Expression interface { 27 | Node 28 | expressionNode() 29 | } 30 | 31 | // Program describes the root node of every ast produced by the parser, 32 | // essentially a program is a sequence of statements which are represented 33 | // by statement nodes in the ast. 34 | type Program struct { 35 | Statements []Statement 36 | } 37 | 38 | // TokenLiteral returns the token literal at the current node. 39 | func (p *Program) TokenLiteral() token.Literal { 40 | 41 | if len(p.Statements) > 0 { 42 | return p.Statements[0].TokenLiteral() 43 | } 44 | 45 | return "" 46 | } 47 | 48 | // String implements the stringer interface 49 | func (p *Program) String() string { 50 | var out bytes.Buffer 51 | 52 | for _, s := range p.Statements { 53 | out.WriteString(s.String()) 54 | 55 | } 56 | 57 | return out.String() 58 | } 59 | -------------------------------------------------------------------------------- /ast/ast_test.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/actuallyachraf/monkey-giggle/token" 7 | ) 8 | 9 | func TestAST(t *testing.T) { 10 | 11 | program := &Program{ 12 | Statements: []Statement{ 13 | &LetStatement{ 14 | Token: token.NewLiteral(token.LET, "let"), 15 | Name: &Identifier{ 16 | Token: token.NewLiteral(token.IDENT, "myVar"), 17 | Value: "myVar", 18 | }, 19 | Value: &Identifier{ 20 | Token: token.NewLiteral(token.IDENT, "anotherVar"), 21 | Value: "anotherVar", 22 | }, 23 | }, 24 | }, 25 | } 26 | expected := "let myVar = anotherVar;" 27 | if program.String() != expected { 28 | t.Fatalf("program.String() failed got %s expected %s", program.String(), expected) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ast/nodes.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "bytes" 5 | "strings" 6 | 7 | "github.com/actuallyachraf/monkey-giggle/token" 8 | ) 9 | 10 | // nodes.go implements Node for various declarations. 11 | 12 | // LetStatement implements the Node interface for let statements 13 | type LetStatement struct { 14 | Token token.Token // Let token 15 | Name *Identifier // Name of the identifier used to hold the left-value expression 16 | Value Expression // The value held by this identifier 17 | } 18 | 19 | func (ls *LetStatement) statementNode() {} 20 | 21 | // TokenLiteral implements the interface and returns the token literal. 22 | func (ls *LetStatement) TokenLiteral() token.Literal { 23 | return ls.Token.Literal 24 | } 25 | 26 | // Stringer implements the stringer interface. 27 | func (ls *LetStatement) String() string { 28 | 29 | var out bytes.Buffer 30 | 31 | out.WriteString(string(ls.TokenLiteral()) + " ") 32 | out.WriteString(ls.Name.String()) 33 | out.WriteString(" = ") 34 | 35 | if ls.Value != nil { 36 | out.WriteString(ls.Value.String()) 37 | } 38 | 39 | out.WriteString(";") 40 | return out.String() 41 | } 42 | 43 | // Identifier implements the Node interface for identifier declarations. 44 | type Identifier struct { 45 | Token token.Token 46 | Value token.Literal 47 | } 48 | 49 | func (i *Identifier) expressionNode() {} 50 | 51 | // TokenLiteral implements the interface and returns the identifier literal. 52 | func (i *Identifier) TokenLiteral() token.Literal { 53 | return i.Token.Literal 54 | } 55 | 56 | // String implements the stringer interface. 57 | func (i *Identifier) String() string { 58 | return string(i.Value) 59 | } 60 | 61 | // ReturnStatement implements the Node interface for identifier declarations. 62 | type ReturnStatement struct { 63 | Token token.Token 64 | ReturnValue Expression 65 | } 66 | 67 | func (rs *ReturnStatement) statementNode() {} 68 | 69 | // TokenLiteral implements the interface and returns the identifier literal. 70 | func (rs *ReturnStatement) TokenLiteral() token.Literal { 71 | return rs.Token.Literal 72 | } 73 | 74 | // String implements the stringer interface. 75 | func (rs *ReturnStatement) String() string { 76 | 77 | var out bytes.Buffer 78 | 79 | out.WriteString(string(rs.TokenLiteral()) + " ") 80 | 81 | if rs.ReturnValue != nil { 82 | out.WriteString(rs.ReturnValue.String()) 83 | } 84 | 85 | out.WriteString(";") 86 | 87 | return out.String() 88 | } 89 | 90 | // ExpressionStatement implements the interface for parsing expressions. 91 | type ExpressionStatement struct { 92 | Token token.Token 93 | Expression Expression 94 | } 95 | 96 | func (es *ExpressionStatement) statementNode() {} 97 | 98 | // TokenLiteral implements the interface and returns the Expression literal. 99 | func (es *ExpressionStatement) TokenLiteral() token.Literal { 100 | return es.Token.Literal 101 | } 102 | 103 | // String implements the stringer interface. 104 | func (es *ExpressionStatement) String() string { 105 | 106 | if es.Expression != nil { 107 | return es.Expression.String() 108 | } 109 | return "" 110 | } 111 | 112 | // IntegerLiteral represents a literal integer expression 113 | type IntegerLiteral struct { 114 | Token token.Token 115 | Value int64 116 | } 117 | 118 | func (il *IntegerLiteral) expressionNode() {} 119 | 120 | // TokenLiteral implements the interface and returns the Expression literal. 121 | func (il *IntegerLiteral) TokenLiteral() token.Literal { 122 | return il.Token.Literal 123 | } 124 | 125 | // String implements the stringer interface. 126 | func (il *IntegerLiteral) String() string { 127 | return string(il.Token.Literal) 128 | } 129 | 130 | // StringLiteral represents a literal string 131 | type StringLiteral struct { 132 | Token token.Token 133 | Value string 134 | } 135 | 136 | func (sl *StringLiteral) expressionNode() {} 137 | 138 | // TokenLiteral implements the node interface 139 | func (sl *StringLiteral) TokenLiteral() token.Literal { 140 | return sl.Token.Literal 141 | } 142 | 143 | // ArrayLiteral represents arrays 144 | type ArrayLiteral struct { 145 | Token token.Token 146 | Elements []Expression 147 | } 148 | 149 | func (al *ArrayLiteral) expressionNode() {} 150 | 151 | // TokenLiteral implements the node interface 152 | func (al *ArrayLiteral) TokenLiteral() token.Literal { 153 | return al.Token.Literal 154 | } 155 | 156 | // String implements the stringer interface 157 | func (al *ArrayLiteral) String() string { 158 | var out bytes.Buffer 159 | 160 | elements := []string{} 161 | 162 | for _, el := range al.Elements { 163 | elements = append(elements, el.String()) 164 | } 165 | 166 | out.WriteString("[") 167 | out.WriteString(strings.Join(elements, ", ")) 168 | out.WriteString("]") 169 | 170 | return out.String() 171 | } 172 | 173 | // String implements the stringer interface 174 | func (sl *StringLiteral) String() string { 175 | return string(sl.Token.Literal) 176 | } 177 | 178 | // PrefixExpression represents prefixed expressions. 179 | type PrefixExpression struct { 180 | Token token.Token 181 | Operator token.Literal 182 | Right Expression 183 | } 184 | 185 | func (pe *PrefixExpression) expressionNode() {} 186 | 187 | // TokenLiteral implements the interface of node and returns the literal prefix expression. 188 | func (pe *PrefixExpression) TokenLiteral() token.Literal { 189 | return pe.Token.Literal 190 | } 191 | 192 | // String implements the stringer interface 193 | func (pe *PrefixExpression) String() string { 194 | 195 | var out bytes.Buffer 196 | 197 | out.WriteString("(") 198 | out.WriteString(string(pe.Token.Literal)) 199 | out.WriteString(pe.Right.String()) 200 | out.WriteString(")") 201 | 202 | return out.String() 203 | 204 | } 205 | 206 | // InfixExpression represents prefixed expressions. 207 | type InfixExpression struct { 208 | Token token.Token 209 | Operator token.Literal 210 | Right Expression 211 | Left Expression 212 | } 213 | 214 | func (ie *InfixExpression) expressionNode() {} 215 | 216 | // TokenLiteral implements the interface of node and returns the literal prefix expression. 217 | func (ie *InfixExpression) TokenLiteral() token.Literal { 218 | return ie.Token.Literal 219 | } 220 | 221 | // String implements the stringer interface 222 | func (ie *InfixExpression) String() string { 223 | 224 | var out bytes.Buffer 225 | 226 | out.WriteString("(") 227 | out.WriteString(string(ie.Left.String()) + " ") 228 | out.WriteString(string(ie.Operator) + " ") 229 | out.WriteString(string(ie.Right.String())) 230 | 231 | out.WriteString(")") 232 | 233 | return out.String() 234 | 235 | } 236 | 237 | // BooleanLiteral represents a literal boolean expression. 238 | type BooleanLiteral struct { 239 | Token token.Token 240 | Value bool 241 | } 242 | 243 | func (bl *BooleanLiteral) expressionNode() {} 244 | 245 | // TokenLiteral implements the interface and returns the Expression literal. 246 | func (bl *BooleanLiteral) TokenLiteral() token.Literal { 247 | return bl.Token.Literal 248 | } 249 | 250 | // String implements the stringer interface. 251 | func (bl *BooleanLiteral) String() string { 252 | return string(bl.Token.Literal) 253 | } 254 | 255 | // IfExpression represents a conditional if expression. 256 | type IfExpression struct { 257 | Token token.Token 258 | Condition Expression 259 | Consequence *BlockStatement 260 | Alternative *BlockStatement 261 | } 262 | 263 | func (ie *IfExpression) expressionNode() {} 264 | 265 | // TokenLiteral implements the expression interface and returns the literal. 266 | func (ie *IfExpression) TokenLiteral() token.Literal { 267 | return ie.Token.Literal 268 | } 269 | 270 | // String implements the stringer interface 271 | func (ie *IfExpression) String() string { 272 | 273 | var out bytes.Buffer 274 | 275 | out.WriteString("if") 276 | out.WriteString(ie.Condition.String()) 277 | out.WriteString(" ") 278 | out.WriteString(ie.Consequence.String()) 279 | 280 | if ie.Alternative != nil { 281 | out.WriteString("else") 282 | out.WriteString(ie.Alternative.String()) 283 | } 284 | 285 | return out.String() 286 | } 287 | 288 | // BlockStatement represents a sequence of statements that execute within 289 | // a conditional branch. 290 | type BlockStatement struct { 291 | Token token.Token 292 | Statements []Statement 293 | } 294 | 295 | func (bs *BlockStatement) statementNode() {} 296 | 297 | // TokenLiteral implements the interface and returns the token literal 298 | func (bs *BlockStatement) TokenLiteral() token.Literal { 299 | return bs.Token.Literal 300 | } 301 | 302 | // String implements the stringer interface. 303 | func (bs *BlockStatement) String() string { 304 | 305 | var out bytes.Buffer 306 | 307 | for _, s := range bs.Statements { 308 | out.WriteString(s.String()) 309 | } 310 | 311 | return out.String() 312 | } 313 | 314 | // FunctionLiteral represents nodes for expressions of the type fn 315 | type FunctionLiteral struct { 316 | Token token.Token 317 | Parameters []*Identifier 318 | Body *BlockStatement 319 | } 320 | 321 | func (fl *FunctionLiteral) expressionNode() {} 322 | 323 | // TokenLiteral implements the interface and returns the token literal fn. 324 | func (fl *FunctionLiteral) TokenLiteral() token.Literal { 325 | return fl.Token.Literal 326 | } 327 | 328 | // String implements the stringer interface. 329 | func (fl *FunctionLiteral) String() string { 330 | var out bytes.Buffer 331 | 332 | params := []string{} 333 | 334 | for _, p := range fl.Parameters { 335 | params = append(params, p.String()) 336 | } 337 | 338 | out.WriteString(string(fl.TokenLiteral())) 339 | out.WriteString("(") 340 | out.WriteString(strings.Join(params, ", ")) 341 | out.WriteString(")") 342 | out.WriteString(fl.Body.String()) 343 | 344 | return out.String() 345 | } 346 | 347 | // CallExpression represents function calls. 348 | type CallExpression struct { 349 | Token token.Token 350 | Function Expression 351 | Arguments []Expression 352 | } 353 | 354 | func (ce *CallExpression) expressionNode() {} 355 | 356 | // TokenLiteral implements the expressionNode interface returns the token literal. 357 | func (ce *CallExpression) TokenLiteral() token.Literal { 358 | return ce.Token.Literal 359 | } 360 | 361 | // String implements the stringer interface. 362 | func (ce *CallExpression) String() string { 363 | var out bytes.Buffer 364 | 365 | args := []string{} 366 | 367 | for _, a := range ce.Arguments { 368 | args = append(args, a.String()) 369 | } 370 | 371 | out.WriteString(ce.Function.String()) 372 | out.WriteString("(") 373 | out.WriteString(strings.Join(args, ", ")) 374 | out.WriteString(")") 375 | 376 | return out.String() 377 | 378 | } 379 | 380 | // IndexExpression represents indexing expressions for index accessible ds. 381 | type IndexExpression struct { 382 | Token token.Token 383 | Left Expression 384 | Index Expression 385 | } 386 | 387 | func (ie *IndexExpression) expressionNode() {} 388 | 389 | // TokenLiteral implements the node interface 390 | func (ie *IndexExpression) TokenLiteral() token.Literal { 391 | return ie.Token.Literal 392 | } 393 | 394 | // String implements the stringer interface 395 | func (ie *IndexExpression) String() string { 396 | 397 | var out bytes.Buffer 398 | 399 | out.WriteString("(") 400 | out.WriteString(ie.Left.String()) 401 | out.WriteString("[") 402 | out.WriteString(ie.Index.String()) 403 | out.WriteString("])") 404 | 405 | return out.String() 406 | } 407 | 408 | // HashmapLiteral represents a hashmap 409 | type HashmapLiteral struct { 410 | Token token.Token 411 | Pairs map[Expression]Expression 412 | } 413 | 414 | func (hl *HashmapLiteral) expressionNode() {} 415 | 416 | // TokenLiteral implements the node interface 417 | func (hl *HashmapLiteral) TokenLiteral() token.Literal { 418 | return hl.Token.Literal 419 | } 420 | 421 | // String implements the stringer interface 422 | func (hl *HashmapLiteral) String() string { 423 | var out bytes.Buffer 424 | 425 | pairs := []string{} 426 | 427 | for k, v := range hl.Pairs { 428 | pairs = append(pairs, k.String()+":"+v.String()) 429 | } 430 | 431 | out.WriteString("{") 432 | out.WriteString(strings.Join(pairs, ", ")) 433 | out.WriteString("}") 434 | 435 | return out.String() 436 | 437 | } 438 | -------------------------------------------------------------------------------- /cmd/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "os/user" 7 | 8 | "github.com/actuallyachraf/monkey-giggle/repl" 9 | ) 10 | 11 | func main() { 12 | user, err := user.Current() 13 | if err != nil { 14 | panic(err) 15 | } 16 | fmt.Printf("Hello %s !\n", user.Username) 17 | fmt.Printf("Feel free to type in commands !\n") 18 | repl.Start(os.Stdin, os.Stdout) 19 | } 20 | -------------------------------------------------------------------------------- /code/code.go: -------------------------------------------------------------------------------- 1 | // Package code implements Opcode semantics for the virtual machine bytecode. 2 | package code 3 | 4 | import ( 5 | "bytes" 6 | "encoding/binary" 7 | "fmt" 8 | ) 9 | 10 | // OpCode is a single byte constant specifying an instruction. 11 | type OpCode byte 12 | 13 | // Instructions are sequences of opcodes. 14 | type Instructions []byte 15 | 16 | const ( 17 | // OpConstant is a fetch and push opcode for constants 18 | OpConstant OpCode = iota 19 | // OpAdd pops two integers from the stack and pushes their sum 20 | OpAdd 21 | // OpSub pops two integers from the stack and pushes their difference 22 | OpSub 23 | // OpMul pops two integers from the stack and pushes their product 24 | OpMul 25 | // OpDiv pops two integers from the stack and pushes their quotient 26 | OpDiv 27 | // OpMod pops two integers from the stack and pushes their remainder 28 | OpMod 29 | // OpNot pops a boolean from the stack and pushes it's opposite 30 | OpNot 31 | // OpNeg pops an integer from the stack and pushes it's negation 32 | OpNeg 33 | // OpPop pops an element from the stack 34 | OpPop 35 | // OpTrue pushes a truth value 36 | OpTrue 37 | // OpFalse pushes a false value 38 | OpFalse 39 | // OpEqual pops two elements from the stack pushes true if equal false otherwise. 40 | OpEqual 41 | // OpNotEqual pops two elements from the stack pushes true if not equal false otherwise. 42 | OpNotEqual 43 | // OpGreaterThan pops two elements from stack push true if a > b 44 | OpGreaterThan 45 | // OpGreaterOrEqual pops two elements from the stack pushes true if a >= b 46 | OpGreaterOrEqual 47 | // OpJNE implements Jump if not equal 48 | OpJNE 49 | // OpJump implements jump to address 50 | OpJump 51 | // OpNull pushes the null value to the stack 52 | OpNull 53 | // OpGetGlobal fetches binding values from the globals state 54 | OpGetGlobal 55 | // OpSetGlobal binds a value to an identifier 56 | OpSetGlobal 57 | // OpArray constructs an array object by popping N objects from the stack 58 | OpArray 59 | // OpHashTable constructs an associative array of objects 60 | OpHashTable 61 | // OpIndex reads the object and index and pushes the indexed element to the stack 62 | OpIndex 63 | // OpCall executes function calls 64 | OpCall 65 | // OpReturnValue explicitly pushes function return values to the stack 66 | OpReturnValue 67 | // OpReturn returns from the function call to the caller 68 | OpReturn 69 | // OpGetLocal is used to get local bindings 70 | OpGetLocal 71 | // OpSetLocal is used to set local bindings 72 | OpSetLocal 73 | // OpGetBuiltin is used to fetch built-in function from their scope 74 | OpGetBuiltin 75 | // OpClosure marks closure functions 76 | OpClosure 77 | // OpGetFree is used to get free closure variables 78 | OpGetFree 79 | ) 80 | 81 | // Definition represents information about opcodes. 82 | type Definition struct { 83 | Name string 84 | OperandWidths []int 85 | } 86 | 87 | var lookupTable = map[OpCode]Definition{ 88 | OpConstant: {"OpConstant", []int{2}}, 89 | OpAdd: {"OpAdd", []int{}}, 90 | OpSub: {"OpSub", []int{}}, 91 | OpMul: {"OpMul", []int{}}, 92 | OpDiv: {"OpDiv", []int{}}, 93 | OpMod: {"OpMod", []int{}}, 94 | OpPop: {"OpPop", []int{}}, 95 | OpTrue: {"OpTrue", []int{}}, 96 | OpFalse: {"OpFalse", []int{}}, 97 | OpEqual: {"OpEqual", []int{}}, 98 | OpNotEqual: {"OpNotEqual", []int{}}, 99 | OpGreaterThan: {"OpGreaterThan", []int{}}, 100 | OpGreaterOrEqual: {"OpGreaterThanOrEqual", []int{}}, 101 | OpNeg: {"OpNeg", []int{}}, 102 | OpNot: {"OpNot", []int{}}, 103 | OpJNE: {"OpJumpIfNotEqual", []int{2}}, 104 | OpJump: {"OpJump", []int{2}}, 105 | OpNull: {"OpNull", []int{}}, 106 | OpGetGlobal: {"OpGetGlobal", []int{2}}, 107 | OpSetGlobal: {"OpSetGlobal", []int{2}}, 108 | OpArray: {"OpArray", []int{2}}, 109 | OpHashTable: {"OpHashTable", []int{2}}, 110 | OpIndex: {"OpIndex", []int{}}, 111 | OpCall: {"OpCall", []int{1}}, 112 | OpReturnValue: {"OpReturnValue", []int{}}, 113 | OpReturn: {"OpReturn", []int{}}, 114 | OpGetLocal: {"OpGetLocal", []int{1}}, 115 | OpSetLocal: {"OpSetLocal", []int{1}}, 116 | OpGetBuiltin: {"OpGetBuiltin", []int{1}}, 117 | OpClosure: {"OpClosure", []int{2, 1}}, 118 | OpGetFree: {"OpGetFree", []int{1}}, 119 | } 120 | 121 | // Lookup fetches the opcode definition. 122 | func Lookup(op OpCode) (Definition, error) { 123 | def, ok := lookupTable[op] 124 | if !ok { 125 | return Definition{}, fmt.Errorf("Opcode %d is undefined", op) 126 | } 127 | 128 | return def, nil 129 | } 130 | 131 | // Make creates an instruction sequence given an opcode and operands. 132 | func Make(op OpCode, operands ...int) []byte { 133 | def, ok := lookupTable[op] 134 | if !ok { 135 | return []byte{} 136 | } 137 | 138 | instLen := 1 139 | 140 | for _, w := range def.OperandWidths { 141 | instLen += w 142 | } 143 | 144 | inst := make([]byte, instLen) 145 | inst[0] = byte(op) 146 | 147 | offset := 1 148 | 149 | for i, operand := range operands { 150 | width := def.OperandWidths[i] 151 | 152 | switch width { 153 | case 2: 154 | binary.BigEndian.PutUint16(inst[offset:], uint16(operand)) 155 | case 1: 156 | inst[offset] = byte(operand) 157 | } 158 | offset += width 159 | } 160 | 161 | return inst 162 | 163 | } 164 | 165 | // FormatInstruction returns a pretty printed instruction 166 | func (inst Instructions) FormatInstruction(def Definition, operands []int) string { 167 | operandCount := len(def.OperandWidths) 168 | 169 | if len(operands) != operandCount { 170 | return fmt.Sprintf("ERROR : operand length %d does not match defined %d", len(operands), operandCount) 171 | } 172 | switch operandCount { 173 | case 0: 174 | return def.Name 175 | case 1: 176 | return fmt.Sprintf("%s %d", def.Name, operands[0]) 177 | case 2: 178 | return fmt.Sprintf("%s %d %d", def.Name, operands[0], operands[1]) 179 | } 180 | 181 | return fmt.Sprintf("ERROR: unhandled operand count for %s", def.Name) 182 | } 183 | 184 | // String implements the stringer interface 185 | func (inst Instructions) String() string { 186 | 187 | var out bytes.Buffer 188 | 189 | i := 0 190 | for i < len(inst) { 191 | def, err := Lookup(OpCode(inst[i])) 192 | if err != nil { 193 | fmt.Fprintf(&out, "ERROR : %s\n", err) 194 | continue 195 | } 196 | 197 | operands, read := ReadOperands(def, inst[i+1:]) 198 | 199 | fmt.Fprintf(&out, "%04d %s\n", i, inst.FormatInstruction(def, operands)) 200 | 201 | i += 1 + read 202 | 203 | } 204 | 205 | return out.String() 206 | } 207 | 208 | // ReadUint16 reads a big-endian encoded uint16 from an instruction slice 209 | func ReadUint16(ins Instructions) uint16 { 210 | return binary.BigEndian.Uint16(ins) 211 | } 212 | 213 | // ReadOperands parses definition operand width 214 | func ReadOperands(def Definition, ins Instructions) ([]int, int) { 215 | operands := make([]int, len(def.OperandWidths)) 216 | 217 | offset := 0 218 | 219 | for i, width := range def.OperandWidths { 220 | switch width { 221 | case 2: 222 | operands[i] = int(ReadUint16(ins[offset:])) 223 | case 1: 224 | operands[i] = int(ReadUint8(ins[offset:])) 225 | } 226 | 227 | offset += width 228 | } 229 | 230 | return operands, offset 231 | } 232 | 233 | // ReadUint8 reads a single byte integer 234 | func ReadUint8(ins Instructions) uint8 { 235 | return uint8(ins[0]) 236 | } 237 | -------------------------------------------------------------------------------- /code/code_test.go: -------------------------------------------------------------------------------- 1 | package code 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestOpcode(t *testing.T) { 8 | t.Run("TestMake", func(t *testing.T) { 9 | 10 | tests := []struct { 11 | op OpCode 12 | operands []int 13 | expected []byte 14 | }{ 15 | {OpConstant, []int{65534}, []byte{byte(OpConstant), 255, 254}}, 16 | {OpAdd, []int{}, []byte{byte(OpAdd)}}, 17 | {OpClosure, []int{65534, 255}, []byte{byte(OpClosure), 255, 254, 255}}, 18 | } 19 | 20 | for _, tt := range tests { 21 | inst := Make(tt.op, tt.operands...) 22 | if len(inst) != len(tt.expected) { 23 | t.Errorf("instruction has wrong length expected %d got %d", len(tt.expected), len(inst)) 24 | } 25 | for i, b := range tt.expected { 26 | if inst[i] != b { 27 | t.Errorf("instruction has wrong byte at pos[%d] expected %d got %d", i, b, inst[i]) 28 | } 29 | } 30 | } 31 | }) 32 | t.Run("TestDisassemble", func(t *testing.T) { 33 | instructions := []Instructions{ 34 | Make(OpAdd), 35 | Make(OpConstant, 2), 36 | Make(OpConstant, 65535), 37 | } 38 | 39 | expected := `0000 OpAdd 40 | 0001 OpConstant 2 41 | 0004 OpConstant 65535 42 | ` 43 | 44 | concatted := Instructions{} 45 | for _, inst := range instructions { 46 | concatted = append(concatted, inst...) 47 | } 48 | if expected != concatted.String() { 49 | t.Errorf("instructions wrongly formatted want %s got %s", expected, concatted.String()) 50 | } 51 | 52 | }) 53 | t.Run("TestReadOperand", func(t *testing.T) { 54 | tests := []struct { 55 | op OpCode 56 | operands []int 57 | bytesRead int 58 | }{ 59 | { 60 | OpConstant, 61 | []int{65535}, 62 | 2, 63 | }, 64 | } 65 | 66 | for _, tt := range tests { 67 | inst := Make(tt.op, tt.operands...) 68 | 69 | def, err := Lookup(tt.op) 70 | if err != nil { 71 | t.Errorf("Lookup failed with error :%s", err) 72 | } 73 | operandsRead, n := ReadOperands(def, inst[1:]) 74 | if n != tt.bytesRead { 75 | t.Errorf("wrong count of read bytes expected %d got %d", tt.bytesRead, n) 76 | } 77 | 78 | for i, operand := range tt.operands { 79 | if operandsRead[i] != operand { 80 | t.Errorf("wrong operand expected %d got %d", operand, operandsRead[i]) 81 | } 82 | } 83 | 84 | } 85 | }) 86 | } 87 | -------------------------------------------------------------------------------- /compiler/compiler.go: -------------------------------------------------------------------------------- 1 | // Package compiler defines the core compiler code. 2 | package compiler 3 | 4 | import ( 5 | "fmt" 6 | "sort" 7 | 8 | "github.com/actuallyachraf/monkey-giggle/ast" 9 | "github.com/actuallyachraf/monkey-giggle/code" 10 | "github.com/actuallyachraf/monkey-giggle/object" 11 | ) 12 | 13 | // Compiler represents structs and defined constant objects 14 | type Compiler struct { 15 | constants []object.Object 16 | symbolTable *SymbolTable 17 | 18 | scopes []CompilationScope 19 | scopeIndex int 20 | } 21 | 22 | // CompilationScope represents scopes for functions 23 | type CompilationScope struct { 24 | instructions code.Instructions 25 | lastInstruction EmittedInstruction 26 | previousInstruction EmittedInstruction 27 | } 28 | 29 | // EmittedInstruction represents an emitted compiler instruction 30 | type EmittedInstruction struct { 31 | Opcode code.OpCode 32 | Position int 33 | } 34 | 35 | // New creates a new compiler instance 36 | func New() *Compiler { 37 | 38 | mainScope := CompilationScope{ 39 | instructions: code.Instructions{}, 40 | lastInstruction: EmittedInstruction{}, 41 | previousInstruction: EmittedInstruction{}, 42 | } 43 | symTable := NewSymbolTable() 44 | 45 | for i, v := range object.Builtins { 46 | symTable.DefineBuiltIn(i, v.Name) 47 | } 48 | return &Compiler{ 49 | constants: []object.Object{}, 50 | symbolTable: symTable, 51 | scopes: []CompilationScope{mainScope}, 52 | scopeIndex: 0, 53 | } 54 | } 55 | 56 | // currentInstructions returns the instructions within the current scope 57 | func (c *Compiler) currentInstructions() code.Instructions { 58 | return c.scopes[c.scopeIndex].instructions 59 | } 60 | 61 | // NewWithState creates a new compiler instance with a predefined symbol table 62 | func NewWithState(symTable *SymbolTable, constants []object.Object) *Compiler { 63 | compiler := New() 64 | compiler.symbolTable = symTable 65 | compiler.constants = constants 66 | 67 | return compiler 68 | } 69 | 70 | // Compile takes an AST Node and returns an equivalent compiler. 71 | func (c *Compiler) Compile(node ast.Node) error { 72 | 73 | switch node := node.(type) { 74 | case *ast.Program: 75 | for _, s := range node.Statements { 76 | err := c.Compile(s) 77 | if err != nil { 78 | return err 79 | } 80 | } 81 | case *ast.ExpressionStatement: 82 | err := c.Compile(node.Expression) 83 | if err != nil { 84 | return err 85 | } 86 | c.emit(code.OpPop) 87 | case *ast.InfixExpression: 88 | if node.Operator == "<" || node.Operator == "<=" { 89 | err := c.Compile(node.Right) 90 | if err != nil { 91 | return err 92 | } 93 | err = c.Compile(node.Left) 94 | if err != nil { 95 | return err 96 | } 97 | if node.Operator == "<" { 98 | c.emit(code.OpGreaterThan) 99 | } else if node.Operator == "<=" { 100 | c.emit(code.OpGreaterOrEqual) 101 | } 102 | return nil 103 | } 104 | err := c.Compile(node.Left) 105 | if err != nil { 106 | return err 107 | } 108 | err = c.Compile(node.Right) 109 | if err != nil { 110 | return err 111 | } 112 | switch node.Operator { 113 | case "+": 114 | c.emit(code.OpAdd) 115 | case "-": 116 | c.emit(code.OpSub) 117 | case "*": 118 | c.emit(code.OpMul) 119 | case "/": 120 | c.emit(code.OpDiv) 121 | case "%": 122 | c.emit(code.OpMod) 123 | case ">": 124 | c.emit(code.OpGreaterThan) 125 | case ">=": 126 | c.emit(code.OpGreaterOrEqual) 127 | case "==": 128 | c.emit(code.OpEqual) 129 | case "!=": 130 | c.emit(code.OpNotEqual) 131 | default: 132 | return fmt.Errorf("Unknown operator %s", node.Operator) 133 | } 134 | case *ast.IntegerLiteral: 135 | integer := &object.Integer{Value: node.Value} 136 | c.emit(code.OpConstant, c.addConstant(integer)) 137 | case *ast.BooleanLiteral: 138 | if node.Value { 139 | c.emit(code.OpTrue) 140 | } else { 141 | c.emit(code.OpFalse) 142 | } 143 | case *ast.PrefixExpression: 144 | err := c.Compile(node.Right) 145 | if err != nil { 146 | return err 147 | } 148 | switch node.Operator { 149 | case "!": 150 | c.emit(code.OpNot) 151 | case "-": 152 | c.emit(code.OpNeg) 153 | default: 154 | return fmt.Errorf("Unknown operator %s", node.Operator) 155 | } 156 | case *ast.IfExpression: 157 | err := c.Compile(node.Condition) 158 | if err != nil { 159 | return err 160 | } 161 | JNEPos := c.emit(code.OpJNE, 9999) 162 | err = c.Compile(node.Consequence) 163 | if err != nil { 164 | return err 165 | } 166 | if c.lastInstructionIs(code.OpPop) { 167 | c.removeLastPop() 168 | } 169 | JMPPos := c.emit(code.OpJump, 9999) 170 | afterConsqPos := len(c.currentInstructions()) 171 | c.changeOperand(JNEPos, afterConsqPos) 172 | 173 | if node.Alternative == nil { 174 | c.emit(code.OpNull) 175 | } else { 176 | err := c.Compile(node.Alternative) 177 | if err != nil { 178 | return err 179 | } 180 | if c.lastInstructionIs(code.OpPop) { 181 | c.removeLastPop() 182 | } 183 | 184 | } 185 | afterAltPos := len(c.currentInstructions()) 186 | c.changeOperand(JMPPos, afterAltPos) 187 | 188 | case *ast.BlockStatement: 189 | for _, s := range node.Statements { 190 | err := c.Compile(s) 191 | if err != nil { 192 | return err 193 | } 194 | } 195 | case *ast.LetStatement: 196 | sym := c.symbolTable.Define(string(node.Name.Value)) 197 | err := c.Compile(node.Value) 198 | if err != nil { 199 | return err 200 | } 201 | if sym.Scope == GlobalScope { 202 | c.emit(code.OpSetGlobal, sym.Index) 203 | } else { 204 | c.emit(code.OpSetLocal, sym.Index) 205 | } 206 | case *ast.Identifier: 207 | sym, ok := c.symbolTable.Resolve(string(node.Value)) 208 | if !ok { 209 | return fmt.Errorf("Undefined variable %s", node.Value) 210 | } 211 | c.loadSymbol(sym) 212 | case *ast.StringLiteral: 213 | str := &object.String{Value: node.Value} 214 | c.emit(code.OpConstant, c.addConstant(str)) 215 | 216 | case *ast.ArrayLiteral: 217 | for _, el := range node.Elements { 218 | err := c.Compile(el) 219 | if err != nil { 220 | return err 221 | } 222 | } 223 | c.emit(code.OpArray, len(node.Elements)) 224 | case *ast.HashmapLiteral: 225 | keys := []ast.Expression{} 226 | for k := range node.Pairs { 227 | keys = append(keys, k) 228 | } 229 | sort.Slice(keys, func(i, j int) bool { 230 | return keys[i].String() < keys[j].String() 231 | }) 232 | 233 | for _, k := range keys { 234 | err := c.Compile(k) 235 | if err != nil { 236 | return err 237 | } 238 | err = c.Compile(node.Pairs[k]) 239 | if err != nil { 240 | return err 241 | } 242 | } 243 | c.emit(code.OpHashTable, len(node.Pairs)*2) 244 | case *ast.IndexExpression: 245 | err := c.Compile(node.Left) 246 | if err != nil { 247 | return err 248 | } 249 | err = c.Compile(node.Index) 250 | if err != nil { 251 | return err 252 | } 253 | c.emit(code.OpIndex) 254 | case *ast.FunctionLiteral: 255 | c.enterScope() 256 | 257 | for _, p := range node.Parameters { 258 | c.symbolTable.Define(string(p.Value)) 259 | } 260 | 261 | err := c.Compile(node.Body) 262 | if err != nil { 263 | return err 264 | } 265 | if c.lastInstructionIs(code.OpPop) { 266 | c.replaceLastPopWithRet() 267 | } 268 | 269 | if !c.lastInstructionIs(code.OpReturnValue) { 270 | c.emit(code.OpReturn) 271 | } 272 | 273 | freeSymbols := c.symbolTable.FreeSyms 274 | numLocals := c.symbolTable.numDefinitions 275 | inst := c.leaveScope() 276 | 277 | for _, s := range freeSymbols { 278 | c.loadSymbol(s) 279 | } 280 | 281 | compiledFn := &object.CompiledFunction{Instructions: inst, NumLocals: numLocals, NumParams: len(node.Parameters)} 282 | fnIndex := c.addConstant(compiledFn) 283 | c.emit(code.OpClosure, fnIndex, len(freeSymbols)) 284 | case *ast.ReturnStatement: 285 | err := c.Compile(node.ReturnValue) 286 | if err != nil { 287 | return err 288 | } 289 | c.emit(code.OpReturnValue) 290 | case *ast.CallExpression: 291 | err := c.Compile(node.Function) 292 | if err != nil { 293 | return err 294 | } 295 | for _, arg := range node.Arguments { 296 | err := c.Compile(arg) 297 | if err != nil { 298 | return err 299 | } 300 | } 301 | c.emit(code.OpCall, len(node.Arguments)) 302 | } 303 | return nil 304 | } 305 | 306 | // Bytecode represents a sequence of instructions and object table. 307 | type Bytecode struct { 308 | Instructions code.Instructions 309 | Constants []object.Object 310 | } 311 | 312 | // Bytecode returns the generated bytecode. 313 | func (c *Compiler) Bytecode() Bytecode { 314 | 315 | return Bytecode{ 316 | Instructions: c.currentInstructions(), 317 | Constants: c.constants, 318 | } 319 | } 320 | 321 | // addConstant adds a constant to the constant pool 322 | func (c *Compiler) addConstant(obj object.Object) int { 323 | c.constants = append(c.constants, obj) 324 | return len(c.constants) - 1 325 | } 326 | 327 | // emit generates an instruction and add it to the bytecode 328 | func (c *Compiler) emit(op code.OpCode, operands ...int) int { 329 | inst := code.Make(op, operands...) 330 | pos := c.addInstruction(inst) 331 | 332 | c.setLastEmittedInstruction(op, pos) 333 | 334 | return pos 335 | } 336 | 337 | // lastInstructionIsPop checks if the last emitted instruction is OpPop 338 | func (c *Compiler) lastInstructionIs(op code.OpCode) bool { 339 | if len(c.currentInstructions()) == 0 { 340 | return false 341 | } 342 | return c.scopes[c.scopeIndex].lastInstruction.Opcode == op 343 | } 344 | 345 | // removeLastPop clears the last emitted pop instruction 346 | func (c *Compiler) removeLastPop() { 347 | 348 | last := c.scopes[c.scopeIndex].lastInstruction 349 | previous := c.scopes[c.scopeIndex].previousInstruction 350 | 351 | old := c.currentInstructions() 352 | newInst := old[:last.Position] 353 | 354 | c.scopes[c.scopeIndex].instructions = newInst 355 | c.scopes[c.scopeIndex].lastInstruction = previous 356 | } 357 | 358 | // setLastEmittedInstruction populates the last instruction from the current 359 | // one before we emit a new one. 360 | func (c *Compiler) setLastEmittedInstruction(op code.OpCode, pos int) { 361 | previous := c.scopes[c.scopeIndex].lastInstruction 362 | last := EmittedInstruction{Opcode: op, Position: pos} 363 | 364 | c.scopes[c.scopeIndex].previousInstruction = previous 365 | c.scopes[c.scopeIndex].lastInstruction = last 366 | } 367 | 368 | // replaceInstruction at position with a new one 369 | func (c *Compiler) replaceInstruction(pos int, newInstruction []byte) { 370 | 371 | inst := c.currentInstructions() 372 | for i := 0; i < len(newInstruction); i++ { 373 | inst[pos+i] = newInstruction[i] 374 | } 375 | } 376 | 377 | // changeOperand replaces an opcode operand's 378 | func (c *Compiler) changeOperand(opPos int, operand int) { 379 | op := code.OpCode(c.currentInstructions()[opPos]) 380 | newInstruction := code.Make(op, operand) 381 | 382 | c.replaceInstruction(opPos, newInstruction) 383 | } 384 | 385 | // addInstruction appends a new instruction to the generated bytecode 386 | func (c *Compiler) addInstruction(inst []byte) int { 387 | posNewInst := len(c.currentInstructions()) 388 | updatedInst := append(c.currentInstructions(), inst...) 389 | 390 | c.scopes[c.scopeIndex].instructions = updatedInst 391 | 392 | return posNewInst 393 | } 394 | 395 | // enterScope creates a new compiler scope and makes it the current working scope 396 | func (c *Compiler) enterScope() { 397 | scope := CompilationScope{ 398 | instructions: code.Instructions{}, 399 | lastInstruction: EmittedInstruction{}, 400 | previousInstruction: EmittedInstruction{}, 401 | } 402 | 403 | c.scopes = append(c.scopes, scope) 404 | c.scopeIndex++ 405 | c.symbolTable = NewEnclosedSymbolTable(c.symbolTable) 406 | } 407 | 408 | // leaveScope dismisses the current working scope and decrement scope index 409 | func (c *Compiler) leaveScope() code.Instructions { 410 | inst := c.currentInstructions() 411 | 412 | c.scopes = c.scopes[:len(c.scopes)-1] 413 | c.scopeIndex-- 414 | c.symbolTable = c.symbolTable.Outer 415 | return inst 416 | } 417 | 418 | // replaceLastPopWithRet replaces the last opPop with opRet 419 | func (c *Compiler) replaceLastPopWithRet() { 420 | lastPos := c.scopes[c.scopeIndex].lastInstruction.Position 421 | c.replaceInstruction(lastPos, code.Make(code.OpReturnValue)) 422 | 423 | c.scopes[c.scopeIndex].lastInstruction.Opcode = code.OpReturnValue 424 | } 425 | 426 | // loadSymbol emits the proper symbol opcode 427 | func (c *Compiler) loadSymbol(sym Symbol) { 428 | switch sym.Scope { 429 | case GlobalScope: 430 | c.emit(code.OpGetGlobal, sym.Index) 431 | case LocalScope: 432 | c.emit(code.OpGetLocal, sym.Index) 433 | case BuiltinScope: 434 | c.emit(code.OpGetBuiltin, sym.Index) 435 | case FreeScope: 436 | c.emit(code.OpGetFree, sym.Index) 437 | } 438 | } 439 | -------------------------------------------------------------------------------- /compiler/compiler_test.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/actuallyachraf/monkey-giggle/ast" 8 | "github.com/actuallyachraf/monkey-giggle/code" 9 | "github.com/actuallyachraf/monkey-giggle/lexer" 10 | "github.com/actuallyachraf/monkey-giggle/object" 11 | "github.com/actuallyachraf/monkey-giggle/parser" 12 | ) 13 | 14 | type compilerTestCase struct { 15 | input string 16 | expectedConstants []interface{} 17 | expectedInstructions []code.Instructions 18 | } 19 | 20 | func TestCompiler(t *testing.T) { 21 | t.Run("TestIntegerArithmetic", func(t *testing.T) { 22 | 23 | tests := []compilerTestCase{ 24 | { 25 | input: "1 + 2", 26 | expectedConstants: []interface{}{1, 2}, 27 | expectedInstructions: []code.Instructions{ 28 | code.Make(code.OpConstant, 0), 29 | code.Make(code.OpConstant, 1), 30 | code.Make(code.OpAdd), 31 | code.Make(code.OpPop), 32 | }, 33 | }, 34 | { 35 | input: "1 - 2", 36 | expectedConstants: []interface{}{1, 2}, 37 | expectedInstructions: []code.Instructions{ 38 | code.Make(code.OpConstant, 0), 39 | code.Make(code.OpConstant, 1), 40 | code.Make(code.OpSub), 41 | code.Make(code.OpPop), 42 | }, 43 | }, 44 | { 45 | input: "1 * 2", 46 | expectedConstants: []interface{}{1, 2}, 47 | expectedInstructions: []code.Instructions{ 48 | code.Make(code.OpConstant, 0), 49 | code.Make(code.OpConstant, 1), 50 | code.Make(code.OpMul), 51 | code.Make(code.OpPop), 52 | }, 53 | }, 54 | { 55 | input: "1 / 2", 56 | expectedConstants: []interface{}{1, 2}, 57 | expectedInstructions: []code.Instructions{ 58 | code.Make(code.OpConstant, 0), 59 | code.Make(code.OpConstant, 1), 60 | code.Make(code.OpDiv), 61 | code.Make(code.OpPop), 62 | }, 63 | }, 64 | { 65 | input: "1 % 2", 66 | expectedConstants: []interface{}{1, 2}, 67 | expectedInstructions: []code.Instructions{ 68 | code.Make(code.OpConstant, 0), 69 | code.Make(code.OpConstant, 1), 70 | code.Make(code.OpMod), 71 | code.Make(code.OpPop), 72 | }, 73 | }, 74 | } 75 | runCompilerTests(t, tests) 76 | }) 77 | t.Run("TestBooleanExpressions", func(t *testing.T) { 78 | tests := []compilerTestCase{ 79 | { 80 | input: "true", 81 | expectedConstants: []interface{}{}, 82 | expectedInstructions: []code.Instructions{ 83 | code.Make(code.OpTrue), 84 | code.Make(code.OpPop), 85 | }, 86 | }, 87 | { 88 | input: "false", 89 | expectedConstants: []interface{}{}, 90 | expectedInstructions: []code.Instructions{ 91 | code.Make(code.OpFalse), 92 | code.Make(code.OpPop), 93 | }, 94 | }, 95 | { 96 | input: "1 > 2", 97 | expectedConstants: []interface{}{1, 2}, 98 | expectedInstructions: []code.Instructions{ 99 | code.Make(code.OpConstant, 0), 100 | code.Make(code.OpConstant, 1), 101 | code.Make(code.OpGreaterThan), 102 | code.Make(code.OpPop), 103 | }, 104 | }, 105 | { 106 | input: "1 >= 2", 107 | expectedConstants: []interface{}{1, 2}, 108 | expectedInstructions: []code.Instructions{ 109 | code.Make(code.OpConstant, 0), 110 | code.Make(code.OpConstant, 1), 111 | code.Make(code.OpGreaterOrEqual), 112 | code.Make(code.OpPop), 113 | }, 114 | }, 115 | { 116 | input: "1 < 2", 117 | expectedConstants: []interface{}{2, 1}, 118 | expectedInstructions: []code.Instructions{ 119 | code.Make(code.OpConstant, 0), 120 | code.Make(code.OpConstant, 1), 121 | code.Make(code.OpGreaterThan), 122 | code.Make(code.OpPop), 123 | }, 124 | }, 125 | { 126 | input: "1 <= 2", 127 | expectedConstants: []interface{}{2, 1}, 128 | expectedInstructions: []code.Instructions{ 129 | code.Make(code.OpConstant, 0), 130 | code.Make(code.OpConstant, 1), 131 | code.Make(code.OpGreaterOrEqual), 132 | code.Make(code.OpPop), 133 | }, 134 | }, 135 | { 136 | input: "1 == 2", 137 | expectedConstants: []interface{}{1, 2}, 138 | expectedInstructions: []code.Instructions{ 139 | code.Make(code.OpConstant, 0), 140 | code.Make(code.OpConstant, 1), 141 | code.Make(code.OpEqual), 142 | code.Make(code.OpPop), 143 | }, 144 | }, 145 | { 146 | input: "1 != 2", 147 | expectedConstants: []interface{}{1, 2}, 148 | expectedInstructions: []code.Instructions{ 149 | code.Make(code.OpConstant, 0), 150 | code.Make(code.OpConstant, 1), 151 | code.Make(code.OpNotEqual), 152 | code.Make(code.OpPop), 153 | }, 154 | }, 155 | { 156 | input: "true != false", 157 | expectedConstants: []interface{}{}, 158 | expectedInstructions: []code.Instructions{ 159 | code.Make(code.OpTrue), 160 | code.Make(code.OpFalse), 161 | code.Make(code.OpNotEqual), 162 | code.Make(code.OpPop), 163 | }, 164 | }, 165 | { 166 | input: "true == true", 167 | expectedConstants: []interface{}{}, 168 | expectedInstructions: []code.Instructions{ 169 | code.Make(code.OpTrue), 170 | code.Make(code.OpTrue), 171 | code.Make(code.OpEqual), 172 | code.Make(code.OpPop), 173 | }, 174 | }, 175 | } 176 | 177 | runCompilerTests(t, tests) 178 | }) 179 | t.Run("TestPrefixExpression", func(t *testing.T) { 180 | tests := []compilerTestCase{ 181 | { 182 | input: "-1", 183 | expectedConstants: []interface{}{1}, 184 | expectedInstructions: []code.Instructions{ 185 | code.Make(code.OpConstant, 0), 186 | code.Make(code.OpNeg), 187 | code.Make(code.OpPop), 188 | }, 189 | }, 190 | { 191 | input: "!true", 192 | expectedConstants: []interface{}{}, 193 | expectedInstructions: []code.Instructions{ 194 | code.Make(code.OpTrue), 195 | code.Make(code.OpNot), 196 | code.Make(code.OpPop), 197 | }, 198 | }, 199 | } 200 | runCompilerTests(t, tests) 201 | }) 202 | t.Run("TestConditionalExpression", func(t *testing.T) { 203 | tests := []compilerTestCase{ 204 | { 205 | input: `if (true) {10}; 3333;`, 206 | expectedConstants: []interface{}{10, 3333}, 207 | expectedInstructions: []code.Instructions{ 208 | code.Make(code.OpTrue), 209 | code.Make(code.OpJNE, 10), 210 | code.Make(code.OpConstant, 0), 211 | code.Make(code.OpJump, 11), 212 | code.Make(code.OpNull), 213 | code.Make(code.OpPop), 214 | code.Make(code.OpConstant, 1), 215 | code.Make(code.OpPop), 216 | }, 217 | }, { 218 | input: `if (true) {10} else {20}; 3333;`, 219 | expectedConstants: []interface{}{10, 20, 3333}, 220 | expectedInstructions: []code.Instructions{ 221 | code.Make(code.OpTrue), 222 | code.Make(code.OpJNE, 10), 223 | code.Make(code.OpConstant, 0), 224 | code.Make(code.OpJump, 13), 225 | code.Make(code.OpConstant, 1), 226 | code.Make(code.OpPop), 227 | code.Make(code.OpConstant, 2), 228 | code.Make(code.OpPop), 229 | }, 230 | }, { 231 | input: `if (false) {10} else {20}; 3333;`, 232 | expectedConstants: []interface{}{10, 20, 3333}, 233 | expectedInstructions: []code.Instructions{ 234 | code.Make(code.OpFalse), 235 | code.Make(code.OpJNE, 10), 236 | code.Make(code.OpConstant, 0), 237 | code.Make(code.OpJump, 13), 238 | code.Make(code.OpConstant, 1), 239 | code.Make(code.OpPop), 240 | code.Make(code.OpConstant, 2), 241 | code.Make(code.OpPop), 242 | }, 243 | }, { 244 | input: `if (false) {10} ; 3333;`, 245 | expectedConstants: []interface{}{10, 3333}, 246 | expectedInstructions: []code.Instructions{ 247 | code.Make(code.OpFalse), 248 | code.Make(code.OpJNE, 10), 249 | code.Make(code.OpConstant, 0), 250 | code.Make(code.OpJump, 11), 251 | code.Make(code.OpNull), 252 | code.Make(code.OpPop), 253 | code.Make(code.OpConstant, 1), 254 | code.Make(code.OpPop), 255 | }, 256 | }, 257 | } 258 | runCompilerTests(t, tests) 259 | }) 260 | t.Run("TestGlobalLetStatement", func(t *testing.T) { 261 | tests := []compilerTestCase{ 262 | { 263 | input: ` 264 | let one = 1; 265 | let two = 2; 266 | `, 267 | expectedConstants: []interface{}{1, 2}, 268 | expectedInstructions: []code.Instructions{ 269 | code.Make(code.OpConstant, 0), 270 | code.Make(code.OpSetGlobal, 0), 271 | code.Make(code.OpConstant, 1), 272 | code.Make(code.OpSetGlobal, 1), 273 | }, 274 | }, { 275 | input: ` 276 | let one = 1; 277 | one; 278 | `, 279 | expectedConstants: []interface{}{1}, 280 | expectedInstructions: []code.Instructions{ 281 | code.Make(code.OpConstant, 0), 282 | code.Make(code.OpSetGlobal, 0), 283 | code.Make(code.OpGetGlobal, 0), 284 | code.Make(code.OpPop), 285 | }, 286 | }, { 287 | input: ` 288 | let one = 1; 289 | let two = one; 290 | two;`, 291 | expectedConstants: []interface{}{1}, 292 | expectedInstructions: []code.Instructions{ 293 | code.Make(code.OpConstant, 0), 294 | code.Make(code.OpSetGlobal, 0), 295 | code.Make(code.OpGetGlobal, 0), 296 | code.Make(code.OpSetGlobal, 1), 297 | code.Make(code.OpGetGlobal, 1), 298 | code.Make(code.OpPop), 299 | }, 300 | }, 301 | } 302 | runCompilerTests(t, tests) 303 | }) 304 | t.Run("TestStringExpression", func(t *testing.T) { 305 | tests := []compilerTestCase{ 306 | { 307 | input: `"monkey"`, 308 | expectedConstants: []interface{}{"monkey"}, 309 | expectedInstructions: []code.Instructions{ 310 | code.Make(code.OpConstant, 0), 311 | code.Make(code.OpPop), 312 | }, 313 | }, { 314 | input: `"mon" + "key"`, 315 | expectedConstants: []interface{}{"mon", "key"}, 316 | expectedInstructions: []code.Instructions{ 317 | code.Make(code.OpConstant, 0), 318 | code.Make(code.OpConstant, 1), 319 | code.Make(code.OpAdd), 320 | code.Make(code.OpPop), 321 | }, 322 | }, 323 | } 324 | runCompilerTests(t, tests) 325 | }) 326 | t.Run("TestArrayLiterals", func(t *testing.T) { 327 | tests := []compilerTestCase{ 328 | { 329 | input: "[]", 330 | expectedConstants: []interface{}{}, 331 | expectedInstructions: []code.Instructions{ 332 | code.Make(code.OpArray, 0), 333 | code.Make(code.OpPop), 334 | }, 335 | }, { 336 | input: "[1,2,3]", 337 | expectedConstants: []interface{}{1, 2, 3}, 338 | expectedInstructions: []code.Instructions{ 339 | code.Make(code.OpConstant, 0), 340 | code.Make(code.OpConstant, 1), 341 | code.Make(code.OpConstant, 2), 342 | code.Make(code.OpArray, 3), 343 | code.Make(code.OpPop), 344 | }, 345 | }, { 346 | input: "[1+2,3-4,5*6]", 347 | expectedConstants: []interface{}{1, 2, 3, 4, 5, 6}, 348 | expectedInstructions: []code.Instructions{ 349 | code.Make(code.OpConstant, 0), 350 | code.Make(code.OpConstant, 1), 351 | code.Make(code.OpAdd), 352 | code.Make(code.OpConstant, 2), 353 | code.Make(code.OpConstant, 3), 354 | code.Make(code.OpSub), 355 | code.Make(code.OpConstant, 4), 356 | code.Make(code.OpConstant, 5), 357 | code.Make(code.OpMul), 358 | code.Make(code.OpArray, 3), 359 | code.Make(code.OpPop), 360 | }, 361 | }, 362 | } 363 | runCompilerTests(t, tests) 364 | }) 365 | t.Run("TestHashmapLiteral", func(t *testing.T) { 366 | tests := []compilerTestCase{ 367 | { 368 | input: "{}", 369 | expectedConstants: []interface{}{}, 370 | expectedInstructions: []code.Instructions{ 371 | code.Make(code.OpHashTable, 0), 372 | code.Make(code.OpPop), 373 | }, 374 | }, { 375 | input: "{1:2,3:4,5:6}", 376 | expectedConstants: []interface{}{1, 2, 3, 4, 5, 6}, 377 | expectedInstructions: []code.Instructions{ 378 | code.Make(code.OpConstant, 0), 379 | code.Make(code.OpConstant, 1), 380 | code.Make(code.OpConstant, 2), 381 | code.Make(code.OpConstant, 3), 382 | code.Make(code.OpConstant, 4), 383 | code.Make(code.OpConstant, 5), 384 | code.Make(code.OpHashTable, 6), 385 | code.Make(code.OpPop), 386 | }, 387 | }, { 388 | input: "{1:2+3,4:5*6}", 389 | expectedConstants: []interface{}{1, 2, 3, 4, 5, 6}, 390 | expectedInstructions: []code.Instructions{ 391 | code.Make(code.OpConstant, 0), 392 | code.Make(code.OpConstant, 1), 393 | code.Make(code.OpConstant, 2), 394 | code.Make(code.OpAdd), 395 | code.Make(code.OpConstant, 3), 396 | code.Make(code.OpConstant, 4), 397 | code.Make(code.OpConstant, 5), 398 | code.Make(code.OpMul), 399 | code.Make(code.OpHashTable, 4), 400 | code.Make(code.OpPop), 401 | }, 402 | }, 403 | } 404 | runCompilerTests(t, tests) 405 | }) 406 | t.Run("TestIndexExpression", func(t *testing.T) { 407 | tests := []compilerTestCase{ 408 | { 409 | input: "[1,2,3][1+1]", 410 | expectedConstants: []interface{}{1, 2, 3, 1, 1}, 411 | expectedInstructions: []code.Instructions{ 412 | code.Make(code.OpConstant, 0), 413 | code.Make(code.OpConstant, 1), 414 | code.Make(code.OpConstant, 2), 415 | code.Make(code.OpArray, 3), 416 | code.Make(code.OpConstant, 3), 417 | code.Make(code.OpConstant, 4), 418 | code.Make(code.OpAdd), 419 | code.Make(code.OpIndex), 420 | code.Make(code.OpPop), 421 | }, 422 | }, 423 | { 424 | input: "{1:2,3:4}[1+1]", 425 | expectedConstants: []interface{}{1, 2, 3, 4, 1, 1}, 426 | expectedInstructions: []code.Instructions{ 427 | code.Make(code.OpConstant, 0), 428 | code.Make(code.OpConstant, 1), 429 | code.Make(code.OpConstant, 2), 430 | code.Make(code.OpConstant, 3), 431 | code.Make(code.OpHashTable, 4), 432 | code.Make(code.OpConstant, 4), 433 | code.Make(code.OpConstant, 5), 434 | code.Make(code.OpAdd), 435 | code.Make(code.OpIndex), 436 | code.Make(code.OpPop), 437 | }, 438 | }, 439 | } 440 | runCompilerTests(t, tests) 441 | }) 442 | t.Run("TestFunctionLiteral", func(t *testing.T) { 443 | tests := []compilerTestCase{ 444 | { 445 | input: "fn() { return 5 + 10; }", 446 | expectedConstants: []interface{}{ 447 | 5, 448 | 10, 449 | []code.Instructions{ 450 | code.Make(code.OpConstant, 0), 451 | code.Make(code.OpConstant, 1), 452 | code.Make(code.OpAdd), 453 | code.Make(code.OpReturnValue), 454 | }, 455 | }, 456 | expectedInstructions: []code.Instructions{ 457 | code.Make(code.OpClosure, 2, 0), 458 | code.Make(code.OpPop), 459 | }, 460 | }, { 461 | input: `fn() { 5 + 10 }`, 462 | expectedConstants: []interface{}{ 463 | 5, 464 | 10, 465 | []code.Instructions{ 466 | code.Make(code.OpConstant, 0), 467 | code.Make(code.OpConstant, 1), 468 | code.Make(code.OpAdd), 469 | code.Make(code.OpReturnValue), 470 | }, 471 | }, 472 | expectedInstructions: []code.Instructions{ 473 | code.Make(code.OpClosure, 2, 0), 474 | code.Make(code.OpPop), 475 | }, 476 | }, { 477 | input: `fn() { 1;2 }`, 478 | expectedConstants: []interface{}{ 479 | 1, 480 | 2, 481 | []code.Instructions{ 482 | code.Make(code.OpConstant, 0), 483 | code.Make(code.OpPop), 484 | code.Make(code.OpConstant, 1), 485 | code.Make(code.OpReturnValue), 486 | }, 487 | }, 488 | expectedInstructions: []code.Instructions{ 489 | code.Make(code.OpClosure, 2, 0), 490 | code.Make(code.OpPop), 491 | }, 492 | }, { 493 | input: `fn() {}`, 494 | expectedConstants: []interface{}{ 495 | []code.Instructions{ 496 | code.Make(code.OpReturn), 497 | }, 498 | }, 499 | expectedInstructions: []code.Instructions{ 500 | code.Make(code.OpClosure, 0, 0), 501 | code.Make(code.OpPop), 502 | }, 503 | }, 504 | } 505 | runCompilerTests(t, tests) 506 | }) 507 | t.Run("TestFunctionCall", func(t *testing.T) { 508 | tests := []compilerTestCase{ 509 | { 510 | input: `fn(){24}();`, 511 | expectedConstants: []interface{}{ 512 | 24, 513 | []code.Instructions{ 514 | code.Make(code.OpConstant, 0), 515 | code.Make(code.OpReturnValue), 516 | }, 517 | }, 518 | expectedInstructions: []code.Instructions{ 519 | code.Make(code.OpClosure, 1, 0), 520 | code.Make(code.OpCall, 0), 521 | code.Make(code.OpPop), 522 | }, 523 | }, { 524 | input: ` 525 | let noArg = fn(){24}; 526 | noArg(); 527 | `, 528 | expectedConstants: []interface{}{ 529 | 24, 530 | []code.Instructions{ 531 | code.Make(code.OpConstant, 0), 532 | code.Make(code.OpReturnValue), 533 | }, 534 | }, 535 | expectedInstructions: []code.Instructions{ 536 | code.Make(code.OpClosure, 1, 0), 537 | code.Make(code.OpSetGlobal, 0), 538 | code.Make(code.OpGetGlobal, 0), 539 | code.Make(code.OpCall, 0), 540 | code.Make(code.OpPop), 541 | }, 542 | }, { 543 | input: ` 544 | let oneArg = fn(a) {}; 545 | oneArg(24); 546 | `, 547 | expectedConstants: []interface{}{ 548 | []code.Instructions{ 549 | code.Make(code.OpReturn), 550 | }, 551 | 24, 552 | }, 553 | expectedInstructions: []code.Instructions{ 554 | code.Make(code.OpClosure, 0, 0), 555 | code.Make(code.OpSetGlobal, 0), 556 | code.Make(code.OpGetGlobal, 0), 557 | code.Make(code.OpConstant, 1), 558 | code.Make(code.OpCall, 1), 559 | code.Make(code.OpPop), 560 | }, 561 | }, { 562 | input: ` 563 | let manyArg = fn(a,b,c){}; 564 | manyArg(24,25,26); 565 | `, 566 | expectedConstants: []interface{}{ 567 | []code.Instructions{ 568 | code.Make(code.OpReturn), 569 | }, 570 | 24, 571 | 25, 572 | 26, 573 | }, 574 | expectedInstructions: []code.Instructions{ 575 | code.Make(code.OpClosure, 0, 0), 576 | code.Make(code.OpSetGlobal, 0), 577 | code.Make(code.OpGetGlobal, 0), 578 | code.Make(code.OpConstant, 1), 579 | code.Make(code.OpConstant, 2), 580 | code.Make(code.OpConstant, 3), 581 | code.Make(code.OpCall, 3), 582 | code.Make(code.OpPop), 583 | }, 584 | }, { 585 | input: ` 586 | let oneArg = fn(a) {a}; 587 | oneArg(24); 588 | `, 589 | expectedConstants: []interface{}{ 590 | []code.Instructions{ 591 | code.Make(code.OpGetLocal, 0), 592 | code.Make(code.OpReturnValue), 593 | }, 594 | 24, 595 | }, 596 | expectedInstructions: []code.Instructions{ 597 | code.Make(code.OpClosure, 0, 0), 598 | code.Make(code.OpSetGlobal, 0), 599 | code.Make(code.OpGetGlobal, 0), 600 | code.Make(code.OpConstant, 1), 601 | code.Make(code.OpCall, 1), 602 | code.Make(code.OpPop), 603 | }, 604 | }, { 605 | input: ` 606 | let manyArg = fn(a,b,c){a;b;c}; 607 | manyArg(24,25,26); 608 | `, 609 | expectedConstants: []interface{}{ 610 | []code.Instructions{ 611 | code.Make(code.OpGetLocal, 0), 612 | code.Make(code.OpPop), 613 | code.Make(code.OpGetLocal, 1), 614 | code.Make(code.OpPop), 615 | code.Make(code.OpGetLocal, 2), 616 | code.Make(code.OpReturnValue), 617 | }, 618 | 24, 619 | 25, 620 | 26, 621 | }, 622 | expectedInstructions: []code.Instructions{ 623 | code.Make(code.OpClosure, 0, 0), 624 | code.Make(code.OpSetGlobal, 0), 625 | code.Make(code.OpGetGlobal, 0), 626 | code.Make(code.OpConstant, 1), 627 | code.Make(code.OpConstant, 2), 628 | code.Make(code.OpConstant, 3), 629 | code.Make(code.OpCall, 3), 630 | code.Make(code.OpPop), 631 | }, 632 | }, 633 | } 634 | runCompilerTests(t, tests) 635 | }) 636 | t.Run("TestLetStatementWithScope", func(t *testing.T) { 637 | tests := []compilerTestCase{ 638 | { 639 | input: ` 640 | let num = 55; 641 | fn() { num } 642 | `, 643 | expectedConstants: []interface{}{ 644 | 55, 645 | []code.Instructions{ 646 | code.Make(code.OpGetGlobal, 0), 647 | code.Make(code.OpReturnValue), 648 | }, 649 | }, 650 | expectedInstructions: []code.Instructions{ 651 | code.Make(code.OpConstant, 0), 652 | code.Make(code.OpSetGlobal, 0), 653 | code.Make(code.OpClosure, 1, 0), 654 | code.Make(code.OpPop), 655 | }, 656 | }, { 657 | input: ` 658 | fn(){ 659 | let num = 55; 660 | num 661 | } 662 | 663 | `, 664 | expectedConstants: []interface{}{ 665 | 55, 666 | []code.Instructions{ 667 | code.Make(code.OpConstant, 0), 668 | code.Make(code.OpSetLocal, 0), 669 | code.Make(code.OpGetLocal, 0), 670 | code.Make(code.OpReturnValue), 671 | }, 672 | }, 673 | expectedInstructions: []code.Instructions{ 674 | code.Make(code.OpClosure, 1, 0), 675 | code.Make(code.OpPop), 676 | }, 677 | }, { 678 | input: ` 679 | fn(){ 680 | let a = 55; 681 | let b = 75; 682 | a+b 683 | } 684 | 685 | `, 686 | expectedConstants: []interface{}{ 687 | 55, 688 | 75, 689 | []code.Instructions{ 690 | code.Make(code.OpConstant, 0), 691 | code.Make(code.OpSetLocal, 0), 692 | code.Make(code.OpConstant, 1), 693 | code.Make(code.OpSetLocal, 1), 694 | code.Make(code.OpGetLocal, 0), 695 | code.Make(code.OpGetLocal, 1), 696 | code.Make(code.OpAdd), 697 | code.Make(code.OpReturnValue), 698 | }, 699 | }, 700 | expectedInstructions: []code.Instructions{ 701 | code.Make(code.OpClosure, 2, 0), 702 | code.Make(code.OpPop), 703 | }, 704 | }, 705 | } 706 | runCompilerTests(t, tests) 707 | }) 708 | t.Run("TestBuiltinFunc", func(t *testing.T) { 709 | tests := []compilerTestCase{ 710 | { 711 | input: ` 712 | len([]); 713 | append([],1); 714 | `, 715 | expectedConstants: []interface{}{1}, 716 | expectedInstructions: []code.Instructions{ 717 | code.Make(code.OpGetBuiltin, 0), 718 | code.Make(code.OpArray, 0), 719 | code.Make(code.OpCall, 1), 720 | code.Make(code.OpPop), 721 | code.Make(code.OpGetBuiltin, 4), 722 | code.Make(code.OpArray, 0), 723 | code.Make(code.OpConstant, 0), 724 | code.Make(code.OpCall, 2), 725 | code.Make(code.OpPop), 726 | }, 727 | }, { 728 | input: ` 729 | fn(){len([])} 730 | `, 731 | expectedConstants: []interface{}{ 732 | []code.Instructions{ 733 | code.Make(code.OpGetBuiltin, 0), 734 | code.Make(code.OpArray, 0), 735 | code.Make(code.OpCall, 1), 736 | code.Make(code.OpReturnValue), 737 | }, 738 | }, 739 | expectedInstructions: []code.Instructions{ 740 | code.Make(code.OpClosure, 0, 0), 741 | code.Make(code.OpPop), 742 | }, 743 | }, 744 | } 745 | runCompilerTests(t, tests) 746 | }) 747 | t.Run("TestClosures", func(t *testing.T) { 748 | tests := []compilerTestCase{ 749 | { 750 | input: ` 751 | fn(a){ 752 | fn(b){ 753 | a+b 754 | } 755 | } 756 | `, 757 | expectedConstants: []interface{}{ 758 | []code.Instructions{ 759 | code.Make(code.OpGetFree, 0), 760 | code.Make(code.OpGetLocal, 0), 761 | code.Make(code.OpAdd), 762 | code.Make(code.OpReturnValue), 763 | }, 764 | []code.Instructions{ 765 | code.Make(code.OpGetLocal, 0), 766 | code.Make(code.OpClosure, 0, 1), 767 | code.Make(code.OpReturnValue), 768 | }, 769 | }, 770 | expectedInstructions: []code.Instructions{ 771 | code.Make(code.OpClosure, 1, 0), 772 | code.Make(code.OpPop), 773 | }, 774 | }, 775 | } 776 | runCompilerTests(t, tests) 777 | }) 778 | } 779 | func TestSymbolTable(t *testing.T) { 780 | t.Run("TestDefine", func(t *testing.T) { 781 | expected := map[string]Symbol{ 782 | "a": {Name: "a", Scope: GlobalScope, Index: 0}, 783 | "b": {Name: "b", Scope: GlobalScope, Index: 1}, 784 | "c": {Name: "c", Scope: LocalScope, Index: 0}, 785 | "d": {Name: "d", Scope: LocalScope, Index: 1}, 786 | "e": {Name: "e", Scope: LocalScope, Index: 0}, 787 | "f": {Name: "f", Scope: LocalScope, Index: 1}, 788 | } 789 | 790 | global := NewSymbolTable() 791 | 792 | a := global.Define("a") 793 | if a != expected["a"] { 794 | t.Errorf("expected a=%+v, got=%+v", expected["a"], a) 795 | } 796 | 797 | b := global.Define("b") 798 | if b != expected["b"] { 799 | t.Errorf("expected b=%+v, got=%+v", expected["b"], b) 800 | } 801 | 802 | firstLocal := NewEnclosedSymbolTable(global) 803 | 804 | c := firstLocal.Define("c") 805 | if c != expected["c"] { 806 | t.Errorf("expected c=%+v, got=%+v", expected["c"], c) 807 | } 808 | 809 | d := firstLocal.Define("d") 810 | if d != expected["d"] { 811 | t.Errorf("expected d=%+v, got=%+v", expected["d"], d) 812 | } 813 | 814 | secondLocal := NewEnclosedSymbolTable(firstLocal) 815 | 816 | e := secondLocal.Define("e") 817 | if e != expected["e"] { 818 | t.Errorf("expected e=%+v, got=%+v", expected["e"], e) 819 | } 820 | 821 | f := secondLocal.Define("f") 822 | if f != expected["f"] { 823 | t.Errorf("expected f=%+v, got=%+v", expected["f"], f) 824 | } 825 | }) 826 | t.Run("TestGlobalResolve", func(t *testing.T) { 827 | 828 | globals := NewSymbolTable() 829 | globals.Define("a") 830 | globals.Define("b") 831 | 832 | expected := map[string]Symbol{ 833 | "a": {Name: "a", Scope: GlobalScope, Index: 0}, 834 | "b": {Name: "b", Scope: GlobalScope, Index: 1}, 835 | } 836 | for _, sym := range expected { 837 | result, ok := globals.Resolve(sym.Name) 838 | if !ok { 839 | t.Errorf("name %s not resolvable", sym.Name) 840 | continue 841 | } 842 | if result != sym { 843 | t.Errorf("expected %s to resolve to %+v instead got %+v", sym.Name, sym, result) 844 | } 845 | } 846 | }) 847 | t.Run("TestLocalResolve", func(t *testing.T) { 848 | globals := NewSymbolTable() 849 | globals.Define("a") 850 | globals.Define("b") 851 | 852 | locals := NewEnclosedSymbolTable(globals) 853 | locals.Define("c") 854 | locals.Define("d") 855 | 856 | expected := map[string]Symbol{ 857 | "a": {Name: "a", Scope: GlobalScope, Index: 0}, 858 | "b": {Name: "b", Scope: GlobalScope, Index: 1}, 859 | "c": {Name: "c", Scope: LocalScope, Index: 0}, 860 | "d": {Name: "d", Scope: LocalScope, Index: 1}, 861 | } 862 | for _, sym := range expected { 863 | result, ok := locals.Resolve(sym.Name) 864 | if !ok { 865 | t.Errorf("name %s not resolvable", sym.Name) 866 | continue 867 | } 868 | if result != sym { 869 | t.Errorf("expected %s to resolve to %+v instead got %+v", sym.Name, sym, result) 870 | } 871 | } 872 | }) 873 | t.Run("TestResolveNested", func(t *testing.T) { 874 | global := NewSymbolTable() 875 | global.Define("a") 876 | global.Define("b") 877 | 878 | firstLocal := NewEnclosedSymbolTable(global) 879 | firstLocal.Define("c") 880 | firstLocal.Define("d") 881 | 882 | secondLocal := NewEnclosedSymbolTable(firstLocal) 883 | secondLocal.Define("e") 884 | secondLocal.Define("f") 885 | 886 | tests := []struct { 887 | table *SymbolTable 888 | expectedSymbols []Symbol 889 | }{ 890 | { 891 | firstLocal, 892 | []Symbol{ 893 | {Name: "a", Scope: GlobalScope, Index: 0}, 894 | {Name: "b", Scope: GlobalScope, Index: 1}, 895 | {Name: "c", Scope: LocalScope, Index: 0}, 896 | {Name: "d", Scope: LocalScope, Index: 1}, 897 | }, 898 | }, 899 | { 900 | secondLocal, 901 | []Symbol{ 902 | {Name: "a", Scope: GlobalScope, Index: 0}, 903 | {Name: "b", Scope: GlobalScope, Index: 1}, 904 | {Name: "e", Scope: LocalScope, Index: 0}, 905 | {Name: "f", Scope: LocalScope, Index: 1}, 906 | }, 907 | }, 908 | } 909 | 910 | for _, tt := range tests { 911 | for _, sym := range tt.expectedSymbols { 912 | result, ok := tt.table.Resolve(sym.Name) 913 | if !ok { 914 | t.Errorf("name %s not resolvable", sym.Name) 915 | continue 916 | } 917 | if result != sym { 918 | t.Errorf("expected %s to resolve to %+v, got=%+v", 919 | sym.Name, sym, result) 920 | } 921 | } 922 | } 923 | }) 924 | } 925 | func TestCompilerScope(t *testing.T) { 926 | compiler := New() 927 | if compiler.scopeIndex != 0 { 928 | t.Errorf("scopeIndex wrong. got=%d, want=%d", compiler.scopeIndex, 0) 929 | } 930 | globalSymbolTable := compiler.symbolTable 931 | 932 | compiler.emit(code.OpMul) 933 | 934 | compiler.enterScope() 935 | if compiler.scopeIndex != 1 { 936 | t.Errorf("scopeIndex wrong. got=%d, want=%d", compiler.scopeIndex, 1) 937 | } 938 | 939 | compiler.emit(code.OpSub) 940 | 941 | if len(compiler.scopes[compiler.scopeIndex].instructions) != 1 { 942 | t.Errorf("instructions length wrong. got=%d", 943 | len(compiler.scopes[compiler.scopeIndex].instructions)) 944 | } 945 | 946 | last := compiler.scopes[compiler.scopeIndex].lastInstruction 947 | if last.Opcode != code.OpSub { 948 | t.Errorf("lastInstruction.Opcode wrong. got=%d, want=%d", 949 | last.Opcode, code.OpSub) 950 | } 951 | 952 | if compiler.symbolTable.Outer != globalSymbolTable { 953 | t.Errorf("compiler did not enclose symbolTable") 954 | } 955 | 956 | compiler.leaveScope() 957 | if compiler.scopeIndex != 0 { 958 | t.Errorf("scopeIndex wrong. got=%d, want=%d", 959 | compiler.scopeIndex, 0) 960 | } 961 | 962 | if compiler.symbolTable != globalSymbolTable { 963 | t.Errorf("compiler did not restore global symbol table") 964 | } 965 | if compiler.symbolTable.Outer != nil { 966 | t.Errorf("compiler modified global symbol table incorrectly") 967 | } 968 | 969 | compiler.emit(code.OpAdd) 970 | 971 | if len(compiler.scopes[compiler.scopeIndex].instructions) != 2 { 972 | t.Errorf("instructions length wrong. got=%d", 973 | len(compiler.scopes[compiler.scopeIndex].instructions)) 974 | } 975 | 976 | last = compiler.scopes[compiler.scopeIndex].lastInstruction 977 | if last.Opcode != code.OpAdd { 978 | t.Errorf("lastInstruction.Opcode wrong. got=%d, want=%d", 979 | last.Opcode, code.OpAdd) 980 | } 981 | 982 | previous := compiler.scopes[compiler.scopeIndex].previousInstruction 983 | if previous.Opcode != code.OpMul { 984 | t.Errorf("previousInstruction.Opcode wrong. got=%d, want=%d", 985 | previous.Opcode, code.OpMul) 986 | } 987 | 988 | } 989 | func parse(input string) *ast.Program { 990 | l := lexer.New(input) 991 | p := parser.New(l) 992 | 993 | program := p.Parse() 994 | 995 | return program 996 | } 997 | func runCompilerTests(t *testing.T, tests []compilerTestCase) { 998 | t.Helper() 999 | 1000 | for _, tt := range tests { 1001 | compiler := New() 1002 | program := parse(tt.input) 1003 | err := compiler.Compile(program) 1004 | 1005 | if err != nil { 1006 | t.Fatalf("compiler error : %s", err) 1007 | } 1008 | 1009 | bytecode := compiler.Bytecode() 1010 | 1011 | err = testInstructions(tt.expectedInstructions, bytecode.Instructions) 1012 | 1013 | if err != nil { 1014 | t.Fatalf("testInstructions failed : %s -- expected %q got %q", err, tt.expectedInstructions, bytecode.Instructions) 1015 | } 1016 | 1017 | err = testConstants(t, tt.expectedConstants, bytecode.Constants) 1018 | 1019 | if err != nil { 1020 | t.Fatalf("testConstants failed : %s", err) 1021 | } 1022 | } 1023 | } 1024 | 1025 | func testInstructions(expected []code.Instructions, actual code.Instructions) error { 1026 | concatted := concatInstructions(expected) 1027 | if len(actual) != len(concatted) { 1028 | return fmt.Errorf("wrong instructions length expected %d %q got %d %q", len(concatted), concatted, len(actual), actual) 1029 | } 1030 | 1031 | for i, inst := range concatted { 1032 | if actual[i] != inst { 1033 | return fmt.Errorf("wrong instruction at offset %d expected %q got %q", i, concatted, actual) 1034 | } 1035 | } 1036 | return nil 1037 | } 1038 | 1039 | func concatInstructions(s []code.Instructions) code.Instructions { 1040 | out := code.Instructions{} 1041 | 1042 | for _, inst := range s { 1043 | out = append(out, inst...) 1044 | } 1045 | 1046 | return out 1047 | } 1048 | 1049 | func testConstants(t *testing.T, expected []interface{}, actual []object.Object) error { 1050 | 1051 | if len(expected) != len(actual) { 1052 | return fmt.Errorf("wrong instructions length expected %d got %d", len(expected), len(actual)) 1053 | } 1054 | 1055 | for i, constant := range expected { 1056 | switch constant := constant.(type) { 1057 | case int: 1058 | err := testIntegerObject(int64(constant), actual[i]) 1059 | if err != nil { 1060 | return fmt.Errorf("constant[%d] = %d - testIntegerObject failed with error : %s", i, constant, err) 1061 | } 1062 | case string: 1063 | err := testStringObject(constant, actual[i]) 1064 | if err != nil { 1065 | return fmt.Errorf("constant[%d] = %s - testStringObject failed with error : %s", i, constant, err) 1066 | } 1067 | case []code.Instructions: 1068 | fn, ok := actual[i].(*object.CompiledFunction) 1069 | if !ok { 1070 | return fmt.Errorf("constant[%d] not a function: %T", i, actual[i]) 1071 | } 1072 | err := testInstructions(constant, fn.Instructions) 1073 | if err != nil { 1074 | return fmt.Errorf("constant[%d] - testInstruction failed with error %s", i, err) 1075 | } 1076 | } 1077 | } 1078 | return nil 1079 | } 1080 | 1081 | func testIntegerObject(expected int64, actual object.Object) error { 1082 | result, ok := actual.(*object.Integer) 1083 | if !ok { 1084 | return fmt.Errorf("object is not Integer got %T (%+v) ", actual, actual) 1085 | } 1086 | if result.Value != expected { 1087 | return fmt.Errorf("object has wrong value expected %d got %d", expected, result.Value) 1088 | } 1089 | return nil 1090 | } 1091 | func testStringObject(expected string, actual object.Object) error { 1092 | result, ok := actual.(*object.String) 1093 | if !ok { 1094 | return fmt.Errorf("object is not Integer got %T (%+v) ", actual, actual) 1095 | } 1096 | if result.Value != expected { 1097 | return fmt.Errorf("object has wrong value expected %s got %s", expected, result.Value) 1098 | } 1099 | return nil 1100 | } 1101 | -------------------------------------------------------------------------------- /compiler/symbol_table.go: -------------------------------------------------------------------------------- 1 | package compiler 2 | 3 | // SymbolScope represents the scope of a symbol 4 | type SymbolScope string 5 | 6 | const ( 7 | // GlobalScope marks symbols that are global (accessible from anywhere) 8 | GlobalScope SymbolScope = "GLOBAL" 9 | // LocalScope marks symbols that are local to the current frame 10 | LocalScope SymbolScope = "LOCAL" 11 | // BuiltinScope marks symbols (functions) that are part of language 12 | BuiltinScope SymbolScope = "BUILTIN" 13 | // FreeScope marks free variables 14 | FreeScope SymbolScope = "FREE" 15 | ) 16 | 17 | // Symbol represents an identifier, its scope and index in the table 18 | type Symbol struct { 19 | Name string 20 | Scope SymbolScope 21 | Index int 22 | } 23 | 24 | // SymbolTable is an associative map that maps identifiers to symbols 25 | type SymbolTable struct { 26 | Outer *SymbolTable 27 | FreeSyms []Symbol 28 | store map[string]Symbol 29 | numDefinitions int 30 | } 31 | 32 | // NewSymbolTable creates a new symbol table instance. 33 | func NewSymbolTable() *SymbolTable { 34 | return &SymbolTable{ 35 | FreeSyms: []Symbol{}, 36 | store: make(map[string]Symbol), 37 | numDefinitions: 0, 38 | } 39 | } 40 | 41 | // NewEnclosedSymbolTable creates a new symbol table instance with outer 42 | func NewEnclosedSymbolTable(outer *SymbolTable) *SymbolTable { 43 | s := NewSymbolTable() 44 | s.Outer = outer 45 | return s 46 | } 47 | 48 | // Define a new symbole for a given identifier 49 | func (s *SymbolTable) Define(name string) Symbol { 50 | sym := Symbol{Name: name, Index: s.numDefinitions} 51 | 52 | if s.Outer == nil { 53 | sym.Scope = GlobalScope 54 | } else { 55 | sym.Scope = LocalScope 56 | } 57 | 58 | s.store[name] = sym 59 | s.numDefinitions++ 60 | 61 | return sym 62 | } 63 | 64 | // Resolve a symbol by it's name 65 | func (s *SymbolTable) Resolve(name string) (Symbol, bool) { 66 | obj, ok := s.store[name] 67 | 68 | if !ok && s.Outer != nil { 69 | obj, ok := s.Outer.Resolve(name) 70 | if !ok { 71 | return obj, ok 72 | } 73 | if obj.Scope == GlobalScope || obj.Scope == BuiltinScope { 74 | return obj, ok 75 | } 76 | free := s.defineFree(obj) 77 | return free, true 78 | } 79 | 80 | return obj, ok 81 | } 82 | 83 | // DefineBuiltIn loads the builtin function into the symbol table 84 | func (s *SymbolTable) DefineBuiltIn(index int, name string) Symbol { 85 | sym := Symbol{Name: name, Index: index, Scope: BuiltinScope} 86 | s.store[name] = sym 87 | return sym 88 | } 89 | 90 | // defineFree adds a symbol to the free var scope 91 | func (s *SymbolTable) defineFree(orig Symbol) Symbol { 92 | s.FreeSyms = append(s.FreeSyms, orig) 93 | sym := Symbol{Name: orig.Name, Index: len(s.FreeSyms) - 1, Scope: FreeScope} 94 | s.store[orig.Name] = sym 95 | 96 | return sym 97 | } 98 | -------------------------------------------------------------------------------- /eval/builtin.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import "github.com/actuallyachraf/monkey-giggle/object" 4 | 5 | var builtins = map[string]*object.BuiltIn{ 6 | "len": object.GetBuiltInByName("len"), 7 | "head": object.GetBuiltInByName("head"), 8 | "tail": object.GetBuiltInByName("tail"), 9 | "last": object.GetBuiltInByName("last"), 10 | "append": object.GetBuiltInByName("append"), 11 | } 12 | -------------------------------------------------------------------------------- /eval/eval.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/actuallyachraf/monkey-giggle/ast" 7 | "github.com/actuallyachraf/monkey-giggle/object" 8 | "github.com/actuallyachraf/monkey-giggle/token" 9 | ) 10 | 11 | // Native Object References are used to reference predefined values 12 | // instead of creating new ones. 13 | var ( 14 | // TRUE denotes the boolean true value 15 | TRUE = &object.Boolean{Value: true} 16 | // FALSE denotes the boolean false value 17 | FALSE = &object.Boolean{Value: false} 18 | // NULL dentoes the null value 19 | NULL = &object.Null{} 20 | ) 21 | 22 | // Eval works by tree walking given an ast.Node it evaluates and returns a host 23 | // type value. 24 | func Eval(node ast.Node, env *object.Environment) object.Object { 25 | switch node := node.(type) { 26 | case *ast.Program: 27 | return evalProgram(node, env) 28 | case *ast.BlockStatement: 29 | return evalBlockStatement(node, env) 30 | case *ast.ExpressionStatement: 31 | return Eval(node.Expression, env) 32 | case *ast.ReturnStatement: 33 | val := Eval(node.ReturnValue, env) 34 | if isError(val) { 35 | return val 36 | } 37 | return &object.ReturnValue{Value: val} 38 | case *ast.LetStatement: 39 | if node == nil { 40 | fmt.Println("Nil node value !") 41 | } 42 | val := Eval(node.Value, env) 43 | if isError(val) { 44 | return val 45 | } 46 | env.Set(string(node.Name.Value), val) 47 | 48 | case *ast.IntegerLiteral: 49 | return &object.Integer{Value: node.Value} 50 | case *ast.StringLiteral: 51 | return &object.String{Value: node.Value} 52 | case *ast.BooleanLiteral: 53 | return nativeBoolToBoolean(node.Value) 54 | case *ast.ArrayLiteral: 55 | elements := evalExpressions(node.Elements, env) 56 | if len(elements) == 1 && isError(elements[0]) { 57 | return elements[0] 58 | } 59 | return &object.Array{Elements: elements} 60 | case *ast.IndexExpression: 61 | left := Eval(node.Left, env) 62 | if isError(left) { 63 | return left 64 | } 65 | index := Eval(node.Index, env) 66 | if isError(index) { 67 | return index 68 | } 69 | return evalIndexExpression(left, index) 70 | case *ast.HashmapLiteral: 71 | return evalHashmapLiteral(node, env) 72 | case *ast.PrefixExpression: 73 | right := Eval(node.Right, env) 74 | if isError(right) { 75 | return right 76 | } 77 | return evalPrefixExpression(node.Operator, right) 78 | case *ast.InfixExpression: 79 | left := Eval(node.Left, env) 80 | if isError(left) { 81 | return left 82 | } 83 | right := Eval(node.Right, env) 84 | if isError(right) { 85 | return right 86 | } 87 | return evalInfixExpression(node.Operator, left, right) 88 | case *ast.IfExpression: 89 | return evalIfExpression(node, env) 90 | case *ast.Identifier: 91 | return evalIdentifier(node, env) 92 | case *ast.FunctionLiteral: 93 | params := node.Parameters 94 | body := node.Body 95 | return &object.Function{Parameters: params, Body: body, Env: env} 96 | case *ast.CallExpression: 97 | function := Eval(node.Function, env) 98 | if isError(function) { 99 | return function 100 | } 101 | args := evalExpressions(node.Arguments, env) 102 | if len(args) == 1 && isError(args[0]) { 103 | return args[0] 104 | } 105 | return applyFunction(function, args) 106 | } 107 | return nil 108 | } 109 | 110 | // evalProgram runs down the eval function on program/block statements. 111 | func evalProgram(program *ast.Program, env *object.Environment) object.Object { 112 | 113 | var res object.Object 114 | 115 | for _, s := range program.Statements { 116 | res = Eval(s, env) 117 | 118 | switch res := res.(type) { 119 | case *object.ReturnValue: 120 | return res.Value 121 | case *object.Error: 122 | return res 123 | } 124 | } 125 | 126 | return res 127 | } 128 | 129 | // evalBlockStatement evaluates block statements 130 | func evalBlockStatement(block *ast.BlockStatement, env *object.Environment) object.Object { 131 | 132 | var res object.Object 133 | 134 | for _, statement := range block.Statements { 135 | res = Eval(statement, env) 136 | 137 | if res != nil { 138 | rt := res.Type() 139 | if rt == object.RETURN || rt == object.ERROR { 140 | return res 141 | } 142 | } 143 | } 144 | 145 | return res 146 | } 147 | 148 | // evalPrefixExpression is called when we need to evaluate a prefixed expression. 149 | func evalPrefixExpression(operator token.Literal, right object.Object) object.Object { 150 | switch operator { 151 | case "!": 152 | return evalBangOperatorExpression(right) 153 | case "-": 154 | return evalMinusOperatorExpression(right) 155 | default: 156 | return newError("unknwon operator :%s%s", operator, right.Type()) 157 | } 158 | } 159 | 160 | // evalInfixExpression is called when we need to evaluate infixed expression 161 | // of the type val op val. 162 | func evalInfixExpression(operator token.Literal, left object.Object, right object.Object) object.Object { 163 | // order matters here if the operator is == we need to evaluate whether it's 164 | // operating on integer operands before assuming left and right are both boolean. 165 | // edge case (1 == 2) == true => false 166 | // this bug is artefact of the fact that we also compare pointer values since the TRUE/FALSE booleans 167 | // have long-life pointers during repl runtime. 168 | switch { 169 | case left.Type() != right.Type(): 170 | return newError("type mismatch: %s %s %s", left.Type(), operator, right.Type()) 171 | case left.Type() == object.INTEGER && right.Type() == object.INTEGER: 172 | return evalIntegerExpression(operator, left, right) 173 | case left.Type() == object.STRING && right.Type() == object.STRING: 174 | return evalStringExpression(operator, left, right) 175 | case operator == "==": 176 | return nativeBoolToBoolean(left == right) 177 | case operator == "!=": 178 | return nativeBoolToBoolean(left != right) 179 | default: 180 | return newError("unknown operator: %s %s %s", left.Type(), operator, right.Type()) 181 | 182 | } 183 | } 184 | 185 | // evalIntegerExpression evaluates infix expression where both operands are integers. 186 | func evalIntegerExpression(operator token.Literal, left object.Object, right object.Object) object.Object { 187 | 188 | leftVal := left.(*object.Integer).Value 189 | rightVal := right.(*object.Integer).Value 190 | 191 | switch operator { 192 | case "+": 193 | return &object.Integer{Value: leftVal + rightVal} 194 | case "-": 195 | return &object.Integer{Value: leftVal - rightVal} 196 | case "*": 197 | return &object.Integer{Value: leftVal * rightVal} 198 | case "/": 199 | return &object.Integer{Value: leftVal / rightVal} 200 | case "%": 201 | return &object.Integer{Value: leftVal % rightVal} 202 | case "<": 203 | return nativeBoolToBoolean(leftVal < rightVal) 204 | case ">": 205 | return nativeBoolToBoolean(leftVal > rightVal) 206 | case "<=": 207 | return nativeBoolToBoolean(leftVal <= rightVal) 208 | case ">=": 209 | return nativeBoolToBoolean(leftVal >= rightVal) 210 | case "==": 211 | return nativeBoolToBoolean(leftVal == rightVal) 212 | case "!=": 213 | return nativeBoolToBoolean(leftVal != rightVal) 214 | default: 215 | return newError("unknown operator: %s %s %s", left.Type(), operator, right.Type()) 216 | } 217 | } 218 | 219 | // evalStringExpression evaluates infix expression where both operands are strings. 220 | func evalStringExpression(operator token.Literal, left object.Object, right object.Object) object.Object { 221 | leftVal := left.(*object.String).Value 222 | rightVal := right.(*object.String).Value 223 | 224 | switch operator { 225 | case "+": 226 | return &object.String{Value: leftVal + rightVal} 227 | default: 228 | return newError("unknown operator for string type %s %s %s", operator, leftVal, rightVal) 229 | } 230 | } 231 | 232 | // evalBangOperatorExpression is used to evaluate expressions prefixed by the 233 | // bang operator. 234 | func evalBangOperatorExpression(right object.Object) object.Object { 235 | switch right { 236 | case TRUE: 237 | return FALSE 238 | case FALSE: 239 | return TRUE 240 | case NULL: 241 | return TRUE 242 | default: 243 | return FALSE 244 | } 245 | } 246 | 247 | // evalMinusOperatorExpression is used to evaluate expressions prefixed by the 248 | // minus operator 249 | func evalMinusOperatorExpression(right object.Object) object.Object { 250 | 251 | if right.Type() != object.INTEGER { 252 | return newError("unknown operator: -%s", right.Type()) 253 | } 254 | 255 | value := right.(*object.Integer).Value 256 | 257 | return &object.Integer{Value: -value} 258 | } 259 | 260 | // evalExpressions evaluations expressions that appear as arguments. 261 | func evalExpressions(exps []ast.Expression, env *object.Environment) []object.Object { 262 | 263 | var res []object.Object 264 | 265 | for _, e := range exps { 266 | evaled := Eval(e, env) 267 | if isError(evaled) { 268 | return []object.Object{evaled} 269 | } 270 | 271 | res = append(res, evaled) 272 | } 273 | 274 | return res 275 | } 276 | 277 | // evalIfExpression is used to evaluate conditionnal branches. 278 | func evalIfExpression(ie *ast.IfExpression, env *object.Environment) object.Object { 279 | condition := Eval(ie.Condition, env) 280 | 281 | if isTruth(condition) { 282 | return Eval(ie.Consequence, env) 283 | } else if ie.Alternative != nil { 284 | return Eval(ie.Alternative, env) 285 | } 286 | 287 | return NULL 288 | 289 | } 290 | 291 | // evalIndexExpression evaluates expressions in the indexing op 292 | func evalIndexExpression(left, index object.Object) object.Object { 293 | switch { 294 | case left.Type() == object.ARRAY && index.Type() == object.INTEGER: 295 | return evalArrayIndexExpression(left, index) 296 | case left.Type() == object.HASH: 297 | return evalHashmapIndexExpression(left, index) 298 | default: 299 | return NULL 300 | } 301 | } 302 | 303 | // evalArrayIndexExpression evaluates expression in array indexes 304 | func evalArrayIndexExpression(array, index object.Object) object.Object { 305 | arrayObj := array.(*object.Array) 306 | idx := index.(*object.Integer).Value 307 | max := int64(len(arrayObj.Elements) - 1) 308 | 309 | if idx < 0 || idx > max { 310 | return NULL 311 | } 312 | 313 | return arrayObj.Elements[idx] 314 | } 315 | 316 | // evalHashmapIndexExpression evaluates expression in hashmap keys 317 | func evalHashmapIndexExpression(hash, index object.Object) object.Object { 318 | 319 | hashObj := hash.(*object.HashMap) 320 | 321 | key, ok := index.(object.Hashable) 322 | if !ok { 323 | return newError("not valid key for hashmap literal : %s", index.Type()) 324 | } 325 | pair, ok := hashObj.Pairs[key.HashKey()] 326 | if !ok { 327 | return NULL 328 | } 329 | return pair.Value 330 | } 331 | 332 | // evalHashmapLiteral evaluates hashmap literals 333 | func evalHashmapLiteral(node *ast.HashmapLiteral, env *object.Environment) object.Object { 334 | pairs := make(map[object.HashKey]object.HashPair) 335 | 336 | for k, v := range node.Pairs { 337 | key := Eval(k, env) 338 | if isError(key) { 339 | return key 340 | } 341 | hashKey, ok := key.(object.Hashable) 342 | if !ok { 343 | return newError("not valid key for hashmap literal : %s", key.Type()) 344 | } 345 | val := Eval(v, env) 346 | if isError(val) { 347 | return val 348 | } 349 | hashed := hashKey.HashKey() 350 | pairs[hashed] = object.HashPair{Key: key, Value: val} 351 | } 352 | 353 | return &object.HashMap{Pairs: pairs} 354 | } 355 | 356 | // applyFunction fn to a list of arguments 357 | func applyFunction(fn object.Object, args []object.Object) object.Object { 358 | 359 | switch fn := fn.(type) { 360 | case *object.Function: 361 | extendedEnv := extendFunctionEnv(fn, args) 362 | evaled := Eval(fn.Body, extendedEnv) 363 | return unwrapReturnValue(evaled) 364 | case *object.BuiltIn: 365 | if res := fn.Fn(args...); res != nil { 366 | return res 367 | } 368 | return NULL 369 | default: 370 | return newError("not a function: %s", fn.Type()) 371 | } 372 | } 373 | 374 | // extendFunctionEnv from current environment 375 | func extendFunctionEnv(fn *object.Function, args []object.Object) *object.Environment { 376 | env := object.NewEnclosedEnvironment(fn.Env) 377 | 378 | for paramIdx, param := range fn.Parameters { 379 | env.Set(string(param.Value), args[paramIdx]) 380 | } 381 | 382 | return env 383 | } 384 | 385 | // unwrapReturnValue unwraps the return value to a return statement 386 | func unwrapReturnValue(obj object.Object) object.Object { 387 | if retVal, ok := obj.(*object.ReturnValue); ok { 388 | return retVal.Value 389 | } 390 | 391 | return obj 392 | } 393 | 394 | // nativeBoolToBoolean returns a referenced type instead of creating a new one 395 | // for the Boolean type. 396 | func nativeBoolToBoolean(in bool) *object.Boolean { 397 | if in { 398 | return TRUE 399 | } 400 | 401 | return FALSE 402 | } 403 | 404 | // isTruth returns whether a given object is a true expression or not. 405 | func isTruth(obj object.Object) bool { 406 | switch obj { 407 | case NULL: 408 | return false 409 | case FALSE: 410 | return false 411 | default: 412 | return true 413 | } 414 | } 415 | 416 | // newError creates a new error message 417 | func newError(format string, a ...interface{}) *object.Error { 418 | return &object.Error{Message: fmt.Sprintf(format, a...)} 419 | } 420 | 421 | // isError checks if a given object is an error 422 | func isError(obj object.Object) bool { 423 | if obj != nil { 424 | return obj.Type() == object.ERROR 425 | } 426 | 427 | return false 428 | } 429 | 430 | // evalIdentifier ... 431 | func evalIdentifier(node *ast.Identifier, env *object.Environment) object.Object { 432 | if val, ok := env.Get(string(node.Value)); ok { 433 | return val 434 | } 435 | if builtin, ok := builtins[string(node.Value)]; ok { 436 | return builtin 437 | } 438 | 439 | return newError("identifier not found: " + string(node.Value)) 440 | } 441 | -------------------------------------------------------------------------------- /eval/eval_test.go: -------------------------------------------------------------------------------- 1 | package eval 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/actuallyachraf/monkey-giggle/lexer" 7 | "github.com/actuallyachraf/monkey-giggle/object" 8 | "github.com/actuallyachraf/monkey-giggle/parser" 9 | ) 10 | 11 | func TestEval(t *testing.T) { 12 | 13 | t.Run("TestEvalIntegerExpression", func(t *testing.T) { 14 | 15 | tests := []struct { 16 | input string 17 | expected int64 18 | }{ 19 | {"5", 5}, 20 | {"10", 10}, 21 | } 22 | 23 | for _, tt := range tests { 24 | evaled := testEval(tt.input) 25 | testIntegerObject(t, evaled, tt.expected) 26 | } 27 | }) 28 | t.Run("TestEvalBooleanExpression", func(t *testing.T) { 29 | tests := []struct { 30 | input string 31 | expected bool 32 | }{ 33 | {"true", true}, 34 | {"false", false}, 35 | {"1 < 2", true}, 36 | {"1 > 2", false}, 37 | {"1 < 1", false}, 38 | {"1 > 1", false}, 39 | {"1 == 1", true}, 40 | {"1 == 2", false}, 41 | {"1 != 2", true}, 42 | {"1 != 1", false}, 43 | {"1 <= 1", true}, 44 | {"1 >= 1", true}, 45 | {"true == true", true}, 46 | {"false == false", true}, 47 | {"true == false", false}, 48 | {"true != false", true}, 49 | {"false != true", true}, 50 | {"(1 < 2) == true", true}, 51 | {"(1 < 2) == false", false}, 52 | {"(1 > 2) == true", false}, 53 | {"(1 > 2) == false", true}, 54 | } 55 | 56 | for _, tt := range tests { 57 | evaled := testEval(tt.input) 58 | testBooleanBoject(t, evaled, tt.expected) 59 | } 60 | }) 61 | t.Run("TestEvalBangOperator", func(t *testing.T) { 62 | tests := []struct { 63 | input string 64 | expected bool 65 | }{ 66 | {"!true", false}, 67 | {"!false", true}, 68 | {"!5", false}, 69 | {"!!true", true}, 70 | {"!!false", false}, 71 | {"!!5", true}, 72 | } 73 | 74 | for _, tt := range tests { 75 | evaled := testEval(tt.input) 76 | testBooleanBoject(t, evaled, tt.expected) 77 | } 78 | }) 79 | t.Run("TestEvalMinusOperator", func(t *testing.T) { 80 | tests := []struct { 81 | input string 82 | expected int64 83 | }{ 84 | {"-5", -5}, 85 | {"-10", -10}, 86 | } 87 | 88 | for _, tt := range tests { 89 | evaled := testEval(tt.input) 90 | testIntegerObject(t, evaled, tt.expected) 91 | } 92 | }) 93 | t.Run("TestEvalIntegerExpression", func(t *testing.T) { 94 | tests := []struct { 95 | input string 96 | expected int64 97 | }{ 98 | {"5", 5}, 99 | {"10", 10}, 100 | {"-5", -5}, 101 | {"-10", -10}, 102 | {"5 + 5 + 5 + 5 - 10", 10}, 103 | {"2 * 2 * 2 * 2 * 2", 32}, 104 | {"-50 + 100 + -50", 0}, 105 | {"5 * 2 + 10", 20}, 106 | {"5 + 2 * 10", 25}, 107 | {"20 + 2 * -10", 0}, 108 | {"50 / 2 * 2 + 10", 60}, 109 | {"2 * (5 + 10)", 30}, 110 | {"3 * 3 * 3 + 10", 37}, 111 | {"3 * (3 * 3) + 10", 37}, 112 | {"(5 + 10 * 2 + 15 / 3) * 2 + -10", 50}, 113 | } 114 | 115 | for _, tt := range tests { 116 | evaluated := testEval(tt.input) 117 | testIntegerObject(t, evaluated, tt.expected) 118 | } 119 | }) 120 | t.Run("TestEvalIfElseExpression", func(t *testing.T) { 121 | tests := []struct { 122 | input string 123 | expected interface{} 124 | }{ 125 | {"if (true) { 10 }", 10}, 126 | {"if (false) { 10 }", nil}, 127 | {"if (1) { 10 }", 10}, 128 | {"if (1 < 2) { 10 }", 10}, 129 | {"if (1 > 2) { 10 }", nil}, 130 | {"if (1 > 2) { 10 } else { 20 }", 20}, 131 | {"if (1 < 2) { 10 } else { 20 }", 10}, 132 | } 133 | 134 | for _, tt := range tests { 135 | evaluated := testEval(tt.input) 136 | integer, ok := tt.expected.(int) 137 | if ok { 138 | testIntegerObject(t, evaluated, int64(integer)) 139 | } else { 140 | testNullObject(t, evaluated) 141 | } 142 | } 143 | }) 144 | t.Run("TestEvalReturnStatement", func(t *testing.T) { 145 | tests := []struct { 146 | input string 147 | expected int64 148 | }{ 149 | {"return 10;", 10}, 150 | {"return 10; 9;", 10}, 151 | {"return 2 * 5; 9;", 10}, 152 | {"9; return 2 * 5; 9;", 10}, 153 | {"if (10 > 1) { return 10; }", 10}, 154 | { 155 | ` 156 | if (10 > 1) { 157 | if (10 > 1) { 158 | return 10; 159 | } 160 | 161 | return 1; 162 | } 163 | `, 164 | 10, 165 | }, 166 | } 167 | 168 | for _, tt := range tests { 169 | evaluated := testEval(tt.input) 170 | testIntegerObject(t, evaluated, tt.expected) 171 | } 172 | }) 173 | t.Run("TestEvalErrorHandling", func(t *testing.T) { 174 | tests := []struct { 175 | input string 176 | expectedMessage string 177 | }{ 178 | { 179 | "5 + true;", 180 | "type mismatch: INTEGER + BOOLEAN", 181 | }, 182 | { 183 | "5 + true; 5;", 184 | "type mismatch: INTEGER + BOOLEAN", 185 | }, 186 | { 187 | "-true", 188 | "unknown operator: -BOOLEAN", 189 | }, 190 | { 191 | "true + false;", 192 | "unknown operator: BOOLEAN + BOOLEAN", 193 | }, 194 | { 195 | "true + false + true + false;", 196 | "unknown operator: BOOLEAN + BOOLEAN", 197 | }, 198 | { 199 | "5; true + false; 5", 200 | "unknown operator: BOOLEAN + BOOLEAN", 201 | }, 202 | { 203 | "if (10 > 1) { true + false; }", 204 | "unknown operator: BOOLEAN + BOOLEAN", 205 | }, 206 | { 207 | ` 208 | if (10 > 1) { 209 | if (10 > 1) { 210 | return true + false; 211 | } 212 | 213 | return 1; 214 | } 215 | `, 216 | "unknown operator: BOOLEAN + BOOLEAN", 217 | }, 218 | { 219 | "foobar", 220 | "identifier not found: foobar", 221 | }, 222 | } 223 | 224 | for _, tt := range tests { 225 | evaluated := testEval(tt.input) 226 | 227 | errObj, ok := evaluated.(*object.Error) 228 | if !ok { 229 | t.Errorf("no error object returned. got=%T(%+v)", 230 | evaluated, evaluated) 231 | continue 232 | } 233 | 234 | if errObj.Message != tt.expectedMessage { 235 | t.Errorf("wrong error message. expected=%q, got=%q", 236 | tt.expectedMessage, errObj.Message) 237 | } 238 | } 239 | }) 240 | t.Run("TestEvalLetStatement", func(t *testing.T) { 241 | tests := []struct { 242 | input string 243 | expected int64 244 | }{ 245 | {"let a = 5; a;", 5}, 246 | {"let a = 5 * 5; a;", 25}, 247 | {"let a = 5; let b = a; b;", 5}, 248 | {"let a = 5; let b = a; let c = a + b + 5; c;", 15}, 249 | } 250 | 251 | for _, tt := range tests { 252 | testIntegerObject(t, testEval(tt.input), tt.expected) 253 | } 254 | }) 255 | t.Run("TestEvalFunctionStatement", func(t *testing.T) { 256 | input := "fn(x) { x + 2; };" 257 | 258 | evaluated := testEval(input) 259 | fn, ok := evaluated.(*object.Function) 260 | if !ok { 261 | t.Fatalf("object is not Function. got=%T (%+v)", evaluated, evaluated) 262 | } 263 | 264 | if len(fn.Parameters) != 1 { 265 | t.Fatalf("function has wrong parameters. Parameters=%+v", 266 | fn.Parameters) 267 | } 268 | 269 | if fn.Parameters[0].String() != "x" { 270 | t.Fatalf("parameter is not 'x'. got=%q", fn.Parameters[0]) 271 | } 272 | 273 | expectedBody := "(x + 2)" 274 | 275 | if fn.Body.String() != expectedBody { 276 | t.Fatalf("body is not %q. got=%q", expectedBody, fn.Body.String()) 277 | } 278 | }) 279 | t.Run("TestEvalFunctionApplication", func(t *testing.T) { 280 | tests := []struct { 281 | input string 282 | expected int64 283 | }{ 284 | {"let identity = fn(x) { x; }; identity(5);", 5}, 285 | {"let identity = fn(x) { return x; }; identity(5);", 5}, 286 | {"let double = fn(x) { x * 2; }; double(5);", 10}, 287 | {"let add = fn(x, y) { x + y; }; add(5, 5);", 10}, 288 | {"let add = fn(x, y) { x + y; }; add(5 + 5, add(5, 5));", 20}, 289 | {"fn(x) { x; }(5)", 5}, 290 | {"let addMod = fn(x,y,m){return (x+y)%m;}; addMod(5,10,3)", 0}, 291 | } 292 | 293 | for _, tt := range tests { 294 | testIntegerObject(t, testEval(tt.input), tt.expected) 295 | } 296 | }) 297 | t.Run("TestEvalEnclosedEnv", func(t *testing.T) { 298 | input := ` 299 | let first = 10; 300 | let second = 10; 301 | let third = 10; 302 | 303 | let ourFunction = fn(first) { 304 | let second = 20; 305 | 306 | first + second + third; 307 | }; 308 | 309 | ourFunction(20) + first + second;` 310 | 311 | testIntegerObject(t, testEval(input), 70) 312 | }) 313 | t.Run("TestEvalStringExpression", func(t *testing.T) { 314 | 315 | input := `"hello world!"` 316 | 317 | evaled := testEval(input) 318 | 319 | str, ok := evaled.(*object.String) 320 | if !ok { 321 | t.Fatalf("object is not String got %T , (%+v)", evaled, evaled) 322 | } 323 | 324 | if str.Value != "hello world!" { 325 | t.Fatalf("wrong object value expected %s got %s", input, str.Value) 326 | } 327 | }) 328 | t.Run("TestEvalStringConcat", func(t *testing.T) { 329 | input := `"Hello" +" " + "World!"` 330 | evaled := testEval(input) 331 | str, ok := evaled.(*object.String) 332 | if !ok { 333 | t.Fatalf("object is not String got %T , (%+v)", evaled, evaled) 334 | } 335 | 336 | if str.Value != "Hello World!" { 337 | t.Fatalf("wrong object value expected %s got %s", input, str.Value) 338 | } 339 | }) 340 | t.Run("TestEvalBuiltInFunction", func(t *testing.T) { 341 | tests := []struct { 342 | input string 343 | expected interface{} 344 | }{ 345 | {`len("")`, 0}, 346 | {`len("four")`, 4}, 347 | {`len("Hello World!")`, 12}, 348 | {`len(1)`, "argument to `len` not supported, got INTEGER"}, 349 | {`len("one","two")`, "wrong number of arguments, expected 1 got 2"}, 350 | {`head([1,2,3])`, 1}, 351 | {`tail([1,2,3])`, []int{2, 3}}, 352 | {`append([],1)`, []int{1}}, 353 | } 354 | 355 | for _, tt := range tests { 356 | evaled := testEval(tt.input) 357 | switch expected := tt.expected.(type) { 358 | case int: 359 | testIntegerObject(t, evaled, int64(expected)) 360 | case string: 361 | errObj, ok := evaled.(*object.Error) 362 | if !ok { 363 | t.Errorf("object is not an error got %T (%+v)", evaled, evaled) 364 | continue 365 | } 366 | if errObj.Message != expected { 367 | t.Errorf("wrong error message ! expected %q got %q", expected, errObj.Message) 368 | } 369 | case []int: 370 | array, ok := evaled.(*object.Array) 371 | if !ok { 372 | t.Errorf("obj not Array. got=%T (%+v)", evaled, evaled) 373 | continue 374 | } 375 | 376 | if len(array.Elements) != len(expected) { 377 | t.Errorf("wrong num of elements. want=%d, got=%d", 378 | len(expected), len(array.Elements)) 379 | continue 380 | } 381 | 382 | for i, expectedElem := range expected { 383 | testIntegerObject(t, array.Elements[i], int64(expectedElem)) 384 | } 385 | } 386 | } 387 | }) 388 | t.Run("TestEvalArrayLiteral", func(t *testing.T) { 389 | input := "[1, 2 * 2, 3 + 3]" 390 | 391 | evaluated := testEval(input) 392 | result, ok := evaluated.(*object.Array) 393 | if !ok { 394 | t.Fatalf("object is not Array. got=%T (%+v)", evaluated, evaluated) 395 | } 396 | 397 | if len(result.Elements) != 3 { 398 | t.Fatalf("array has wrong num of elements. got=%d", 399 | len(result.Elements)) 400 | } 401 | 402 | testIntegerObject(t, result.Elements[0], 1) 403 | testIntegerObject(t, result.Elements[1], 4) 404 | testIntegerObject(t, result.Elements[2], 6) 405 | }) 406 | t.Run("TestEvalIndexExpression", func(t *testing.T) { 407 | tests := []struct { 408 | input string 409 | expected interface{} 410 | }{ 411 | { 412 | "[1, 2, 3][0]", 413 | 1, 414 | }, 415 | { 416 | "[1, 2, 3][1]", 417 | 2, 418 | }, 419 | { 420 | "[1, 2, 3][2]", 421 | 3, 422 | }, 423 | { 424 | "let i = 0; [1][i];", 425 | 1, 426 | }, 427 | { 428 | "[1, 2, 3][1 + 1];", 429 | 3, 430 | }, 431 | { 432 | "let myArray = [1, 2, 3]; myArray[2];", 433 | 3, 434 | }, 435 | { 436 | "let myArray = [1, 2, 3]; myArray[0] + myArray[1] + myArray[2];", 437 | 6, 438 | }, 439 | { 440 | "let myArray = [1, 2, 3]; let i = myArray[0]; myArray[i]", 441 | 2, 442 | }, 443 | { 444 | "[1, 2, 3][3]", 445 | nil, 446 | }, 447 | { 448 | "[1, 2, 3][-1]", 449 | nil, 450 | }, 451 | } 452 | 453 | for _, tt := range tests { 454 | evaluated := testEval(tt.input) 455 | integer, ok := tt.expected.(int) 456 | if ok { 457 | testIntegerObject(t, evaluated, int64(integer)) 458 | } else { 459 | testNullObject(t, evaluated) 460 | } 461 | } 462 | }) 463 | t.Run("TestEvalHashmapLiteral", func(t *testing.T) { 464 | input := `let two = "two"; 465 | { 466 | "one": 10 - 9, 467 | two: 1 + 1, 468 | "thr" + "ee": 6 / 2, 469 | 4: 4, 470 | true: 5, 471 | false: 6 472 | }` 473 | 474 | evaluated := testEval(input) 475 | result, ok := evaluated.(*object.HashMap) 476 | if !ok { 477 | t.Fatalf("Eval didn't return Hash. got=%T (%+v)", evaluated, evaluated) 478 | } 479 | 480 | expected := map[object.HashKey]int64{ 481 | (&object.String{Value: "one"}).HashKey(): 1, 482 | (&object.String{Value: "two"}).HashKey(): 2, 483 | (&object.String{Value: "three"}).HashKey(): 3, 484 | (&object.Integer{Value: 4}).HashKey(): 4, 485 | TRUE.HashKey(): 5, 486 | FALSE.HashKey(): 6, 487 | } 488 | 489 | if len(result.Pairs) != len(expected) { 490 | t.Fatalf("Hash has wrong num of pairs. got=%d", len(result.Pairs)) 491 | } 492 | 493 | for expectedKey, expectedValue := range expected { 494 | pair, ok := result.Pairs[expectedKey] 495 | if !ok { 496 | t.Errorf("no pair for given key in Pairs") 497 | } 498 | 499 | testIntegerObject(t, pair.Value, expectedValue) 500 | } 501 | }) 502 | t.Run("TestEvalHashmapIndexExpression", func(t *testing.T) { 503 | tests := []struct { 504 | input string 505 | expected interface{} 506 | }{ 507 | { 508 | `{"foo": 5}["foo"]`, 509 | 5, 510 | }, 511 | { 512 | `{"foo": 5}["bar"]`, 513 | nil, 514 | }, 515 | { 516 | `let key = "foo"; {"foo": 5}[key]`, 517 | 5, 518 | }, 519 | { 520 | `{}["foo"]`, 521 | nil, 522 | }, 523 | { 524 | `{5: 5}[5]`, 525 | 5, 526 | }, 527 | { 528 | `{true: 5}[true]`, 529 | 5, 530 | }, 531 | { 532 | `{false: 5}[false]`, 533 | 5, 534 | }, 535 | } 536 | 537 | for _, tt := range tests { 538 | evaluated := testEval(tt.input) 539 | integer, ok := tt.expected.(int) 540 | if ok { 541 | testIntegerObject(t, evaluated, int64(integer)) 542 | } else { 543 | testNullObject(t, evaluated) 544 | } 545 | } 546 | }) 547 | } 548 | 549 | func testEval(input string) object.Object { 550 | l := lexer.New(input) 551 | p := parser.New(l) 552 | program := p.Parse() 553 | env := object.NewEnv() 554 | 555 | return Eval(program, env) 556 | } 557 | 558 | func testIntegerObject(t *testing.T, obj object.Object, expected int64) bool { 559 | res, ok := obj.(*object.Integer) 560 | if !ok { 561 | t.Errorf("object is not an integer got %T", obj) 562 | return false 563 | } 564 | 565 | if res.Value != expected { 566 | t.Errorf("object value mismatch expected %d got %d", expected, res.Value) 567 | return false 568 | } 569 | 570 | return true 571 | } 572 | 573 | func testBooleanBoject(t *testing.T, obj object.Object, expected bool) bool { 574 | res, ok := obj.(*object.Boolean) 575 | if !ok { 576 | t.Errorf("object is not a boolean got %T", obj) 577 | return false 578 | } 579 | 580 | if res.Value != expected { 581 | t.Errorf("object value mismatch expected %t got %t", expected, res.Value) 582 | return false 583 | } 584 | 585 | return true 586 | } 587 | func testNullObject(t *testing.T, obj object.Object) bool { 588 | if obj != NULL { 589 | t.Errorf("object is not NULL. got=%T (%+v)", obj, obj) 590 | return false 591 | } 592 | return true 593 | } 594 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/actuallyachraf/monkey-giggle 2 | 3 | go 1.14 4 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/actuallyachraf/monkey-giggle/491f9a55753eb24d617f93927bfd20f03ccdc429/go.sum -------------------------------------------------------------------------------- /lexer/lexer.go: -------------------------------------------------------------------------------- 1 | // Package lexer implements lexical analysis for the monkey source code 2 | // the goal is to simply read a string representation of code 3 | // and turn it into a token representation. 4 | package lexer 5 | 6 | import "github.com/actuallyachraf/monkey-giggle/token" 7 | 8 | // Lexer represents a lexical analysis engine. 9 | type Lexer struct { 10 | input string // represents the input string (TODO:replace with io.Reader) 11 | pos int // current position in input (current char) 12 | readPos int // next position in input 13 | ch byte // current char 14 | } 15 | 16 | // New creates a new instance of lexer. 17 | func New(input string) *Lexer { 18 | l := &Lexer{ 19 | input: input, 20 | } 21 | l.readChar() 22 | return l 23 | } 24 | 25 | // readChar reads a single byte from the string and update positions.a 26 | func (l *Lexer) readChar() { 27 | if l.readPos >= len(l.input) { 28 | l.ch = 0 29 | } else { 30 | l.ch = l.input[l.readPos] 31 | } 32 | 33 | l.pos = l.readPos 34 | l.readPos++ 35 | } 36 | 37 | // NextToken parses the and returns the next token in the input. 38 | func (l *Lexer) NextToken() token.Token { 39 | 40 | var tok token.Token 41 | l.skipWhitespace() 42 | 43 | switch l.ch { 44 | case '=': 45 | if l.peekChar() == '=' { 46 | ch := l.ch 47 | l.readChar() 48 | tok = token.NewLiteral(token.EQ, string(ch)+string(l.ch)) 49 | } else { 50 | tok = token.New(token.ASSIGN, l.ch) 51 | } 52 | case '+': 53 | tok = token.New(token.ADD, l.ch) 54 | case '-': 55 | tok = token.New(token.SUB, l.ch) 56 | case '*': 57 | tok = token.New(token.MUL, l.ch) 58 | case '/': 59 | tok = token.New(token.DIV, l.ch) 60 | case '%': 61 | tok = token.New(token.MOD, l.ch) 62 | case ':': 63 | tok = token.New(token.COLON, l.ch) 64 | case ';': 65 | tok = token.New(token.SEMICOLON, l.ch) 66 | case ',': 67 | tok = token.New(token.COMMA, l.ch) 68 | case '"': 69 | tok = token.NewLiteral(token.STRING, l.readString()) 70 | case '[': 71 | tok = token.New(token.LBRACKET, l.ch) 72 | case ']': 73 | tok = token.New(token.RBRACKET, l.ch) 74 | case '(': 75 | tok = token.New(token.LPAREN, l.ch) 76 | case ')': 77 | tok = token.New(token.RPAREN, l.ch) 78 | case '{': 79 | tok = token.New(token.LBRACE, l.ch) 80 | case '}': 81 | tok = token.New(token.RBRACE, l.ch) 82 | case '!': 83 | if l.peekChar() == '=' { 84 | ch := l.ch 85 | l.readChar() 86 | tok = token.NewLiteral(token.NEQ, string(ch)+string(l.ch)) 87 | } else { 88 | tok = token.New(token.BANG, l.ch) 89 | } 90 | 91 | case '<': 92 | if l.peekChar() == '=' { 93 | ch := l.ch 94 | l.readChar() 95 | tok = token.NewLiteral(token.LE, string(ch)+string(l.ch)) 96 | } else { 97 | tok = token.New(token.LT, l.ch) 98 | } 99 | 100 | case '>': 101 | if l.peekChar() == '=' { 102 | ch := l.ch 103 | l.readChar() 104 | tok = token.NewLiteral(token.GE, string(ch)+string(l.ch)) 105 | } else { 106 | tok = token.New(token.GT, l.ch) 107 | } 108 | case 0: 109 | tok = token.Token{Type: token.EOF, Literal: token.Literal("")} 110 | 111 | default: 112 | if isLetter(l.ch) { 113 | tok.Literal = token.Literal(l.readIdentifier()) 114 | tok.Type = token.LookupIdent(tok.Literal) 115 | return tok 116 | } else if isDigit(l.ch) { 117 | tok.Type = token.INT 118 | tok.Literal = token.Literal(l.readNumber()) 119 | return tok 120 | } else { 121 | tok = token.New(token.ILLEGAL, l.ch) 122 | } 123 | } 124 | 125 | // move to the next char 126 | l.readChar() 127 | 128 | return tok 129 | } 130 | 131 | // readIdentifier reads the next identifier 132 | func (l *Lexer) readIdentifier() string { 133 | 134 | pos := l.pos 135 | for isLetter(l.ch) { 136 | l.readChar() 137 | } 138 | 139 | return l.input[pos:l.pos] 140 | } 141 | 142 | // skipWhitespace is used to escape whitespace between keywords and literal identifiers. 143 | func (l *Lexer) skipWhitespace() { 144 | for l.ch == ' ' || l.ch == '\t' || l.ch == '\n' || l.ch == '\r' { 145 | l.readChar() 146 | } 147 | } 148 | 149 | // readNumber is used to read a whole number (composed of multiplie digits). 150 | func (l *Lexer) readNumber() string { 151 | pos := l.pos 152 | 153 | for isDigit(l.ch) { 154 | l.readChar() 155 | } 156 | 157 | return l.input[pos:l.pos] 158 | } 159 | 160 | // readString is used to read strings that appear between quotes. 161 | func (l *Lexer) readString() string { 162 | pos := l.pos + 1 // skip the quote 163 | for { 164 | l.readChar() 165 | if l.ch == '"' || l.ch == 0 { 166 | break 167 | } 168 | } 169 | 170 | return l.input[pos:l.pos] 171 | } 172 | 173 | // peekChar is used to read past the current char to determine multi char tokens 174 | func (l *Lexer) peekChar() byte { 175 | if l.readPos >= len(l.input) { 176 | return 0 177 | } 178 | return l.input[l.readPos] 179 | 180 | } 181 | 182 | // isLetter checks whether the current char is valid ASCII letter 183 | func isLetter(ch byte) bool { 184 | return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' 185 | } 186 | 187 | // isDigit checks whether the current char is a digit 188 | func isDigit(ch byte) bool { 189 | return '0' <= ch && ch <= '9' 190 | } 191 | -------------------------------------------------------------------------------- /lexer/lexer_test.go: -------------------------------------------------------------------------------- 1 | package lexer 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/actuallyachraf/monkey-giggle/token" 7 | ) 8 | 9 | func TestLexer(t *testing.T) { 10 | 11 | t.Run("TestBasicToken", func(t *testing.T) { 12 | 13 | input := `=+-*/%(){},;` 14 | 15 | tests := []struct { 16 | expectedType token.Type 17 | expectedLiteral token.Literal 18 | }{ 19 | {token.ASSIGN, "="}, 20 | {token.ADD, "+"}, 21 | {token.SUB, "-"}, 22 | {token.MUL, "*"}, 23 | {token.DIV, "/"}, 24 | {token.MOD, "%"}, 25 | {token.LPAREN, "("}, 26 | {token.RPAREN, ")"}, 27 | {token.LBRACE, "{"}, 28 | {token.RBRACE, "}"}, 29 | {token.COMMA, ","}, 30 | {token.SEMICOLON, ";"}, 31 | } 32 | l := New(input) 33 | 34 | for i, tt := range tests { 35 | tok := l.NextToken() 36 | if tok.Type != tt.expectedType { 37 | t.Fatalf("tests[%d] - wrong token type : expected %q, got %q", i, tt.expectedType, tok.Type) 38 | } 39 | 40 | if tok.Literal != tt.expectedLiteral { 41 | t.Fatalf("tests[%d] - wrong token literal : expected %q, got %q", i, tt.expectedLiteral, tok.Literal) 42 | 43 | } 44 | } 45 | }) 46 | t.Run("TestTokenChain", func(t *testing.T) { 47 | 48 | input := `let five = 5; 49 | let ten = 10; 50 | let add = fn(x,y){ 51 | x + y; 52 | }; 53 | let Result = add(five,ten); 54 | !-/*5; 55 | 5 < 10 > 5; 56 | if (5 < 10 ){ 57 | return true; 58 | } else { 59 | return false; 60 | } 61 | 10 == 10; 62 | 10 != 9; 63 | 5 <= 5; 64 | 6 >= 6; 65 | "foobar" 66 | "foo bar" 67 | [1,2]; 68 | {"foo":"bar"} 69 | ` 70 | tests := []struct { 71 | expectedType token.Type 72 | expectedLiteral token.Literal 73 | }{ 74 | {token.LET, "let"}, 75 | {token.IDENT, "five"}, 76 | {token.ASSIGN, "="}, 77 | {token.INT, "5"}, 78 | {token.SEMICOLON, ";"}, 79 | {token.LET, "let"}, 80 | {token.IDENT, "ten"}, 81 | {token.ASSIGN, "="}, 82 | {token.INT, "10"}, 83 | {token.SEMICOLON, ";"}, 84 | {token.LET, "let"}, 85 | {token.IDENT, "add"}, 86 | {token.ASSIGN, "="}, 87 | {token.FUNCTION, "fn"}, 88 | {token.LPAREN, "("}, 89 | {token.IDENT, "x"}, 90 | {token.COMMA, ","}, 91 | {token.IDENT, "y"}, 92 | {token.RPAREN, ")"}, 93 | {token.LBRACE, "{"}, 94 | {token.IDENT, "x"}, 95 | {token.ADD, "+"}, 96 | {token.IDENT, "y"}, 97 | {token.SEMICOLON, ";"}, 98 | {token.RBRACE, "}"}, 99 | {token.SEMICOLON, ";"}, 100 | {token.LET, "let"}, 101 | {token.IDENT, "Result"}, 102 | {token.ASSIGN, "="}, 103 | {token.IDENT, "add"}, 104 | {token.LPAREN, "("}, 105 | {token.IDENT, "five"}, 106 | {token.COMMA, ","}, 107 | {token.IDENT, "ten"}, 108 | {token.RPAREN, ")"}, 109 | {token.SEMICOLON, ";"}, 110 | {token.BANG, "!"}, 111 | {token.SUB, "-"}, 112 | {token.DIV, "/"}, 113 | {token.MUL, "*"}, 114 | {token.INT, "5"}, 115 | {token.SEMICOLON, ";"}, 116 | {token.INT, "5"}, 117 | {token.LT, "<"}, 118 | {token.INT, "10"}, 119 | {token.GT, ">"}, 120 | {token.INT, "5"}, 121 | {token.SEMICOLON, ";"}, 122 | {token.IF, "if"}, 123 | {token.LPAREN, "("}, 124 | {token.INT, "5"}, 125 | {token.LT, "<"}, 126 | {token.INT, "10"}, 127 | {token.RPAREN, ")"}, 128 | {token.LBRACE, "{"}, 129 | {token.RETURN, "return"}, 130 | {token.TRUE, "true"}, 131 | {token.SEMICOLON, ";"}, 132 | {token.RBRACE, "}"}, 133 | {token.ELSE, "else"}, 134 | {token.LBRACE, "{"}, 135 | {token.RETURN, "return"}, 136 | {token.FALSE, "false"}, 137 | {token.SEMICOLON, ";"}, 138 | {token.RBRACE, "}"}, 139 | {token.INT, "10"}, 140 | {token.EQ, "=="}, 141 | {token.INT, "10"}, 142 | {token.SEMICOLON, ";"}, 143 | {token.INT, "10"}, 144 | {token.NEQ, "!="}, 145 | {token.INT, "9"}, 146 | {token.SEMICOLON, ";"}, 147 | {token.INT, "5"}, 148 | {token.LE, "<="}, 149 | {token.INT, "5"}, 150 | {token.SEMICOLON, ";"}, 151 | {token.INT, "6"}, 152 | {token.GE, ">="}, 153 | {token.INT, "6"}, 154 | {token.SEMICOLON, ";"}, 155 | {token.STRING, "foobar"}, 156 | {token.STRING, "foo bar"}, 157 | {token.LBRACKET, "["}, 158 | {token.INT, "1"}, 159 | {token.COMMA, ","}, 160 | {token.INT, "2"}, 161 | {token.RBRACKET, "]"}, 162 | {token.SEMICOLON, ";"}, 163 | {token.LBRACE, "{"}, 164 | {token.STRING, "foo"}, 165 | {token.COLON, ":"}, 166 | {token.STRING, "bar"}, 167 | {token.RBRACE, "}"}, 168 | {token.EOF, ""}, 169 | } 170 | l := New(input) 171 | 172 | for i, tt := range tests { 173 | tok := l.NextToken() 174 | if tok.Type != tt.expectedType { 175 | t.Fatalf("tests[%d] - wrong token type : expected %q, got %q", i, tt.expectedType, tok.Type) 176 | } 177 | 178 | if tok.Literal != tt.expectedLiteral { 179 | t.Fatalf("tests[%d] - wrong token literal : expected %q, got %q", i, tt.expectedLiteral, tok.Literal) 180 | 181 | } 182 | } 183 | }) 184 | } 185 | -------------------------------------------------------------------------------- /object/array.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "bytes" 5 | "strings" 6 | ) 7 | 8 | // Array wraps native arrays 9 | type Array struct { 10 | Elements []Object 11 | } 12 | 13 | // Type implements the object interface 14 | func (a *Array) Type() Type { 15 | return ARRAY 16 | } 17 | 18 | // Inspect implements the object interface 19 | func (a *Array) Inspect() string { 20 | 21 | var out bytes.Buffer 22 | 23 | elements := []string{} 24 | 25 | for _, e := range a.Elements { 26 | elements = append(elements, e.Inspect()) 27 | } 28 | 29 | out.WriteString("[") 30 | out.WriteString(strings.Join(elements, ", ")) 31 | out.WriteString("]") 32 | 33 | return out.String() 34 | 35 | } 36 | -------------------------------------------------------------------------------- /object/boolean.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import "fmt" 4 | 5 | // Boolean represents a boolean value 6 | type Boolean struct { 7 | Value bool 8 | } 9 | 10 | // Inspect implements the object interface 11 | func (b *Boolean) Inspect() string { 12 | return fmt.Sprintf("%t", b.Value) 13 | } 14 | 15 | // Type returns the integer type enum. 16 | func (b *Boolean) Type() Type { 17 | return BOOLEAN 18 | } 19 | -------------------------------------------------------------------------------- /object/builtinfn.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import "fmt" 4 | 5 | // Builtins indexes built-in functions 6 | var Builtins = []struct { 7 | Name string 8 | Fn *BuiltIn 9 | }{ 10 | { 11 | Name: "len", 12 | Fn: &BuiltIn{ 13 | func(args ...Object) Object { 14 | if len(args) != 1 { 15 | return newError("wrong number of arguments, expected %d got %d", 1, len(args)) 16 | } 17 | switch arg := args[0].(type) { 18 | case *String: 19 | return &Integer{Value: int64(len(arg.Value))} 20 | case *Array: 21 | return &Integer{Value: int64(len(arg.Elements))} 22 | default: 23 | return newError("argument to `len` not supported, got %s", args[0].Type()) 24 | } 25 | 26 | }, 27 | }, 28 | }, { 29 | Name: "head", 30 | 31 | Fn: &BuiltIn{func(args ...Object) Object { 32 | if len(args) != 1 { 33 | return newError("wrong number of arguments, expected %d got %d", 1, len(args)) 34 | } 35 | if args[0].Type() != ARRAY { 36 | return newError("argument to `head` not supported got %s", args[0].Type()) 37 | } 38 | arr := args[0].(*Array) 39 | if len(arr.Elements) > 0 { 40 | return arr.Elements[0] 41 | } 42 | return nil 43 | }, 44 | }, 45 | }, { 46 | Name: "tail", 47 | Fn: &BuiltIn{ 48 | Fn: func(args ...Object) Object { 49 | if len(args) != 1 { 50 | return newError("wrong number of arguments, expected %d got %d", 1, len(args)) 51 | } 52 | if args[0].Type() != ARRAY { 53 | return newError("argument to `push` not supported got %s", args[0].Type()) 54 | } 55 | arr := args[0].(*Array) 56 | length := len(arr.Elements) 57 | if length > 0 { 58 | newElems := make([]Object, length-1, length-1) 59 | copy(newElems, arr.Elements[1:length]) 60 | return &Array{Elements: newElems} 61 | } 62 | return nil 63 | }, 64 | }, 65 | }, { 66 | Name: "last", 67 | Fn: &BuiltIn{ 68 | Fn: func(args ...Object) Object { 69 | if len(args) != 1 { 70 | return newError("wrong number of arguments, expected %d got %d", 1, len(args)) 71 | } 72 | if args[0].Type() != ARRAY { 73 | return newError("argument to `push` not supported got %s", args[0].Type()) 74 | } 75 | arr := args[0].(*Array) 76 | length := len(arr.Elements) 77 | if length > 0 { 78 | return arr.Elements[length-1] 79 | } 80 | return nil 81 | }, 82 | }, 83 | }, { 84 | Name: "append", 85 | Fn: &BuiltIn{ 86 | Fn: func(args ...Object) Object { 87 | if len(args) != 2 { 88 | return newError("wrong number of arguments, expected %d got %d", 2, len(args)) 89 | } 90 | if args[0].Type() != ARRAY { 91 | return newError("argument to `append` must be ARRAY got %s", args[0].Type()) 92 | } 93 | arr := args[0].(*Array) 94 | length := len(arr.Elements) 95 | newElems := make([]Object, length+1, length+1) 96 | copy(newElems, arr.Elements) 97 | newElems[length] = args[1] 98 | return &Array{Elements: newElems} 99 | 100 | }, 101 | }, 102 | }, { 103 | Name: "concat", 104 | Fn: &BuiltIn{ 105 | Fn: func(args ...Object) Object { 106 | if len(args) != 2 { 107 | return newError("wrong number of arguments, expected %d got %d", 2, len(args)) 108 | } 109 | if args[0].Type() != ARRAY || args[1].Type() != ARRAY { 110 | return newError("argument to `concat` must be ARRAY got %s", args[0].Type()) 111 | } 112 | arr1 := args[0].(*Array) 113 | arr2 := args[1].(*Array) 114 | length := len(arr1.Elements) + len(arr2.Elements) 115 | newElemens := make([]Object, 0, length) 116 | newElemens = append(newElemens, arr1.Elements...) 117 | newElemens = append(newElemens, arr2.Elements...) 118 | 119 | return &Array{Elements: newElemens} 120 | }, 121 | }, 122 | }, 123 | } 124 | 125 | // newError creates a new error message 126 | func newError(format string, a ...interface{}) *Error { 127 | return &Error{Message: fmt.Sprintf(format, a...)} 128 | } 129 | 130 | // GetBuiltInByName fetches a built-in func by its name 131 | func GetBuiltInByName(name string) *BuiltIn { 132 | for _, def := range Builtins { 133 | if def.Name == name { 134 | return def.Fn 135 | } 136 | } 137 | return nil 138 | } 139 | -------------------------------------------------------------------------------- /object/closure.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import "fmt" 4 | 5 | // Closure represents compiled closures 6 | type Closure struct { 7 | Fn *CompiledFunction 8 | FreeVariables []Object 9 | } 10 | 11 | // Type implements the object interface 12 | func (c *Closure) Type() Type { 13 | return CLOSURE 14 | } 15 | 16 | // Inspect implements the object interface 17 | func (c *Closure) Inspect() string { 18 | return fmt.Sprintf("Closure[%p]", c) 19 | } 20 | -------------------------------------------------------------------------------- /object/env.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | // Environment represents binding maps for let statements. 4 | type Environment struct { 5 | store map[string]Object 6 | outer *Environment 7 | } 8 | 9 | // NewEnv creates a new environment instance 10 | func NewEnv() *Environment { 11 | return &Environment{ 12 | store: make(map[string]Object), 13 | outer: nil, 14 | } 15 | } 16 | 17 | // NewEnclosedEnvironment creates an environment that extends an outer one. 18 | func NewEnclosedEnvironment(outerEnv *Environment) *Environment { 19 | return &Environment{ 20 | store: make(map[string]Object), 21 | outer: outerEnv, 22 | } 23 | } 24 | 25 | // Get an object by it's binding identifier 26 | func (env *Environment) Get(name string) (Object, bool) { 27 | obj, ok := env.store[name] 28 | if !ok && env.outer != nil { 29 | obj, ok = env.outer.Get(name) 30 | } 31 | return obj, ok 32 | } 33 | 34 | // Set an object by it's binding identifier 35 | func (env *Environment) Set(name string, obj Object) Object { 36 | env.store[name] = obj 37 | return obj 38 | } 39 | -------------------------------------------------------------------------------- /object/error.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | // Error represents an error (a string with the error message). 4 | type Error struct { 5 | Message string 6 | } 7 | 8 | // Type implements the Object interface. 9 | func (e *Error) Type() Type { 10 | return ERROR 11 | } 12 | 13 | // Inspect implements the Object interface. 14 | func (e *Error) Inspect() string { 15 | return "ERROR :" + e.Message 16 | } 17 | -------------------------------------------------------------------------------- /object/function.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "strings" 7 | 8 | "github.com/actuallyachraf/monkey-giggle/ast" 9 | "github.com/actuallyachraf/monkey-giggle/code" 10 | ) 11 | 12 | // Function represents a function literal each function gets a local environment. 13 | // to enforce scope rules and prevent variable shadowing. 14 | type Function struct { 15 | Parameters []*ast.Identifier 16 | Body *ast.BlockStatement 17 | Env *Environment 18 | } 19 | 20 | // Type implements the Object interface. 21 | func (f *Function) Type() Type { 22 | return FUNCTION 23 | } 24 | 25 | // Inspect implements the Object interface. 26 | func (f *Function) Inspect() string { 27 | var out bytes.Buffer 28 | 29 | params := []string{} 30 | for _, p := range params { 31 | params = append(params, p) 32 | } 33 | 34 | out.WriteString("fn") 35 | out.WriteString("(") 36 | out.WriteString(strings.Join(params, ", ")) 37 | out.WriteString(") {\n") 38 | out.WriteString(f.Body.String()) 39 | out.WriteString("\n}") 40 | 41 | return out.String() 42 | } 43 | 44 | // BuiltInFunc defines functions that are part of the language and operate 45 | // on native objects. 46 | type BuiltInFunc func(args ...Object) Object 47 | 48 | // BuiltIn describes builtin function objects 49 | type BuiltIn struct { 50 | Fn BuiltInFunc 51 | } 52 | 53 | // Type implements the object interface 54 | func (b *BuiltIn) Type() Type { 55 | return BUILTIN 56 | } 57 | 58 | // Inspect implements the object interface 59 | func (b *BuiltIn) Inspect() string { 60 | return "built-in function" 61 | } 62 | 63 | // CompiledFunction unlike function holds compiled bytecode instructions 64 | type CompiledFunction struct { 65 | Instructions code.Instructions 66 | NumLocals int 67 | NumParams int 68 | } 69 | 70 | // Type implements the object interface 71 | func (c *CompiledFunction) Type() Type { 72 | return COMPILEDFUNC 73 | } 74 | 75 | // Inspect implements the object interface 76 | func (c *CompiledFunction) Inspect() string { 77 | return fmt.Sprintf("CompiledFunc[%p]", c) 78 | } 79 | -------------------------------------------------------------------------------- /object/hashmap.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "hash/fnv" 7 | "strings" 8 | ) 9 | 10 | // HashKey represents keys for hashmaps 11 | type HashKey struct { 12 | Type Type 13 | Value uint64 14 | } 15 | 16 | // Each type we implement Integers,Boolean will implement a hashkey method 17 | 18 | // HashKey for boolean types is either 1 for true or 0 for false 19 | func (b *Boolean) HashKey() HashKey { 20 | var val uint64 21 | 22 | if b.Value { 23 | val = 1 24 | } else { 25 | val = 0 26 | } 27 | 28 | return HashKey{Type: b.Type(), Value: val} 29 | } 30 | 31 | // HashKey for Integers uses the integer value as key 32 | func (i *Integer) HashKey() HashKey { 33 | return HashKey{Type: i.Type(), Value: uint64(i.Value)} 34 | } 35 | 36 | // HashKey for strings is the fnv hash of the string literal 37 | func (s *String) HashKey() HashKey { 38 | h := fnv.New64a() 39 | h.Write([]byte(s.Value)) 40 | 41 | return HashKey{Type: s.Type(), Value: h.Sum64()} 42 | } 43 | 44 | // HashPair represents a key value entry in the hashmap 45 | type HashPair struct { 46 | Key Object 47 | Value Object 48 | } 49 | 50 | // HashMap represents the actual hashmap 51 | type HashMap struct { 52 | Pairs map[HashKey]HashPair 53 | } 54 | 55 | // Type implements the object interface 56 | func (hm *HashMap) Type() Type { 57 | return HASH 58 | } 59 | 60 | // Inspect implements the object interface 61 | func (hm *HashMap) Inspect() string { 62 | 63 | var out bytes.Buffer 64 | 65 | pairs := []string{} 66 | 67 | for _, pair := range hm.Pairs { 68 | pairs = append(pairs, fmt.Sprintf("%s: %s", pair.Key.Inspect(), pair.Value.Inspect())) 69 | } 70 | 71 | out.WriteString("{") 72 | out.WriteString(strings.Join(pairs, ", ")) 73 | out.WriteString("}") 74 | 75 | return out.String() 76 | } 77 | -------------------------------------------------------------------------------- /object/integer.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | import "fmt" 4 | 5 | // Integer represents the language integer type. 6 | type Integer struct { 7 | Value int64 8 | } 9 | 10 | // Inspect implements the object interface 11 | func (i *Integer) Inspect() string { 12 | return fmt.Sprintf("%d", i.Value) 13 | } 14 | 15 | // Type returns the integer type enum. 16 | func (i *Integer) Type() Type { 17 | return INTEGER 18 | } 19 | -------------------------------------------------------------------------------- /object/null.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | // Null represents the null type (absence of value) a better idea would 4 | // be to use Option instead I think. 5 | type Null struct{} 6 | 7 | // Inspect implements the object interface 8 | func (n *Null) Inspect() string { 9 | return "null" 10 | } 11 | 12 | // Type returns the integer type enum. 13 | func (n *Null) Type() Type { 14 | return NULL 15 | } 16 | -------------------------------------------------------------------------------- /object/object.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | const ( 4 | // INTEGER are wrapped 64 integer values. 5 | INTEGER = "INTEGER" 6 | // BOOLEAN represents a wrapped bool value. 7 | BOOLEAN = "BOOLEAN" 8 | // STRING represents a wrapped Go string 9 | STRING = "STRING" 10 | // NULL represents a null object which is just an empty struct 11 | NULL = "NULL" 12 | // RETURN represents a value that's to be "returned" 13 | RETURN = "RETURN" 14 | // FUNCTION represents a function. 15 | FUNCTION = "FUNCTION" 16 | // BUILTIN represents built in functions 17 | BUILTIN = "BUILTIN" 18 | // ARRAY represents array data type 19 | ARRAY = "ARRAY" 20 | // HASH represents a hashmap data type 21 | HASH = "HASH" 22 | // ERROR represents errors that happen during runtime 23 | ERROR = "ERROR" 24 | // COMPILEDFUNC represents a compiled function 25 | COMPILEDFUNC = "COMPILEDFUNC" 26 | // CLOSURE represents a closure (a function that returns a new function) 27 | CLOSURE = "CLOSURE" 28 | ) 29 | 30 | // Type represents the type of a given object. 31 | type Type string 32 | 33 | // Object is an interface that the host (Go) wrapped types for the language 34 | // implement. 35 | type Object interface { 36 | Type() Type 37 | Inspect() string 38 | } 39 | 40 | // Hashable tells us whether an object can be used as a key in a hashmap 41 | type Hashable interface { 42 | HashKey() HashKey 43 | } 44 | -------------------------------------------------------------------------------- /object/return.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | // ReturnValue wraps values that are return expressions. 4 | type ReturnValue struct { 5 | Value Object 6 | } 7 | 8 | // Type implements the object interface 9 | func (rv *ReturnValue) Type() Type { 10 | return RETURN 11 | } 12 | 13 | // Inspect implements the ojbect interface 14 | func (rv *ReturnValue) Inspect() string { 15 | return rv.Value.Inspect() 16 | } 17 | -------------------------------------------------------------------------------- /object/string.go: -------------------------------------------------------------------------------- 1 | package object 2 | 3 | // String represents the language String type. 4 | type String struct { 5 | Value string 6 | } 7 | 8 | // Inspect implements the object interface 9 | func (i *String) Inspect() string { 10 | return i.Value 11 | } 12 | 13 | // Type returns the String type enum. 14 | func (i *String) Type() Type { 15 | return STRING 16 | } 17 | -------------------------------------------------------------------------------- /parser/parser.go: -------------------------------------------------------------------------------- 1 | // Package parser implement a recursive descent parser. 2 | package parser 3 | 4 | import ( 5 | "fmt" 6 | "strconv" 7 | 8 | "github.com/actuallyachraf/monkey-giggle/ast" 9 | "github.com/actuallyachraf/monkey-giggle/lexer" 10 | "github.com/actuallyachraf/monkey-giggle/token" 11 | ) 12 | 13 | const ( 14 | // Enumerate operations by their precedence order. 15 | _ int = iota 16 | // LOWEST marks lowest precedence order 17 | LOWEST 18 | // EQUALS marks equality 19 | EQUALS 20 | // LESSGREATER marks lesser or greater than operations 21 | LESSGREATER 22 | // SUM marks sum operation 23 | SUM 24 | // PRODUCT marks product operation 25 | PRODUCT 26 | // PREFIX marks prefix operators 27 | PREFIX 28 | // CALL marks function calls 29 | CALL 30 | // INDEX marks index operations 31 | INDEX 32 | ) 33 | 34 | var precedenceTable = map[token.Type]int{ 35 | token.EQ: EQUALS, 36 | token.NEQ: EQUALS, 37 | token.LT: LESSGREATER, 38 | token.GT: LESSGREATER, 39 | token.GE: LESSGREATER, 40 | token.LE: LESSGREATER, 41 | token.ADD: SUM, 42 | token.SUB: SUM, 43 | token.MUL: PRODUCT, 44 | token.DIV: PRODUCT, 45 | token.MOD: PRODUCT, 46 | token.LPAREN: CALL, 47 | token.LBRACKET: INDEX, 48 | } 49 | 50 | type ( 51 | prefixParseFn func() ast.Expression 52 | infixParseFn func(ast.Expression) ast.Expression 53 | ) 54 | 55 | // Parser implements the main parsing structure. 56 | type Parser struct { 57 | l *lexer.Lexer 58 | currToken token.Token 59 | peekToken token.Token 60 | 61 | errors []string 62 | 63 | prefixParseFuncs map[token.Type]prefixParseFn 64 | infixParseFuncs map[token.Type]infixParseFn 65 | } 66 | 67 | // New creates a new parser instance 68 | func New(l *lexer.Lexer) *Parser { 69 | p := &Parser{ 70 | l: l, 71 | errors: []string{}, 72 | prefixParseFuncs: make(map[token.Type]prefixParseFn), 73 | infixParseFuncs: make(map[token.Type]infixParseFn), 74 | } 75 | 76 | p.registerPrefix(token.IDENT, p.parseIdentifier) 77 | p.registerPrefix(token.INT, p.parseIntegerLiteral) 78 | p.registerPrefix(token.STRING, p.parseStringLiteral) 79 | p.registerPrefix(token.LPAREN, p.parseGroupedExpression) 80 | p.registerPrefix(token.IF, p.parseIfExpression) 81 | p.registerPrefix(token.TRUE, p.parseBooleanLiteral) 82 | p.registerPrefix(token.FALSE, p.parseBooleanLiteral) 83 | p.registerPrefix(token.BANG, p.parsePrefixExpression) 84 | p.registerPrefix(token.SUB, p.parsePrefixExpression) 85 | p.registerPrefix(token.LBRACKET, p.parseArrayLiteral) 86 | p.registerPrefix(token.FUNCTION, p.parseFunctionLiteral) 87 | p.registerPrefix(token.LBRACE, p.parseHashmapLiteral) 88 | 89 | p.registerInfix(token.LPAREN, p.parseCallExpression) 90 | p.registerInfix(token.LBRACKET, p.parseIndexExpression) 91 | 92 | p.registerInfix(token.ADD, p.parseInfixExpression) 93 | p.registerInfix(token.SUB, p.parseInfixExpression) 94 | p.registerInfix(token.MUL, p.parseInfixExpression) 95 | p.registerInfix(token.DIV, p.parseInfixExpression) 96 | p.registerInfix(token.MOD, p.parseInfixExpression) 97 | p.registerInfix(token.EQ, p.parseInfixExpression) 98 | p.registerInfix(token.NEQ, p.parseInfixExpression) 99 | p.registerInfix(token.GT, p.parseInfixExpression) 100 | p.registerInfix(token.LT, p.parseInfixExpression) 101 | p.registerInfix(token.GE, p.parseInfixExpression) 102 | p.registerInfix(token.LE, p.parseInfixExpression) 103 | 104 | p.nextToken() 105 | p.nextToken() 106 | return p 107 | } 108 | 109 | // registerPrefix parsing function for a token type 110 | func (p *Parser) registerPrefix(t token.Type, fn prefixParseFn) { 111 | p.prefixParseFuncs[t] = fn 112 | } 113 | 114 | // registerInfix parsing function for a token type 115 | func (p *Parser) registerInfix(t token.Type, fn infixParseFn) { 116 | p.infixParseFuncs[t] = fn 117 | } 118 | 119 | // Errors returns the list of errors that occured during parsing 120 | func (p *Parser) Errors() []string { 121 | return p.errors 122 | } 123 | 124 | func (p *Parser) peekError(t token.Type) { 125 | msg := fmt.Sprintf("expected next token to be %s, got %s instead", t, p.peekToken.Type) 126 | p.errors = append(p.errors, msg) 127 | } 128 | 129 | // nextToken reads the next token and updates the fields. 130 | func (p *Parser) nextToken() { 131 | p.currToken = p.peekToken 132 | p.peekToken = p.l.NextToken() 133 | } 134 | 135 | // parseIdentifier expression. 136 | func (p *Parser) parseIdentifier() ast.Expression { 137 | return &ast.Identifier{Token: p.currToken, Value: p.currToken.Literal} 138 | } 139 | 140 | // Parse is the main function call given a lexer instance it will parse 141 | // and construct an abstract syntax tree for the given input. 142 | func (p *Parser) Parse() *ast.Program { 143 | 144 | program := &ast.Program{Statements: []ast.Statement{}} 145 | 146 | for p.currToken.Type != token.EOF { 147 | stmt := p.parseStatement() 148 | if stmt != nil { 149 | program.Statements = append(program.Statements, stmt) 150 | } 151 | p.nextToken() 152 | } 153 | 154 | return program 155 | } 156 | 157 | // parseStatement parse a statement given it's defining token. 158 | func (p *Parser) parseStatement() ast.Statement { 159 | 160 | switch p.currToken.Type { 161 | case token.LET: 162 | return p.parseLetStatement() 163 | case token.RETURN: 164 | return p.parseReturnStatement() 165 | default: 166 | return p.parseExpressionStatement() 167 | } 168 | } 169 | 170 | // parseIntegerLiteral parse a literal int from string to int64 171 | func (p *Parser) parseIntegerLiteral() ast.Expression { 172 | 173 | value, err := strconv.ParseInt(string(p.currToken.Literal), 0, 64) 174 | if err != nil { 175 | msg := fmt.Sprintf("failed to parse %q as int64", p.currToken.Literal) 176 | p.errors = append(p.errors, msg) 177 | return nil 178 | } 179 | 180 | return &ast.IntegerLiteral{ 181 | Token: p.currToken, 182 | Value: value, 183 | } 184 | } 185 | 186 | // parseStringLiteral parse a literal string 187 | func (p *Parser) parseStringLiteral() ast.Expression { 188 | return &ast.StringLiteral{Token: p.currToken, Value: string(p.currToken.Literal)} 189 | } 190 | 191 | // parsePrefixExpression constructs an AST expression node for prefix expression. 192 | func (p *Parser) parsePrefixExpression() ast.Expression { 193 | 194 | expression := &ast.PrefixExpression{ 195 | Token: p.currToken, 196 | Operator: p.currToken.Literal, 197 | } 198 | p.nextToken() 199 | 200 | expression.Right = p.parseExpression(PREFIX) 201 | 202 | return expression 203 | } 204 | 205 | // parseInfixExpression constructs an AST expression node for infix expressions. 206 | func (p *Parser) parseInfixExpression(left ast.Expression) ast.Expression { 207 | 208 | expression := &ast.InfixExpression{ 209 | Token: p.currToken, 210 | Operator: p.currToken.Literal, 211 | Left: left, 212 | } 213 | precedence := p.currPrecendence() 214 | p.nextToken() 215 | expression.Right = p.parseExpression(precedence) 216 | 217 | return expression 218 | } 219 | 220 | // noPrefixParseFnError writes an error message when a prefix parsing func 221 | // isn't found for a given token type. 222 | func (p *Parser) noPrefixParseFnError(t token.Type) { 223 | msg := fmt.Sprintf("no prefix parse func found for token type %s", t) 224 | p.errors = append(p.errors, msg) 225 | } 226 | 227 | // peekPrecedence returns the precedence level of the peek token 228 | func (p *Parser) peekPrecedence() int { 229 | if p, ok := precedenceTable[p.peekToken.Type]; ok { 230 | return p 231 | } 232 | 233 | return LOWEST 234 | } 235 | 236 | // currPrecendence returns the precedence level of the current token 237 | func (p *Parser) currPrecendence() int { 238 | if p, ok := precedenceTable[p.currToken.Type]; ok { 239 | return p 240 | } 241 | 242 | return LOWEST 243 | } 244 | 245 | // noInfixParseFnError writes an error message when an infix parsing func 246 | // isn't found for a given token type. 247 | func (p *Parser) noInfixParseFnError(t token.Type) { 248 | msg := fmt.Sprintf("no infix parse func found for token type %s", t) 249 | p.errors = append(p.errors, msg) 250 | } 251 | 252 | // parseExpression parses an expression given a precedence enum 253 | func (p *Parser) parseExpression(precedence int) ast.Expression { 254 | 255 | prefix := p.prefixParseFuncs[p.currToken.Type] 256 | 257 | if prefix == nil { 258 | p.noPrefixParseFnError(p.currToken.Type) 259 | return nil 260 | } 261 | 262 | leftExp := prefix() 263 | 264 | for !p.peekTokenIs(token.SEMICOLON) && precedence < p.peekPrecedence() { 265 | infix := p.infixParseFuncs[p.peekToken.Type] 266 | if infix == nil { 267 | return leftExp 268 | } 269 | 270 | p.nextToken() 271 | 272 | leftExp = infix(leftExp) 273 | } 274 | 275 | return leftExp 276 | } 277 | 278 | // parseLetStatement parses and construct an ast node for the let statement. 279 | func (p *Parser) parseLetStatement() *ast.LetStatement { 280 | 281 | stmt := &ast.LetStatement{ 282 | Token: p.currToken, 283 | } 284 | 285 | if !p.expectPeek(token.IDENT) { 286 | return nil 287 | } 288 | 289 | stmt.Name = &ast.Identifier{Token: p.currToken, Value: p.currToken.Literal} 290 | 291 | if !p.expectPeek(token.ASSIGN) { 292 | return nil 293 | } 294 | 295 | p.nextToken() 296 | 297 | stmt.Value = p.parseExpression(LOWEST) 298 | 299 | if p.peekTokenIs(token.SEMICOLON) { 300 | p.nextToken() 301 | } 302 | 303 | return stmt 304 | } 305 | 306 | // parseReturnStatement parses and construct an ast node for return statements. 307 | func (p *Parser) parseReturnStatement() *ast.ReturnStatement { 308 | 309 | stmt := &ast.ReturnStatement{Token: p.currToken} 310 | p.nextToken() 311 | 312 | stmt.ReturnValue = p.parseExpression(LOWEST) 313 | 314 | for !p.currTokenIs(token.SEMICOLON) { 315 | p.nextToken() 316 | } 317 | 318 | return stmt 319 | } 320 | 321 | // parseExpressionStatement parses and construct an ast node for expressions. 322 | func (p *Parser) parseExpressionStatement() *ast.ExpressionStatement { 323 | stmt := &ast.ExpressionStatement{Token: p.currToken} 324 | stmt.Expression = p.parseExpression(LOWEST) 325 | 326 | if p.peekTokenIs(token.SEMICOLON) { 327 | p.nextToken() 328 | } 329 | 330 | return stmt 331 | } 332 | 333 | // parseBooleanLiteral parses and construct an ast node for boolean literals. 334 | func (p *Parser) parseBooleanLiteral() ast.Expression { 335 | return &ast.BooleanLiteral{ 336 | Token: p.currToken, 337 | Value: p.currTokenIs(token.TRUE), 338 | } 339 | } 340 | 341 | // parseGroupedExpression parses and construct an ast node for expressions 342 | // of the type ((a*b)+c). 343 | func (p *Parser) parseGroupedExpression() ast.Expression { 344 | p.nextToken() 345 | 346 | exp := p.parseExpression(LOWEST) 347 | 348 | if !p.expectPeek(token.RPAREN) { 349 | return nil 350 | } 351 | 352 | return exp 353 | } 354 | 355 | // parseIfExpression parses and construct an ast branch for conditionals 356 | func (p *Parser) parseIfExpression() ast.Expression { 357 | 358 | exp := &ast.IfExpression{Token: p.currToken} 359 | 360 | if !p.expectPeek(token.LPAREN) { 361 | return nil 362 | } 363 | 364 | p.nextToken() 365 | exp.Condition = p.parseExpression(LOWEST) 366 | 367 | if !p.expectPeek(token.RPAREN) { 368 | return nil 369 | } 370 | 371 | if !p.expectPeek(token.LBRACE) { 372 | return nil 373 | } 374 | 375 | exp.Consequence = p.parseBlockStatement() 376 | 377 | if p.peekTokenIs(token.ELSE) { 378 | p.nextToken() 379 | 380 | if !p.expectPeek(token.LBRACE) { 381 | return nil 382 | } 383 | 384 | exp.Alternative = p.parseBlockStatement() 385 | } 386 | return exp 387 | } 388 | 389 | // parseBlockStatement parses and construct ast nodes for the block statements 390 | // that follow a conditional branch. 391 | func (p *Parser) parseBlockStatement() *ast.BlockStatement { 392 | 393 | block := &ast.BlockStatement{Token: p.currToken} 394 | block.Statements = []ast.Statement{} 395 | 396 | p.nextToken() 397 | 398 | for !p.currTokenIs(token.RBRACE) && !p.currTokenIs(token.EOF) { 399 | stmt := p.parseStatement() 400 | 401 | if stmt != nil { 402 | block.Statements = append(block.Statements, stmt) 403 | } 404 | 405 | p.nextToken() 406 | } 407 | 408 | return block 409 | } 410 | 411 | // parseFunctionParameters is used to construct a list of identifiers for 412 | // function literal parameters 413 | func (p *Parser) parseFunctionParameters() []*ast.Identifier { 414 | 415 | identifiers := []*ast.Identifier{} 416 | 417 | // if the next token is the right parenthesis return (they are no params) 418 | if p.peekTokenIs(token.RPAREN) { 419 | p.nextToken() 420 | return identifiers 421 | } 422 | 423 | p.nextToken() 424 | 425 | ident := &ast.Identifier{Token: p.currToken, Value: p.currToken.Literal} 426 | identifiers = append(identifiers, ident) 427 | 428 | for p.peekTokenIs(token.COMMA) { 429 | p.nextToken() 430 | p.nextToken() 431 | 432 | ident := &ast.Identifier{Token: p.currToken, Value: p.currToken.Literal} 433 | identifiers = append(identifiers, ident) 434 | } 435 | 436 | if !p.expectPeek(token.RPAREN) { 437 | return nil 438 | } 439 | 440 | return identifiers 441 | } 442 | 443 | // parseFunctionLiteral constructs an ast branch for function literal expressions. 444 | func (p *Parser) parseFunctionLiteral() ast.Expression { 445 | 446 | lit := &ast.FunctionLiteral{Token: p.currToken} 447 | 448 | if !p.expectPeek(token.LPAREN) { 449 | return nil 450 | } 451 | 452 | lit.Parameters = p.parseFunctionParameters() 453 | 454 | if !p.expectPeek(token.LBRACE) { 455 | return nil 456 | } 457 | 458 | lit.Body = p.parseBlockStatement() 459 | 460 | return lit 461 | } 462 | 463 | // parseCallArguments is used to parse function arguments which are expressions. 464 | func (p *Parser) parseCallArguments() []ast.Expression { 465 | 466 | args := []ast.Expression{} 467 | 468 | if p.peekTokenIs(token.RPAREN) { 469 | p.nextToken() 470 | return args 471 | } 472 | 473 | p.nextToken() 474 | 475 | args = append(args, p.parseExpression(LOWEST)) 476 | 477 | for p.peekTokenIs(token.COMMA) { 478 | p.nextToken() 479 | p.nextToken() 480 | 481 | args = append(args, p.parseExpression(LOWEST)) 482 | } 483 | 484 | if !p.expectPeek(token.RPAREN) { 485 | return nil 486 | } 487 | 488 | return args 489 | } 490 | 491 | // parseCallExpression parses function calls. 492 | func (p *Parser) parseCallExpression(function ast.Expression) ast.Expression { 493 | 494 | return &ast.CallExpression{ 495 | Token: p.currToken, 496 | Function: function, 497 | Arguments: p.parseExpressionList(token.RPAREN), 498 | } 499 | } 500 | 501 | // parseExpressionList parses a list of expressions 502 | func (p *Parser) parseExpressionList(end token.Type) []ast.Expression { 503 | 504 | list := []ast.Expression{} 505 | // check if list is empty 506 | if p.peekTokenIs(end) { 507 | p.nextToken() 508 | return list 509 | } 510 | 511 | p.nextToken() 512 | list = append(list, p.parseExpression(LOWEST)) 513 | 514 | for p.peekTokenIs(token.COMMA) { 515 | p.nextToken() 516 | p.nextToken() 517 | 518 | list = append(list, p.parseExpression(LOWEST)) 519 | } 520 | 521 | if !p.expectPeek(end) { 522 | return nil 523 | } 524 | 525 | return list 526 | } 527 | 528 | // parseArrayLiteral parses an array 529 | func (p *Parser) parseArrayLiteral() ast.Expression { 530 | 531 | return &ast.ArrayLiteral{ 532 | Token: p.currToken, 533 | Elements: p.parseExpressionList(token.RBRACKET), 534 | } 535 | } 536 | 537 | // parseIndexExpression parses an expression within the index op 538 | func (p *Parser) parseIndexExpression(left ast.Expression) ast.Expression { 539 | exp := &ast.IndexExpression{Token: p.currToken, Left: left} 540 | 541 | p.nextToken() 542 | 543 | exp.Index = p.parseExpression(LOWEST) 544 | 545 | if !p.expectPeek(token.RBRACKET) { 546 | return nil 547 | } 548 | 549 | return exp 550 | } 551 | 552 | // parseHashmapLiteral parses a hashmap 553 | func (p *Parser) parseHashmapLiteral() ast.Expression { 554 | hash := &ast.HashmapLiteral{Token: p.currToken} 555 | hash.Pairs = make(map[ast.Expression]ast.Expression) 556 | 557 | for !p.peekTokenIs(token.RBRACE) { 558 | p.nextToken() 559 | key := p.parseExpression(LOWEST) 560 | 561 | if !p.expectPeek(token.COLON) { 562 | return nil 563 | } 564 | p.nextToken() 565 | value := p.parseExpression(LOWEST) 566 | 567 | hash.Pairs[key] = value 568 | 569 | if !p.peekTokenIs(token.RBRACE) && !p.expectPeek(token.COMMA) { 570 | return nil 571 | } 572 | } 573 | if !p.expectPeek(token.RBRACE) { 574 | return nil 575 | } 576 | 577 | return hash 578 | } 579 | 580 | func (p *Parser) currTokenIs(t token.Type) bool { 581 | return p.currToken.Type == t 582 | } 583 | 584 | func (p *Parser) peekTokenIs(t token.Type) bool { 585 | return p.peekToken.Type == t 586 | } 587 | 588 | func (p *Parser) expectPeek(t token.Type) bool { 589 | 590 | if p.peekTokenIs(t) { 591 | p.nextToken() 592 | return true 593 | } 594 | p.peekError(t) 595 | return false 596 | } 597 | -------------------------------------------------------------------------------- /parser/parser_test.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/actuallyachraf/monkey-giggle/ast" 9 | "github.com/actuallyachraf/monkey-giggle/lexer" 10 | "github.com/actuallyachraf/monkey-giggle/token" 11 | ) 12 | 13 | func TestParser(t *testing.T) { 14 | 15 | t.Run("TestParseLetStatement", func(t *testing.T) { 16 | input := ` 17 | 18 | let x = 5; 19 | let y = 10; 20 | let foobar = 9876543210; 21 | ` 22 | badInput := ` 23 | 24 | let x 5; 25 | let = 10; 26 | let 9876543210; 27 | ` 28 | 29 | l := lexer.New(input) 30 | p := New(l) 31 | 32 | program := p.Parse() 33 | 34 | checkParserError(t, p) 35 | 36 | if program == nil { 37 | t.Fatal("Parse() returned nil") 38 | } 39 | if len(program.Statements) != 3 { 40 | t.Fatal("Parse() error : expected 3 statments got ", len(program.Statements), " instead") 41 | } 42 | tests := []struct { 43 | expectedIdentifier token.Literal 44 | }{ 45 | {"x"}, 46 | {"y"}, 47 | {"foobar"}, 48 | } 49 | 50 | for i, tt := range tests { 51 | stmt := program.Statements[i] 52 | if !testLetStatement(t, stmt, tt.expectedIdentifier) { 53 | return 54 | } 55 | } 56 | 57 | l = lexer.New(badInput) 58 | p = New(l) 59 | 60 | program = p.Parse() 61 | 62 | if checkParserError(t, p) == nil { 63 | t.Fatal("Parser should fail on bad input") 64 | } 65 | }) 66 | t.Run("TestParseLetStatementWithExpressions", func(t *testing.T) { 67 | tests := []struct { 68 | input string 69 | expectedIdentifier token.Literal 70 | expectedValue interface{} 71 | }{ 72 | {"let x = 5;", "x", 5}, 73 | {"let y = true;", "y", true}, 74 | {"let foobar = y;", "foobar", "y"}, 75 | {"let ourFunction = 5;", "ourFunction", 5}, 76 | } 77 | 78 | for _, tt := range tests { 79 | l := lexer.New(tt.input) 80 | p := New(l) 81 | program := p.Parse() 82 | checkParserError(t, p) 83 | 84 | if len(program.Statements) != 1 { 85 | t.Fatalf("program.Statements does not contain 1 statements. got=%d", 86 | len(program.Statements)) 87 | } 88 | 89 | stmt := program.Statements[0] 90 | if !testLetStatement(t, stmt, tt.expectedIdentifier) { 91 | return 92 | } 93 | 94 | val := stmt.(*ast.LetStatement).Value 95 | if !testLiteralExpression(t, val, tt.expectedValue) { 96 | return 97 | } 98 | } 99 | }) 100 | t.Run("TestParseReturnStatement", func(t *testing.T) { 101 | 102 | input := ` 103 | return 5; 104 | return 10; 105 | return 935834; 106 | ` 107 | l := lexer.New(input) 108 | p := New(l) 109 | 110 | program := p.Parse() 111 | 112 | checkParserError(t, p) 113 | 114 | if program == nil { 115 | t.Fatal("Parse() returned nil") 116 | } 117 | if len(program.Statements) != 3 { 118 | t.Fatal("Parse() error : expected 3 statments got ", len(program.Statements), " instead") 119 | } 120 | for _, stmt := range program.Statements { 121 | returnStmt, ok := stmt.(*ast.ReturnStatement) 122 | if !ok { 123 | t.Errorf("statement not *ast.ReturnStatement got %T", returnStmt) 124 | continue 125 | } 126 | if returnStmt.TokenLiteral() != "return" { 127 | t.Errorf("returnStmt.TokenLiteral not return got %s", returnStmt.TokenLiteral()) 128 | } 129 | } 130 | }) 131 | t.Run("TestParseIdentifierExpression", func(t *testing.T) { 132 | 133 | input := "foobar" 134 | 135 | l := lexer.New(input) 136 | p := New(l) 137 | program := p.Parse() 138 | checkParserError(t, p) 139 | if len(program.Statements) != 1 { 140 | t.Fatal("program has not enough statement expected 1 got ", len(program.Statements)) 141 | } 142 | 143 | stmt, ok := program.Statements[0].(*ast.ExpressionStatement) 144 | if !ok { 145 | t.Fatalf("program has wrong statement expected ExpressionStatement got %T", program.Statements[0]) 146 | } 147 | ident, ok := stmt.Expression.(*ast.Identifier) 148 | if !testIdentifier(t, ident, input) { 149 | t.Fatal("program failed to parse identifier") 150 | } 151 | }) 152 | t.Run("TestParseIntegerLiteralExpression", func(t *testing.T) { 153 | input := "5" 154 | 155 | l := lexer.New(input) 156 | p := New(l) 157 | program := p.Parse() 158 | checkParserError(t, p) 159 | if len(program.Statements) != 1 { 160 | t.Fatal("program has not enough statement expected 1 got ", len(program.Statements)) 161 | } 162 | 163 | stmt, ok := program.Statements[0].(*ast.ExpressionStatement) 164 | if !ok { 165 | t.Fatalf("program has wrong statement expected ExpressionStatement got %T", program.Statements[0]) 166 | } 167 | literal, ok := stmt.Expression.(*ast.IntegerLiteral) 168 | if !ok { 169 | t.Fatalf("program has wrong expression expected *ast.Identifier got %T", stmt.Expression) 170 | } 171 | if literal.Value != 5 { 172 | t.Errorf("program has wrong identifier value expected %d got %d", 5, literal.Value) 173 | } 174 | if literal.TokenLiteral() != "5" { 175 | t.Errorf("program has wrong token literal expected %s got %s", "5", literal.TokenLiteral()) 176 | } 177 | }) 178 | t.Run("TestParseStringLiteralExpression", func(t *testing.T) { 179 | input := `"hello world!` 180 | 181 | l := lexer.New(input) 182 | p := New(l) 183 | program := p.Parse() 184 | checkParserError(t, p) 185 | 186 | stmt := program.Statements[0].(*ast.ExpressionStatement) 187 | literal, ok := stmt.Expression.(*ast.StringLiteral) 188 | 189 | if !ok { 190 | t.Fatalf("expression not *ast.StringLiteral got %T", stmt.Expression) 191 | } 192 | if literal.Value != "hello world!" { 193 | t.Fatalf("literal value not as expected want %s got %s", "hello world!", literal.Value) 194 | } 195 | }) 196 | t.Run("TestParsePrefixExpression", func(t *testing.T) { 197 | prefixTests := []struct { 198 | input string 199 | operator string 200 | value interface{} 201 | }{ 202 | {"!5", "!", 5}, 203 | {"-15", "-", 15}, 204 | {"!true", "!", true}, 205 | {"!false", "!", false}, 206 | } 207 | 208 | for _, tt := range prefixTests { 209 | 210 | l := lexer.New(tt.input) 211 | p := New(l) 212 | program := p.Parse() 213 | checkParserError(t, p) 214 | 215 | if len(program.Statements) != 1 { 216 | t.Fatal("program has not enough statement expected 1 got ", len(program.Statements)) 217 | } 218 | stmt, ok := program.Statements[0].(*ast.ExpressionStatement) 219 | if !ok { 220 | t.Fatalf("program has wrong statement expected ExpressionStatement got %T", program.Statements[0]) 221 | } 222 | exp, ok := stmt.Expression.(*ast.PrefixExpression) 223 | if !ok { 224 | t.Fatalf("program has wrong expression expected PrefixExpression got %T", program.Statements[0]) 225 | } 226 | 227 | if string(exp.Operator) != tt.operator { 228 | t.Fatalf("program has wrong expression operator expected %s got %s", exp.Operator, tt.operator) 229 | } 230 | if !testLiteralExpression(t, exp.Right, tt.value) { 231 | return 232 | } 233 | } 234 | }) 235 | t.Run("TestParseInfixExpression", func(t *testing.T) { 236 | infixTests := []struct { 237 | input string 238 | leftVal interface{} 239 | operator string 240 | rightVal interface{} 241 | }{ 242 | {"5 + 5;", 5, "+", 5}, 243 | {"5 - 5;", 5, "-", 5}, 244 | {"5 * 5;", 5, "*", 5}, 245 | {"5 / 5;", 5, "/", 5}, 246 | {"5 % 5;", 5, "%", 5}, 247 | {"5 < 5;", 5, "<", 5}, 248 | {"5 <= 5;", 5, "<=", 5}, 249 | {"5 >= 5;", 5, ">=", 5}, 250 | {"5 == 5;", 5, "==", 5}, 251 | {"5 != 5;", 5, "!=", 5}, 252 | {"true == true", true, "==", true}, 253 | {"true != false", true, "!=", false}, 254 | {"false == false", false, "==", false}, 255 | } 256 | 257 | for _, tt := range infixTests { 258 | 259 | l := lexer.New(tt.input) 260 | p := New(l) 261 | program := p.Parse() 262 | checkParserError(t, p) 263 | 264 | if len(program.Statements) != 1 { 265 | t.Fatal("program has not enough statement expected 1 got ", len(program.Statements)) 266 | } 267 | stmt, ok := program.Statements[0].(*ast.ExpressionStatement) 268 | if !ok { 269 | t.Fatalf("program has wrong statement expected ExpressionStatement got %T", program.Statements[0]) 270 | } 271 | exp, ok := stmt.Expression.(*ast.InfixExpression) 272 | 273 | if !testInfixExpression(t, exp, tt.leftVal, tt.operator, tt.rightVal) { 274 | t.Fatalf("program has failed to parse infix expression") 275 | } 276 | 277 | } 278 | }) 279 | t.Run("TestParseOperatorPrecedence", func(t *testing.T) { 280 | tests := []struct { 281 | input string 282 | expected string 283 | }{ 284 | { 285 | "-a * b", 286 | "((-a) * b)", 287 | }, 288 | { 289 | "!-a", 290 | "(!(-a))", 291 | }, 292 | { 293 | "a + b + c", 294 | "((a + b) + c)", 295 | }, 296 | { 297 | "a + b - c", 298 | "((a + b) - c)", 299 | }, 300 | { 301 | "a * b * c", 302 | "((a * b) * c)", 303 | }, 304 | { 305 | "a * b / c", 306 | "((a * b) / c)", 307 | }, 308 | { 309 | "a + b / c", 310 | "(a + (b / c))", 311 | }, 312 | { 313 | "a + b * c + d / e - f", 314 | "(((a + (b * c)) + (d / e)) - f)", 315 | }, 316 | { 317 | "3 + 4; -5 * 5", 318 | "(3 + 4)((-5) * 5)", 319 | }, 320 | { 321 | "5 > 4 == 3 < 4", 322 | "((5 > 4) == (3 < 4))", 323 | }, 324 | { 325 | "5 < 4 != 3 > 4", 326 | "((5 < 4) != (3 > 4))", 327 | }, 328 | { 329 | "3 + 4 * 5 == 3 * 1 + 4 * 5", 330 | "((3 + (4 * 5)) == ((3 * 1) + (4 * 5)))", 331 | }, 332 | { 333 | "true", 334 | "true", 335 | }, 336 | { 337 | "false", 338 | "false", 339 | }, 340 | { 341 | "3 > 5 == false", 342 | "((3 > 5) == false)", 343 | }, 344 | { 345 | "3 < 5 == true", 346 | "((3 < 5) == true)", 347 | }, 348 | { 349 | "1 + (2 + 3) + 4", 350 | "((1 + (2 + 3)) + 4)", 351 | }, 352 | { 353 | "(5 + 5) * 2", 354 | "((5 + 5) * 2)", 355 | }, 356 | { 357 | "2 / (5 + 5)", 358 | "(2 / (5 + 5))", 359 | }, 360 | { 361 | "(5 + 5) * 2 * (5 + 5)", 362 | "(((5 + 5) * 2) * (5 + 5))", 363 | }, 364 | { 365 | "-(5 + 5)", 366 | "(-(5 + 5))", 367 | }, 368 | { 369 | "!(true == true)", 370 | "(!(true == true))", 371 | }, 372 | { 373 | "a + add(b * c) + d", 374 | "((a + add((b * c))) + d)", 375 | }, 376 | { 377 | "add(a, b, 1, 2 * 3, 4 + 5, add(6, 7 * 8))", 378 | "add(a, b, 1, (2 * 3), (4 + 5), add(6, (7 * 8)))", 379 | }, 380 | { 381 | "add(a + b + c * d / f + g)", 382 | "add((((a + b) + ((c * d) / f)) + g))", 383 | }, 384 | { 385 | "a * [1, 2, 3, 4][b * c] * d", 386 | "((a * ([1, 2, 3, 4][(b * c)])) * d)", 387 | }, 388 | { 389 | "add(a * b[2], b[1], 2 * [1, 2][1])", 390 | "add((a * (b[2])), (b[1]), (2 * ([1, 2][1])))", 391 | }, 392 | } 393 | 394 | for _, tt := range tests { 395 | l := lexer.New(tt.input) 396 | p := New(l) 397 | program := p.Parse() 398 | checkParserError(t, p) 399 | 400 | actual := program.String() 401 | if actual != tt.expected { 402 | t.Errorf("expected=%q, got=%q", tt.expected, actual) 403 | } 404 | } 405 | }) 406 | t.Run("TestParseBooleanLiteralExpression", func(t *testing.T) { 407 | tests := []struct { 408 | input string 409 | expectedBoolean bool 410 | }{ 411 | {"true;", true}, 412 | {"false;", false}, 413 | } 414 | 415 | for _, tt := range tests { 416 | l := lexer.New(tt.input) 417 | p := New(l) 418 | program := p.Parse() 419 | checkParserError(t, p) 420 | 421 | if len(program.Statements) != 1 { 422 | t.Fatalf("program has not enough statements. got=%d", 423 | len(program.Statements)) 424 | } 425 | 426 | stmt, ok := program.Statements[0].(*ast.ExpressionStatement) 427 | if !ok { 428 | t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T", 429 | program.Statements[0]) 430 | } 431 | 432 | boolean, ok := stmt.Expression.(*ast.BooleanLiteral) 433 | if !ok { 434 | t.Fatalf("exp not *ast.Boolean. got=%T", stmt.Expression) 435 | } 436 | if boolean.Value != tt.expectedBoolean { 437 | t.Errorf("boolean.Value not %t. got=%t", tt.expectedBoolean, 438 | boolean.Value) 439 | } 440 | } 441 | }) 442 | t.Run("TestParseIfExpression", func(t *testing.T) { 443 | input := `if (x < y) { x }` 444 | 445 | l := lexer.New(input) 446 | p := New(l) 447 | program := p.Parse() 448 | checkParserError(t, p) 449 | 450 | if len(program.Statements) != 1 { 451 | t.Fatalf("program has wrong number of statements expected 1 got %d", len(program.Statements)) 452 | } 453 | 454 | stmt, ok := program.Statements[0].(*ast.ExpressionStatement) 455 | if !ok { 456 | t.Fatalf("program has wrong statement type expected *ast.ExpressionStatement got %T", program.Statements[0]) 457 | } 458 | 459 | exp, ok := stmt.Expression.(*ast.IfExpression) 460 | if !ok { 461 | t.Fatalf("program has wrong expression type exepcted *ast.IfExpression got %T", stmt.Expression) 462 | } 463 | 464 | if !testInfixExpression(t, exp.Condition, "x", "<", "y") { 465 | return 466 | } 467 | 468 | if len(exp.Consequence.Statements) != 1 { 469 | t.Fatalf("program has wrong number of statements expected 1 got %d", len(exp.Consequence.Statements)) 470 | 471 | } 472 | 473 | consequence, ok := exp.Consequence.Statements[0].(*ast.ExpressionStatement) 474 | if !ok { 475 | t.Fatalf("program has wrong statement type expected *ast.ExpressionStatement got %T", exp.Consequence.Statements[0]) 476 | } 477 | 478 | if !testIdentifier(t, consequence.Expression, "x") { 479 | return 480 | } 481 | if exp.Alternative != nil { 482 | t.Fatalf("program has wrong alternative expected nil got %+v", exp.Alternative) 483 | } 484 | }) 485 | t.Run("TestParseIfElseExpression", func(t *testing.T) { 486 | input := `if (x < y) { x } else { y }` 487 | 488 | l := lexer.New(input) 489 | p := New(l) 490 | program := p.Parse() 491 | checkParserError(t, p) 492 | 493 | if len(program.Statements) != 1 { 494 | t.Fatalf("program.Body does not contain %d statements. got=%d\n", 495 | 1, len(program.Statements)) 496 | } 497 | 498 | stmt, ok := program.Statements[0].(*ast.ExpressionStatement) 499 | if !ok { 500 | t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T", 501 | program.Statements[0]) 502 | } 503 | 504 | exp, ok := stmt.Expression.(*ast.IfExpression) 505 | if !ok { 506 | t.Fatalf("stmt.Expression is not ast.IfExpression. got=%T", stmt.Expression) 507 | } 508 | 509 | if !testInfixExpression(t, exp.Condition, "x", "<", "y") { 510 | return 511 | } 512 | 513 | if len(exp.Consequence.Statements) != 1 { 514 | t.Errorf("consequence is not 1 statements. got=%d\n", 515 | len(exp.Consequence.Statements)) 516 | } 517 | 518 | consequence, ok := exp.Consequence.Statements[0].(*ast.ExpressionStatement) 519 | if !ok { 520 | t.Fatalf("Statements[0] is not ast.ExpressionStatement. got=%T", 521 | exp.Consequence.Statements[0]) 522 | } 523 | 524 | if !testIdentifier(t, consequence.Expression, "x") { 525 | return 526 | } 527 | 528 | if len(exp.Alternative.Statements) != 1 { 529 | t.Errorf("exp.Alternative.Statements does not contain 1 statements. got=%d\n", 530 | len(exp.Alternative.Statements)) 531 | } 532 | 533 | alternative, ok := exp.Alternative.Statements[0].(*ast.ExpressionStatement) 534 | if !ok { 535 | t.Fatalf("Statements[0] is not ast.ExpressionStatement. got=%T", 536 | exp.Alternative.Statements[0]) 537 | } 538 | 539 | if !testIdentifier(t, alternative.Expression, "y") { 540 | return 541 | } 542 | }) 543 | t.Run("TestParseFunctionLiterals", func(t *testing.T) { 544 | input := `fn(x, y) { x + y }` 545 | 546 | l := lexer.New(input) 547 | p := New(l) 548 | program := p.Parse() 549 | checkParserError(t, p) 550 | 551 | if len(program.Statements) != 1 { 552 | t.Fatalf("program.Body does not contain %d statements. got=%d\n", 553 | 1, len(program.Statements)) 554 | } 555 | 556 | stmt, ok := program.Statements[0].(*ast.ExpressionStatement) 557 | if !ok { 558 | t.Fatalf("program.Statements[0] is not ast.ExpressionStatement. got=%T", 559 | program.Statements[0]) 560 | } 561 | 562 | function, ok := stmt.Expression.(*ast.FunctionLiteral) 563 | if !ok { 564 | t.Fatalf("stmt.Expression is not ast.FunctionLiteral. got=%T", 565 | stmt.Expression) 566 | } 567 | 568 | if len(function.Parameters) != 2 { 569 | t.Fatalf("function literal parameters wrong. want 2, got=%d\n", 570 | len(function.Parameters)) 571 | } 572 | 573 | testLiteralExpression(t, function.Parameters[0], "x") 574 | testLiteralExpression(t, function.Parameters[1], "y") 575 | 576 | if len(function.Body.Statements) != 1 { 577 | t.Fatalf("function.Body.Statements has not 1 statements. got=%d\n", 578 | len(function.Body.Statements)) 579 | } 580 | 581 | bodyStmt, ok := function.Body.Statements[0].(*ast.ExpressionStatement) 582 | if !ok { 583 | t.Fatalf("function body stmt is not ast.ExpressionStatement. got=%T", 584 | function.Body.Statements[0]) 585 | } 586 | 587 | testInfixExpression(t, bodyStmt.Expression, "x", "+", "y") 588 | }) 589 | t.Run("TestParseFunctionParameters", func(t *testing.T) { 590 | tests := []struct { 591 | input string 592 | expectedParams []string 593 | }{ 594 | {input: "fn() {};", expectedParams: []string{}}, 595 | {input: "fn(x) {};", expectedParams: []string{"x"}}, 596 | {input: "fn(x, y, z) {};", expectedParams: []string{"x", "y", "z"}}, 597 | } 598 | 599 | for _, tt := range tests { 600 | l := lexer.New(tt.input) 601 | p := New(l) 602 | program := p.Parse() 603 | checkParserError(t, p) 604 | 605 | stmt := program.Statements[0].(*ast.ExpressionStatement) 606 | function := stmt.Expression.(*ast.FunctionLiteral) 607 | 608 | if len(function.Parameters) != len(tt.expectedParams) { 609 | t.Errorf("length parameters wrong. want %d, got=%d\n", 610 | len(tt.expectedParams), len(function.Parameters)) 611 | } 612 | 613 | for i, ident := range tt.expectedParams { 614 | testLiteralExpression(t, function.Parameters[i], ident) 615 | } 616 | } 617 | }) 618 | t.Run("TestParseCallExpression", func(t *testing.T) { 619 | input := "add(1, 2 * 3, 4 + 5);" 620 | 621 | l := lexer.New(input) 622 | p := New(l) 623 | program := p.Parse() 624 | checkParserError(t, p) 625 | 626 | if len(program.Statements) != 1 { 627 | t.Fatalf("program.Statements does not contain %d statements. got=%d\n", 628 | 1, len(program.Statements)) 629 | } 630 | 631 | stmt, ok := program.Statements[0].(*ast.ExpressionStatement) 632 | if !ok { 633 | t.Fatalf("stmt is not ast.ExpressionStatement. got=%T", 634 | program.Statements[0]) 635 | } 636 | 637 | exp, ok := stmt.Expression.(*ast.CallExpression) 638 | if !ok { 639 | t.Fatalf("stmt.Expression is not ast.CallExpression. got=%T", 640 | stmt.Expression) 641 | } 642 | 643 | if !testIdentifier(t, exp.Function, "add") { 644 | return 645 | } 646 | 647 | if len(exp.Arguments) != 3 { 648 | t.Fatalf("wrong length of arguments. got=%d", len(exp.Arguments)) 649 | } 650 | 651 | testLiteralExpression(t, exp.Arguments[0], 1) 652 | testInfixExpression(t, exp.Arguments[1], 2, "*", 3) 653 | testInfixExpression(t, exp.Arguments[2], 4, "+", 5) 654 | }) 655 | t.Run("TestParseCallArguments", func(t *testing.T) { 656 | tests := []struct { 657 | input string 658 | expectedIdent string 659 | expectedArgs []string 660 | }{ 661 | { 662 | input: "add();", 663 | expectedIdent: "add", 664 | expectedArgs: []string{}, 665 | }, 666 | { 667 | input: "add(1);", 668 | expectedIdent: "add", 669 | expectedArgs: []string{"1"}, 670 | }, 671 | { 672 | input: "add(1, 2 * 3, 4 + 5);", 673 | expectedIdent: "add", 674 | expectedArgs: []string{"1", "(2 * 3)", "(4 + 5)"}, 675 | }, 676 | } 677 | 678 | for _, tt := range tests { 679 | l := lexer.New(tt.input) 680 | p := New(l) 681 | program := p.Parse() 682 | checkParserError(t, p) 683 | 684 | stmt := program.Statements[0].(*ast.ExpressionStatement) 685 | exp, ok := stmt.Expression.(*ast.CallExpression) 686 | if !ok { 687 | t.Fatalf("stmt.Expression is not ast.CallExpression. got=%T", 688 | stmt.Expression) 689 | } 690 | 691 | if !testIdentifier(t, exp.Function, tt.expectedIdent) { 692 | return 693 | } 694 | 695 | if len(exp.Arguments) != len(tt.expectedArgs) { 696 | t.Fatalf("wrong number of arguments. want=%d, got=%d", 697 | len(tt.expectedArgs), len(exp.Arguments)) 698 | } 699 | 700 | for i, arg := range tt.expectedArgs { 701 | if exp.Arguments[i].String() != arg { 702 | t.Errorf("argument %d wrong. want=%q, got=%q", i, 703 | arg, exp.Arguments[i].String()) 704 | } 705 | } 706 | } 707 | }) 708 | t.Run("TestParseEmptyArrayLiteral", func(t *testing.T) { 709 | input := "[]" 710 | 711 | l := lexer.New(input) 712 | p := New(l) 713 | program := p.Parse() 714 | checkParserError(t, p) 715 | 716 | stmt, ok := program.Statements[0].(*ast.ExpressionStatement) 717 | array, ok := stmt.Expression.(*ast.ArrayLiteral) 718 | if !ok { 719 | t.Fatalf("exp not ast.ArrayLiteral. got=%T", stmt.Expression) 720 | } 721 | 722 | if len(array.Elements) != 0 { 723 | t.Errorf("len(array.Elements) not 0. got=%d", len(array.Elements)) 724 | } 725 | }) 726 | t.Run("TestParseArrayLiteral", func(t *testing.T) { 727 | input := "[1, 2 * 2, 3 + 3]" 728 | 729 | l := lexer.New(input) 730 | p := New(l) 731 | program := p.Parse() 732 | checkParserError(t, p) 733 | 734 | stmt, ok := program.Statements[0].(*ast.ExpressionStatement) 735 | array, ok := stmt.Expression.(*ast.ArrayLiteral) 736 | if !ok { 737 | t.Fatalf("exp not ast.ArrayLiteral. got=%T", stmt.Expression) 738 | } 739 | 740 | if len(array.Elements) != 3 { 741 | t.Fatalf("len(array.Elements) not 3. got=%d", len(array.Elements)) 742 | } 743 | 744 | testIntegerLiteral(t, array.Elements[0], 1) 745 | testInfixExpression(t, array.Elements[1], 2, "*", 2) 746 | testInfixExpression(t, array.Elements[2], 3, "+", 3) 747 | }) 748 | t.Run("TestParseIndexExpression", func(t *testing.T) { 749 | input := "myArray[1 + 1]" 750 | 751 | l := lexer.New(input) 752 | p := New(l) 753 | program := p.Parse() 754 | checkParserError(t, p) 755 | 756 | stmt, ok := program.Statements[0].(*ast.ExpressionStatement) 757 | indexExp, ok := stmt.Expression.(*ast.IndexExpression) 758 | if !ok { 759 | t.Fatalf("exp not *ast.IndexExpression. got=%T", stmt.Expression) 760 | } 761 | 762 | if !testIdentifier(t, indexExp.Left, "myArray") { 763 | return 764 | } 765 | 766 | if !testInfixExpression(t, indexExp.Index, 1, "+", 1) { 767 | return 768 | } 769 | }) 770 | t.Run("TestParseHashmapLiteral", func(t *testing.T) { 771 | 772 | input := `{"one":1,"two":2,"three":3}` 773 | 774 | l := lexer.New(input) 775 | p := New(l) 776 | program := p.Parse() 777 | checkParserError(t, p) 778 | 779 | stmt := program.Statements[0].(*ast.ExpressionStatement) 780 | 781 | hashmap, ok := stmt.Expression.(*ast.HashmapLiteral) 782 | if !ok { 783 | t.Fatalf("hashmap not ast.HashmapLiteral. got=%T", stmt.Expression) 784 | } 785 | if len(hashmap.Pairs) != 3 { 786 | t.Errorf("wrong parameter number expected 3 got %d", len(hashmap.Pairs)) 787 | } 788 | expected := map[string]int64{ 789 | "one": 1, 790 | "two": 2, 791 | "three": 3, 792 | } 793 | 794 | for key, val := range hashmap.Pairs { 795 | literal, ok := key.(*ast.StringLiteral) 796 | if !ok { 797 | t.Errorf("wrong literal type expected StringLiteral got %T", key) 798 | } 799 | 800 | expectedValue := expected[literal.String()] 801 | testIntegerLiteral(t, val, expectedValue) 802 | } 803 | }) 804 | } 805 | 806 | func testLetStatement(t *testing.T, s ast.Statement, name token.Literal) bool { 807 | 808 | if s.TokenLiteral() != "let" { 809 | t.Errorf("s.TokenLiteral got %s instead of let", s.TokenLiteral()) 810 | return false 811 | } 812 | letStatement, ok := s.(*ast.LetStatement) 813 | if !ok { 814 | t.Errorf("interface breakage expected *ast.LetStatement got %T", s) 815 | return false 816 | } 817 | 818 | if letStatement.Name.Value != name { 819 | t.Errorf("letStatement.Name.Value not %s got %s", name, letStatement.Name.Value) 820 | return false 821 | } 822 | 823 | if letStatement.Name.TokenLiteral() != name { 824 | t.Errorf("letStatement.Name not %s got %s", name, letStatement.Name) 825 | return false 826 | } 827 | 828 | return true 829 | } 830 | func testIntegerLiteral(t *testing.T, il ast.Expression, value int64) bool { 831 | integ, ok := il.(*ast.IntegerLiteral) 832 | 833 | if !ok { 834 | t.Errorf("wrong expression type expected *ast.IntegerLiteral got %T", il) 835 | return false 836 | } 837 | if integ.Value != value { 838 | t.Errorf("wrong integer literal expected %d got %d", value, integ.Value) 839 | } 840 | 841 | valueLiteral := fmt.Sprintf("%d", value) 842 | 843 | if integ.String() != valueLiteral { 844 | t.Errorf("wrong token literal expected %s got %s", valueLiteral, integ.TokenLiteral()) 845 | return false 846 | } 847 | 848 | return true 849 | } 850 | func testIdentifier(t *testing.T, exp ast.Expression, value string) bool { 851 | ident, ok := exp.(*ast.Identifier) 852 | if !ok { 853 | t.Errorf("exp not *ast.Identifier got %T", exp) 854 | return false 855 | } 856 | if string(ident.Value) != value { 857 | t.Errorf("wrong identifier value expected %s got %s", value, ident.Value) 858 | return false 859 | } 860 | if string(ident.TokenLiteral()) != value { 861 | t.Errorf("wrong token literal value expected %s got %s", value, ident.TokenLiteral()) 862 | return false 863 | } 864 | 865 | return true 866 | } 867 | func testLiteralExpression(t *testing.T, exp ast.Expression, expected interface{}) bool { 868 | switch v := expected.(type) { 869 | case int: 870 | return testIntegerLiteral(t, exp, int64(v)) 871 | case int64: 872 | return testIntegerLiteral(t, exp, v) 873 | case string: 874 | return testIdentifier(t, exp, v) 875 | case bool: 876 | return testBooleanLiteral(t, exp, v) 877 | } 878 | t.Errorf("type of expression not handled got %T", exp) 879 | return false 880 | } 881 | func testInfixExpression(t *testing.T, exp ast.Expression, left interface{}, operator string, right interface{}) bool { 882 | 883 | opExp, ok := exp.(*ast.InfixExpression) 884 | if !ok { 885 | t.Errorf("exp is not Operator expression got %T", opExp) 886 | return false 887 | } 888 | 889 | if !testLiteralExpression(t, opExp.Left, left) { 890 | return false 891 | } 892 | if string(opExp.Operator) != operator { 893 | t.Errorf("exp.Operator is not %s got %q", operator, opExp.Operator) 894 | return false 895 | } 896 | if !testLiteralExpression(t, opExp.Right, right) { 897 | return false 898 | } 899 | 900 | return true 901 | } 902 | func testBooleanLiteral(t *testing.T, exp ast.Expression, value bool) bool { 903 | bo, ok := exp.(*ast.BooleanLiteral) 904 | if !ok { 905 | t.Errorf("exp not *ast.Boolean. got=%T", exp) 906 | return false 907 | } 908 | 909 | if bo.Value != value { 910 | t.Errorf("bo.Value not %t. got=%t", value, bo.Value) 911 | return false 912 | } 913 | 914 | if string(bo.TokenLiteral()) != fmt.Sprintf("%t", value) { 915 | t.Errorf("bo.TokenLiteral not %t. got=%s", 916 | value, bo.TokenLiteral()) 917 | return false 918 | } 919 | 920 | return true 921 | } 922 | func checkParserError(t *testing.T, p *Parser) error { 923 | parserErrors := p.Errors() 924 | 925 | if len(parserErrors) == 0 { 926 | return nil 927 | } 928 | 929 | t.Logf("parse failed with %d errors", len(parserErrors)) 930 | 931 | for _, msg := range parserErrors { 932 | t.Logf("parser error :%q", msg) 933 | } 934 | 935 | return errors.New("Parser Failed") 936 | } 937 | -------------------------------------------------------------------------------- /repl/repl.go: -------------------------------------------------------------------------------- 1 | // Package repl implements the Read Eval Print Loop function 2 | package repl 3 | 4 | import ( 5 | "bufio" 6 | "fmt" 7 | "io" 8 | 9 | "github.com/actuallyachraf/monkey-giggle/compiler" 10 | "github.com/actuallyachraf/monkey-giggle/object" 11 | "github.com/actuallyachraf/monkey-giggle/parser" 12 | "github.com/actuallyachraf/monkey-giggle/vm" 13 | 14 | "github.com/actuallyachraf/monkey-giggle/lexer" 15 | ) 16 | 17 | // PROMPT marks prompt level console 18 | const PROMPT = "giggle>> " 19 | 20 | // WELCOME the user to make us giggle 21 | const WELCOME = "Make me giggle !\n" 22 | 23 | // EXIT the repl 24 | const EXIT = "Ohhh you're leaving already !" 25 | 26 | // Start the read eval print loop. 27 | func Start(in io.Reader, out io.Writer) { 28 | scanner := bufio.NewScanner(in) 29 | 30 | constants := []object.Object{} 31 | globals := make([]object.Object, vm.GlobalsSize) 32 | 33 | symbolTable := compiler.NewSymbolTable() 34 | for i, v := range object.Builtins { 35 | symbolTable.DefineBuiltIn(i, v.Name) 36 | } 37 | 38 | for { 39 | fmt.Printf(PROMPT) 40 | scanned := scanner.Scan() 41 | if !scanned { 42 | return 43 | } 44 | 45 | line := scanner.Text() 46 | 47 | if line == "exit" { 48 | fmt.Println(EXIT) 49 | break 50 | } 51 | l := lexer.New(line) 52 | p := parser.New(l) 53 | 54 | program := p.Parse() 55 | if len(p.Errors()) != 0 { 56 | printParserErrors(out, p.Errors()) 57 | continue 58 | } 59 | 60 | comp := compiler.NewWithState(symbolTable, constants) 61 | err := comp.Compile(program) 62 | if err != nil { 63 | fmt.Fprintf(out, "Woops! Compilation failed:\n %s\n", err) 64 | continue 65 | } 66 | 67 | code := comp.Bytecode() 68 | constants = code.Constants 69 | 70 | machine := vm.NewWithGlobalState(code, globals) 71 | err = machine.Run() 72 | if err != nil { 73 | fmt.Fprintf(out, "Woops! Executing bytecode failed:\n %s\n", err) 74 | continue 75 | } 76 | 77 | lastPopped := machine.LastPoppedStackElem() 78 | io.WriteString(out, lastPopped.Inspect()) 79 | io.WriteString(out, "\n") 80 | } 81 | } 82 | 83 | func printParserErrors(out io.Writer, errors []string) { 84 | for _, msg := range errors { 85 | io.WriteString(out, "\t"+msg+"\n") 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /token/keywords.go: -------------------------------------------------------------------------------- 1 | package token 2 | 3 | // file keywords.go define the language proper keywords. 4 | 5 | var keywords = map[Literal]Type{ 6 | "fn": FUNCTION, 7 | "let": LET, 8 | "true": TRUE, 9 | "false": FALSE, 10 | "if": IF, 11 | "else": ELSE, 12 | "return": RETURN, 13 | } 14 | 15 | // LookupIdent checks whether an identifier string is a keyword or not. 16 | func LookupIdent(ident Literal) Type { 17 | if tok, ok := keywords[ident]; ok { 18 | return tok 19 | } 20 | return IDENT 21 | } 22 | -------------------------------------------------------------------------------- /token/token.go: -------------------------------------------------------------------------------- 1 | package token 2 | 3 | // Type encodes the type of the token 4 | type Type string 5 | 6 | // Literal encodes a literal value 7 | type Literal string 8 | 9 | // Token represents the actual token holds the type and it's literal representation. 10 | type Token struct { 11 | Type 12 | Literal 13 | } 14 | 15 | // New creates a new token instance 16 | func New(tok Type, ch byte) Token { 17 | return Token{ 18 | Type: tok, 19 | Literal: Literal(ch), 20 | } 21 | } 22 | 23 | // NewLiteral creates a new token with a literal 24 | func NewLiteral(tok Type, lit string) Token { 25 | return Token{ 26 | Type: tok, 27 | Literal: Literal(lit), 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /token/types.go: -------------------------------------------------------------------------------- 1 | package token 2 | 3 | // types.go holds the defined tokens of the language.defined 4 | 5 | const ( 6 | // ILLEGAL denotes an unknown (illegal) token type 7 | ILLEGAL Type = "ILLEGAL" 8 | // EOF marks end of file attributes 9 | EOF = "EOF" 10 | 11 | // IDENT denotes the identifier which is the "name" used for a token foo, bar x,y,z and such. 12 | IDENT = "IDENT" 13 | // INT denotes the integer type 14 | INT = "INT" 15 | // STRING denotes the string type 16 | STRING = "STRING" 17 | 18 | // Operators 19 | 20 | // ASSIGN denotes the assignment operator token. 21 | ASSIGN = "ASSIGN" 22 | // ADD denotes addition token 23 | ADD = "ADD" 24 | // SUB denotes substraction token 25 | SUB = "SUB" 26 | // MUL denotes multiplication token 27 | MUL = "MUL" 28 | // DIV denotes integer division token 29 | DIV = "DIV" 30 | // MOD denotes modulo operation token 31 | MOD = "MOD" 32 | // LT denotes lesser than 33 | LT = "<" 34 | // GT denotes greater than 35 | GT = ">" 36 | // EQ denotes equality test 37 | EQ = "==" 38 | // NEQ denotes the not equal test 39 | NEQ = "!=" 40 | // LE denotes the lesser than or equal 41 | LE = "<=" 42 | // GE denotes the greater than or equal 43 | GE = ">=" 44 | 45 | // BANG denotes the bang token 46 | BANG = "!" 47 | 48 | // Delimiters are used to separate text representations 49 | 50 | // COLON represents the colon assignment for hashmaps 51 | COLON = ":" 52 | // SEMICOLON represents the semicolon delimiter for scopes 53 | SEMICOLON = ";" 54 | // COMMA represents the comma delimiter for values 55 | COMMA = "," 56 | // LPAREN represents a left parenthesis 57 | LPAREN = "(" 58 | // RPAREN represents a right parenthesis 59 | RPAREN = ")" 60 | // LBRACE represents a left curly brace 61 | LBRACE = "{" 62 | // RBRACE represents a right curly brace 63 | RBRACE = "}" 64 | // LBRACKET represents a left bracket 65 | LBRACKET = "[" 66 | // RBRACKET represents a right bracked 67 | RBRACKET = "]" 68 | 69 | // Keywords are special words that can't be used as identifiers 70 | // such as the "let" declaration for values, "fn" for functions. 71 | 72 | // FUNCTION represents a new function declaration 73 | FUNCTION = "FUNCTION" 74 | // LET represents the value declaration token 75 | LET = "LET" 76 | // TRUE represents the boolean value true 77 | TRUE = "TRUE" 78 | // FALSE represents the boolean value false 79 | FALSE = "FALSE" 80 | // IF represents the conditional if branch 81 | IF = "IF" 82 | // ELSE represents the conditional else branch 83 | ELSE = "ELSE" 84 | // RETURN represents the return instruction 85 | RETURN = "RETURN" 86 | ) 87 | -------------------------------------------------------------------------------- /vm/frame.go: -------------------------------------------------------------------------------- 1 | package vm 2 | 3 | import ( 4 | "github.com/actuallyachraf/monkey-giggle/code" 5 | "github.com/actuallyachraf/monkey-giggle/object" 6 | ) 7 | 8 | // Frame represents a stack frame used to execute function calls 9 | type Frame struct { 10 | cl *object.Closure 11 | ip int 12 | basePointer int 13 | } 14 | 15 | // NewFrame creates a new frame for a given compiled function 16 | func NewFrame(cl *object.Closure, bp int) *Frame { 17 | return &Frame{ 18 | cl: cl, 19 | ip: -1, 20 | basePointer: bp, 21 | } 22 | } 23 | 24 | // Instructions returns the set of instructions inside the call frame 25 | func (f *Frame) Instructions() code.Instructions { 26 | return f.cl.Fn.Instructions 27 | } 28 | -------------------------------------------------------------------------------- /vm/vm.go: -------------------------------------------------------------------------------- 1 | package vm 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/actuallyachraf/monkey-giggle/code" 7 | "github.com/actuallyachraf/monkey-giggle/compiler" 8 | "github.com/actuallyachraf/monkey-giggle/object" 9 | ) 10 | 11 | const ( 12 | // StackSize is the max number of items that can be on the stack. 13 | StackSize = 2048 14 | // GlobalsSize is the max number of global binds in a given program. 15 | GlobalsSize = 65536 16 | // MaxFrames is the max number of call frames that can be on the stack frame 17 | MaxFrames = 1024 18 | ) 19 | 20 | var ( 21 | // True marks truth value 22 | True = &object.Boolean{Value: true} 23 | // False marks false value 24 | False = &object.Boolean{Value: false} 25 | // Null marks the null value 26 | Null = &object.Null{} 27 | ) 28 | 29 | // VM represents a virtual machine. 30 | type VM struct { 31 | constants []object.Object 32 | 33 | stack []object.Object 34 | sp int 35 | 36 | globals []object.Object 37 | 38 | frames []*Frame 39 | framesIndex int 40 | } 41 | 42 | // New creates a new instance of VM using bytecode to execute. 43 | func New(bytecode compiler.Bytecode) *VM { 44 | 45 | // the program bytecode is considered an entire function and is pushed 46 | // as part of it's own call frame 47 | mainFn := &object.CompiledFunction{Instructions: bytecode.Instructions} 48 | mainClosure := &object.Closure{Fn: mainFn} 49 | mainFrame := NewFrame(mainClosure, 0) 50 | 51 | frames := make([]*Frame, MaxFrames) 52 | frames[0] = mainFrame 53 | 54 | return &VM{ 55 | constants: bytecode.Constants, 56 | globals: make([]object.Object, GlobalsSize), 57 | stack: make([]object.Object, StackSize), 58 | sp: 0, 59 | frames: frames, 60 | framesIndex: 1, 61 | } 62 | } 63 | 64 | // NewWithGlobalState creates a new instace of VM with a global state 65 | func NewWithGlobalState(bytecode compiler.Bytecode, s []object.Object) *VM { 66 | vm := New(bytecode) 67 | vm.globals = s 68 | 69 | return vm 70 | } 71 | 72 | // CurrentFrame returns the current call frame 73 | func (vm *VM) currentFrame() *Frame { 74 | return vm.frames[vm.framesIndex-1] 75 | } 76 | 77 | // pushFrame pushes a new call frame to the frame stack 78 | func (vm *VM) pushFrame(f *Frame) { 79 | vm.frames[vm.framesIndex] = f 80 | vm.framesIndex++ 81 | } 82 | 83 | // popFrame removes the last used call frame from the frame stack 84 | func (vm *VM) popFrame() *Frame { 85 | vm.framesIndex-- 86 | return vm.frames[vm.framesIndex] 87 | } 88 | 89 | // StackTop returns the top most element of the stack. 90 | func (vm *VM) StackTop() object.Object { 91 | if vm.sp == 0 { 92 | return nil 93 | } 94 | 95 | return vm.stack[vm.sp-1] 96 | } 97 | 98 | // Run is the main loop that runs a fetch-decode-execute cycle. 99 | func (vm *VM) Run() error { 100 | 101 | var ip int 102 | var inst code.Instructions 103 | var op code.OpCode 104 | 105 | for vm.currentFrame().ip < len(vm.currentFrame().Instructions())-1 { 106 | 107 | vm.currentFrame().ip++ 108 | 109 | ip = vm.currentFrame().ip 110 | inst = vm.currentFrame().Instructions() 111 | op = code.OpCode(inst[ip]) 112 | 113 | switch op { 114 | case code.OpPop: 115 | vm.pop() 116 | case code.OpConstant: 117 | constIndex := code.ReadUint16(inst[ip+1:]) 118 | vm.currentFrame().ip += 2 119 | 120 | err := vm.push(vm.constants[constIndex]) 121 | if err != nil { 122 | return err 123 | } 124 | case code.OpAdd, code.OpSub, code.OpMul, code.OpMod, code.OpDiv: 125 | err := vm.executeBinOp(op) 126 | if err != nil { 127 | return err 128 | } 129 | case code.OpEqual, code.OpNotEqual, code.OpGreaterOrEqual, code.OpGreaterThan: 130 | err := vm.executeCompare(op) 131 | if err != nil { 132 | return err 133 | } 134 | case code.OpNot: 135 | err := vm.executeNotOp() 136 | if err != nil { 137 | return err 138 | } 139 | case code.OpNeg: 140 | err := vm.executeNegOp() 141 | if err != nil { 142 | return err 143 | } 144 | case code.OpTrue: 145 | err := vm.push(True) 146 | if err != nil { 147 | return err 148 | } 149 | case code.OpFalse: 150 | err := vm.push(False) 151 | if err != nil { 152 | return err 153 | } 154 | case code.OpJump: 155 | pos := int(code.ReadUint16(inst[ip+1:])) 156 | vm.currentFrame().ip = pos - 1 157 | case code.OpJNE: 158 | pos := int(code.ReadUint16(inst[ip+1:])) 159 | vm.currentFrame().ip += 2 160 | 161 | condition := vm.pop() 162 | if !isTrue(condition) { 163 | vm.currentFrame().ip = pos - 1 164 | } 165 | case code.OpNull: 166 | err := vm.push(Null) 167 | if err != nil { 168 | return err 169 | } 170 | case code.OpSetGlobal: 171 | globalIndex := code.ReadUint16(inst[ip+1:]) 172 | vm.currentFrame().ip += 2 173 | 174 | vm.globals[globalIndex] = vm.pop() 175 | case code.OpGetGlobal: 176 | globalIndex := code.ReadUint16(inst[ip+1:]) 177 | vm.currentFrame().ip += 2 178 | 179 | err := vm.push(vm.globals[globalIndex]) 180 | if err != nil { 181 | return err 182 | } 183 | case code.OpSetLocal: 184 | localIndex := code.ReadUint8(inst[ip+1:]) 185 | vm.currentFrame().ip++ 186 | 187 | frame := vm.currentFrame() 188 | vm.stack[frame.basePointer+int(localIndex)] = vm.pop() 189 | case code.OpGetLocal: 190 | localIndex := code.ReadUint8(inst[ip+1:]) 191 | vm.currentFrame().ip++ 192 | 193 | frame := vm.currentFrame() 194 | err := vm.push(vm.stack[frame.basePointer+int(localIndex)]) 195 | if err != nil { 196 | return err 197 | } 198 | case code.OpArray: 199 | numElements := int(code.ReadUint16(inst[ip+1:])) 200 | vm.currentFrame().ip += 2 201 | 202 | array := vm.buildArrayObject(vm.sp-numElements, vm.sp) 203 | vm.sp = vm.sp - numElements 204 | 205 | err := vm.push(array) 206 | if err != nil { 207 | return err 208 | } 209 | case code.OpHashTable: 210 | numElements := int(code.ReadUint16(inst[ip+1:])) 211 | vm.currentFrame().ip += 2 212 | 213 | hashmap, err := vm.buildHashmapObject(vm.sp-numElements, vm.sp) 214 | if err != nil { 215 | return err 216 | } 217 | vm.sp = vm.sp - numElements 218 | 219 | err = vm.push(hashmap) 220 | if err != nil { 221 | return err 222 | } 223 | case code.OpIndex: 224 | index := vm.pop() 225 | left := vm.pop() 226 | 227 | err := vm.executeIndexExpression(left, index) 228 | if err != nil { 229 | return err 230 | } 231 | case code.OpCall: 232 | 233 | numArgs := code.ReadUint8(inst[ip+1:]) 234 | vm.currentFrame().ip++ 235 | err := vm.executeFunctionCall(int(numArgs)) 236 | if err != nil { 237 | return err 238 | } 239 | case code.OpReturnValue: 240 | // pop the return value from stack 241 | returnVal := vm.pop() 242 | // pop the stack frame 243 | frame := vm.popFrame() 244 | // restore the stack pointer 245 | // we substract 1 to throw away the last value on the stack (function call) 246 | // without explicitly poping the value from the stack 247 | vm.sp = frame.basePointer - 1 248 | // push the actual return value 249 | err := vm.push(returnVal) 250 | if err != nil { 251 | return err 252 | } 253 | case code.OpReturn: 254 | frame := vm.popFrame() 255 | vm.sp = frame.basePointer - 1 256 | 257 | err := vm.push(Null) 258 | if err != nil { 259 | return err 260 | } 261 | case code.OpGetBuiltin: 262 | builtinIndex := code.ReadUint8(inst[ip+1:]) 263 | vm.currentFrame().ip++ 264 | 265 | def := object.Builtins[builtinIndex] 266 | 267 | err := vm.push(def.Fn) 268 | if err != nil { 269 | return err 270 | } 271 | case code.OpClosure: 272 | constIndex := code.ReadUint16(inst[ip+1:]) 273 | numFree := code.ReadUint8(inst[ip+3:]) 274 | vm.currentFrame().ip += 3 275 | 276 | err := vm.pushClosure(int(constIndex), int(numFree)) 277 | if err != nil { 278 | return err 279 | } 280 | case code.OpGetFree: 281 | freeIndex := code.ReadUint8(inst[ip+1:]) 282 | vm.currentFrame().ip++ 283 | 284 | currentClosure := vm.currentFrame().cl 285 | err := vm.push(currentClosure.FreeVariables[freeIndex]) 286 | if err != nil { 287 | return err 288 | } 289 | } 290 | } 291 | 292 | return nil 293 | } 294 | 295 | // push an element to the stack and increment the stack pointer. 296 | func (vm *VM) push(obj object.Object) error { 297 | if vm.sp >= StackSize { 298 | return fmt.Errorf("stack overflow") 299 | } 300 | 301 | vm.stack[vm.sp] = obj 302 | vm.sp++ 303 | 304 | return nil 305 | } 306 | 307 | // pop the top element of the stack 308 | func (vm *VM) pop() object.Object { 309 | obj := vm.stack[vm.sp-1] 310 | vm.sp-- 311 | 312 | return obj 313 | } 314 | 315 | // isTrue checks if the given object evaluates to boolean true 316 | func isTrue(obj object.Object) bool { 317 | switch obj := obj.(type) { 318 | case *object.Boolean: 319 | return obj.Value 320 | case *object.Null: 321 | return false 322 | default: 323 | return true 324 | } 325 | } 326 | 327 | // LastPoppedStackElem returns the last pop'd item. 328 | func (vm *VM) LastPoppedStackElem() object.Object { 329 | return vm.stack[vm.sp] 330 | } 331 | 332 | // executeFunctionCall executes a function using arguments 333 | func (vm *VM) executeFunctionCall(numArgs int) error { 334 | 335 | callee := vm.stack[vm.sp-1-int(numArgs)] 336 | 337 | switch callee := callee.(type) { 338 | case *object.Closure: 339 | return vm.callClosure(callee, numArgs) 340 | case *object.BuiltIn: 341 | return vm.callBuiltIn(callee, numArgs) 342 | default: 343 | return fmt.Errorf("calling non-function") 344 | } 345 | } 346 | 347 | // callClosure executes a function call on user defined functions 348 | func (vm *VM) callClosure(cl *object.Closure, numArgs int) error { 349 | if numArgs != cl.Fn.NumParams { 350 | return fmt.Errorf("wrong number of parameters : want %d, got %d", cl.Fn.NumParams, numArgs) 351 | } 352 | // substract numArgs to correctly set bp 353 | frame := NewFrame(cl, vm.sp-numArgs) 354 | vm.pushFrame(frame) 355 | // increment the stack pointer to make place for local variables 356 | vm.sp = frame.basePointer + cl.Fn.NumLocals 357 | return nil 358 | } 359 | 360 | // callBuiltIn executes a builtin function call 361 | func (vm *VM) callBuiltIn(builtin *object.BuiltIn, numArgs int) error { 362 | 363 | args := vm.stack[vm.sp-numArgs : vm.sp] 364 | 365 | res := builtin.Fn(args...) 366 | vm.sp = vm.sp - numArgs - 1 367 | 368 | if res != nil { 369 | vm.push(res) 370 | } else { 371 | vm.push(Null) 372 | } 373 | 374 | return nil 375 | } 376 | 377 | // executeBinOp executes a binary opeartion 378 | func (vm *VM) executeBinOp(op code.OpCode) error { 379 | right := vm.pop() 380 | left := vm.pop() 381 | 382 | rightType := right.Type() 383 | leftType := right.Type() 384 | 385 | if rightType == object.INTEGER && leftType == object.INTEGER { 386 | return vm.executeIntegerOp(op, left, right) 387 | } else if rightType == object.STRING && leftType == object.STRING { 388 | return vm.executeStringOp(op, left, right) 389 | } 390 | 391 | return fmt.Errorf("unsupported types for binary operation for %d %s %s", op, rightType, leftType) 392 | } 393 | 394 | // executeIntegerOp executes a binary operation on integers 395 | func (vm *VM) executeIntegerOp(op code.OpCode, left, right object.Object) error { 396 | 397 | rightVal := right.(*object.Integer).Value 398 | leftVal := left.(*object.Integer).Value 399 | 400 | var result int64 401 | 402 | switch op { 403 | case code.OpAdd: 404 | result = leftVal + rightVal 405 | case code.OpSub: 406 | result = leftVal - rightVal 407 | case code.OpMul: 408 | result = leftVal * rightVal 409 | case code.OpDiv: 410 | result = leftVal / rightVal 411 | case code.OpMod: 412 | result = leftVal % rightVal 413 | } 414 | return vm.push(&object.Integer{Value: result}) 415 | } 416 | 417 | // executeStringOp executes a binary operation on integers 418 | func (vm *VM) executeStringOp(op code.OpCode, left, right object.Object) error { 419 | 420 | rightVal := right.(*object.String).Value 421 | leftVal := left.(*object.String).Value 422 | 423 | if op != code.OpAdd { 424 | return fmt.Errorf("Unknown operator %d for type %s %s", op, left.Type(), right.Type()) 425 | } 426 | result := leftVal + rightVal 427 | 428 | return vm.push(&object.String{Value: result}) 429 | } 430 | 431 | // executeCompare executes comparison opcodes pushing the result to the stack 432 | func (vm *VM) executeCompare(op code.OpCode) error { 433 | 434 | right := vm.pop() 435 | left := vm.pop() 436 | 437 | leftType := left.Type() 438 | rightType := right.Type() 439 | 440 | if rightType == object.INTEGER && leftType == object.INTEGER { 441 | return vm.executeIntegerCompare(op, left, right) 442 | } 443 | 444 | switch op { 445 | case code.OpEqual: 446 | return vm.push(nativeBoolToBooleanObject(left == right)) 447 | case code.OpNotEqual: 448 | return vm.push(nativeBoolToBooleanObject(left != right)) 449 | default: 450 | return fmt.Errorf("unknown operator : %d for type %s %s", op, leftType, rightType) 451 | } 452 | } 453 | 454 | // executeIntegerCompare compares two integers using a comparison operator and pushes the result to the stack 455 | func (vm *VM) executeIntegerCompare(op code.OpCode, left, right object.Object) error { 456 | leftVal := left.(*object.Integer).Value 457 | rightVal := right.(*object.Integer).Value 458 | 459 | switch op { 460 | 461 | case code.OpEqual: 462 | return vm.push(nativeBoolToBooleanObject(leftVal == rightVal)) 463 | case code.OpNotEqual: 464 | return vm.push(nativeBoolToBooleanObject(leftVal != rightVal)) 465 | case code.OpGreaterOrEqual: 466 | return vm.push(nativeBoolToBooleanObject(leftVal >= rightVal)) 467 | case code.OpGreaterThan: 468 | return vm.push(nativeBoolToBooleanObject(leftVal > rightVal)) 469 | default: 470 | return fmt.Errorf("unknown operator %d for INTEGER Type", op) 471 | } 472 | } 473 | 474 | // executeNotOp on the top item of the stack 475 | func (vm *VM) executeNotOp() error { 476 | 477 | operand := vm.pop() 478 | 479 | switch operand { 480 | case True: 481 | return vm.push(False) 482 | case False: 483 | return vm.push(True) 484 | case Null: 485 | return vm.push(True) 486 | default: 487 | return vm.push(False) 488 | } 489 | } 490 | 491 | // executeNegOp on the top item of the stack (must be an INTEGER) 492 | func (vm *VM) executeNegOp() error { 493 | operand := vm.pop() 494 | 495 | switch operand.Type() { 496 | case object.INTEGER: 497 | val := operand.(*object.Integer).Value 498 | return vm.push(&object.Integer{Value: -val}) 499 | default: 500 | return fmt.Errorf("Unsupported operand type %s for NEG operator", operand.Type()) 501 | } 502 | } 503 | 504 | // executeIndexExpression pops the index and the object to be indexed from the stack 505 | // and pushes the value 506 | func (vm *VM) executeIndexExpression(left, index object.Object) error { 507 | switch { 508 | case left.Type() == object.ARRAY && index.Type() == object.INTEGER: 509 | return vm.executeArrayIndex(left, index) 510 | case left.Type() == object.HASH: 511 | return vm.executeHashMapIndex(left, index) 512 | default: 513 | return fmt.Errorf("index operator %T not supported for type %T", index.Type(), left.Type()) 514 | } 515 | } 516 | 517 | // executeArrayIndex executes the indexing operations for array cases 518 | func (vm *VM) executeArrayIndex(array, index object.Object) error { 519 | arrayObj := array.(*object.Array) 520 | idx := index.(*object.Integer).Value 521 | 522 | max := int64(len(arrayObj.Elements) - 1) 523 | 524 | if idx < 0 || idx > max { 525 | return vm.push(Null) 526 | } 527 | return vm.push(arrayObj.Elements[idx]) 528 | } 529 | 530 | // executeHashmapIndex executes the indexing operations for hashmaps 531 | func (vm *VM) executeHashMapIndex(hash, index object.Object) error { 532 | hashObj := hash.(*object.HashMap) 533 | 534 | key, ok := index.(object.Hashable) 535 | if !ok { 536 | return fmt.Errorf("invalid key for hashmap type :%T", index.Type()) 537 | } 538 | pair, ok := hashObj.Pairs[key.HashKey()] 539 | if !ok { 540 | return vm.push(Null) 541 | } 542 | 543 | return vm.push(pair.Value) 544 | } 545 | 546 | // nativeBoolToBooleanObject returns a boolean object equivalent to input 547 | func nativeBoolToBooleanObject(input bool) *object.Boolean { 548 | if input { 549 | return True 550 | } 551 | 552 | return False 553 | } 554 | 555 | // buildArrayObject pops n elements from the stack starting at index k 556 | // and ending at index j and builds an array object of those. 557 | func (vm *VM) buildArrayObject(startIndex, endIndex int) object.Object { 558 | elements := make([]object.Object, endIndex-startIndex) 559 | 560 | for i := startIndex; i < endIndex; i++ { 561 | elements[i-startIndex] = vm.stack[i] 562 | } 563 | 564 | return &object.Array{Elements: elements} 565 | } 566 | 567 | // buildHashmapObject creates a new object.Hashmap from stack elements 568 | func (vm *VM) buildHashmapObject(startIndex, endIndex int) (object.Object, error) { 569 | 570 | hashedPairs := make(map[object.HashKey]object.HashPair) 571 | 572 | for i := startIndex; i < endIndex; i += 2 { 573 | key := vm.stack[i] 574 | val := vm.stack[i+1] 575 | 576 | pair := object.HashPair{Key: key, Value: val} 577 | hashKey, ok := key.(object.Hashable) 578 | if !ok { 579 | return nil, fmt.Errorf("Type unusable for hashmap key : %s", key.Type()) 580 | } 581 | hashedPairs[hashKey.HashKey()] = pair 582 | } 583 | 584 | return &object.HashMap{Pairs: hashedPairs}, nil 585 | } 586 | 587 | // pushClosure builds a closure and pushes it to the stack 588 | func (vm *VM) pushClosure(constIndex int, numFree int) error { 589 | 590 | constant := vm.constants[constIndex] 591 | fn, ok := constant.(*object.CompiledFunction) 592 | if !ok { 593 | return fmt.Errorf("not a function : %+v", constant) 594 | } 595 | free := make([]object.Object, numFree) 596 | for i := 0; i < numFree; i++ { 597 | free[i] = vm.stack[vm.sp-numFree+i] 598 | } 599 | vm.sp = vm.sp - numFree 600 | cl := &object.Closure{Fn: fn, FreeVariables: free} 601 | 602 | return vm.push(cl) 603 | } 604 | -------------------------------------------------------------------------------- /vm/vm_test.go: -------------------------------------------------------------------------------- 1 | package vm 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/actuallyachraf/monkey-giggle/ast" 8 | "github.com/actuallyachraf/monkey-giggle/compiler" 9 | "github.com/actuallyachraf/monkey-giggle/lexer" 10 | "github.com/actuallyachraf/monkey-giggle/object" 11 | "github.com/actuallyachraf/monkey-giggle/parser" 12 | ) 13 | 14 | func TestVM(t *testing.T) { 15 | t.Run("TestIntegerArithmetic", func(t *testing.T) { 16 | tests := []vmTestCase{ 17 | { 18 | "1", 1, 19 | }, 20 | { 21 | "2", 2, 22 | }, 23 | { 24 | "1 + 2", 3, 25 | }, 26 | { 27 | "50 / 2 * 2 + 10 -5", 55, 28 | }, 29 | { 30 | "5 * (2 + 10)", 60, 31 | }, 32 | { 33 | "350 % 3", 2, 34 | }, 35 | } 36 | runVMTests(t, tests) 37 | }) 38 | t.Run("TestBooleanExpression", func(t *testing.T) { 39 | tests := []vmTestCase{ 40 | {"true", true}, 41 | {"false", false}, 42 | {"1 < 2", true}, 43 | {"1 > 2", false}, 44 | {"1 >= 1", true}, 45 | {"2 <= 2", true}, 46 | {"1 == 1", true}, 47 | {"1 == 2", false}, 48 | {"1 != 2", true}, 49 | {"1 != 1", false}, 50 | {"true == true", true}, 51 | {"false == false", true}, 52 | {"true == false", false}, 53 | {"true != false", true}, 54 | {"false != true", true}, 55 | {"false != false", false}, 56 | {"(1 < 2) == true", true}, 57 | {"(1 > 2) == false", true}, 58 | } 59 | runVMTests(t, tests) 60 | }) 61 | t.Run("TestPrefixExpression", func(t *testing.T) { 62 | tests := []vmTestCase{ 63 | {"!true", false}, 64 | {"!false", true}, 65 | {"-5", -5}, 66 | {"-6", -6}, 67 | } 68 | runVMTests(t, tests) 69 | }) 70 | t.Run("TestConditionalExpression", func(t *testing.T) { 71 | tests := []vmTestCase{ 72 | {"if (true) {10}", 10}, 73 | {"if (true) {10} else {20}", 10}, 74 | {"if (false) {10} else {20}", 20}, 75 | {"if (1) {10}", 10}, 76 | {"if (1 < 2) {10} else {20}", 10}, 77 | {"if (1 > 2) {10} else {20}", 20}, 78 | {"if (false) {10}", Null}, 79 | } 80 | runVMTests(t, tests) 81 | }) 82 | t.Run("TestGlobalLetStatement", func(t *testing.T) { 83 | tests := []vmTestCase{ 84 | {"let one = 1;let two = 2;one + two;", 3}, 85 | {"let one = 1;let two = 2; let three = one + two; three;", 3}, 86 | {"let one = 1;one;", 1}, 87 | {"let one = 1;let two = one + one; one + two;", 3}, 88 | } 89 | runVMTests(t, tests) 90 | }) 91 | t.Run("TestStringExpression", func(t *testing.T) { 92 | tests := []vmTestCase{ 93 | {`"foobar"`, "foobar"}, 94 | {`"foo"+"bar"`, "foobar"}, 95 | {`"foo"+"bar"+"banana"`, "foobarbanana"}, 96 | {`"Hello " + " World !"`, "Hello World !"}, 97 | } 98 | runVMTests(t, tests) 99 | }) 100 | t.Run("TestArrayLiterals", func(t *testing.T) { 101 | tests := []vmTestCase{ 102 | {"[]", []int{}}, 103 | {"[1,2,3]", []int{1, 2, 3}}, 104 | {"[1+2,3-4,5*6]", []int{3, -1, 30}}, 105 | {`["foobar",3*4,"Hello"+"World"]`, []interface{}{"foobar", 12, "HelloWorld"}}, 106 | } 107 | runVMTests(t, tests) 108 | }) 109 | t.Run("TestHashmapLiterals", func(t *testing.T) { 110 | tests := []vmTestCase{ 111 | { 112 | "{}", map[object.HashKey]int64{}, 113 | }, { 114 | "{1:2,3:4,5:6}", 115 | map[object.HashKey]int64{ 116 | (&object.Integer{Value: 1}).HashKey(): 2, 117 | (&object.Integer{Value: 3}).HashKey(): 4, 118 | (&object.Integer{Value: 5}).HashKey(): 6, 119 | }, 120 | }, 121 | { 122 | "{1 + 1:2*2,3+3:4*4}", 123 | map[object.HashKey]int64{ 124 | (&object.Integer{Value: 2}).HashKey(): 4, 125 | (&object.Integer{Value: 6}).HashKey(): 16, 126 | }, 127 | }, 128 | } 129 | runVMTests(t, tests) 130 | }) 131 | t.Run("TestIndexExpression", func(t *testing.T) { 132 | tests := []vmTestCase{ 133 | {"[1,2,3][1]", 2}, 134 | {"[1,2,3][0+2]", 3}, 135 | {"[[1,2,3],[4,5,6]][1][1]", 5}, 136 | {"[[1,2,3]][0][0]", 1}, 137 | {"[1,2][-1]", Null}, 138 | {"[][0]", Null}, 139 | {"[1,2,3][99]", Null}, 140 | {"{1:1,2:2}[1]", 1}, 141 | {"{}[0]", Null}, 142 | {"{1:1}[0]", Null}, 143 | {"{1:1,2:2}[2]", 2}, 144 | } 145 | runVMTests(t, tests) 146 | }) 147 | t.Run("TestFunctionCallNoArgs", func(t *testing.T) { 148 | tests := []vmTestCase{ 149 | { 150 | input: ` 151 | let fivePlusTen = fn(){ 5 + 10;}; 152 | fivePlusTen() 153 | `, 154 | expected: 15, 155 | }, 156 | { 157 | input: ` 158 | 159 | let one = fn(){1 ;}; 160 | let two = fn(){2;}; 161 | one() + two() 162 | `, 163 | expected: 3, 164 | }, { 165 | input: ` 166 | let returnBefore = fn(){ return 99;100;} 167 | returnBefore() 168 | `, 169 | expected: 99, 170 | }, { 171 | input: ` 172 | let earlyRet = fn(){ return 99; return 100;} 173 | earlyRet() 174 | `, 175 | expected: 99, 176 | }, { 177 | input: ` 178 | let noRet = fn(){}; 179 | noRet(); 180 | `, 181 | expected: Null, 182 | }, { 183 | input: ` 184 | let noRet = fn(){ }; 185 | let noRetBis = fn(){ noRet(); }; 186 | noRet(); 187 | noRetBis(); 188 | `, 189 | expected: Null, 190 | }, { 191 | input: ` 192 | let returnsOne = fn(){1;}; 193 | let returnsOnRet = fn() { returnsOne;}; 194 | returnsOnRet()() 195 | `, 196 | expected: 1, 197 | }, 198 | } 199 | runVMTests(t, tests) 200 | }) 201 | t.Run("TestFunctionCallWithArgs", func(t *testing.T) { 202 | tests := []vmTestCase{ 203 | { 204 | input: ` 205 | let iden = fn(a){a;}; 206 | iden(4); 207 | `, 208 | expected: 4, 209 | }, { 210 | input: ` 211 | let sum = fn(a,b){a+b;}; 212 | sum(1,2); 213 | `, 214 | expected: 3, 215 | }, 216 | } 217 | runVMTests(t, tests) 218 | }) 219 | t.Run("TestFunctionCallWithBindings", func(t *testing.T) { 220 | tests := []vmTestCase{ 221 | { 222 | input: ` 223 | let one = fn() { let one = 1; one }; 224 | one(); 225 | `, 226 | expected: 1, 227 | }, 228 | { 229 | input: ` 230 | let oneAndTwo = fn() { let one = 1; let two = 2; one + two; }; 231 | oneAndTwo(); 232 | `, 233 | expected: 3, 234 | }, 235 | { 236 | input: ` 237 | let oneAndTwo = fn() { let one = 1; let two = 2; one + two; }; 238 | let threeAndFour = fn() { let three = 3; let four = 4; three + four; }; 239 | oneAndTwo() + threeAndFour(); 240 | `, 241 | expected: 10, 242 | }, 243 | { 244 | input: ` 245 | let firstFoobar = fn() { let foobar = 50; foobar; }; 246 | let secondFoobar = fn() { let foobar = 100; foobar; }; 247 | firstFoobar() + secondFoobar(); 248 | `, 249 | expected: 150, 250 | }, 251 | { 252 | input: ` 253 | let globalSeed = 50; 254 | let minusOne = fn() { 255 | let num = 1; 256 | globalSeed - num; 257 | } 258 | let minusTwo = fn() { 259 | let num = 2; 260 | globalSeed - num; 261 | } 262 | minusOne() + minusTwo(); 263 | `, 264 | expected: 97, 265 | }, { 266 | input: ` 267 | let globalNum = 10; 268 | let sum = fn(a,b){ 269 | let c = a+b; 270 | c + globalNum; 271 | }; 272 | let outer = fn(){ 273 | sum(1,2)+sum(3,4)+globalNum; 274 | }; 275 | outer() + globalNum; 276 | `, 277 | expected: 50, 278 | }, 279 | } 280 | 281 | runVMTests(t, tests) 282 | }) 283 | t.Run("TestBuiltInFunction", func(t *testing.T) { 284 | tests := []vmTestCase{ 285 | {`len("")`, 0}, 286 | {`len("four")`, 4}, 287 | {`len("Hello World!")`, 12}, 288 | {`len(1)`, &object.Error{ 289 | Message: "argument to `len` not supported, got INTEGER", 290 | }}, 291 | {`len("one","two")`, &object.Error{ 292 | Message: "wrong number of arguments, expected 1 got 2", 293 | }}, 294 | {`len([1,2,3])`, 3}, 295 | {`head([1,2,3])`, 1}, 296 | {`head([])`, Null}, 297 | {`tail([1,2,3])`, []int{2, 3}}, 298 | {`append([],1)`, []int{1}}, 299 | {`append(1,1)`, &object.Error{ 300 | Message: "argument to `append` must be ARRAY got INTEGER", 301 | }}, 302 | } 303 | runVMTests(t, tests) 304 | }) 305 | t.Run("TestFibonnacci", func(t *testing.T) { 306 | tests := []vmTestCase{ 307 | { 308 | input: ` 309 | let fib = fn(x){ 310 | if (x == 0){ 311 | return 0; 312 | } else { 313 | if (x == 1){ 314 | return 1; 315 | } else { 316 | fib(x - 1) + fib(x - 2); 317 | } 318 | } 319 | }; 320 | fib(15); 321 | `, 322 | expected: 610, 323 | }, 324 | } 325 | runVMTests(t, tests) 326 | }) 327 | t.Run("TestInsertionSort", func(t *testing.T) { 328 | tests := []vmTestCase{{ 329 | input: ` 330 | let toSort = [9,8,7,6,5,4,3,2,1]; 331 | let insert = fn(arr,elem){ 332 | if (len(arr) == 0){ 333 | return [elem]; 334 | } else { 335 | if (elem < head(arr)){ 336 | return concat(concat([elem],[head(arr)]),tail(arr)); 337 | } else { 338 | return concat([head(arr)],insert(tail(arr),elem)); 339 | } 340 | } 341 | }; 342 | let sortByInsert = fn(arr){ 343 | if (len(arr) == 0){ 344 | return []; 345 | } else { 346 | insert(sortByInsert(tail(arr)),head(arr)); 347 | } 348 | }; 349 | 350 | sortByInsert(toSort) 351 | `, 352 | expected: []int{1, 2, 3, 3, 5, 6, 7, 8, 9}, 353 | }, 354 | } 355 | runVMTests(t, tests) 356 | }) 357 | t.Run("TestClosure", func(t *testing.T) { 358 | tests := []vmTestCase{ 359 | { 360 | input: ` 361 | let a = 1; 362 | let newAdderOuter = fn(b) { 363 | fn(c) { 364 | fn(d) { a + b + c + d }; 365 | }; 366 | }; 367 | let newAdderInner = newAdderOuter(2) 368 | let adder = newAdderInner(3); 369 | adder(8); 370 | `, 371 | expected: 14, 372 | }, { 373 | input: ` 374 | let newClosure = fn(a) { 375 | fn() { a; }; 376 | }; 377 | let closure = newClosure(99); 378 | closure(); 379 | `, 380 | expected: 99, 381 | }, 382 | { 383 | input: ` 384 | let newAdder = fn(a, b) { 385 | fn(c) { a + b + c }; 386 | }; 387 | let adder = newAdder(1, 2); 388 | adder(8); 389 | `, 390 | expected: 11, 391 | }, 392 | { 393 | input: ` 394 | let newAdder = fn(a, b) { 395 | let c = a + b; 396 | fn(d) { c + d }; 397 | }; 398 | let adder = newAdder(1, 2); 399 | adder(8); 400 | `, 401 | expected: 11, 402 | }, 403 | { 404 | input: ` 405 | let newAdderOuter = fn(a, b) { 406 | let c = a + b; 407 | fn(d) { 408 | let e = d + c; 409 | fn(f) { e + f; }; 410 | }; 411 | }; 412 | let newAdderInner = newAdderOuter(1, 2) 413 | let adder = newAdderInner(3); 414 | adder(8); 415 | `, 416 | expected: 14, 417 | }, 418 | { 419 | input: ` 420 | let a = 1; 421 | let newAdderOuter = fn(b) { 422 | fn(c) { 423 | fn(d) { a + b + c + d }; 424 | }; 425 | }; 426 | let newAdderInner = newAdderOuter(2) 427 | let adder = newAdderInner(3); 428 | adder(8); 429 | `, 430 | expected: 14, 431 | }, 432 | { 433 | input: ` 434 | let newClosure = fn(a, b) { 435 | let one = fn() { a; }; 436 | let two = fn() { b; }; 437 | fn() { one() + two(); }; 438 | }; 439 | let closure = newClosure(9, 90); 440 | closure(); 441 | `, 442 | expected: 99, 443 | }, 444 | } 445 | runVMTests(t, tests) 446 | }) 447 | } 448 | 449 | // parse takes an input string and returns an ast.Program 450 | func parse(input string) *ast.Program { 451 | l := lexer.New(input) 452 | p := parser.New(l) 453 | 454 | return p.Parse() 455 | } 456 | 457 | func testIntegerObject(expected int64, actual object.Object) error { 458 | result, ok := actual.(*object.Integer) 459 | if !ok { 460 | return fmt.Errorf("object is not Integer got %T (%+v) ", actual, actual) 461 | } 462 | if result.Value != expected { 463 | return fmt.Errorf("object has wrong value expected %d got %d", expected, result.Value) 464 | } 465 | return nil 466 | } 467 | func testBooleanBoject(obj object.Object, expected bool) error { 468 | res, ok := obj.(*object.Boolean) 469 | if !ok { 470 | return fmt.Errorf("object is not a boolean got %T", obj) 471 | } 472 | 473 | if res.Value != expected { 474 | return fmt.Errorf("object value mismatch expected %t got %t", expected, res.Value) 475 | } 476 | 477 | return nil 478 | } 479 | func testStringObject(expected string, actual object.Object) error { 480 | result, ok := actual.(*object.String) 481 | if !ok { 482 | return fmt.Errorf("object is not Integer got %T (%+v) ", actual, actual) 483 | } 484 | if result.Value != expected { 485 | return fmt.Errorf("object has wrong value expected %s got %s", expected, result.Value) 486 | } 487 | return nil 488 | } 489 | 490 | type vmTestCase struct { 491 | input string 492 | expected interface{} 493 | } 494 | 495 | func runVMTests(t *testing.T, tests []vmTestCase) { 496 | t.Helper() 497 | 498 | for i, tt := range tests { 499 | program := parse(tt.input) 500 | comp := compiler.New() 501 | err := comp.Compile(program) 502 | 503 | if err != nil { 504 | t.Fatalf("runVMTests failed with error : %s", err) 505 | } 506 | 507 | vm := New(comp.Bytecode()) 508 | err = vm.Run() 509 | if err != nil { 510 | t.Fatalf("runVMTests failed with error : %s", err) 511 | } 512 | 513 | stackElem := vm.LastPoppedStackElem() 514 | 515 | testExpectedObject(i, t, tt.expected, stackElem) 516 | } 517 | } 518 | 519 | func testExpectedObject(i int, t *testing.T, expected interface{}, actual object.Object) { 520 | 521 | t.Helper() 522 | switch expected := expected.(type) { 523 | case int: 524 | err := testIntegerObject(int64(expected), actual) 525 | if err != nil { 526 | t.Errorf("testIntegerObject[%d] failed with error : %s", i, err) 527 | } 528 | case bool: 529 | err := testBooleanBoject(actual, bool(expected)) 530 | if err != nil { 531 | t.Errorf("testBooleanObject[%d] failed with error : %s", i, err) 532 | } 533 | case string: 534 | err := testStringObject(expected, actual) 535 | if err != nil { 536 | t.Errorf("testStringObject[%d] failed with error : %s", i, err) 537 | } 538 | case *object.Null: 539 | if actual != Null { 540 | t.Errorf("object not Null ! %s", actual.Type()) 541 | } 542 | case []interface{}: 543 | array, ok := actual.(*object.Array) 544 | if !ok { 545 | t.Errorf("Object not Array expect %T (%+v)", actual.Type(), actual) 546 | } 547 | if len(array.Elements) != len(expected) { 548 | t.Errorf("Array object has wrong number of elements expected %d got %d", len(expected), len(array.Elements)) 549 | } 550 | for i, exepectedElem := range expected { 551 | 552 | switch exepectedElem.(type) { 553 | case int: 554 | err := testIntegerObject(int64(exepectedElem.(int)), array.Elements[i]) 555 | if err != nil { 556 | t.Errorf("testIntegerObject failed with error : %s", err) 557 | } 558 | case string: 559 | err := testStringObject(exepectedElem.(string), array.Elements[i]) 560 | if err != nil { 561 | t.Errorf("testStringObject failed with error : %s", err) 562 | } 563 | } 564 | 565 | } 566 | case map[object.HashKey]int64: 567 | hash, ok := actual.(*object.HashMap) 568 | if !ok { 569 | t.Errorf("object is not Hashmap got %T (%+v)", actual.Type(), actual) 570 | } 571 | if len(hash.Pairs) != len(expected) { 572 | t.Errorf("Hashmap object has wrong number of elements expected %d got %d", len(expected), len(hash.Pairs)) 573 | } 574 | for expectedKey, expectedVal := range expected { 575 | pair, ok := hash.Pairs[expectedKey] 576 | if !ok { 577 | t.Errorf("No pair found with given key %d", expectedKey.Value) 578 | } 579 | err := testIntegerObject(expectedVal, pair.Value) 580 | if err != nil { 581 | t.Errorf("testIntegerObject failed with error : %s", err) 582 | } 583 | } 584 | case *object.Error: 585 | errObj, ok := actual.(*object.Error) 586 | if !ok { 587 | t.Errorf("object is not error got %T (%+v)", actual.Type(), actual) 588 | } 589 | if errObj.Message != expected.Message { 590 | t.Errorf("wrong error message got %s expected %s", errObj.Message, expected.Message) 591 | } 592 | } 593 | } 594 | --------------------------------------------------------------------------------