Skip to content

Commit 6180b0b

Browse files
committed
Refactor kind promotion
1 parent dd275da commit 6180b0b

File tree

8 files changed

+79
-61
lines changed

8 files changed

+79
-61
lines changed

ast/node.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ type IntegerNode struct {
3030
l file.Location
3131
t reflect.Type
3232

33-
Value int
34-
Certain bool
33+
Value int
3534
}
3635

3736
type FloatNode struct {

checker/checker.go

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -141,15 +141,16 @@ func (v *visitor) BinaryNode(node *ast.BinaryNode) reflect.Type {
141141
l := v.visit(node.Left)
142142
r := v.visit(node.Right)
143143

144+
// Kind Promotion.
145+
// If the operands of a binary operation are different kinds of untyped constants,
146+
// the result use the kind that appears later in this list: int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, float32, float64
144147
if isNumber(l) && isNumber(r) && !isInterface(l) && !isInterface(r) {
145-
// Real integer type is unknown until binary node,
146-
// it maybe int, int64, float64, etc.
147-
if !isCertain(node.Left) && isCertain(node.Right) {
148+
if integerTypeWeight(l) < integerTypeWeight(r) {
149+
setTypeForIntegers(node.Left, r)
148150
l = r
149-
setUncertainType(node.Left, dereference(r))
150-
} else if isCertain(node.Left) && !isCertain(node.Right) {
151+
} else if integerTypeWeight(l) > integerTypeWeight(r) {
152+
setTypeForIntegers(node.Right, l)
151153
r = l
152-
setUncertainType(node.Right, dereference(l))
153154
}
154155
}
155156

@@ -168,9 +169,19 @@ func (v *visitor) BinaryNode(node *ast.BinaryNode) reflect.Type {
168169
if isString(l) && isStruct(r) {
169170
return boolType
170171
}
171-
if isArray(r) || isMap(r) {
172-
if isNumber(l) && isCertain(node.Left) && !isCertain(node.Right) {
173-
setUncertainType(node.Right, dereference(l))
172+
if isMap(r) {
173+
return boolType
174+
}
175+
if isArray(r) {
176+
// Kind Promotion.
177+
// Example:
178+
// foo in 0..9
179+
// where foo is int64.
180+
if b, ok := node.Right.(*ast.BinaryNode); ok {
181+
if isInteger(l) && b.Operator == ".." {
182+
setTypeForIntegers(b.Left, l)
183+
setTypeForIntegers(b.Right, l)
184+
}
174185
}
175186
return boolType
176187
}
@@ -190,12 +201,6 @@ func (v *visitor) BinaryNode(node *ast.BinaryNode) reflect.Type {
190201

191202
case "**":
192203
if isNumber(l) && isNumber(r) && isComparable(l, r) {
193-
if !isCertain(node.Left) {
194-
setUncertainType(node.Left, integerType)
195-
}
196-
if !isCertain(node.Right) {
197-
setUncertainType(node.Right, integerType)
198-
}
199204
return floatType
200205
}
201206

@@ -257,7 +262,7 @@ func (v *visitor) IndexNode(node *ast.IndexNode) reflect.Type {
257262

258263
if t, ok := indexType(t); ok {
259264
if !isInteger(i) && !isString(i) {
260-
panic(v.error(node, "invalid operation: can't use %v as index to %v", i, t))
265+
panic(v.error(node, "invalid operation: cannot use %v as index to %v", i, t))
261266
}
262267
return t
263268
}
@@ -305,13 +310,13 @@ func (v *visitor) FunctionNode(node *ast.FunctionNode) reflect.Type {
305310
t := v.visit(arg)
306311
in := fn.In(n)
307312

308-
if !isCertain(arg) {
313+
if isIntegerOrArithmeticOperation(arg) {
309314
t = in
310-
setUncertainType(arg, in)
315+
setTypeForIntegers(arg, t)
311316
}
312317

313318
if !t.AssignableTo(in) {
314-
panic(v.error(arg, "can't use %v as argument (type %v) to call %v ", t, in, node.Name))
319+
panic(v.error(arg, "cannot use %v as argument (type %v) to call %v ", t, in, node.Name))
315320
}
316321
n++
317322
}
@@ -364,13 +369,13 @@ func (v *visitor) MethodNode(node *ast.MethodNode) reflect.Type {
364369
t := v.visit(arg)
365370
in := fn.In(n)
366371

367-
if !isCertain(arg) {
372+
if isIntegerOrArithmeticOperation(arg) {
368373
t = in
369-
setUncertainType(arg, in)
374+
setTypeForIntegers(arg, t)
370375
}
371376

372377
if !t.AssignableTo(in) {
373-
panic(v.error(arg, "can't use %v as argument (type %v) to call %v ", t, in, node.Method))
378+
panic(v.error(arg, "cannot use %v as argument (type %v) to call %v ", t, in, node.Method))
374379
}
375380
n++
376381
}
@@ -465,7 +470,7 @@ func (v *visitor) PointerNode(node *ast.PointerNode) reflect.Type {
465470
if t, ok := indexType(collection); ok {
466471
return t
467472
}
468-
panic(v.error(node, "can't use %v as array", collection))
473+
panic(v.error(node, "cannot use %v as array", collection))
469474
}
470475

471476
func (v *visitor) ConditionalNode(node *ast.ConditionalNode) reflect.Type {

checker/checker_test.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -130,10 +130,13 @@ func TestCheck(t *testing.T) {
130130
"'foo' contains 'bar'",
131131
"'foo' endsWith 'bar'",
132132
"'foo' startsWith 'bar'",
133+
"(1 == 1) || (String matches Any)",
134+
"1 + 2 + Int64",
135+
"1 + 2 == FloatPtr",
136+
"1 + 2 + Float + 3 + 4",
133137
"1 < Float",
134138
"1 <= Float",
135139
"1 == 2 and true or Bool",
136-
"1 == FloatPtr",
137140
"1 > Float",
138141
"1 >= Float",
139142
"2**3 + 1",
@@ -144,6 +147,7 @@ func TestCheck(t *testing.T) {
144147
"Any.Thing.Is.Bool",
145148
"ArrayOfAny['string'].next.goes['any thing']",
146149
"ArrayOfFoo[0].Bar.Baz",
150+
"ArrayOfFoo[1].Int64 + 1",
147151
"Bool && Any",
148152
"BoolFn() and BoolFn()",
149153
"EmbedPtr.EmbPtrStr + String",
@@ -161,6 +165,7 @@ func TestCheck(t *testing.T) {
161165
"Int + Int + Int",
162166
"Int == Any",
163167
"Int in Int..Int",
168+
"Int64 % 1",
164169
"IntPtr == Int",
165170
"len([])",
166171
"Map.id.Bar.Baz",
@@ -175,7 +180,6 @@ func TestCheck(t *testing.T) {
175180
"String in Foo",
176181
"String matches 'ok'",
177182
"String matches Any",
178-
"(1 == 1) || (String matches Any)",
179183
"String not in Foo2p",
180184
"StringPtr == nil",
181185
"Sub.Method(0) + String",
@@ -442,9 +446,10 @@ type bar struct {
442446
}
443447

444448
type foo struct {
445-
Bar bar
446-
Fn func() bool
447-
Abc abc
449+
Int64 int64
450+
Bar bar
451+
Fn func() bool
452+
Abc abc
448453
}
449454

450455
type SubSub struct {
@@ -477,7 +482,7 @@ type mockEnv2 struct {
477482
Fn func(bool, int, string, interface{}) string
478483
Bool bool
479484
Float float64
480-
Int64 int
485+
Int64 int64
481486
Int int
482487
String string
483488
BoolPtr *bool

checker/types.go

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -268,33 +268,42 @@ func funcType(ntype reflect.Type) (reflect.Type, bool) {
268268
return nil, false
269269
}
270270

271-
type setVisitor struct {
272-
ast.BaseVisitor
273-
t reflect.Type
271+
func integerTypeWeight(t reflect.Type) uint {
272+
return uint(t.Kind())
274273
}
275274

276-
func (v setVisitor) IntegerNode(node *ast.IntegerNode) {
277-
if !node.Certain {
278-
node.SetType(v.t)
279-
node.Certain = true
275+
func isIntegerOrArithmeticOperation(node ast.Node) bool {
276+
switch n := node.(type) {
277+
case *ast.IntegerNode:
278+
return true
279+
case *ast.UnaryNode:
280+
switch n.Operator {
281+
case "+", "-":
282+
return true
283+
}
284+
case *ast.BinaryNode:
285+
switch n.Operator {
286+
case "+", "/", "-", "*":
287+
return true
288+
}
280289
}
290+
return false
281291
}
282292

283-
func setUncertainType(node ast.Node, t reflect.Type) {
284-
ast.Walk(&node, setVisitor{t: dereference(t)})
285-
}
286-
287-
type hasVisitor struct {
288-
ast.BaseVisitor
289-
certain bool
290-
}
291-
292-
func (v *hasVisitor) IntegerNode(node *ast.IntegerNode) {
293-
v.certain = v.certain && node.Certain
294-
}
295-
296-
func isCertain(node ast.Node) bool {
297-
v := &hasVisitor{certain: true}
298-
ast.Walk(&node, v)
299-
return v.certain
293+
func setTypeForIntegers(node ast.Node, t reflect.Type) {
294+
switch n := node.(type) {
295+
case *ast.IntegerNode:
296+
n.SetType(t)
297+
case *ast.UnaryNode:
298+
switch n.Operator {
299+
case "+", "-":
300+
setTypeForIntegers(n.Node, t)
301+
}
302+
case *ast.BinaryNode:
303+
switch n.Operator {
304+
case "+", "/", "-", "*":
305+
setTypeForIntegers(n.Left, t)
306+
setTypeForIntegers(n.Right, t)
307+
}
308+
}
300309
}

compiler/compiler.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -210,7 +210,7 @@ func (c *compiler) IntegerNode(node *ast.IntegerNode) {
210210
c.emit(OpConst, c.makeConstant(uint64(node.Value))...)
211211

212212
default:
213-
panic(fmt.Sprintf("can't compile %v to %v", node.Value, node.GetType()))
213+
panic(fmt.Sprintf("cannot compile %v to %v", node.Value, node.GetType()))
214214
}
215215
}
216216

expr.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ import (
1010

1111
// Eval parses, compiles and runs given input.
1212
func Eval(input string, env interface{}) (interface{}, error) {
13-
node, err := parser.Parse(input)
13+
tree, err := parser.Parse(input)
1414
if err != nil {
1515
return nil, err
1616
}
1717

18-
program, err := compiler.Compile(node)
18+
program, err := compiler.Compile(tree)
1919
if err != nil {
2020
return nil, err
2121
}

vm/runtime.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ func fetchFn(from interface{}, name string) reflect.Value {
7676
return value
7777
}
7878
}
79-
panic(fmt.Sprintf(`can't get "%v" from %T`, name, from))
79+
panic(fmt.Sprintf(`cannot get "%v" from %T`, name, from))
8080
}
8181

8282
func in(needle interface{}, array interface{}) bool {

vm/vm_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,8 +189,8 @@ func TestRun(t *testing.T) {
189189
true,
190190
},
191191
{
192-
`1.5 in [1] && 1 in [1.5]`,
193-
false,
192+
`1.5 in [1.5] && 1 in [1]`,
193+
true,
194194
},
195195
{
196196
`(true ? 0+1 : 2+3) + (false ? -1 : -2)`,

0 commit comments

Comments
 (0)