Explorar o código

keeping track of names --can use let statements to bind values to an identifier

simon %!s(int64=3) %!d(string=hai) anos
pai
achega
0c1502e74b
Modificáronse 3 ficheiros con 91 adicións e 0 borrados
  1. 12 0
      compiler/compiler.go
  2. 35 0
      compiler/symbol_table.go
  3. 44 0
      compiler/symbol_table_test.go

+ 12 - 0
compiler/compiler.go

@@ -22,6 +22,8 @@ type Compiler struct {
 
 	lastInstruction     EmittedInstruction
 	previousInstruction EmittedInstruction
+
+	symbolTable *SymbolTable
 }
 
 func New() *Compiler {
@@ -30,6 +32,7 @@ func New() *Compiler {
 		constants:           []object.Object{},
 		lastInstruction:     EmittedInstruction{},
 		previousInstruction: EmittedInstruction{},
+		symbolTable:         NewSymbolTable(),
 	}
 }
 
@@ -153,6 +156,15 @@ func (c *Compiler) Compile(node ast.Node) error {
 		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 variable %s", node.Value)
+		}
+		c.emit(code.OpGetGlobal, symbol.Index)
 
 	case *ast.Boolean:
 		if node.Value {

+ 35 - 0
compiler/symbol_table.go

@@ -0,0 +1,35 @@
+package compiler
+
+type SymbolScope string
+
+const (
+	GlobalScope SymbolScope = "GLOBAL"
+)
+
+type Symbol struct {
+	Name  string // identifier
+	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, Index: s.numDefinitions, Scope: GlobalScope}
+	s.store[name] = symbol
+	s.numDefinitions++
+	return symbol
+}
+
+func (s *SymbolTable) Resolve(name string) (Symbol, bool) {
+	obj, ok := s.store[name]
+	return obj, ok
+}

+ 44 - 0
compiler/symbol_table_test.go

@@ -0,0 +1,44 @@
+package compiler
+
+import "testing"
+
+func TestDefine(t *testing.T) {
+	expected := map[string]Symbol{
+		"a": {"a", GlobalScope, 0},
+		"b": {"b", GlobalScope, 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 b != 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{
+		{Name: "a", Scope: GlobalScope, Index: 0},
+		{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)
+			continue
+		}
+		if result != sym {
+			t.Errorf("expected %s to resolve to %+v, got=%+v",
+				sym.Name, sym, result)
+		}
+	}
+}