Skip to content

Commit 96b81f8

Browse files
Denys Smirnovdennwc
authored andcommitted
properly handle function argument modifiers
Signed-off-by: Denys Smirnov <denys@sourced.tech>
1 parent b809a64 commit 96b81f8

4 files changed

Lines changed: 250 additions & 92 deletions

File tree

driver/normalizer/normalizer.go

Lines changed: 132 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package normalizer
22

33
import (
4+
"errors"
5+
46
"gopkg.in/bblfsh/sdk.v2/uast"
57
"gopkg.in/bblfsh/sdk.v2/uast/nodes"
68
. "gopkg.in/bblfsh/sdk.v2/uast/transformer"
@@ -15,59 +17,141 @@ var Normalize = Transformers([][]Transformer{
1517
{Mappings(Normalizers...)},
1618
}...)
1719

18-
type opArrHasParams struct {
19-
origArr Op
20+
var _ Op = opArrHasKeyword{}
21+
22+
type opArrHasKeyword struct {
23+
keyword string
24+
opHas Op
25+
opRest Op
2026
}
2127

22-
func (op opArrHasParams) Kinds() nodes.Kind {
28+
func (op opArrHasKeyword) Kinds() nodes.Kind {
2329
return nodes.KindArray
2430
}
2531

26-
func (op opArrHasParams) Check(st *State, n nodes.Node) (bool, error) {
32+
func (op opArrHasKeyword) Check(st *State, n nodes.Node) (bool, error) {
2733
arr, ok := n.(nodes.Array)
2834
if !ok && arr != nil {
2935
return false, nil
3036
}
37+
// find a node with a specified type and drop if from array
38+
// the boolean flag that we pass to a sub-op will indicate
39+
// if we found it or not
40+
for i, n := range arr {
41+
obj, ok := n.(nodes.Object)
42+
if !ok {
43+
continue
44+
}
45+
v, ok := obj[uast.KeyType]
46+
if !ok {
47+
continue
48+
}
49+
typ, ok := v.(nodes.String)
50+
if !ok || string(typ) != op.keyword {
51+
continue
52+
}
53+
// found the keyword
54+
if ok, err := op.opHas.Check(st, nodes.Bool(true)); err != nil || !ok {
55+
return ok, err
56+
}
57+
rest := make(nodes.Array, len(arr)-1)
58+
copy(rest, arr[:i])
59+
copy(rest[i:], arr[i+1:])
60+
return op.opRest.Check(st, rest)
61+
}
62+
// not found, default to false
63+
if ok, err := op.opHas.Check(st, nodes.Bool(false)); err != nil || !ok {
64+
return ok, err
65+
}
66+
return op.opRest.Check(st, n)
67+
}
68+
69+
func (op opArrHasKeyword) Construct(st *State, n nodes.Node) (nodes.Node, error) {
70+
// first, we will need to read the flag from sub-op
71+
// if it's false, we will just pass and array as-is
72+
// if it's true, we will synthesize and append a node to it
3173

32-
res, err := op.origArr.Check(st, arr)
74+
v, err := op.opHas.Construct(st, nil)
75+
if err != nil {
76+
return nil, err
77+
}
78+
has, ok := v.(nodes.Bool)
79+
if !ok {
80+
return nil, ErrUnexpectedType.New(nodes.Bool(false), v)
81+
}
82+
n, err = op.opRest.Construct(st, n)
3383
if err != nil {
34-
return false, err
84+
return nil, err
85+
} else if !has {
86+
// pass as-is
87+
return n, nil
3588
}
89+
// synthesize the node
3690

37-
return res, nil
91+
// TODO(dennwc): synthesize the node once we care about reverse transform
92+
return n, nil
3893
}
3994

40-
func (op opArrHasParams) Construct(st *State, n nodes.Node) (nodes.Node, error) {
41-
arr, err := op.origArr.Construct(st, n)
42-
if err != nil || arr == nil {
43-
return n, err
44-
}
95+
var _ Op = opArrToChain{}
4596

46-
arr2, ok := arr.(nodes.Array)
47-
if !ok && arr2 != nil {
48-
return nil, ErrExpectedList.New(arr2)
49-
}
97+
type opArrToChain struct {
98+
opMods Op
99+
opType Op
100+
// TODO(dennwc): maybe whitelist only known modifiers? seen so far:
101+
// - RefKeyword
102+
// - OutKeyword (we should move it to Returns)
103+
}
50104

51-
retVal := false
105+
func (op opArrToChain) Kinds() nodes.Kind {
106+
return nodes.KindObject
107+
}
52108

53-
for _, n := range arr2 {
54-
nobj, ok := n.(nodes.Object)
55-
if !ok {
56-
return nil, ErrExpectedObject.New(nobj)
57-
}
109+
func (op opArrToChain) Check(st *State, n nodes.Node) (bool, error) {
110+
// we assert that the passed node is an object and start
111+
// checking the Type field recursively
112+
// if there is one, we will remove it from the "Type" field
113+
// from current node and append it to an array
114+
// and we repeat it recursively on the value of the "Type" field
115+
var mods nodes.Array
116+
117+
// TODO(dennwc): implement when we will need a reversal
118+
if ok, err := op.opType.Check(st, n); err != nil || !ok {
119+
return ok, err
120+
}
121+
return op.opMods.Check(st, mods)
122+
}
58123

59-
objType, ok := nobj["@type"].(nodes.String)
124+
func (op opArrToChain) Construct(st *State, n nodes.Node) (nodes.Node, error) {
125+
// load two nodes:
126+
// - the first one is an array of modifiers (objects)
127+
// - the second one is a type node
128+
nd, err := op.opMods.Construct(st, nil)
129+
if err != nil {
130+
return nil, err
131+
}
132+
mods, ok := nd.(nodes.Array)
133+
if !ok {
134+
return nil, ErrUnexpectedType.New(nodes.Array{}, nd)
135+
}
136+
typ, err := op.opType.Construct(st, n)
137+
if err != nil {
138+
return nil, err
139+
}
140+
// we will now use each modifier to construct a chain or a linked list of nodes
141+
// by adding a "Type" field to each modifier, that will point to the current node
142+
for _, nd := range mods {
143+
mod, ok := nd.(nodes.Object)
60144
if !ok {
61-
return nil, ErrExpectedValue.New(nobj["@type"])
145+
return nil, ErrUnexpectedType.New(nodes.Object{}, nd)
62146
}
63-
64-
if objType == "ParamsKeyword" {
65-
retVal = true
66-
break
147+
mod = mod.CloneObject()
148+
if _, ok := mod["Type"]; ok {
149+
return nil, errors.New("unexpected field in modifier: Type")
67150
}
151+
mod["Type"] = typ
152+
typ = mod
68153
}
69-
70-
return nodes.Bool(retVal), nil
154+
return typ, nil
71155
}
72156

73157
func funcDefMap(typ string) Mapping {
@@ -563,7 +647,7 @@ var Normalizers = []Mapping{
563647
"Default": Var("def_init"),
564648
"IsMissing": Bool(false),
565649
"IsStructuredTrivia": Bool(false),
566-
"Modifiers": Arr(),
650+
"Modifiers": Arr(), // TODO(dennwc): any cases when it's not empty?
567651
"Type": Var("type"),
568652
},
569653
Obj{
@@ -586,16 +670,27 @@ var Normalizers = []Mapping{
586670
"Default": Var("def_init"),
587671
"IsMissing": Bool(false),
588672
"IsStructuredTrivia": Any(),
589-
"Modifiers": opArrHasParams{origArr: Var("modifiers")},
590-
"Type": Var("type"),
673+
"Modifiers": opArrHasKeyword{
674+
keyword: "ParamsKeyword",
675+
opHas: Var("variadic"),
676+
opRest: opArrHasKeyword{
677+
keyword: "ThisKeyword",
678+
opHas: Var("this"),
679+
opRest: Var("rest"),
680+
},
681+
},
682+
"Type": Var("type"),
591683
},
592684
Obj{
593-
"Name": Var("name"),
594-
"Type": Var("type"),
685+
"Name": Var("name"),
686+
"Type": opArrToChain{
687+
opMods: Var("rest"),
688+
opType: Var("type"),
689+
},
595690
"Init": Var("def_init"),
596-
"Variadic": opArrHasParams{Var("modifiers")},
691+
"Variadic": Var("variadic"),
597692
"MapVariadic": Bool(false),
598-
"Receiver": Bool(false),
693+
"Receiver": Var("this"),
599694
},
600695
)),
601696

0 commit comments

Comments
 (0)