From 8d5ccc469c61c72f53e875cc1151f74c6b0cde2d Mon Sep 17 00:00:00 2001 From: Patrick Dufour Date: Thu, 21 Nov 2019 13:02:00 -0800 Subject: [PATCH 1/2] serialize to map --- pkg/dfl/Add.go | 13 ++- pkg/dfl/Attribute.go | 11 +- pkg/dfl/BinaryOperator.go | 4 +- pkg/dfl/Dictionary.go | 70 +++++++----- pkg/dfl/FormatSql.go | 2 +- pkg/dfl/Function.go | 19 +++- pkg/dfl/GreaterThan.go | 16 ++- pkg/dfl/GreaterThanOrEqual.go | 16 ++- pkg/dfl/Item.go | 97 +++++++++++++++++ pkg/dfl/LessThan.go | 16 ++- pkg/dfl/LessThanOrEqual.go | 16 ++- pkg/dfl/Literal.go | 13 ++- pkg/dfl/Null.go | 30 +++++- pkg/dfl/Parse.go | 4 +- pkg/dfl/ParseKeyValue.go | 26 ++--- pkg/dfl/ParseListOrKeyValue.go | 26 ++--- pkg/dfl/ParseSetOrDictionary.go | 6 +- pkg/dfl/Subtract.go | 12 ++- pkg/dfl/TernaryOperator.go | 14 ++- pkg/dfl/TryConvertString.go | 2 +- pkg/dfl/TryFormatLiteral.go | 2 +- pkg/dfl/Variable.go | 11 +- pkg/dfl/syntax/IsNull.go | 13 +++ pkg/dfl/syntax/IsSetOrDictionary.go | 2 +- pkg/dfl/syntax/syntax.go | 6 ++ pkg/factory/Node.go | 158 ++++++++++++++++++++++++++++ 26 files changed, 511 insertions(+), 94 deletions(-) create mode 100644 pkg/dfl/Item.go create mode 100644 pkg/dfl/syntax/IsNull.go create mode 100644 pkg/factory/Node.go diff --git a/pkg/dfl/Add.go b/pkg/dfl/Add.go index 9402d85..224b828 100644 --- a/pkg/dfl/Add.go +++ b/pkg/dfl/Add.go @@ -30,9 +30,18 @@ func (a Add) Sql(pretty bool, tabs int) string { return a.BinaryOperator.Sql("+", pretty, tabs) } -// Map returns a map representation of this node. func (a Add) Map() map[string]interface{} { - return a.BinaryOperator.Map("+", a.Left, a.Right) + return map[string]interface{}{ + "@type": "+", + "@value": map[string]interface{}{ + "left": a.BinaryOperator.Left.Map(), + "right": a.BinaryOperator.Right.Map(), + }, + } +} + +func (a Add) MarshalMap() (interface{}, error) { + return a.Map(), nil } // Compile returns a compiled version of this node. diff --git a/pkg/dfl/Attribute.go b/pkg/dfl/Attribute.go index d03ea57..339fe65 100644 --- a/pkg/dfl/Attribute.go +++ b/pkg/dfl/Attribute.go @@ -36,12 +36,19 @@ func (a Attribute) Sql(pretty bool, tabs int) string { func (a Attribute) Map() map[string]interface{} { return map[string]interface{}{ - "attribute": syntax.AttributePrefix + a.Name, + "@type": "attribute", + "@value": map[string]interface{}{ + "name": a.Name, + }, } } +func (a Attribute) MarshalMap() (interface{}, error) { + return a.Map(), nil +} + func (a Attribute) Compile() Node { - return Attribute{Name: a.Name} + return &Attribute{Name: a.Name} } func (a Attribute) Evaluate(vars map[string]interface{}, ctx interface{}, funcs FunctionMap, quotes []string) (map[string]interface{}, interface{}, error) { diff --git a/pkg/dfl/BinaryOperator.go b/pkg/dfl/BinaryOperator.go index e87c112..df84181 100644 --- a/pkg/dfl/BinaryOperator.go +++ b/pkg/dfl/BinaryOperator.go @@ -14,8 +14,8 @@ import ( // BinaryOperator is a DFL Node that represents the binary operator of a left value and right value. // This struct functions as an embedded struct for many comparator operations. type BinaryOperator struct { - Left Node - Right Node + Left Node `map:"left"` + Right Node `map:"right"` } func (bo BinaryOperator) Builder(operator string, quotes []string, tabs int) builder.Builder { diff --git a/pkg/dfl/Dictionary.go b/pkg/dfl/Dictionary.go index c751771..4e9c118 100644 --- a/pkg/dfl/Dictionary.go +++ b/pkg/dfl/Dictionary.go @@ -13,33 +13,40 @@ import ( // Dictionary is a Node representing a dictionary of key value pairs. type Dictionary struct { - Nodes map[Node]Node + //Nodes map[Node]Node + Items []Item } func NewDictionary(m map[string]interface{}) *Dictionary { - nodes := map[Node]Node{} + items := make([]Item, 0) for k, v := range m { if d, ok := v.(map[string]interface{}); ok { - nodes[&Literal{Value: k}] = NewDictionary(d) + items = append(items, Item{ + Key: &Literal{Value: k}, + Value: NewDictionary(d), + }) } else { - nodes[&Literal{Value: k}] = &Literal{Value: v} + items = append(items, Item{ + Key: &Literal{Value: k}, + Value: &Literal{Value: v}, + }) } } - return &Dictionary{Nodes: nodes} + return &Dictionary{Items: items} } // Len returns the length of the underlying array. func (d Dictionary) Len() int { - return len(d.Nodes) + return len(d.Items) } func (d Dictionary) Dfl(quotes []string, pretty bool, tabs int) string { - if len(d.Nodes) == 0 { + if len(d.Items) == 0 { return "{}" } values := make([]string, 0) - for k, v := range d.Nodes { - values = append(values, k.Dfl(quotes, pretty, tabs+1)+": "+v.Dfl(quotes, pretty, tabs+1)) + for _, i := range d.Items { + values = append(values, i.Key.Dfl(quotes, pretty, tabs+1)+": "+i.Value.Dfl(quotes, pretty, tabs+1)) } if pretty { return "{" + "\n" + FormatList(values, ",", pretty, tabs+1) + "\n" + strings.Repeat(DefaultTab, tabs) + "}" @@ -51,12 +58,11 @@ func (d Dictionary) Dfl(quotes []string, pretty bool, tabs int) string { // Sql returns the SQL representation of this node as a string func (d Dictionary) Sql(pretty bool, tabs int) string { str := SqlQuote + SqlArrayPrefix - i := 0 - for k, v := range d.Nodes { + for i, item := range d.Items { if i > 0 { str += ", " } - str += k.Sql(pretty, tabs) + ":" + v.Sql(pretty, tabs) + str += item.Key.Sql(pretty, tabs) + ":" + item.Value.Sql(pretty, tabs) i += 1 } str = str + SqlArraySuffix + SqlQuote + "::json" @@ -64,28 +70,40 @@ func (d Dictionary) Sql(pretty bool, tabs int) string { } func (d Dictionary) Map() map[string]interface{} { + items := []map[string]interface{}{} + for _, item := range d.Items { + items = append(items, item.Map()) + } return map[string]interface{}{ - "nodes": d.Nodes, + "@type": "dictionary", + "@value": items, } } +func (d Dictionary) MarshalMap() (interface{}, error) { + return d.Map(), nil +} + // Compile returns a compiled version of this node. func (d Dictionary) Compile() Node { - nodes := map[Node]Node{} - for k, v := range d.Nodes { - nodes[k.Compile()] = v.Compile() + items := make([]Item, 0) + for _, i := range d.Items { + items = append(items, Item{ + Key: i.Key.Compile(), + Value: i.Value.Compile(), + }) } - return Dictionary{Nodes: nodes} + return &Dictionary{Items: items} } func (d Dictionary) Evaluate(vars map[string]interface{}, ctx interface{}, funcs FunctionMap, quotes []string) (map[string]interface{}, interface{}, error) { values := map[interface{}]interface{}{} - for k, v := range d.Nodes { - _, keyValue, err := k.Evaluate(vars, ctx, funcs, quotes) + for _, i := range d.Items { + _, keyValue, err := i.Key.Evaluate(vars, ctx, funcs, quotes) if err != nil { return vars, values, err } - _, valueValue, err := v.Evaluate(vars, ctx, funcs, quotes) + _, valueValue, err := i.Value.Evaluate(vars, ctx, funcs, quotes) if err != nil { return vars, values, err } @@ -96,11 +114,11 @@ func (d Dictionary) Evaluate(vars map[string]interface{}, ctx interface{}, funcs func (d Dictionary) Attributes() []string { set := make(map[string]struct{}) - for k, v := range d.Nodes { - for _, x := range k.Attributes() { + for _, i := range d.Items { + for _, x := range i.Key.Attributes() { set[x] = struct{}{} } - for _, x := range v.Attributes() { + for _, x := range i.Value.Attributes() { set[x] = struct{}{} } } @@ -113,11 +131,11 @@ func (d Dictionary) Attributes() []string { func (d Dictionary) Variables() []string { set := make(map[string]struct{}) - for k, v := range d.Nodes { - for _, x := range k.Variables() { + for _, i := range d.Items { + for _, x := range i.Key.Variables() { set[x] = struct{}{} } - for _, x := range v.Variables() { + for _, x := range i.Value.Variables() { set[x] = struct{}{} } } diff --git a/pkg/dfl/FormatSql.go b/pkg/dfl/FormatSql.go index ef46e3f..8e3052e 100644 --- a/pkg/dfl/FormatSql.go +++ b/pkg/dfl/FormatSql.go @@ -42,7 +42,7 @@ func FormatSql(value interface{}, pretty bool, tabs int) string { // Just format sets as slices. return FormatSql(value.Slice(true), pretty, tabs) case Null: - return value.Sql() + return value.Sql(pretty, tabs) } return fmt.Sprint(value) diff --git a/pkg/dfl/Function.go b/pkg/dfl/Function.go index b0fd218..69f56d1 100644 --- a/pkg/dfl/Function.go +++ b/pkg/dfl/Function.go @@ -80,7 +80,12 @@ func (f Function) Sql(pretty bool, tabs int) string { } func (f Function) Compile() Node { - return f + return &Function{ + Name: f.Name, + MultiOperator: &MultiOperator{ + Arguments: f.MultiOperator.Arguments, + }, + } } func (f Function) Map() map[string]interface{} { @@ -89,12 +94,18 @@ func (f Function) Map() map[string]interface{} { arguments = append(arguments, a.Map()) } return map[string]interface{}{ - "op": "function", - "name": f.Name, - "arguments": arguments, + "@type": "function", + "@value": map[string]interface{}{ + "name": f.Name, + "arguments": arguments, + }, } } +func (f Function) MarshalMap() (interface{}, error) { + return f.Map(), nil +} + func (f Function) Evaluate(vars map[string]interface{}, ctx interface{}, funcs FunctionMap, quotes []string) (map[string]interface{}, interface{}, error) { if fn, ok := funcs[f.Name]; ok { values := make([]interface{}, 0, len(f.Arguments)) diff --git a/pkg/dfl/GreaterThan.go b/pkg/dfl/GreaterThan.go index 0ec5a1c..c91de61 100644 --- a/pkg/dfl/GreaterThan.go +++ b/pkg/dfl/GreaterThan.go @@ -23,7 +23,17 @@ func (gt GreaterThan) Sql(pretty bool, tabs int) string { } func (gt GreaterThan) Map() map[string]interface{} { - return gt.BinaryOperator.Map(">", gt.Left, gt.Right) + return map[string]interface{}{ + "@type": ">", + "@value": map[string]interface{}{ + "left": gt.Left.Map(), + "right": gt.Right.Map(), + }, + } +} + +func (gt GreaterThan) MarshalMap() (interface{}, error) { + return gt.Map(), nil } func (gt GreaterThan) Compile() Node { @@ -37,10 +47,10 @@ func (gt GreaterThan) Compile() Node { if err != nil { panic(err) } - return Literal{Value: (v > 0)} + return &Literal{Value: (v > 0)} } } - return GreaterThan{&NumericBinaryOperator{&BinaryOperator{Left: left, Right: right}}} + return &GreaterThan{&NumericBinaryOperator{&BinaryOperator{Left: left, Right: right}}} } func (gt GreaterThan) Evaluate(vars map[string]interface{}, ctx interface{}, funcs FunctionMap, quotes []string) (map[string]interface{}, interface{}, error) { diff --git a/pkg/dfl/GreaterThanOrEqual.go b/pkg/dfl/GreaterThanOrEqual.go index ae83429..1b51801 100644 --- a/pkg/dfl/GreaterThanOrEqual.go +++ b/pkg/dfl/GreaterThanOrEqual.go @@ -23,7 +23,17 @@ func (gte GreaterThanOrEqual) Sql(pretty bool, tabs int) string { } func (gte GreaterThanOrEqual) Map() map[string]interface{} { - return gte.BinaryOperator.Map(">=", gte.Left, gte.Right) + return map[string]interface{}{ + "@type": ">=", + "@value": map[string]interface{}{ + "left": gte.Left.Map(), + "right": gte.Right.Map(), + }, + } +} + +func (gte GreaterThanOrEqual) MarshalMap() (interface{}, error) { + return gte.Map(), nil } func (gte GreaterThanOrEqual) Compile() Node { @@ -37,10 +47,10 @@ func (gte GreaterThanOrEqual) Compile() Node { if err != nil { panic(err) } - return Literal{Value: (v >= 0)} + return &Literal{Value: (v >= 0)} } } - return GreaterThanOrEqual{&NumericBinaryOperator{&BinaryOperator{Left: left, Right: right}}} + return &GreaterThanOrEqual{&NumericBinaryOperator{&BinaryOperator{Left: left, Right: right}}} } func (gte GreaterThanOrEqual) Evaluate(vars map[string]interface{}, ctx interface{}, funcs FunctionMap, quotes []string) (map[string]interface{}, interface{}, error) { diff --git a/pkg/dfl/Item.go b/pkg/dfl/Item.go new file mode 100644 index 0000000..43e52b2 --- /dev/null +++ b/pkg/dfl/Item.go @@ -0,0 +1,97 @@ +// ================================================================= +// +// Copyright (C) 2019 Spatial Current, Inc. - All Rights Reserved +// Released as open source under the MIT License. See LICENSE file. +// +// ================================================================= + +package dfl + +import ( + "fmt" + "github.com/pkg/errors" + + "github.com/spatialcurrent/go-dfl/pkg/dfl/syntax" +) + +// Item is a Node representing a key-value pair. +type Item struct { + Key Node + Value Node +} + +func (i Item) Dfl(quotes []string, pretty bool, tabs int) string { + return fmt.Sprintf( + "%v %v %v", + i.Key.Dfl(quotes, pretty, tabs), + syntax.DictionarySeparator, + i.Value.Dfl(quotes, pretty, tabs), + ) +} + +func (i Item) Sql(pretty bool, tabs int) string { + return i.Key.Sql(pretty, tabs) + ": " + i.Value.Sql(pretty, tabs) +} + +func (i Item) Map() map[string]interface{} { + return map[string]interface{}{ + "@type": "item", + "@value": map[string]interface{}{ + "key": i.Key.Map(), + "value": i.Value.Map(), + }, + } +} + +func (i Item) MarshalMap() (interface{}, error) { + return i.Map(), nil +} + +func (i Item) Compile() Node { + return Item{ + Key: i.Key.Compile(), + Value: i.Value.Compile(), + } +} + +func (i Item) Evaluate(vars map[string]interface{}, ctx interface{}, funcs FunctionMap, quotes []string) (map[string]interface{}, interface{}, error) { + vars, k, err := i.Key.Evaluate(vars, ctx, funcs, quotes) + if err != nil { + return map[string]interface{}{}, nil, errors.Wrap(err, "error evaluating item key") + } + vars, v, err := i.Value.Evaluate(vars, ctx, funcs, quotes) + if err != nil { + return map[string]interface{}{}, nil, errors.Wrap(err, "error evaluating item value") + } + return vars, []interface{}{k, v}, nil +} + +func (i Item) Attributes() []string { + set := make(map[string]struct{}) + for _, x := range i.Key.Attributes() { + set[x] = struct{}{} + } + for _, x := range i.Value.Attributes() { + set[x] = struct{}{} + } + attrs := make([]string, 0, len(set)) + for x := range set { + attrs = append(attrs, x) + } + return attrs +} + +func (i Item) Variables() []string { + set := make(map[string]struct{}) + for _, x := range i.Key.Variables() { + set[x] = struct{}{} + } + for _, x := range i.Value.Variables() { + set[x] = struct{}{} + } + attrs := make([]string, 0, len(set)) + for x := range set { + attrs = append(attrs, x) + } + return attrs +} diff --git a/pkg/dfl/LessThan.go b/pkg/dfl/LessThan.go index 58f5755..bd28709 100644 --- a/pkg/dfl/LessThan.go +++ b/pkg/dfl/LessThan.go @@ -24,7 +24,17 @@ func (lt LessThan) Sql(pretty bool, tabs int) string { } func (lt LessThan) Map() map[string]interface{} { - return lt.BinaryOperator.Map("<", lt.Left, lt.Right) + return map[string]interface{}{ + "@type": "<", + "@value": map[string]interface{}{ + "left": lt.Left.Map(), + "right": lt.Right.Map(), + }, + } +} + +func (lt LessThan) MarshalMap() (interface{}, error) { + return lt.Map(), nil } func (lt LessThan) Compile() Node { @@ -38,10 +48,10 @@ func (lt LessThan) Compile() Node { if err != nil { panic(err) } - return Literal{Value: (v < 0)} + return &Literal{Value: (v < 0)} } } - return LessThan{&NumericBinaryOperator{&BinaryOperator{Left: left, Right: right}}} + return &LessThan{&NumericBinaryOperator{&BinaryOperator{Left: left, Right: right}}} } func (lt LessThan) Evaluate(vars map[string]interface{}, ctx interface{}, funcs FunctionMap, quotes []string) (map[string]interface{}, interface{}, error) { diff --git a/pkg/dfl/LessThanOrEqual.go b/pkg/dfl/LessThanOrEqual.go index bef200d..555b842 100644 --- a/pkg/dfl/LessThanOrEqual.go +++ b/pkg/dfl/LessThanOrEqual.go @@ -24,7 +24,17 @@ func (lte LessThanOrEqual) Sql(pretty bool, tabs int) string { } func (lte LessThanOrEqual) Map() map[string]interface{} { - return lte.BinaryOperator.Map("<=", lte.Left, lte.Right) + return map[string]interface{}{ + "@type": "<=", + "@value": map[string]interface{}{ + "left": lte.Left.Map(), + "right": lte.Right.Map(), + }, + } +} + +func (lte LessThanOrEqual) MarshalMap() (interface{}, error) { + return lte.Map(), nil } func (lte LessThanOrEqual) Compile() Node { @@ -38,10 +48,10 @@ func (lte LessThanOrEqual) Compile() Node { if err != nil { panic(err) } - return Literal{Value: (v <= 0)} + return &Literal{Value: (v <= 0)} } } - return LessThanOrEqual{&NumericBinaryOperator{&BinaryOperator{Left: left, Right: right}}} + return &LessThanOrEqual{&NumericBinaryOperator{&BinaryOperator{Left: left, Right: right}}} } func (lte LessThanOrEqual) Evaluate(vars map[string]interface{}, ctx interface{}, funcs FunctionMap, quotes []string) (map[string]interface{}, interface{}, error) { diff --git a/pkg/dfl/Literal.go b/pkg/dfl/Literal.go index db280a0..9b852d5 100644 --- a/pkg/dfl/Literal.go +++ b/pkg/dfl/Literal.go @@ -17,7 +17,7 @@ import ( // Literal{Value: ""} // Literal{Value: 0.0} type Literal struct { - Value interface{} // the field containing the actual value + Value interface{} } func (l Literal) Dfl(quotes []string, pretty bool, tabs int) string { @@ -37,12 +37,19 @@ func (l Literal) Sql(pretty bool, tabs int) string { func (l Literal) Map() map[string]interface{} { return map[string]interface{}{ - "value": l.Value, + "@type": "literal", + "@value": map[string]interface{}{ + "value": l.Value, + }, } } +func (l Literal) MarshalMap() (interface{}, error) { + return l.Map(), nil +} + func (l Literal) Compile() Node { - return l + return &Literal{Value: l.Value} } func (l Literal) Evaluate(vars map[string]interface{}, ctx interface{}, funcs FunctionMap, quotes []string) (map[string]interface{}, interface{}, error) { diff --git a/pkg/dfl/Null.go b/pkg/dfl/Null.go index ba5c546..d157e48 100644 --- a/pkg/dfl/Null.go +++ b/pkg/dfl/Null.go @@ -10,10 +10,36 @@ package dfl // Null is used as return value for Extract and DFL functions instead of returning nil pointers. type Null struct{} -func (n Null) Dfl() string { +func (n Null) Dfl(quotes []string, pretty bool, tabs int) string { return "null" } -func (n Null) Sql() string { +func (n Null) Sql(pretty bool, tabs int) string { return "NULL" } + +func (n Null) Map() map[string]interface{} { + return map[string]interface{}{ + "@type": "null", + } +} + +func (n Null) MarshalMap() (interface{}, error) { + return n.Map(), nil +} + +func (n Null) Compile() Node { + return &Null{} +} + +func (n Null) Evaluate(vars map[string]interface{}, ctx interface{}, funcs FunctionMap, quotes []string) (map[string]interface{}, interface{}, error) { + return vars, nil, nil +} + +func (n Null) Attributes() []string { + return []string{} +} + +func (n Null) Variables() []string { + return []string{} +} diff --git a/pkg/dfl/Parse.go b/pkg/dfl/Parse.go index 4ed43b2..d0444ae 100644 --- a/pkg/dfl/Parse.go +++ b/pkg/dfl/Parse.go @@ -229,7 +229,9 @@ func Parse(in string) (Node, string, error) { return &AssignMultiply{&BinaryOperator{Right: right}}, remainder, nil } else if len(remainder) == 0 || in[i+1] == ' ' || in[i+1] == '\n' { - if syntax.IsQuoted(s) { + if syntax.IsNull(s_lc) { + return &Null{}, strings.TrimSpace(remainder), nil + } else if syntax.IsQuoted(s) { return ParseLiteral(UnescapeString(s[1:len(s)-1]), remainder) } else if syntax.IsAttribute(s) { return ParseAttribute(s, remainder) diff --git a/pkg/dfl/ParseKeyValue.go b/pkg/dfl/ParseKeyValue.go index 6678965..8820cfe 100644 --- a/pkg/dfl/ParseKeyValue.go +++ b/pkg/dfl/ParseKeyValue.go @@ -61,9 +61,9 @@ func parseKeyOrValueString(s string) (Node, error) { } // ParseKeyValue parses a sequence of key value pairs -func ParseKeyValue(in string) (map[Node]Node, error) { +func ParseKeyValue(in string) ([]Item, error) { - nodes := map[Node]Node{} + items := []Item{} singlequotes := 0 doublequotes := 0 @@ -138,16 +138,16 @@ func ParseKeyValue(in string) (map[Node]Node, error) { } else if len(key) > 0 { keyNode, err := parseKeyOrValueString(key) if err != nil { - return nodes, errors.Wrap(err, "error parsing key for key-pair") + return items, errors.Wrap(err, "error parsing key for key-pair") } valueNode, err := parseKeyOrValueString(strings.TrimSpace(s)) if err != nil { - return nodes, errors.Wrap(err, "error parsing value for key-pair") + return items, errors.Wrap(err, "error parsing value for key-pair") } - nodes[keyNode] = valueNode + items = append(items, Item{Key: keyNode, Value: valueNode}) key = "" } else { - return nodes, errors.New("missing key when parsing key-value pairs") + return items, errors.New("missing key when parsing key-value pairs") } s = "" } @@ -156,18 +156,18 @@ func ParseKeyValue(in string) (map[Node]Node, error) { } if leftparentheses > rightparentheses { - return nodes, errors.New("too few closing parentheses " + fmt.Sprint(leftparentheses) + " | " + fmt.Sprint(rightparentheses)) + return items, errors.New("too few closing parentheses " + fmt.Sprint(leftparentheses) + " | " + fmt.Sprint(rightparentheses)) } else if leftparentheses < rightparentheses { - return nodes, errors.New("too many closing parentheses " + fmt.Sprint(leftparentheses) + " | " + fmt.Sprint(rightparentheses)) + return items, errors.New("too many closing parentheses " + fmt.Sprint(leftparentheses) + " | " + fmt.Sprint(rightparentheses)) } else if leftcurlybrackets > rightcurlybrackets { - return nodes, errors.New("too few closing curly brackets " + fmt.Sprint(leftcurlybrackets) + " | " + fmt.Sprint(rightcurlybrackets)) + return items, errors.New("too few closing curly brackets " + fmt.Sprint(leftcurlybrackets) + " | " + fmt.Sprint(rightcurlybrackets)) } else if leftcurlybrackets < rightcurlybrackets { - return nodes, errors.New("too many closing curly brackets " + fmt.Sprint(leftcurlybrackets) + " | " + fmt.Sprint(rightparentheses)) + return items, errors.New("too many closing curly brackets " + fmt.Sprint(leftcurlybrackets) + " | " + fmt.Sprint(rightparentheses)) } else if leftsquarebrackets > rightsquarebrackets { - return nodes, errors.New("too few closing square brackets " + fmt.Sprint(leftsquarebrackets) + " | " + fmt.Sprint(rightsquarebrackets)) + return items, errors.New("too few closing square brackets " + fmt.Sprint(leftsquarebrackets) + " | " + fmt.Sprint(rightsquarebrackets)) } else if leftsquarebrackets < rightsquarebrackets { - return nodes, errors.New("too many closing square brackets " + fmt.Sprint(leftsquarebrackets) + " | " + fmt.Sprint(rightsquarebrackets)) + return items, errors.New("too many closing square brackets " + fmt.Sprint(leftsquarebrackets) + " | " + fmt.Sprint(rightsquarebrackets)) } - return nodes, nil + return items, nil } diff --git a/pkg/dfl/ParseListOrKeyValue.go b/pkg/dfl/ParseListOrKeyValue.go index b8b247c..0f6d14e 100644 --- a/pkg/dfl/ParseListOrKeyValue.go +++ b/pkg/dfl/ParseListOrKeyValue.go @@ -15,7 +15,7 @@ import ( ) // ParseList parses a list of values. -func ParseListOrKeyValue(in string) (bool, []Node, map[Node]Node, error) { +func ParseListOrKeyValue(in string) (bool, []Node, []Item, error) { singlequotes := 0 doublequotes := 0 @@ -86,33 +86,33 @@ func ParseListOrKeyValue(in string) (bool, []Node, map[Node]Node, error) { if i+1 == len(in) { value, _, err := Parse(in) if err != nil { - return true, make([]Node, 0), map[Node]Node{}, errors.Wrap(err, "error parsing single value in set") + return true, make([]Node, 0), []Item{}, errors.Wrap(err, "error parsing single value in set") } - return true, []Node{value}, map[Node]Node{}, nil + return true, []Node{value}, []Item{}, nil //return true, []Node{&Literal{Value: TryConvertString(in)}}, map[Node]Node{}, nil } else if in[i+1] == ',' { nodes, err := ParseList(in) - return true, nodes, map[Node]Node{}, err + return true, nodes, []Item{}, err } else if in[i+1] == ':' { - nodes, err := ParseKeyValue(in) - return false, make([]Node, 0), nodes, err + items, err := ParseKeyValue(in) + return false, make([]Node, 0), items, err } } } if leftparentheses > rightparentheses { - return true, make([]Node, 0), map[Node]Node{}, errors.New("too few closing parentheses " + fmt.Sprint(leftparentheses) + " | " + fmt.Sprint(rightparentheses)) + return true, make([]Node, 0), []Item{}, errors.New("too few closing parentheses " + fmt.Sprint(leftparentheses) + " | " + fmt.Sprint(rightparentheses)) } else if leftparentheses < rightparentheses { - return true, make([]Node, 0), map[Node]Node{}, errors.New("too many closing parentheses " + fmt.Sprint(leftparentheses) + " | " + fmt.Sprint(rightparentheses)) + return true, make([]Node, 0), []Item{}, errors.New("too many closing parentheses " + fmt.Sprint(leftparentheses) + " | " + fmt.Sprint(rightparentheses)) } else if leftcurlybrackets > rightcurlybrackets { - return true, make([]Node, 0), map[Node]Node{}, errors.New("too few closing curly brackets " + fmt.Sprint(leftcurlybrackets) + " | " + fmt.Sprint(rightcurlybrackets)) + return true, make([]Node, 0), []Item{}, errors.New("too few closing curly brackets " + fmt.Sprint(leftcurlybrackets) + " | " + fmt.Sprint(rightcurlybrackets)) } else if leftcurlybrackets < rightcurlybrackets { - return true, make([]Node, 0), map[Node]Node{}, errors.New("too many closing curly brackets " + fmt.Sprint(leftcurlybrackets) + " | " + fmt.Sprint(rightparentheses)) + return true, make([]Node, 0), []Item{}, errors.New("too many closing curly brackets " + fmt.Sprint(leftcurlybrackets) + " | " + fmt.Sprint(rightparentheses)) } else if leftsquarebrackets > rightsquarebrackets { - return true, make([]Node, 0), map[Node]Node{}, errors.New("too few closing square brackets " + fmt.Sprint(leftsquarebrackets) + " | " + fmt.Sprint(rightsquarebrackets)) + return true, make([]Node, 0), []Item{}, errors.New("too few closing square brackets " + fmt.Sprint(leftsquarebrackets) + " | " + fmt.Sprint(rightsquarebrackets)) } else if leftsquarebrackets < rightsquarebrackets { - return true, make([]Node, 0), map[Node]Node{}, errors.New("too many closing square brackets " + fmt.Sprint(leftsquarebrackets) + " | " + fmt.Sprint(rightsquarebrackets)) + return true, make([]Node, 0), []Item{}, errors.New("too many closing square brackets " + fmt.Sprint(leftsquarebrackets) + " | " + fmt.Sprint(rightsquarebrackets)) } - return true, make([]Node, 0), map[Node]Node{}, nil + return true, make([]Node, 0), []Item{}, nil } diff --git a/pkg/dfl/ParseSetOrDictionary.go b/pkg/dfl/ParseSetOrDictionary.go index 1a7e957..94f8f75 100644 --- a/pkg/dfl/ParseSetOrDictionary.go +++ b/pkg/dfl/ParseSetOrDictionary.go @@ -23,7 +23,7 @@ import ( // {amenity: bank} func ParseSetOrDictionary(in string, remainder string) (Node, string, error) { - isSet, list, kv, err := ParseListOrKeyValue(in) + isSet, list, items, err := ParseListOrKeyValue(in) if err != nil { return &Set{}, remainder, errors.Wrap(err, "error parsing set "+in) } @@ -32,7 +32,7 @@ func ParseSetOrDictionary(in string, remainder string) (Node, string, error) { if isSet { return &Set{Nodes: list}, remainder, nil } - return &Dictionary{Nodes: kv}, remainder, nil + return &Dictionary{Items: items}, remainder, nil } root, remainder, err := Parse(remainder) @@ -43,7 +43,7 @@ func ParseSetOrDictionary(in string, remainder string) (Node, string, error) { if isSet { err = AttachLeft(root, &Set{Nodes: list}) } else { - err = AttachLeft(root, &Dictionary{Nodes: kv}) + err = AttachLeft(root, &Dictionary{Items: items}) } if err != nil { diff --git a/pkg/dfl/Subtract.go b/pkg/dfl/Subtract.go index cc17a4d..b249629 100644 --- a/pkg/dfl/Subtract.go +++ b/pkg/dfl/Subtract.go @@ -26,7 +26,17 @@ func (s Subtract) Sql(pretty bool, tabs int) string { } func (s Subtract) Map() map[string]interface{} { - return s.BinaryOperator.Map("subtract", s.Left, s.Right) + return map[string]interface{}{ + "@type": "-", + "@value": map[string]interface{}{ + "left": s.Left.Map(), + "right": s.Right.Map(), + }, + } +} + +func (s Subtract) MarshalMap() (interface{}, error) { + return s.Map(), nil } func (s Subtract) Compile() Node { diff --git a/pkg/dfl/TernaryOperator.go b/pkg/dfl/TernaryOperator.go index c0e09b5..ee0d919 100644 --- a/pkg/dfl/TernaryOperator.go +++ b/pkg/dfl/TernaryOperator.go @@ -37,13 +37,19 @@ func (to TernaryOperator) Sql(pretty bool, tabs int) string { func (to TernaryOperator) Map() map[string]interface{} { return map[string]interface{}{ - "op": "ternary", - "condition": to.Left.Map(), - "true": to.True.Map(), - "false": to.False.Map(), + "@type": "ternary", + "@value": map[string]interface{}{ + "condition": to.Left.Map(), + "true": to.True.Map(), + "false": to.False.Map(), + }, } } +func (to TernaryOperator) MarshalMap() (interface{}, error) { + return to.Map(), nil +} + func (to TernaryOperator) Compile() Node { left := to.Left.Compile() switch left.(type) { diff --git a/pkg/dfl/TryConvertString.go b/pkg/dfl/TryConvertString.go index 0dd0c57..64a3276 100644 --- a/pkg/dfl/TryConvertString.go +++ b/pkg/dfl/TryConvertString.go @@ -42,7 +42,7 @@ func TryConvertString(s string) interface{} { } if s_lc == "null" || s_lc == "none" || s_lc == "nil" { - return Null{} + return &Null{} } if s_lc == "true" { diff --git a/pkg/dfl/TryFormatLiteral.go b/pkg/dfl/TryFormatLiteral.go index 4f3aaa9..b99ed5c 100644 --- a/pkg/dfl/TryFormatLiteral.go +++ b/pkg/dfl/TryFormatLiteral.go @@ -49,7 +49,7 @@ func TryFormatLiteral(value interface{}, quotes []string, pretty bool, tabs int) case StringSet: return value.Dfl(quotes, pretty, tabs) case Null: - return value.Dfl() + return value.Dfl(quotes, pretty, tabs) } return fmt.Sprint(value) diff --git a/pkg/dfl/Variable.go b/pkg/dfl/Variable.go index 4b789bc..35db376 100644 --- a/pkg/dfl/Variable.go +++ b/pkg/dfl/Variable.go @@ -33,12 +33,19 @@ func (v Variable) Sql(pretty bool, tabs int) string { func (v Variable) Map() map[string]interface{} { return map[string]interface{}{ - "variable": syntax.VariablePrefix + v.Name, + "@type": "variable", + "@value": map[string]interface{}{ + "name": v.Name, + }, } } +func (v Variable) MarshalMap() (interface{}, error) { + return v.Map(), nil +} + func (v Variable) Compile() Node { - return Variable{Name: v.Name} + return &Variable{Name: v.Name} } func (v Variable) Evaluate(vars map[string]interface{}, ctx interface{}, funcs FunctionMap, quotes []string) (map[string]interface{}, interface{}, error) { diff --git a/pkg/dfl/syntax/IsNull.go b/pkg/dfl/syntax/IsNull.go new file mode 100644 index 0000000..683c826 --- /dev/null +++ b/pkg/dfl/syntax/IsNull.go @@ -0,0 +1,13 @@ +// ================================================================= +// +// Copyright (C) 2019 Spatial Current, Inc. - All Rights Reserved +// Released as open source under the MIT License. See LICENSE file. +// +// ================================================================= + +package syntax + +// IsNull returns true if the string is a formatted null. +func IsNull(s string) bool { + return s == "null" || s == "none" || s == "nil" +} diff --git a/pkg/dfl/syntax/IsSetOrDictionary.go b/pkg/dfl/syntax/IsSetOrDictionary.go index dd2ec9c..794b29a 100644 --- a/pkg/dfl/syntax/IsSetOrDictionary.go +++ b/pkg/dfl/syntax/IsSetOrDictionary.go @@ -13,5 +13,5 @@ import ( // IsSetOrDictionary returns true if the string is a formatted set or dictionary. func IsSetOrDictionary(s string) bool { - return len(s) >= 2 && strings.HasPrefix(s, "{") && strings.HasSuffix(s, "}") + return len(s) >= 2 && strings.HasPrefix(s, DictionaryPrefix) && strings.HasSuffix(s, DictionarySuffix) } diff --git a/pkg/dfl/syntax/syntax.go b/pkg/dfl/syntax/syntax.go index f830c9e..126c953 100644 --- a/pkg/dfl/syntax/syntax.go +++ b/pkg/dfl/syntax/syntax.go @@ -12,3 +12,9 @@ package syntax var AttributePrefix = "@" var VariablePrefix = "$" + +var DictionaryPrefix = "{" + +var DictionarySuffix = "}" + +var DictionarySeparator = ":" diff --git a/pkg/factory/Node.go b/pkg/factory/Node.go new file mode 100644 index 0000000..ef62cc1 --- /dev/null +++ b/pkg/factory/Node.go @@ -0,0 +1,158 @@ +// ================================================================= +// +// Copyright (C) 2019 Spatial Current, Inc. - All Rights Reserved +// Released as open source under the MIT License. See LICENSE file. +// +// ================================================================= + +package factory + +import ( + "github.com/spatialcurrent/go-dfl/pkg/dfl" +) + +type Node struct { + name string + function string + attribute string + value interface{} + left dfl.Node + right dfl.Node + items []dfl.Item + arguments []dfl.Node + condition dfl.Node + trueNode dfl.Node + falseNode dfl.Node +} + +func NewNode(name string) *Node { + return &Node{ + name: name, + } +} + +func (n *Node) Function(function string) *Node { + n.function = function + return n +} + +func (n *Node) Attribute(attribute string) *Node { + n.attribute = attribute + return n +} + +func (n *Node) Condition(condition dfl.Node) *Node { + n.condition = condition + return n +} + +func (n *Node) True(node dfl.Node) *Node { + n.trueNode = node + return n +} + +func (n *Node) False(node dfl.Node) *Node { + n.falseNode = node + return n +} + +func (n *Node) Value(value interface{}) *Node { + n.value = value + return n +} + +func (n *Node) Left(left dfl.Node) *Node { + n.left = left + return n +} + +func (n *Node) Right(right dfl.Node) *Node { + n.right = right + return n +} + +func (n *Node) Items(items []dfl.Item) *Node { + n.items = items + return n +} + +func (n *Node) Arguments(arguments []dfl.Node) *Node { + n.arguments = arguments + return n +} + +func (n *Node) Node() dfl.Node { + switch n.name { + case "add", "+": + return &dfl.Add{BinaryOperator: &dfl.BinaryOperator{Left: n.left, Right: n.right}} + case "subtract", "-": + return &dfl.Subtract{ + NumericBinaryOperator: &dfl.NumericBinaryOperator{ + BinaryOperator: &dfl.BinaryOperator{ + Left: n.left, + Right: n.right, + }, + }, + } + case ">": + return &dfl.GreaterThan{ + NumericBinaryOperator: &dfl.NumericBinaryOperator{ + BinaryOperator: &dfl.BinaryOperator{ + Left: n.left, + Right: n.right, + }, + }, + } + case ">=": + return &dfl.GreaterThanOrEqual{ + NumericBinaryOperator: &dfl.NumericBinaryOperator{ + BinaryOperator: &dfl.BinaryOperator{ + Left: n.left, + Right: n.right, + }, + }, + } + case "<": + return &dfl.LessThan{ + NumericBinaryOperator: &dfl.NumericBinaryOperator{ + BinaryOperator: &dfl.BinaryOperator{ + Left: n.left, + Right: n.right, + }, + }, + } + case "<=": + return &dfl.LessThanOrEqual{ + NumericBinaryOperator: &dfl.NumericBinaryOperator{ + BinaryOperator: &dfl.BinaryOperator{ + Left: n.left, + Right: n.right, + }, + }, + } + case "ternary": + return &dfl.TernaryOperator{ + Left: n.condition, + True: n.trueNode, + False: n.falseNode, + } + case "attribute": + return &dfl.Attribute{Name: n.attribute} + case "dictionary": + return &dfl.Dictionary{Items: n.items} + case "item": + return &dfl.Item{Key: n.left, Value: n.right} + case "literal": + return &dfl.Literal{Value: n.value} + case "null", "nil": + return &dfl.Null{} + case "function": + return &dfl.Function{ + Name: n.function, + MultiOperator: &dfl.MultiOperator{ + Arguments: n.arguments, + }, + } + } + return nil +} From 1405446e70764dd2839cac837c3775b3f73af272 Mon Sep 17 00:00:00 2001 From: Patrick Dufour Date: Sun, 29 Dec 2019 12:20:44 -0800 Subject: [PATCH 2/2] update --- pkg/dfl/Array.go | 9 ++++++++- pkg/dfl/Assign.go | 12 +++++++++++- pkg/dfl/AssignAdd.go | 25 +++++++++++++++++++------ pkg/dfl/AssignMultiply.go | 4 ++-- pkg/dfl/AssignSubtract.go | 4 ++-- pkg/dfl/ErrorEvaluate.go | 7 ++++++- pkg/dfl/Name.go | 22 ++++++++++++++++++++++ pkg/dfl/Name_test.go | 18 ++++++++++++++++++ pkg/factory/Node.go | 28 +++++++++++++++++++++++++++- 9 files changed, 115 insertions(+), 14 deletions(-) create mode 100644 pkg/dfl/Name.go create mode 100644 pkg/dfl/Name_test.go diff --git a/pkg/dfl/Array.go b/pkg/dfl/Array.go index 0d7c46c..a820e47 100644 --- a/pkg/dfl/Array.go +++ b/pkg/dfl/Array.go @@ -51,10 +51,17 @@ func (a Array) Sql(pretty bool, tabs int) string { func (a Array) Map() map[string]interface{} { return map[string]interface{}{ - "nodes": a.Nodes, + "@type": "array", + "@value": map[string]interface{}{ + "nodes": a.Nodes, + }, } } +func (a Array) MarshalMap() (interface{}, error) { + return a.Map(), nil +} + // Compile returns a compiled version of this node. // If all the values of an Set are literals, returns a single Literal with the corresponding array as its value. // Otherwise returns the original node.. diff --git a/pkg/dfl/Assign.go b/pkg/dfl/Assign.go index eda5bc2..6ab5a1a 100644 --- a/pkg/dfl/Assign.go +++ b/pkg/dfl/Assign.go @@ -62,7 +62,17 @@ func (a Assign) Sql(pretty bool, tabs int) string { } func (a Assign) Map() map[string]interface{} { - return a.BinaryOperator.Map("assign", a.Left, a.Right) + return map[string]interface{}{ + "@type": ":=", + "@value": map[string]interface{}{ + "left": a.BinaryOperator.Left.Map(), + "right": a.BinaryOperator.Right.Map(), + }, + } +} + +func (a Assign) MarshalMap() (interface{}, error) { + return a.Map(), nil } // Compile returns a compiled version of this node. diff --git a/pkg/dfl/AssignAdd.go b/pkg/dfl/AssignAdd.go index f15acfa..54930a5 100644 --- a/pkg/dfl/AssignAdd.go +++ b/pkg/dfl/AssignAdd.go @@ -63,19 +63,32 @@ func (a AssignAdd) Sql(pretty bool, tabs int) string { } func (a AssignAdd) Map() map[string]interface{} { - return a.BinaryOperator.Map("assignadd", a.Left, a.Right) + return map[string]interface{}{ + "@type": "+=", + "@value": map[string]interface{}{ + "left": a.BinaryOperator.Left.Map(), + "right": a.BinaryOperator.Right.Map(), + }, + } +} + +func (a AssignAdd) MarshalMap() (interface{}, error) { + return a.Map(), nil } // Compile returns a compiled version of this node. func (a AssignAdd) Compile() Node { - left := a.Left.Compile() - right := a.Right.Compile() - return &AssignAdd{&BinaryOperator{Left: left, Right: right}} + return &AssignAdd{ + &BinaryOperator{ + Left: a.Left.Compile(), + Right: a.Right.Compile(), + }, + } } func (a AssignAdd) Evaluate(vars map[string]interface{}, ctx interface{}, funcs FunctionMap, quotes []string) (map[string]interface{}, interface{}, error) { switch left := a.Left.(type) { - case Attribute: + case *Attribute: vars, lv, rv, err := a.EvaluateLeftAndRight(vars, ctx, funcs, quotes) if err != nil { return vars, 0, err @@ -110,7 +123,7 @@ func (a AssignAdd) Evaluate(vars map[string]interface{}, ctx interface{}, funcs path = pair[1] } return vars, ctx, nil - case Variable: + case *Variable: vars, lv, rv, err := a.EvaluateLeftAndRight(vars, ctx, funcs, quotes) if err != nil { return vars, 0, err diff --git a/pkg/dfl/AssignMultiply.go b/pkg/dfl/AssignMultiply.go index 9de4437..239decf 100644 --- a/pkg/dfl/AssignMultiply.go +++ b/pkg/dfl/AssignMultiply.go @@ -75,7 +75,7 @@ func (a AssignMultiply) Compile() Node { func (a AssignMultiply) Evaluate(vars map[string]interface{}, ctx interface{}, funcs FunctionMap, quotes []string) (map[string]interface{}, interface{}, error) { switch left := a.Left.(type) { - case Attribute: + case *Attribute: vars, lv, rv, err := a.EvaluateLeftAndRight(vars, ctx, funcs, quotes) if err != nil { return vars, 0, err @@ -110,7 +110,7 @@ func (a AssignMultiply) Evaluate(vars map[string]interface{}, ctx interface{}, f path = pair[1] } return vars, ctx, nil - case Variable: + case *Variable: vars, lv, rv, err := a.EvaluateLeftAndRight(vars, ctx, funcs, quotes) if err != nil { return vars, 0, err diff --git a/pkg/dfl/AssignSubtract.go b/pkg/dfl/AssignSubtract.go index 704abc5..e41dd44 100644 --- a/pkg/dfl/AssignSubtract.go +++ b/pkg/dfl/AssignSubtract.go @@ -75,7 +75,7 @@ func (a AssignSubtract) Compile() Node { func (a AssignSubtract) Evaluate(vars map[string]interface{}, ctx interface{}, funcs FunctionMap, quotes []string) (map[string]interface{}, interface{}, error) { switch left := a.Left.(type) { - case Attribute: + case *Attribute: vars, lv, rv, err := a.EvaluateLeftAndRight(vars, ctx, funcs, quotes) if err != nil { return vars, 0, err @@ -107,7 +107,7 @@ func (a AssignSubtract) Evaluate(vars map[string]interface{}, ctx interface{}, f path = pair[1] } return vars, ctx, nil - case Variable: + case *Variable: vars, lv, rv, err := a.EvaluateLeftAndRight(vars, ctx, funcs, quotes) if err != nil { return vars, 0, err diff --git a/pkg/dfl/ErrorEvaluate.go b/pkg/dfl/ErrorEvaluate.go index a49972c..42c0c9e 100644 --- a/pkg/dfl/ErrorEvaluate.go +++ b/pkg/dfl/ErrorEvaluate.go @@ -7,6 +7,10 @@ package dfl +import ( + "fmt" +) + // ErrorEvaluate is an error returned when an error occurs during evaluation of a Node. type ErrorEvaluate struct { Node Node // the name of the Function @@ -15,5 +19,6 @@ type ErrorEvaluate struct { // Error returns the error as a string. func (e ErrorEvaluate) Error() string { - return "error evaluating expression " + e.Node.Dfl(e.Quotes, false, 0) + //return fmt.Sprintf("error evaluating %s (%q)", Name(e.Node), e.Node.Dfl(e.Quotes, false, 0)) + return fmt.Sprintf("error evaluating %s (%#v)", Name(e.Node), e.Node.Map()) } diff --git a/pkg/dfl/Name.go b/pkg/dfl/Name.go new file mode 100644 index 0000000..72fa1b9 --- /dev/null +++ b/pkg/dfl/Name.go @@ -0,0 +1,22 @@ +// ================================================================= +// +// Copyright (C) 2019 Spatial Current, Inc. - All Rights Reserved +// Released as open source under the MIT License. See LICENSE file. +// +// ================================================================= + +package dfl + +import ( + "fmt" + "reflect" +) + +func Name(node Node) string { + t := reflect.TypeOf(node) + k := t.Kind() + if k == reflect.Ptr { + return fmt.Sprintf("*%s", t.Elem().Name()) + } + return t.Name() +} diff --git a/pkg/dfl/Name_test.go b/pkg/dfl/Name_test.go new file mode 100644 index 0000000..9641ff3 --- /dev/null +++ b/pkg/dfl/Name_test.go @@ -0,0 +1,18 @@ +// ================================================================= +// +// Copyright (C) 2019 Spatial Current, Inc. - All Rights Reserved +// Released as open source under the MIT License. See LICENSE file. +// +// ================================================================= + +package dfl + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestName(t *testing.T) { + assert.Equal(t, "AssignAdd", Name(&AssignAdd{})) +} diff --git a/pkg/factory/Node.go b/pkg/factory/Node.go index ef62cc1..7dc7cad 100644 --- a/pkg/factory/Node.go +++ b/pkg/factory/Node.go @@ -84,7 +84,26 @@ func (n *Node) Arguments(arguments []dfl.Node) *Node { func (n *Node) Node() dfl.Node { switch n.name { case "add", "+": - return &dfl.Add{BinaryOperator: &dfl.BinaryOperator{Left: n.left, Right: n.right}} + return &dfl.Add{ + BinaryOperator: &dfl.BinaryOperator{ + Left: n.left, + Right: n.right, + }, + } + case "assign", ":=": + return &dfl.Assign{ + BinaryOperator: &dfl.BinaryOperator{ + Left: n.left, + Right: n.right, + }, + } + case "assignadd", "+=": + return &dfl.AssignAdd{ + BinaryOperator: &dfl.BinaryOperator{ + Left: n.left, + Right: n.right, + }, + } case "subtract", "-": return &dfl.Subtract{ NumericBinaryOperator: &dfl.NumericBinaryOperator{ @@ -94,6 +113,13 @@ func (n *Node) Node() dfl.Node { }, }, } + case "assignsubtract", "-=": + return &dfl.AssignSubtract{ + BinaryOperator: &dfl.BinaryOperator{ + Left: n.left, + Right: n.right, + }, + } case ">": return &dfl.GreaterThan{ NumericBinaryOperator: &dfl.NumericBinaryOperator{