Globals to compiler
This commit is contained in:
@@ -18,6 +18,8 @@ type Compiler struct {
|
||||
|
||||
lastInstruction EmittedInstruction
|
||||
previousInstruction EmittedInstruction
|
||||
|
||||
symbolTable *SymbolTable
|
||||
}
|
||||
|
||||
func New() *Compiler {
|
||||
@@ -26,6 +28,7 @@ func New() *Compiler {
|
||||
constants: []object.Object{},
|
||||
lastInstruction: EmittedInstruction{},
|
||||
previousInstruction: EmittedInstruction{},
|
||||
symbolTable: NewSymbolTable(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -164,6 +167,22 @@ func (c *Compiler) Compile(node ast.Node) error {
|
||||
}
|
||||
}
|
||||
|
||||
case *ast.LetStatement:
|
||||
err := c.Compile(node.Value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
symbol := c.symbolTable.Define(node.Name.Value)
|
||||
c.emit(code.OpSetGlobal, symbol.Index)
|
||||
|
||||
case *ast.Identifier:
|
||||
symbol, ok := c.symbolTable.Resolve(node.Value)
|
||||
if !ok {
|
||||
return fmt.Errorf("undefined varible %s", node.Value)
|
||||
}
|
||||
|
||||
c.emit(code.OpGetGlobal, symbol.Index)
|
||||
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
@@ -228,6 +228,55 @@ func TestConditionals(t *testing.T) {
|
||||
runCompilerTests(t, tests)
|
||||
}
|
||||
|
||||
func TestGlobalLetStatements(t *testing.T) {
|
||||
tests := []compilerTestCase{
|
||||
{
|
||||
input: `
|
||||
let one = 1;
|
||||
let two = 2;
|
||||
`,
|
||||
expectedConstants: []interface{}{1, 2},
|
||||
expectedInstructions: []code.Instructions{
|
||||
code.Make(code.OpConstant, 0),
|
||||
code.Make(code.OpSetGlobal, 0),
|
||||
code.Make(code.OpConstant, 1),
|
||||
code.Make(code.OpSetGlobal, 1),
|
||||
},
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let one = 1;
|
||||
one;
|
||||
`,
|
||||
expectedConstants: []interface{}{1},
|
||||
expectedInstructions: []code.Instructions{
|
||||
code.Make(code.OpConstant, 0),
|
||||
code.Make(code.OpSetGlobal, 0),
|
||||
code.Make(code.OpGetGlobal, 0),
|
||||
code.Make(code.OpPop),
|
||||
},
|
||||
},
|
||||
{
|
||||
input: `
|
||||
let one = 1;
|
||||
let two = one;
|
||||
two;
|
||||
`,
|
||||
expectedConstants: []interface{}{1},
|
||||
expectedInstructions: []code.Instructions{
|
||||
code.Make(code.OpConstant, 0),
|
||||
code.Make(code.OpSetGlobal, 0),
|
||||
code.Make(code.OpGetGlobal, 0),
|
||||
code.Make(code.OpSetGlobal, 1),
|
||||
code.Make(code.OpGetGlobal, 1),
|
||||
code.Make(code.OpPop),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
runCompilerTests(t, tests)
|
||||
}
|
||||
|
||||
func runCompilerTests(t *testing.T, tests []compilerTestCase) {
|
||||
t.Helper()
|
||||
|
||||
|
||||
39
compiler/symbol_table.go
Normal file
39
compiler/symbol_table.go
Normal file
@@ -0,0 +1,39 @@
|
||||
package compiler
|
||||
|
||||
type SymbolScope string
|
||||
|
||||
const (
|
||||
GlobalScope SymbolScope = "Global"
|
||||
)
|
||||
|
||||
type Symbol struct {
|
||||
Name string
|
||||
Scope SymbolScope
|
||||
Index int
|
||||
}
|
||||
|
||||
type SymbolTable struct {
|
||||
store map[string]Symbol
|
||||
numDefinitions int
|
||||
}
|
||||
|
||||
func NewSymbolTable() *SymbolTable {
|
||||
s := make(map[string]Symbol)
|
||||
return &SymbolTable{store: s}
|
||||
}
|
||||
|
||||
func (s *SymbolTable) Define(name string) Symbol {
|
||||
symbol := Symbol{
|
||||
Name: name,
|
||||
Scope: GlobalScope,
|
||||
Index: s.numDefinitions,
|
||||
}
|
||||
s.store[name] = symbol
|
||||
s.numDefinitions++
|
||||
return symbol
|
||||
}
|
||||
|
||||
func (s *SymbolTable) Resolve(name string) (Symbol, bool) {
|
||||
obj, ok := s.store[name]
|
||||
return obj, ok
|
||||
}
|
||||
59
compiler/symbol_table_test.go
Normal file
59
compiler/symbol_table_test.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package compiler
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestDefine(t *testing.T) {
|
||||
expected := map[string]Symbol{
|
||||
"a": {
|
||||
Name: "a",
|
||||
Scope: GlobalScope,
|
||||
Index: 0,
|
||||
},
|
||||
"b": {
|
||||
Name: "b",
|
||||
Scope: GlobalScope,
|
||||
Index: 1,
|
||||
},
|
||||
}
|
||||
|
||||
global := NewSymbolTable()
|
||||
|
||||
a := global.Define("a")
|
||||
if a != expected["a"] {
|
||||
t.Errorf("expected a=%+v, got=%+v", expected["a"], a)
|
||||
}
|
||||
|
||||
b := global.Define("b")
|
||||
if a != expected["b"] {
|
||||
t.Errorf("expected b=%+v, got=%+v", expected["b"], b)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveGlobal(t *testing.T) {
|
||||
global := NewSymbolTable()
|
||||
global.Define("a")
|
||||
global.Define("b")
|
||||
|
||||
expected := []Symbol{
|
||||
Symbol{
|
||||
Name: "a",
|
||||
Scope: GlobalScope,
|
||||
Index: 0,
|
||||
},
|
||||
Symbol{
|
||||
Name: "b",
|
||||
Scope: GlobalScope,
|
||||
Index: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, sym := range expected {
|
||||
result, ok := global.Resolve(sym.Name)
|
||||
if !ok {
|
||||
t.Errorf("name %s not resolvable", sym.Name)
|
||||
}
|
||||
if result != sym {
|
||||
t.Errorf("expected %s to resolve to %+v, got=%+v", sym.Name, sym, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user