Skip to content

Commit 8294514

Browse files
committed
Fix fast call type checker and add error message to pointer accessor outside closure
1 parent b222077 commit 8294514

File tree

4 files changed

+51
-7
lines changed

4 files changed

+51
-7
lines changed

checker/checker.go

+14-2
Original file line numberDiff line numberDiff line change
@@ -327,8 +327,16 @@ func (v *visitor) FunctionNode(node *ast.FunctionNode) reflect.Type {
327327
if f, ok := v.types[node.Name]; ok {
328328
if fn, ok := isFuncType(f.Type); ok {
329329

330-
if !isInterface(fn) && fn.IsVariadic() && fn.NumOut() == 1 {
331-
rest := fn.In(fn.NumIn() - 1)
330+
inputParamsCount := 1 // for functions
331+
if f.Method {
332+
inputParamsCount = 2 // for methods
333+
}
334+
335+
if !isInterface(fn) &&
336+
fn.IsVariadic() &&
337+
fn.NumIn() == inputParamsCount &&
338+
fn.NumOut() == 1 {
339+
rest := fn.In(fn.NumIn() - 1) // function has only one param for functions and two for methods
332340
if rest.Kind() == reflect.Slice && rest.Elem().Kind() == reflect.Interface {
333341
node.Fast = true
334342
}
@@ -499,6 +507,10 @@ func (v *visitor) ClosureNode(node *ast.ClosureNode) reflect.Type {
499507
}
500508

501509
func (v *visitor) PointerNode(node *ast.PointerNode) reflect.Type {
510+
if len(v.collections) == 0 {
511+
panic(v.error(node, "cannot use pointer accessor outside closure"))
512+
}
513+
502514
collection := v.collections[len(v.collections)-1]
503515

504516
if t, ok := indexType(collection); ok {

expr_test.go

+18
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,24 @@ func TestOperator_interface(t *testing.T) {
419419
require.Equal(t, true, output)
420420
}
421421

422+
func TestExpr_readme_example(t *testing.T) {
423+
env := map[string]interface{}{
424+
"greet": "Hello, %v!",
425+
"names": []string{"world", "you"},
426+
"sprintf": fmt.Sprintf,
427+
}
428+
429+
code := `sprintf(greet, names[0])`
430+
431+
program, err := expr.Compile(code, expr.Env(env))
432+
require.NoError(t, err)
433+
434+
output, err := expr.Run(program, env)
435+
require.NoError(t, err)
436+
437+
require.Equal(t, "Hello, world!", output)
438+
}
439+
422440
func TestExpr(t *testing.T) {
423441
env := &mockEnv{
424442
Any: "any",

parser/parser.go

+14-5
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ type parser struct {
7777
current Token
7878
pos int
7979
err *file.Error
80+
closure bool
8081
}
8182

8283
type Tree struct {
@@ -217,12 +218,18 @@ func (p *parser) parsePrimary() Node {
217218
return p.parsePostfixExpression(expr)
218219
}
219220

220-
if token.Is(Operator, "#") || token.Is(Operator, ".") {
221-
if token.Is(Operator, "#") {
222-
p.next()
221+
if p.closure {
222+
if token.Is(Operator, "#") || token.Is(Operator, ".") {
223+
if token.Is(Operator, "#") {
224+
p.next()
225+
}
226+
node := &PointerNode{Base: Loc(token.Location)}
227+
return p.parsePostfixExpression(node)
228+
}
229+
} else {
230+
if token.Is(Operator, "#") || token.Is(Operator, ".") {
231+
p.error("cannot use pointer accessor outside closure")
223232
}
224-
node := &PointerNode{Base: Loc(token.Location)}
225-
return p.parsePostfixExpression(node)
226233
}
227234

228235
return p.parsePrimaryExpression()
@@ -353,7 +360,9 @@ func (p *parser) parseClosure() Node {
353360
token := p.current
354361
p.expect(Bracket, "{")
355362

363+
p.closure = true
356364
node := p.parseExpression(0)
365+
p.closure = false
357366

358367
p.expect(Bracket, "}")
359368
return &ClosureNode{

parser/parser_test.go

+5
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,11 @@ foo({.bar})
265265
a map key must be a quoted string, a number, a identifier, or an expression enclosed in parentheses (unexpected token Operator(".")) (1:6)
266266
| foo({.bar})
267267
| .....^
268+
269+
.foo
270+
cannot use pointer accessor outside closure (1:1)
271+
| .foo
272+
| ^
268273
`
269274

270275
func TestParse_error(t *testing.T) {

0 commit comments

Comments
 (0)