diff --git a/object/environment.go b/object/environment.go new file mode 100644 index 0000000..aed4862 --- /dev/null +++ b/object/environment.go @@ -0,0 +1,30 @@ +package object + +func NewEnvironment() *Environment { + s := make(map[string]Object) + return &Environment{store: s} +} + +type Environment struct { + store map[string]Object + outer *Environment +} + +func (e *Environment) Get(name string) (Object, bool) { + obj, ok := e.store[name] + if !ok && e.outer != nil { + obj, ok = e.outer.Get(name) + } + return obj, ok +} + +func (e *Environment) Set(name string, val Object) Object { + e.store[name] = val + return val +} + +func NewEnclosedEnvironment(outer *Environment) *Environment { + env := NewEnvironment() + env.outer = outer + return env +} diff --git a/object/object.go b/object/object.go new file mode 100644 index 0000000..91806e6 --- /dev/null +++ b/object/object.go @@ -0,0 +1,181 @@ +package object + +import ( + "bytes" + "fmt" + "hash/fnv" + "strings" + + "github.com/0daryo/ody/ast" +) + +const ( + INTEGER_OBJ = "INTEGER" + BOOLEAN_OBJ = "BOOLEAN" + NULL_OBJ = "NULL" + RETURN_VALUE_OBJECT = "RETURN_VALUE" + ERROR_OBJ = "ERROR" + FUNCTION_OBJ = "FUNCTION" + STRING_OBJ = "STRING" + BUILTIN_OBJ = "BUILTIN" + ARRAY_OBJ = "ARRAY" + HASH_OBJ = "HASH" +) + +type HashPair struct { + Key Object + Value Object +} + +type Hash struct { + Pairs map[HashKey]HashPair +} + +func (h *Hash) Type() ObjectType { return HASH_OBJ } +func (h *Hash) Inspect() string { + var out bytes.Buffer + + pairs := []string{} + for _, pair := range h.Pairs { + pairs = append(pairs, fmt.Sprintf("%s: %s", pair.Key.Inspect(), pair.Value.Inspect())) + } + out.WriteString("{") + out.WriteString(strings.Join(pairs, ", ")) + out.WriteString("}") + + return out.String() +} + +type Array struct { + Elements []Object +} + +func (ao *Array) Type() ObjectType { return ARRAY_OBJ } +func (ao *Array) Inspect() string { + var out bytes.Buffer + + elements := []string{} + for _, e := range ao.Elements { + elements = append(elements, e.Inspect()) + } + + out.WriteString("[") + out.WriteString(strings.Join(elements, ", ")) + out.WriteString("]") + + return out.String() +} + +type BuiltinFunction func(args ...Object) Object + +type Builtin struct { + Fn BuiltinFunction +} + +func (b *Builtin) Type() ObjectType { return BUILTIN_OBJ } +func (b *Builtin) Inspect() string { return "builtin function" } + +type String struct { + Value string +} + +func (s *String) Type() ObjectType { + return STRING_OBJ +} +func (s *String) Inspect() string { return s.Value } + +type Error struct { + Message string +} + +func (e *Error) Type() ObjectType { return ERROR_OBJ } +func (e *Error) Inspect() string { return "ERROR: " + e.Message } + +type ReturnValue struct { + Value Object +} + +func (rv *ReturnValue) Type() ObjectType { return RETURN_VALUE_OBJECT } +func (rv *ReturnValue) Inspect() string { return rv.Value.Inspect() } + +type ObjectType string + +type Object interface { + Type() ObjectType + Inspect() string +} + +type Integer struct { + Value int64 +} + +func (i *Integer) Inspect() string { return fmt.Sprintf("%d", i.Value) } +func (i *Integer) Type() ObjectType { return INTEGER_OBJ } + +type Boolean struct { + Value bool +} + +func (b *Boolean) Type() ObjectType { return BOOLEAN_OBJ } +func (b *Boolean) Inspect() string { return fmt.Sprintf("%t", b.Value) } + +type Null struct{} + +func (n *Null) Type() ObjectType { return NULL_OBJ } +func (n *Null) Inspect() string { return "null" } + +type Function struct { + Parameters []*ast.Identifier + Body *ast.BlockStatement + Env *Environment +} + +func (f *Function) Type() ObjectType { return FUNCTION_OBJ } +func (f *Function) Inspect() string { + var out bytes.Buffer + + params := []string{} + for _, p := range f.Parameters { + params = append(params, p.String()) + } + + out.WriteString("fn") + out.WriteString("(") + out.WriteString(strings.Join(params, ", ")) + out.WriteString(") {\n") + out.WriteString(f.Body.String()) + out.WriteString("\n") + + return out.String() +} + +type HashKey struct { + Type ObjectType + Value uint64 +} + +func (b *Boolean) HashKey() HashKey { + var value uint64 + + if b.Value { + value = 1 + } else { + value = 0 + } + return HashKey{Type: b.Type(), Value: value} +} + +func (i *Integer) HashKey() HashKey { + return HashKey{Type: i.Type(), Value: uint64(i.Value)} +} + +func (s *String) HashKey() HashKey { + h := fnv.New64a() + h.Write([]byte(s.Value)) + + return HashKey{Type: s.Type(), Value: h.Sum64()} +} + +type Hashable interface { + HashKey() HashKey +} diff --git a/object/object_test.go b/object/object_test.go new file mode 100644 index 0000000..34d8882 --- /dev/null +++ b/object/object_test.go @@ -0,0 +1,22 @@ +package object + +import "testing" + +func TestStringHashKey(t *testing.T) { + hello1 := &String{Value: "Hello World"} + hello2 := &String{Value: "Hello World"} + diff1 := &String{Value: "My name is johnny"} + diff2 := &String{Value: "My name is johnny"} + + if hello1.HashKey() != hello2.HashKey() { + t.Errorf("strings with same content have different hash keys") + } + + if diff1.HashKey() != diff2.HashKey() { + t.Errorf("strings with same content have different hash keys") + } + + if hello1.HashKey() == diff1.HashKey() { + t.Errorf("strings with different content have same hash keys") + } +}