mirror of
https://github.com/hanzoai/expr.git
synced 2026-09-24 16:58:58 +00:00
Refactor to use new parser
This commit is contained in:
+8
-3
@@ -10,17 +10,22 @@ import (
|
||||
// Node represents items of abstract syntax tree.
|
||||
type Node interface {
|
||||
Location() file.Location
|
||||
SetLocation(file.Location)
|
||||
Type() reflect.Type
|
||||
SetType(reflect.Type)
|
||||
}
|
||||
|
||||
type Base struct {
|
||||
Loc file.Location
|
||||
loc file.Location
|
||||
nodeType reflect.Type
|
||||
}
|
||||
|
||||
func (n *Base) Location() file.Location {
|
||||
return n.Loc
|
||||
return n.loc
|
||||
}
|
||||
|
||||
func (n *Base) SetLocation(loc file.Location) {
|
||||
n.loc = loc
|
||||
}
|
||||
|
||||
func (n *Base) Type() reflect.Type {
|
||||
@@ -32,7 +37,7 @@ func (n *Base) SetType(t reflect.Type) {
|
||||
}
|
||||
|
||||
func Loc(l file.Location) Base {
|
||||
return Base{Loc: l}
|
||||
return Base{loc: l}
|
||||
}
|
||||
|
||||
type NilNode struct {
|
||||
|
||||
+1
-1
@@ -115,7 +115,7 @@ func (v *visitor) visit(node ast.Node) reflect.Type {
|
||||
|
||||
func (v *visitor) error(node ast.Node, format string, args ...interface{}) file.Error {
|
||||
return file.Error{
|
||||
Location: node.GetLocation(),
|
||||
Location: node.Location(),
|
||||
Message: fmt.Sprintf(format, args...),
|
||||
}
|
||||
}
|
||||
|
||||
+4
-4
@@ -23,8 +23,8 @@ func (p *operatorPatcher) Exit(node *ast.Node) {
|
||||
return
|
||||
}
|
||||
|
||||
leftType := binaryNode.Left.GetType()
|
||||
rightType := binaryNode.Right.GetType()
|
||||
leftType := binaryNode.Left.Type()
|
||||
rightType := binaryNode.Right.Type()
|
||||
|
||||
_, fn, ok := conf.FindSuitableOperatorOverload(fns, p.types, leftType, rightType)
|
||||
if ok {
|
||||
@@ -32,8 +32,8 @@ func (p *operatorPatcher) Exit(node *ast.Node) {
|
||||
Name: fn,
|
||||
Arguments: []ast.Node{binaryNode.Left, binaryNode.Right},
|
||||
}
|
||||
newNode.SetType((*node).GetType())
|
||||
newNode.SetLocation((*node).GetLocation())
|
||||
newNode.SetType((*node).Type())
|
||||
newNode.SetLocation((*node).Location())
|
||||
*node = newNode
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ func (c *compiler) emit(op byte, b ...byte) int {
|
||||
c.bytecode = append(c.bytecode, b...)
|
||||
|
||||
for i := 0; i < 1+len(b); i++ {
|
||||
c.locations = append(c.locations, c.currentNode.GetLocation())
|
||||
c.locations = append(c.locations, c.currentNode.Location())
|
||||
}
|
||||
|
||||
return current
|
||||
@@ -178,7 +178,7 @@ func (c *compiler) IdentifierNode(node *ast.IdentifierNode) {
|
||||
}
|
||||
|
||||
func (c *compiler) IntegerNode(node *ast.IntegerNode) {
|
||||
t := node.GetType()
|
||||
t := node.Type()
|
||||
if t == nil {
|
||||
c.emitPush(node.Value)
|
||||
return
|
||||
@@ -632,7 +632,7 @@ func encode(i uint16) []byte {
|
||||
}
|
||||
|
||||
func kind(node ast.Node) reflect.Kind {
|
||||
t := node.GetType()
|
||||
t := node.Type()
|
||||
if t == nil {
|
||||
return reflect.Invalid
|
||||
}
|
||||
|
||||
+3
-3
@@ -94,7 +94,7 @@ func ExampleEval_error() {
|
||||
|
||||
fmt.Printf("%v", output)
|
||||
|
||||
// Output: syntax error: mismatched input ']' expecting ')' (1:11)
|
||||
// Output: unexpected token Bracket("]") (1:11)
|
||||
// | (boo + bar]
|
||||
// | ..........^
|
||||
}
|
||||
@@ -108,9 +108,9 @@ func ExampleEval_matches() {
|
||||
|
||||
fmt.Printf("%v", output)
|
||||
|
||||
// Output: error parsing regexp: missing closing ): `a(` (1:13)
|
||||
// Output: error parsing regexp: missing closing ): `a(` (1:17)
|
||||
// | "a" matches "a("
|
||||
// | ............^
|
||||
// | ................^
|
||||
}
|
||||
|
||||
func ExampleCompile() {
|
||||
|
||||
+3
-3
@@ -27,9 +27,9 @@ func (e *Error) Error() string {
|
||||
return e.Message
|
||||
}
|
||||
|
||||
func (e *Error) Format(source *Source) error {
|
||||
func (e *Error) Format(source *Source) string {
|
||||
if e.Location.Empty() {
|
||||
return fmt.Errorf(e.Message)
|
||||
return e.Message
|
||||
}
|
||||
var result = fmt.Sprintf(
|
||||
"%s (%d:%d)",
|
||||
@@ -58,5 +58,5 @@ func (e *Error) Format(source *Source) error {
|
||||
}
|
||||
result += srcLine + indLine
|
||||
}
|
||||
return fmt.Errorf(result)
|
||||
return result
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ func (*inArray) Exit(node *Node) {
|
||||
if n.Operator == "in" || n.Operator == "not in" {
|
||||
if array, ok := n.Right.(*ArrayNode); ok {
|
||||
if len(array.Nodes) > 0 {
|
||||
t := n.Left.GetType()
|
||||
t := n.Left.Type()
|
||||
if t == nil || t.Kind() != reflect.Int {
|
||||
// This optimization can be only performed if left side is int type,
|
||||
// as runtime.in func uses reflect.Map.MapIndex and keys of map must,
|
||||
@@ -251,7 +251,7 @@ func (*constRange) Exit(node *Node) {
|
||||
}
|
||||
|
||||
func patch(node *Node, newNode Node) {
|
||||
newNode.SetType((*node).GetType())
|
||||
newNode.SetLocation((*node).GetLocation())
|
||||
newNode.SetType((*node).Type())
|
||||
newNode.SetLocation((*node).Location())
|
||||
*node = newNode
|
||||
}
|
||||
|
||||
@@ -17,11 +17,11 @@ func Lex(source *file.Source) ([]Token, error) {
|
||||
state = state(l)
|
||||
}
|
||||
|
||||
if l.err == nil {
|
||||
return l.tokens, nil
|
||||
if l.err != nil {
|
||||
return nil, fmt.Errorf("%v", l.err.Format(source))
|
||||
}
|
||||
|
||||
return nil, l.err.Format(source)
|
||||
return l.tokens, nil
|
||||
}
|
||||
|
||||
type lexer struct {
|
||||
@@ -62,7 +62,7 @@ func (l *lexer) emit(t Kind) {
|
||||
|
||||
func (l *lexer) emitValue(t Kind, value string) {
|
||||
l.tokens = append(l.tokens, Token{
|
||||
Location: l.loc(),
|
||||
Location: l.loc(l.start),
|
||||
Kind: t,
|
||||
Value: value,
|
||||
})
|
||||
@@ -105,17 +105,17 @@ func (l *lexer) acceptWord(word string) bool {
|
||||
func (l *lexer) error(format string, args ...interface{}) stateFn {
|
||||
if l.err == nil { // show first error
|
||||
l.err = &file.Error{
|
||||
Location: l.loc(),
|
||||
Location: l.loc(l.end - 1),
|
||||
Message: fmt.Sprintf(format, args...),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (l *lexer) loc() file.Location {
|
||||
func (l *lexer) loc(pos int) file.Location {
|
||||
line, column := 1, 0
|
||||
for i, ch := range []rune(l.input) {
|
||||
if i == l.end-1 {
|
||||
if i == pos {
|
||||
break
|
||||
}
|
||||
if ch == '\n' {
|
||||
|
||||
@@ -20,7 +20,7 @@ type lexErrorTest struct {
|
||||
|
||||
var lexTests = []lexTest{
|
||||
{
|
||||
".5 1 02 1e3 0xFF 1.2e-4 1_000_000 _42",
|
||||
".5 1 02 1e3 0xFF 1.2e-4 1_000_000 _42 -.5",
|
||||
[]Token{
|
||||
{Kind: Number, Value: ".5"},
|
||||
{Kind: Number, Value: "1"},
|
||||
@@ -30,6 +30,8 @@ var lexTests = []lexTest{
|
||||
{Kind: Number, Value: "1.2e-4"},
|
||||
{Kind: Number, Value: "1_000_000"},
|
||||
{Kind: Identifier, Value: "_42"},
|
||||
{Kind: Operator, Value: "-"},
|
||||
{Kind: Number, Value: ".5"},
|
||||
{Kind: EOF},
|
||||
},
|
||||
},
|
||||
|
||||
+4
-11
@@ -28,10 +28,10 @@ func root(l *lexer) stateFn {
|
||||
l.emit(Bracket)
|
||||
case strings.ContainsRune(")]}", r):
|
||||
l.emit(Bracket)
|
||||
case strings.ContainsRune(",?!:%&*+-/<=>^|", r):
|
||||
l.backup()
|
||||
return operator
|
||||
case r == '#':
|
||||
case strings.ContainsRune("#,?:%+-/", r): // single rune operator
|
||||
l.emit(Operator)
|
||||
case strings.ContainsRune("&|!=*<>", r): // possible double rune operator
|
||||
l.accept("&|=*")
|
||||
l.emit(Operator)
|
||||
case r == '.':
|
||||
l.backup()
|
||||
@@ -87,13 +87,6 @@ func (l *lexer) scanNumber() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func operator(l *lexer) stateFn {
|
||||
l.next()
|
||||
l.accept(".|&=*")
|
||||
l.emit(Operator)
|
||||
return root
|
||||
}
|
||||
|
||||
func dot(l *lexer) stateFn {
|
||||
l.next()
|
||||
if l.accept("0123456789") {
|
||||
|
||||
+40
-12
@@ -104,7 +104,7 @@ func Parse(input string) (*Tree, error) {
|
||||
}
|
||||
|
||||
if p.err != nil {
|
||||
return nil, p.err.Format(source)
|
||||
return nil, fmt.Errorf("%v", p.err.Format(source))
|
||||
}
|
||||
|
||||
return &Tree{
|
||||
@@ -203,7 +203,7 @@ func (p *parser) parsePrimary() Node {
|
||||
p.next()
|
||||
expr := p.parseExpression(op.precedence)
|
||||
return p.parsePostfixExpression(&UnaryNode{
|
||||
Base: Base{Loc: token.Location},
|
||||
Base: Loc(token.Location),
|
||||
Operator: token.Value,
|
||||
Node: expr,
|
||||
})
|
||||
@@ -447,25 +447,53 @@ func (p *parser) parsePostfixExpression(node Node) Node {
|
||||
|
||||
} else if token.Value == "[" {
|
||||
p.next()
|
||||
arg := p.parseExpression(0)
|
||||
if p.current.Is(Operator, ":") {
|
||||
var from, to Node
|
||||
|
||||
if p.current.Is(Operator, ":") { // slice without from [:1]
|
||||
p.next()
|
||||
from := arg
|
||||
to := p.parseExpression(0)
|
||||
|
||||
if !p.current.Is(Bracket, "]") { // slice without from and to [:]
|
||||
to = p.parseExpression(0)
|
||||
}
|
||||
|
||||
node = &SliceNode{
|
||||
Base: Loc(p.current.Location),
|
||||
Node: node,
|
||||
From: from,
|
||||
To: to,
|
||||
}
|
||||
p.expect(Bracket, "]")
|
||||
|
||||
} else {
|
||||
node = &IndexNode{
|
||||
Base: Loc(token.Location),
|
||||
Node: node,
|
||||
Index: arg,
|
||||
|
||||
from = p.parseExpression(0)
|
||||
|
||||
if p.current.Is(Operator, ":") {
|
||||
p.next()
|
||||
|
||||
if !p.current.Is(Bracket, "]") { // slice without to [1:]
|
||||
to = p.parseExpression(0)
|
||||
}
|
||||
|
||||
node = &SliceNode{
|
||||
Base: Loc(p.current.Location),
|
||||
Node: node,
|
||||
From: from,
|
||||
To: to,
|
||||
}
|
||||
p.expect(Bracket, "]")
|
||||
|
||||
} else {
|
||||
// Slice operator [:] was not found, it should by just index node.
|
||||
|
||||
node = &IndexNode{
|
||||
Base: Loc(token.Location),
|
||||
Node: node,
|
||||
Index: from,
|
||||
}
|
||||
p.expect(Bracket, "]")
|
||||
}
|
||||
}
|
||||
p.expect(Bracket, "]")
|
||||
|
||||
} else {
|
||||
break
|
||||
}
|
||||
|
||||
+18
-6
@@ -198,6 +198,18 @@ func TestParse(t *testing.T) {
|
||||
"array[1:2]",
|
||||
&ast.SliceNode{Node: &ast.IdentifierNode{Value: "array"}, From: &ast.IntegerNode{Value: 1}, To: &ast.IntegerNode{Value: 2}},
|
||||
},
|
||||
{
|
||||
"array[:2]",
|
||||
&ast.SliceNode{Node: &ast.IdentifierNode{Value: "array"}, To: &ast.IntegerNode{Value: 2}},
|
||||
},
|
||||
{
|
||||
"array[1:]",
|
||||
&ast.SliceNode{Node: &ast.IdentifierNode{Value: "array"}, From: &ast.IntegerNode{Value: 1}},
|
||||
},
|
||||
{
|
||||
"array[:]",
|
||||
&ast.SliceNode{Node: &ast.IdentifierNode{Value: "array"}},
|
||||
},
|
||||
}
|
||||
for _, test := range parseTests {
|
||||
actual, err := parser.Parse(test.input)
|
||||
@@ -215,14 +227,14 @@ func TestParse(t *testing.T) {
|
||||
|
||||
const errorTests = `
|
||||
foo.
|
||||
unexpected end of expression (1:4)
|
||||
unexpected end of expression (1:5)
|
||||
| foo.
|
||||
| ...^
|
||||
| ....^
|
||||
|
||||
a+
|
||||
unexpected token EOF (1:2)
|
||||
unexpected token EOF (1:3)
|
||||
| a+
|
||||
| .^
|
||||
| ..^
|
||||
|
||||
a ? (1+2) c
|
||||
unexpected token Identifier("c") (1:11)
|
||||
@@ -245,9 +257,9 @@ a map key must be a quoted string, a number, a identifier, or an expression encl
|
||||
| .^
|
||||
|
||||
a matches 'a:)b'
|
||||
error parsing regexp: unexpected ): ` + "`a:)b`" + ` (1:16)
|
||||
error parsing regexp: unexpected ): ` + "`a:)b`" + ` (1:17)
|
||||
| a matches 'a:)b'
|
||||
| ...............^
|
||||
| ................^
|
||||
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user