mirror of
https://github.com/danog/liquid.git
synced 2024-11-30 09:59:02 +01:00
a.b syntax
This commit is contained in:
parent
87b8198e4e
commit
5dbd972765
@ -6,7 +6,7 @@
|
||||
|
||||
- [ ] Basics
|
||||
- [ ] Constants
|
||||
- [ ] Variables
|
||||
- [x] Variables
|
||||
- [ ] Operators
|
||||
- [ ] Arrays
|
||||
- [ ] Whitespace Control
|
||||
|
@ -11,6 +11,9 @@ import (
|
||||
func TestChunkParser(t *testing.T) {
|
||||
ctx := Context{map[string]interface{}{
|
||||
"x": 123,
|
||||
"page": map[string]interface{}{
|
||||
"title": "Introduction",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@ -32,6 +35,7 @@ func TestChunkParser(t *testing.T) {
|
||||
var chunkTests = []struct{ in, expected string }{
|
||||
{"{{12}}", "12"},
|
||||
{"{{x}}", "123"},
|
||||
{"{{page.title}}", "Introduction"},
|
||||
{"{%if true%}true{%endif%}", "true"},
|
||||
{"{%if false%}false{%endif%}", ""},
|
||||
{"{%if 0%}true{%endif%}", "true"},
|
||||
|
@ -2,6 +2,7 @@
|
||||
package main
|
||||
import (
|
||||
_ "fmt"
|
||||
"reflect"
|
||||
)
|
||||
%}
|
||||
%union {
|
||||
@ -12,9 +13,24 @@ import (
|
||||
%type <f> expr
|
||||
%token <val> LITERAL
|
||||
%token <name> IDENTIFIER RELATION
|
||||
%left DOT
|
||||
%%
|
||||
top: expr { yylex.(*lexer).val = $1 };
|
||||
top: expr ';' { yylex.(*lexer).val = $1 };
|
||||
|
||||
expr:
|
||||
LITERAL { $$ = func(_ Context) interface{} { return $1 } }
|
||||
| IDENTIFIER { $$ = func(ctx Context) interface{} { return ctx.Variables[$1] } }
|
||||
| expr DOT IDENTIFIER {
|
||||
e, a := $1, $3
|
||||
$$ = func(ctx Context) interface{} {
|
||||
input := e(ctx)
|
||||
ref := reflect.ValueOf(input)
|
||||
switch ref.Kind() {
|
||||
case reflect.Map:
|
||||
return ref.MapIndex(reflect.ValueOf(a)).Interface()
|
||||
default:
|
||||
return input
|
||||
}
|
||||
}
|
||||
}
|
||||
;
|
||||
|
@ -4,7 +4,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@ -32,19 +31,19 @@ func ScanExpression(data string) ([]yySymType, error) {
|
||||
func TestExpressionScanner(t *testing.T) {
|
||||
tokens, err := ScanExpression("abc > 123")
|
||||
require.NoError(t, err)
|
||||
fmt.Println("tokens =", tokens)
|
||||
require.Len(t, tokens, 3)
|
||||
}
|
||||
|
||||
func TestExpressionParser(t *testing.T) {
|
||||
ctx := Context{map[string]interface{}{
|
||||
"abc": 123,
|
||||
}}
|
||||
lexer := newLexer([]byte(`12`))
|
||||
lexer := newLexer([]byte(`12;`))
|
||||
n := yyParse(lexer)
|
||||
require.Zero(t, n)
|
||||
require.Equal(t, float64(12), lexer.val(ctx))
|
||||
|
||||
lexer = newLexer([]byte(`abc`))
|
||||
lexer = newLexer([]byte(`abc;`))
|
||||
n = yyParse(lexer)
|
||||
require.Zero(t, n)
|
||||
require.Equal(t, 123, lexer.val(ctx))
|
||||
|
@ -29,7 +29,7 @@ func (n *ASTText) Render(w io.Writer, _ Context) error {
|
||||
}
|
||||
|
||||
func EvaluateExpr(expr string, ctx Context) (interface{}, error) {
|
||||
lexer := newLexer([]byte(expr))
|
||||
lexer := newLexer([]byte(expr + ";"))
|
||||
n := yyParse(lexer)
|
||||
if n != 0 {
|
||||
return nil, fmt.Errorf("parse error in %s", expr)
|
||||
|
169
scanner.go
169
scanner.go
@ -11,43 +11,44 @@ import "strconv"
|
||||
var _expression_actions []byte = []byte{
|
||||
0, 1, 0, 1, 1, 1, 6, 1, 7,
|
||||
1, 8, 1, 9, 1, 10, 1, 11,
|
||||
2, 2, 3, 2, 2, 4, 2, 2,
|
||||
5,
|
||||
1, 12, 1, 13, 2, 2, 3, 2,
|
||||
2, 4, 2, 2, 5,
|
||||
}
|
||||
|
||||
var _expression_key_offsets []byte = []byte{
|
||||
0, 0, 1, 3, 23, 26, 29, 31,
|
||||
32, 39, 47, 55, 63, 71, 79, 87,
|
||||
95, 103, 111, 119, 127, 135, 143, 151,
|
||||
159,
|
||||
0, 0, 1, 3, 25, 28, 31, 33,
|
||||
34, 41, 49, 57, 65, 73, 81, 89,
|
||||
97, 105, 113, 121, 129, 137, 145, 153,
|
||||
161,
|
||||
}
|
||||
|
||||
var _expression_trans_keys []byte = []byte{
|
||||
61, 48, 57, 32, 33, 45, 62, 95,
|
||||
97, 99, 102, 111, 116, 9, 13, 48,
|
||||
57, 60, 61, 65, 90, 98, 122, 32,
|
||||
9, 13, 46, 48, 57, 48, 57, 61,
|
||||
95, 48, 57, 65, 90, 97, 122, 95,
|
||||
110, 48, 57, 65, 90, 97, 122, 95,
|
||||
100, 48, 57, 65, 90, 97, 122, 95,
|
||||
111, 48, 57, 65, 90, 97, 122, 95,
|
||||
110, 48, 57, 65, 90, 97, 122, 95,
|
||||
116, 48, 57, 65, 90, 97, 122, 95,
|
||||
97, 48, 57, 65, 90, 98, 122, 95,
|
||||
105, 48, 57, 65, 90, 97, 122, 95,
|
||||
110, 48, 57, 65, 90, 97, 122, 95,
|
||||
115, 48, 57, 65, 90, 97, 122, 95,
|
||||
97, 48, 57, 65, 90, 98, 122, 95,
|
||||
108, 48, 57, 65, 90, 97, 122, 95,
|
||||
115, 48, 57, 65, 90, 97, 122, 95,
|
||||
101, 48, 57, 65, 90, 97, 122, 95,
|
||||
114, 48, 57, 65, 90, 97, 122, 95,
|
||||
114, 48, 57, 65, 90, 97, 122, 95,
|
||||
117, 48, 57, 65, 90, 97, 122,
|
||||
61, 48, 57, 32, 33, 45, 46, 59,
|
||||
62, 95, 97, 99, 102, 111, 116, 9,
|
||||
13, 48, 57, 60, 61, 65, 90, 98,
|
||||
122, 32, 9, 13, 46, 48, 57, 48,
|
||||
57, 61, 95, 48, 57, 65, 90, 97,
|
||||
122, 95, 110, 48, 57, 65, 90, 97,
|
||||
122, 95, 100, 48, 57, 65, 90, 97,
|
||||
122, 95, 111, 48, 57, 65, 90, 97,
|
||||
122, 95, 110, 48, 57, 65, 90, 97,
|
||||
122, 95, 116, 48, 57, 65, 90, 97,
|
||||
122, 95, 97, 48, 57, 65, 90, 98,
|
||||
122, 95, 105, 48, 57, 65, 90, 97,
|
||||
122, 95, 110, 48, 57, 65, 90, 97,
|
||||
122, 95, 115, 48, 57, 65, 90, 97,
|
||||
122, 95, 97, 48, 57, 65, 90, 98,
|
||||
122, 95, 108, 48, 57, 65, 90, 97,
|
||||
122, 95, 115, 48, 57, 65, 90, 97,
|
||||
122, 95, 101, 48, 57, 65, 90, 97,
|
||||
122, 95, 114, 48, 57, 65, 90, 97,
|
||||
122, 95, 114, 48, 57, 65, 90, 97,
|
||||
122, 95, 117, 48, 57, 65, 90, 97,
|
||||
122,
|
||||
}
|
||||
|
||||
var _expression_single_lengths []byte = []byte{
|
||||
0, 1, 0, 10, 1, 1, 0, 1,
|
||||
0, 1, 0, 12, 1, 1, 0, 1,
|
||||
1, 2, 2, 2, 2, 2, 2, 2,
|
||||
2, 2, 2, 2, 2, 2, 2, 2,
|
||||
2,
|
||||
@ -61,44 +62,46 @@ var _expression_range_lengths []byte = []byte{
|
||||
}
|
||||
|
||||
var _expression_index_offsets []byte = []byte{
|
||||
0, 0, 2, 4, 20, 23, 26, 28,
|
||||
30, 35, 41, 47, 53, 59, 65, 71,
|
||||
77, 83, 89, 95, 101, 107, 113, 119,
|
||||
125,
|
||||
0, 0, 2, 4, 22, 25, 28, 30,
|
||||
32, 37, 43, 49, 55, 61, 67, 73,
|
||||
79, 85, 91, 97, 103, 109, 115, 121,
|
||||
127,
|
||||
}
|
||||
|
||||
var _expression_indicies []byte = []byte{
|
||||
0, 1, 2, 1, 3, 4, 5, 6,
|
||||
7, 8, 9, 10, 11, 12, 3, 2,
|
||||
4, 7, 7, 1, 3, 3, 13, 15,
|
||||
2, 14, 15, 14, 0, 16, 7, 7,
|
||||
7, 7, 17, 7, 19, 7, 7, 7,
|
||||
18, 7, 20, 7, 7, 7, 18, 7,
|
||||
21, 7, 7, 7, 18, 7, 22, 7,
|
||||
7, 7, 18, 7, 23, 7, 7, 7,
|
||||
18, 7, 24, 7, 7, 7, 18, 7,
|
||||
25, 7, 7, 7, 18, 7, 26, 7,
|
||||
7, 7, 18, 7, 20, 7, 7, 7,
|
||||
18, 7, 27, 7, 7, 7, 18, 7,
|
||||
28, 7, 7, 7, 18, 7, 29, 7,
|
||||
7, 7, 18, 7, 30, 7, 7, 7,
|
||||
18, 7, 20, 7, 7, 7, 18, 7,
|
||||
31, 7, 7, 7, 18, 7, 29, 7,
|
||||
7, 7, 18,
|
||||
7, 8, 9, 10, 11, 12, 13, 14,
|
||||
3, 2, 4, 9, 9, 1, 3, 3,
|
||||
15, 17, 2, 16, 17, 16, 0, 18,
|
||||
9, 9, 9, 9, 19, 9, 21, 9,
|
||||
9, 9, 20, 9, 22, 9, 9, 9,
|
||||
20, 9, 23, 9, 9, 9, 20, 9,
|
||||
24, 9, 9, 9, 20, 9, 25, 9,
|
||||
9, 9, 20, 9, 26, 9, 9, 9,
|
||||
20, 9, 27, 9, 9, 9, 20, 9,
|
||||
28, 9, 9, 9, 20, 9, 22, 9,
|
||||
9, 9, 20, 9, 29, 9, 9, 9,
|
||||
20, 9, 30, 9, 9, 9, 20, 9,
|
||||
31, 9, 9, 9, 20, 9, 32, 9,
|
||||
9, 9, 20, 9, 22, 9, 9, 9,
|
||||
20, 9, 33, 9, 9, 9, 20, 9,
|
||||
31, 9, 9, 9, 20,
|
||||
}
|
||||
|
||||
var _expression_trans_targs []byte = []byte{
|
||||
3, 0, 5, 4, 1, 2, 7, 8,
|
||||
9, 11, 18, 22, 23, 3, 3, 6,
|
||||
3, 3, 3, 10, 8, 12, 13, 14,
|
||||
15, 16, 17, 19, 20, 21, 8, 24,
|
||||
3, 0, 5, 4, 1, 2, 3, 3,
|
||||
7, 8, 9, 11, 18, 22, 23, 3,
|
||||
3, 6, 3, 3, 3, 10, 8, 12,
|
||||
13, 14, 15, 16, 17, 19, 20, 21,
|
||||
8, 24,
|
||||
}
|
||||
|
||||
var _expression_trans_actions []byte = []byte{
|
||||
5, 0, 0, 0, 0, 0, 0, 23,
|
||||
0, 0, 0, 0, 0, 13, 7, 0,
|
||||
9, 15, 11, 0, 20, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 17, 0,
|
||||
9, 0, 0, 0, 0, 0, 5, 7,
|
||||
0, 27, 0, 0, 0, 0, 0, 17,
|
||||
11, 0, 13, 19, 15, 0, 24, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0,
|
||||
21, 0,
|
||||
}
|
||||
|
||||
var _expression_to_state_actions []byte = []byte{
|
||||
@ -116,10 +119,10 @@ var _expression_from_state_actions []byte = []byte{
|
||||
}
|
||||
|
||||
var _expression_eof_trans []byte = []byte{
|
||||
0, 0, 0, 0, 14, 15, 15, 17,
|
||||
18, 19, 19, 19, 19, 19, 19, 19,
|
||||
19, 19, 19, 19, 19, 19, 19, 19,
|
||||
19,
|
||||
0, 0, 0, 0, 16, 17, 17, 19,
|
||||
20, 21, 21, 21, 21, 21, 21, 21,
|
||||
21, 21, 21, 21, 21, 21, 21, 21,
|
||||
21,
|
||||
}
|
||||
|
||||
const expression_start int = 3
|
||||
@ -149,7 +152,7 @@ func newLexer(data []byte) *lexer {
|
||||
pe: len(data),
|
||||
}
|
||||
|
||||
//line scanner.go:153
|
||||
//line scanner.go:156
|
||||
{
|
||||
lex.cs = expression_start
|
||||
lex.ts = 0
|
||||
@ -166,7 +169,7 @@ func (lex *lexer) Lex(out *yySymType) int {
|
||||
tok := 0
|
||||
|
||||
|
||||
//line scanner.go:170
|
||||
//line scanner.go:173
|
||||
{
|
||||
var _klen int
|
||||
var _trans int
|
||||
@ -189,7 +192,7 @@ _resume:
|
||||
//line NONE:1
|
||||
lex.ts = ( lex.p)
|
||||
|
||||
//line scanner.go:193
|
||||
//line scanner.go:196
|
||||
}
|
||||
}
|
||||
|
||||
@ -265,19 +268,29 @@ _eof_trans:
|
||||
|
||||
case 3:
|
||||
//line scanner.rl:40
|
||||
lex.act = 2;
|
||||
lex.act = 4;
|
||||
case 4:
|
||||
//line scanner.rl:59
|
||||
lex.act = 4;
|
||||
lex.act = 6;
|
||||
case 5:
|
||||
//line scanner.rl:45
|
||||
lex.act = 5;
|
||||
lex.act = 7;
|
||||
case 6:
|
||||
//line scanner.rl:66
|
||||
lex.te = ( lex.p)+1
|
||||
{ tok = DOT; ( lex.p)++; goto _out
|
||||
}
|
||||
case 7:
|
||||
//line scanner.rl:67
|
||||
lex.te = ( lex.p)+1
|
||||
{ tok = ';'; ( lex.p)++; goto _out
|
||||
}
|
||||
case 8:
|
||||
//line scanner.rl:59
|
||||
lex.te = ( lex.p)+1
|
||||
{ tok = RELATION; out.name = lex.token(); ( lex.p)++; goto _out
|
||||
}
|
||||
case 7:
|
||||
case 9:
|
||||
//line scanner.rl:50
|
||||
lex.te = ( lex.p)
|
||||
( lex.p)--
|
||||
@ -291,13 +304,13 @@ _eof_trans:
|
||||
( lex.p)++; goto _out
|
||||
|
||||
}
|
||||
case 8:
|
||||
case 10:
|
||||
//line scanner.rl:59
|
||||
lex.te = ( lex.p)
|
||||
( lex.p)--
|
||||
{ tok = RELATION; out.name = lex.token(); ( lex.p)++; goto _out
|
||||
}
|
||||
case 9:
|
||||
case 11:
|
||||
//line scanner.rl:45
|
||||
lex.te = ( lex.p)
|
||||
( lex.p)--
|
||||
@ -307,15 +320,15 @@ _eof_trans:
|
||||
( lex.p)++; goto _out
|
||||
|
||||
}
|
||||
case 10:
|
||||
//line scanner.rl:70
|
||||
case 12:
|
||||
//line scanner.rl:72
|
||||
lex.te = ( lex.p)
|
||||
( lex.p)--
|
||||
|
||||
case 11:
|
||||
case 13:
|
||||
//line NONE:1
|
||||
switch lex.act {
|
||||
case 2:
|
||||
case 4:
|
||||
{( lex.p) = ( lex.te) - 1
|
||||
|
||||
tok = LITERAL
|
||||
@ -323,11 +336,11 @@ _eof_trans:
|
||||
( lex.p)++; goto _out
|
||||
|
||||
}
|
||||
case 4:
|
||||
case 6:
|
||||
{( lex.p) = ( lex.te) - 1
|
||||
tok = RELATION; out.name = lex.token(); ( lex.p)++; goto _out
|
||||
}
|
||||
case 5:
|
||||
case 7:
|
||||
{( lex.p) = ( lex.te) - 1
|
||||
|
||||
tok = IDENTIFIER
|
||||
@ -337,7 +350,7 @@ _eof_trans:
|
||||
}
|
||||
}
|
||||
|
||||
//line scanner.go:341
|
||||
//line scanner.go:354
|
||||
}
|
||||
}
|
||||
|
||||
@ -351,7 +364,7 @@ _again:
|
||||
//line NONE:1
|
||||
lex.ts = 0
|
||||
|
||||
//line scanner.go:355
|
||||
//line scanner.go:368
|
||||
}
|
||||
}
|
||||
|
||||
@ -373,7 +386,7 @@ _again:
|
||||
_out: {}
|
||||
}
|
||||
|
||||
//line scanner.rl:74
|
||||
//line scanner.rl:76
|
||||
|
||||
|
||||
return tok
|
||||
|
@ -63,6 +63,8 @@ func (lex *lexer) Lex(out *yySymType) int {
|
||||
|
||||
main := |*
|
||||
number => Number;
|
||||
'.' => { tok = DOT; fbreak; };
|
||||
';' => { tok = ';'; fbreak; };
|
||||
("true" | "false") => Bool;
|
||||
("==" | "!=" | ">" | ">" | ">=" | "<=") => Relation;
|
||||
("and" | "or" | "contains") => Relation;
|
||||
|
44
y.go
44
y.go
@ -6,7 +6,7 @@ import __yyfmt__ "fmt"
|
||||
//line expression_parser.y:2
|
||||
import (
|
||||
_ "fmt"
|
||||
_ "reflect"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
//line expression_parser.y:8
|
||||
@ -30,6 +30,7 @@ var yyToknames = [...]string{
|
||||
"IDENTIFIER",
|
||||
"RELATION",
|
||||
"DOT",
|
||||
"';'",
|
||||
}
|
||||
var yyStatenames = [...]string{}
|
||||
|
||||
@ -46,39 +47,44 @@ var yyExca = [...]int{
|
||||
|
||||
const yyPrivate = 57344
|
||||
|
||||
const yyLast = 4
|
||||
const yyLast = 7
|
||||
|
||||
var yyAct = [...]int{
|
||||
|
||||
3, 4, 1, 2,
|
||||
6, 5, 3, 4, 7, 1, 2,
|
||||
}
|
||||
var yyPact = [...]int{
|
||||
|
||||
-4, -1000, -1000, -1000, -1000,
|
||||
-2, -1000, -7, -1000, -1000, -1000, -1, -1000,
|
||||
}
|
||||
var yyPgo = [...]int{
|
||||
|
||||
0, 3, 2,
|
||||
0, 6, 5,
|
||||
}
|
||||
var yyR1 = [...]int{
|
||||
|
||||
0, 2, 1, 1,
|
||||
0, 2, 1, 1, 1,
|
||||
}
|
||||
var yyR2 = [...]int{
|
||||
|
||||
0, 1, 1, 1,
|
||||
0, 2, 1, 1, 3,
|
||||
}
|
||||
var yyChk = [...]int{
|
||||
|
||||
-1000, -2, -1, 4, 5,
|
||||
-1000, -2, -1, 4, 5, 8, 7, 5,
|
||||
}
|
||||
var yyDef = [...]int{
|
||||
|
||||
0, -2, 1, 2, 3,
|
||||
0, -2, 0, 2, 3, 1, 0, 4,
|
||||
}
|
||||
var yyTok1 = [...]int{
|
||||
|
||||
1,
|
||||
1, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
|
||||
3, 3, 3, 3, 3, 3, 3, 3, 3, 8,
|
||||
}
|
||||
var yyTok2 = [...]int{
|
||||
|
||||
@ -426,7 +432,7 @@ yydefault:
|
||||
switch yynt {
|
||||
|
||||
case 1:
|
||||
yyDollar = yyS[yypt-1 : yypt+1]
|
||||
yyDollar = yyS[yypt-2 : yypt+1]
|
||||
//line expression_parser.y:18
|
||||
{
|
||||
yylex.(*lexer).val = yyDollar[1].f
|
||||
@ -443,6 +449,22 @@ yydefault:
|
||||
{
|
||||
yyVAL.f = func(ctx Context) interface{} { return ctx.Variables[yyDollar[1].name] }
|
||||
}
|
||||
case 4:
|
||||
yyDollar = yyS[yypt-3 : yypt+1]
|
||||
//line expression_parser.y:23
|
||||
{
|
||||
e, a := yyDollar[1].f, yyDollar[3].name
|
||||
yyVAL.f = func(ctx Context) interface{} {
|
||||
input := e(ctx)
|
||||
ref := reflect.ValueOf(input)
|
||||
switch ref.Kind() {
|
||||
case reflect.Map:
|
||||
return ref.MapIndex(reflect.ValueOf(a)).Interface()
|
||||
default:
|
||||
return input
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
goto yystack /* stack new state and value */
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user