Skip to content

Commit

Permalink
Interpreter now does a depth-first, recursive tree walk
Browse files Browse the repository at this point in the history
  - 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
  • Loading branch information
marques-work committed Jul 15, 2017
1 parent 3cb71e6 commit e283d39
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 56 deletions.
94 changes: 57 additions & 37 deletions interpreter/interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{}
Expand All @@ -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 {
Expand All @@ -59,6 +94,7 @@ func (i *Interpreter) EntityFromNode(node dsl.Node) *generator.Generator {
}
}

i.entities[node.Name] = entity
return entity
}

Expand Down Expand Up @@ -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
}
32 changes: 14 additions & 18 deletions interpreter/interpreter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down

0 comments on commit e283d39

Please sign in to comment.