Globals to compiler

This commit is contained in:
Chuck Smith
2024-02-20 16:07:01 -05:00
parent 6ba2d3abe4
commit e8254fc996
7 changed files with 213 additions and 0 deletions

View File

@@ -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

View File

@@ -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
View 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
}

View 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)
}
}
}