From e283d39f57942bba1776afbfdcf576332d663e3c Mon Sep 17 00:00:00 2001 From: Marques Lee Date: Fri, 14 Jul 2017 23:30:58 -0700 Subject: [PATCH] Interpreter now does a depth-first, recursive tree walk - previously we were doing 2 passes: once to define entities, and once to generate - previous impl was not capable of looking beyond 1-level deep - can change to iterative tree-walk if performance or stack-depth becomes problematic --- interpreter/interpreter.go | 94 ++++++++++++++++++++------------- interpreter/interpreter_test.go | 32 +++++------ main.go | 2 +- 3 files changed, 72 insertions(+), 56 deletions(-) diff --git a/interpreter/interpreter.go b/interpreter/interpreter.go index 0cc4370..ced73a5 100644 --- a/interpreter/interpreter.go +++ b/interpreter/interpreter.go @@ -13,6 +13,26 @@ type Interpreter struct { l logging.ILogger } +type NodeSet []dsl.Node +type Iterator func(index int, node dsl.Node) +type Collector func(index int, node dsl.Node) interface{} + +func (nodes NodeSet) Each(f Iterator) NodeSet { + for i, size := 0, len(nodes); i < size; i++ { + f(i, nodes[i]) + } + return nodes +} + +func (nodes NodeSet) Map(f Collector) []interface{} { + size := len(nodes) + result := make([]interface{}, size) + nodes.Each(func(index int, node dsl.Node) { + result[index] = f(index, node) + }) + return result +} + func New(logger logging.ILogger) *Interpreter { if logger == nil { logger = &logging.DefaultLogger{} @@ -21,25 +41,40 @@ func New(logger logging.ILogger) *Interpreter { return &Interpreter{l: logger, entities: make(map[string]*generator.Generator)} } -func (i *Interpreter) defaultArgumentFor(fieldType string) interface{} { - var arg interface{} +func (i *Interpreter) Visit(node dsl.Node) error { + switch node.Kind { + case "root": + NodeSet(node.Children).Each(func(_ int, node dsl.Node) { + i.Visit(node) + }) + return nil + case "definition": + i.EntityFromNode(node) + return nil + case "generation": + return i.GenerateFromNode(node) + } + + return nil +} +func (i *Interpreter) defaultArgumentFor(fieldType string) interface{} { switch fieldType { case "string": - arg = 5 + return 5 case "integer": - arg = [2]int{1, 10} + return [2]int{1, 10} case "decimal": - arg = [2]float64{1, 10} + return [2]float64{1, 10} case "date": t1, _ := time.Parse("2006-01-02", "1945-01-01") t2, _ := time.Parse("2006-01-02", "2017-01-01") - arg = [2]time.Time{t1, t2} + return [2]time.Time{t1, t2} default: i.l.Die("Field of type `%s` requires arguments", fieldType) } - return arg + return nil } func (i *Interpreter) EntityFromNode(node dsl.Node) *generator.Generator { @@ -59,6 +94,7 @@ func (i *Interpreter) EntityFromNode(node dsl.Node) *generator.Generator { } } + i.entities[node.Name] = entity return entity } @@ -125,38 +161,22 @@ func (i *Interpreter) withDynamicField(entity *generator.Generator, field dsl.No } } -func (i *Interpreter) translateEntities(tree dsl.Node) map[string]*generator.Generator { - for _, node := range tree.Children { - if node.Kind == "definition" { - i.entities[node.Name] = i.EntityFromNode(node) - } +func (i *Interpreter) GenerateFromNode(node dsl.Node) error { + count, ok := node.Args[0].Value.(int64) + entity, exists := i.entities[node.Name] + + if !ok { + return err("generate %s takes an integer count", node.Name) } - return i.entities -} -func (i *Interpreter) generateEntities(tree dsl.Node) error { - for _, node := range tree.Children { - if node.Kind == "generation" { - count, e := node.Args[0].Value.(int64) - entity, exists := i.entities[node.Name] - - if e { - if count <= int64(1) { - return err("Must generate at least 1 `%s` entity", node.Name) - } else if !exists { - return err("Unknown symbol `%s` -- expected an entity. Did you mean to define an entity named `%s`?", node.Name, node.Name) - } else { - entity.Generate(count) - } - } else { - return err("generate %s takes an integer count", node.Name) - } - } + if count <= int64(1) { + return err("Must generate at least 1 `%s` entity", node.Name) } - return nil -} -func (i *Interpreter) Consume(tree dsl.Node) error { - i.translateEntities(tree) - return i.generateEntities(tree) + if !exists { + return err("Unknown symbol `%s` -- expected an entity. Did you mean to define an entity named `%s`?", node.Name, node.Name) + } + + entity.Generate(count) + return nil } diff --git a/interpreter/interpreter_test.go b/interpreter/interpreter_test.go index ede1867..3f08a25 100644 --- a/interpreter/interpreter_test.go +++ b/interpreter/interpreter_test.go @@ -21,42 +21,38 @@ func interp() *Interpreter { return New(GetLogger()) } -func TestTranslateEntities(t *testing.T) { - entity1 := EntityNode("cat", validFields) - entity2 := EntityNode("dog", validFields) - for _, entity := range interp().translateEntities(RootNode(entity1, entity2)) { - for _, field := range validFields { - AssertShouldHaveField(t, entity, field) - } - } -} - -func TestValidConsume(t *testing.T) { +func TestValidVisit(t *testing.T) { node := RootNode(EntityNode("person", validFields), GenerationNode("person", 2)) i := interp() - err := i.Consume(node) + err := i.Visit(node) if err != nil { t.Errorf("There was a problem generating entities: %v", err) } + + for _, entity := range i.entities { + for _, field := range validFields { + AssertShouldHaveField(t, entity, field) + } + } } func TestInvalidGenerationNodeBadArgType(t *testing.T) { i := interp() i.EntityFromNode(EntityNode("burp", validFields)) - node := RootNode(dsl.Node{Kind: "generation", Name: "burp", Args: StringArgs("blah")}) - ExpectsError(t, "ERROR: generate burp takes an integer count", i.generateEntities(node)) + node := dsl.Node{Kind: "generation", Name: "burp", Args: StringArgs("blah")} + ExpectsError(t, "ERROR: generate burp takes an integer count", i.GenerateFromNode(node)) } func TestInvalidGenerationNodeBadCountArg(t *testing.T) { i := interp() i.EntityFromNode(EntityNode("person", validFields)) - node := RootNode(GenerationNode("person", 0)) - ExpectsError(t, "ERROR: Must generate at least 1 `person` entity", i.generateEntities(node)) + node := GenerationNode("person", 0) + ExpectsError(t, "ERROR: Must generate at least 1 `person` entity", i.GenerateFromNode(node)) } func TestGenerateEntitiesCannotResolveEntity(t *testing.T) { - node := RootNode(GenerationNode("tree", 2)) - ExpectsError(t, "ERROR: Unknown symbol `tree` -- expected an entity. Did you mean to define an entity named `tree`?", interp().generateEntities(node)) + node := GenerationNode("tree", 2) + ExpectsError(t, "ERROR: Unknown symbol `tree` -- expected an entity. Did you mean to define an entity named `tree`?", interp().GenerateFromNode(node)) } func TestDefaultArguments(t *testing.T) { diff --git a/main.go b/main.go index 3f05772..c0a116f 100644 --- a/main.go +++ b/main.go @@ -31,7 +31,7 @@ func main() { if err != nil { fmt.Println("got an error", err) } else { - errors := interpreter.New(nil).Consume(tree.(dsl.Node)) + errors := interpreter.New(nil).Visit(tree.(dsl.Node)) if errors != nil { fmt.Println(errors)