Skip to content

Commit

Permalink
Fix fast call type checker and add error message to pointer accessor …
Browse files Browse the repository at this point in the history
…outside closure
  • Loading branch information
antonmedv committed Feb 25, 2020
1 parent b222077 commit 8294514
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 7 deletions.
16 changes: 14 additions & 2 deletions checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -327,8 +327,16 @@ func (v *visitor) FunctionNode(node *ast.FunctionNode) reflect.Type {
if f, ok := v.types[node.Name]; ok {
if fn, ok := isFuncType(f.Type); ok {

if !isInterface(fn) && fn.IsVariadic() && fn.NumOut() == 1 {
rest := fn.In(fn.NumIn() - 1)
inputParamsCount := 1 // for functions
if f.Method {
inputParamsCount = 2 // for methods
}

if !isInterface(fn) &&
fn.IsVariadic() &&
fn.NumIn() == inputParamsCount &&
fn.NumOut() == 1 {
rest := fn.In(fn.NumIn() - 1) // function has only one param for functions and two for methods
if rest.Kind() == reflect.Slice && rest.Elem().Kind() == reflect.Interface {
node.Fast = true
}
Expand Down Expand Up @@ -499,6 +507,10 @@ func (v *visitor) ClosureNode(node *ast.ClosureNode) reflect.Type {
}

func (v *visitor) PointerNode(node *ast.PointerNode) reflect.Type {
if len(v.collections) == 0 {
panic(v.error(node, "cannot use pointer accessor outside closure"))
}

collection := v.collections[len(v.collections)-1]

if t, ok := indexType(collection); ok {
Expand Down
18 changes: 18 additions & 0 deletions expr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,24 @@ func TestOperator_interface(t *testing.T) {
require.Equal(t, true, output)
}

func TestExpr_readme_example(t *testing.T) {
env := map[string]interface{}{
"greet": "Hello, %v!",
"names": []string{"world", "you"},
"sprintf": fmt.Sprintf,
}

code := `sprintf(greet, names[0])`

program, err := expr.Compile(code, expr.Env(env))
require.NoError(t, err)

output, err := expr.Run(program, env)
require.NoError(t, err)

require.Equal(t, "Hello, world!", output)
}

func TestExpr(t *testing.T) {
env := &mockEnv{
Any: "any",
Expand Down
19 changes: 14 additions & 5 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ type parser struct {
current Token
pos int
err *file.Error
closure bool
}

type Tree struct {
Expand Down Expand Up @@ -217,12 +218,18 @@ func (p *parser) parsePrimary() Node {
return p.parsePostfixExpression(expr)
}

if token.Is(Operator, "#") || token.Is(Operator, ".") {
if token.Is(Operator, "#") {
p.next()
if p.closure {
if token.Is(Operator, "#") || token.Is(Operator, ".") {
if token.Is(Operator, "#") {
p.next()
}
node := &PointerNode{Base: Loc(token.Location)}
return p.parsePostfixExpression(node)
}
} else {
if token.Is(Operator, "#") || token.Is(Operator, ".") {
p.error("cannot use pointer accessor outside closure")
}
node := &PointerNode{Base: Loc(token.Location)}
return p.parsePostfixExpression(node)
}

return p.parsePrimaryExpression()
Expand Down Expand Up @@ -353,7 +360,9 @@ func (p *parser) parseClosure() Node {
token := p.current
p.expect(Bracket, "{")

p.closure = true
node := p.parseExpression(0)
p.closure = false

p.expect(Bracket, "}")
return &ClosureNode{
Expand Down
5 changes: 5 additions & 0 deletions parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,11 @@ foo({.bar})
a map key must be a quoted string, a number, a identifier, or an expression enclosed in parentheses (unexpected token Operator(".")) (1:6)
| foo({.bar})
| .....^
.foo
cannot use pointer accessor outside closure (1:1)
| .foo
| ^
`

func TestParse_error(t *testing.T) {
Expand Down

0 comments on commit 8294514

Please sign in to comment.