Skip to content

Commit 4252e09

Browse files
authored
Merge branch 'expr-lang:master' into master
2 parents c4d4625 + b24c7f3 commit 4252e09

File tree

158 files changed

+26211
-2165
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

158 files changed

+26211
-2165
lines changed

.github/scripts/coverage.mjs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1-
#!/usr/bin/env zx --experimental
1+
#!/usr/bin/env zx
22

33
const expected = 90
44
const exclude = [
55
'expr/test',
66
'checker/mock',
77
'vm/func_types',
88
'vm/runtime/helpers',
9+
'internal/difflib',
10+
'internal/spew',
11+
'internal/testify',
912
]
1013

1114
cd(path.resolve(__dirname, '..', '..'))
@@ -24,9 +27,11 @@ await spinner('Running tests', async () => {
2427
await $`go tool cover -html=coverage.out -o coverage.html`
2528
})
2629

27-
const cover = await $`go tool cover -func=coverage.out`
30+
const cover = await $({verbose: true})`go tool cover -func=coverage.out`
2831
const total = +cover.stdout.match(/total:\s+\(statements\)\s+(\d+\.\d+)%/)[1]
2932
if (total < expected) {
3033
echo(chalk.red(`Coverage is too low: ${total}% < ${expected}% (expected)`))
3134
process.exit(1)
35+
} else {
36+
echo(`Coverage is good: ${chalk.green(total + '%')} >= ${expected}% (expected)`)
3237
}

.github/workflows/build.yml

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
name: build
2+
3+
on:
4+
push:
5+
branches: [ master ]
6+
pull_request:
7+
branches: [ master ]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
go-versions: [ '1.18', '1.22' ]
15+
go-arch: [ '386' ]
16+
steps:
17+
- uses: actions/checkout@v3
18+
- name: Setup Go ${{ matrix.go-version }}
19+
uses: actions/setup-go@v4
20+
with:
21+
go-version: ${{ matrix.go-version }}
22+
- name: Build
23+
run: GOARCH=${{ matrix.go-arch }} go build

.github/workflows/check.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,4 @@ jobs:
1616
with:
1717
go-version: 1.18
1818
- name: Test
19-
run: npx zx --experimental .github/scripts/coverage.mjs
19+
run: npx zx .github/scripts/coverage.mjs

.github/workflows/test.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ jobs:
1111
runs-on: ubuntu-latest
1212
strategy:
1313
matrix:
14-
go-versions: [ '1.18', '1.19', '1.20', '1.21' ]
14+
go-versions: [ '1.18', '1.19', '1.20', '1.21', '1.22' ]
1515
steps:
1616
- uses: actions/checkout@v3
1717
- name: Setup Go ${{ matrix.go-version }}

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,9 @@ func main() {
162162
* [Visually.io](https://visually.io) employs Expr as a business rule engine for its personalization targeting algorithm.
163163
* [Akvorado](https://github.com/akvorado/akvorado) utilizes Expr to classify exporters and interfaces in network flows.
164164
* [keda.sh](https://keda.sh) uses Expr to allow customization of its Kubernetes-based event-driven autoscaling.
165+
* [Span Digital](https://spandigital.com/) uses Expr in it's Knowledge Management products.
166+
* [Xiaohongshu](https://www.xiaohongshu.com/) combining yaml with Expr for dynamically policies delivery.
167+
* [Melrōse](https://melrōse.org) uses Expr to implement its music programming language.
165168

166169
[Add your company too](https://github.com/expr-lang/expr/edit/master/README.md)
167170

ast/node.go

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,20 @@ package ast
33
import (
44
"reflect"
55

6+
"github.com/expr-lang/expr/checker/nature"
67
"github.com/expr-lang/expr/file"
78
)
89

10+
var (
11+
anyType = reflect.TypeOf(new(any)).Elem()
12+
)
13+
914
// Node represents items of abstract syntax tree.
1015
type Node interface {
1116
Location() file.Location
1217
SetLocation(file.Location)
18+
Nature() nature.Nature
19+
SetNature(nature.Nature)
1320
Type() reflect.Type
1421
SetType(reflect.Type)
1522
String() string
@@ -25,8 +32,8 @@ func Patch(node *Node, newNode Node) {
2532

2633
// base is a base struct for all nodes.
2734
type base struct {
28-
loc file.Location
29-
nodeType reflect.Type
35+
loc file.Location
36+
nature nature.Nature
3037
}
3138

3239
// Location returns the location of the node in the source code.
@@ -39,14 +46,27 @@ func (n *base) SetLocation(loc file.Location) {
3946
n.loc = loc
4047
}
4148

49+
// Nature returns the nature of the node.
50+
func (n *base) Nature() nature.Nature {
51+
return n.nature
52+
}
53+
54+
// SetNature sets the nature of the node.
55+
func (n *base) SetNature(nature nature.Nature) {
56+
n.nature = nature
57+
}
58+
4259
// Type returns the type of the node.
4360
func (n *base) Type() reflect.Type {
44-
return n.nodeType
61+
if n.nature.Type == nil {
62+
return anyType
63+
}
64+
return n.nature.Type
4565
}
4666

4767
// SetType sets the type of the node.
4868
func (n *base) SetType(t reflect.Type) {
49-
n.nodeType = t
69+
n.nature.Type = t
5070
}
5171

5272
// NilNode represents nil.
@@ -163,13 +183,13 @@ type BuiltinNode struct {
163183
Map Node // Used by optimizer to fold filter() and map() builtins.
164184
}
165185

166-
// ClosureNode represents a predicate.
186+
// PredicateNode represents a predicate.
167187
// Example:
168188
//
169189
// filter(foo, .bar == 1)
170190
//
171191
// The predicate is ".bar == 1".
172-
type ClosureNode struct {
192+
type PredicateNode struct {
173193
base
174194
Node Node // Node of the predicate body.
175195
}

ast/print.go

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -65,8 +65,7 @@ func (n *BinaryNode) String() string {
6565
var lhs, rhs string
6666
var lwrap, rwrap bool
6767

68-
lb, ok := n.Left.(*BinaryNode)
69-
if ok {
68+
if lb, ok := n.Left.(*BinaryNode); ok {
7069
if operator.Less(lb.Operator, n.Operator) {
7170
lwrap = true
7271
}
@@ -77,9 +76,7 @@ func (n *BinaryNode) String() string {
7776
lwrap = true
7877
}
7978
}
80-
81-
rb, ok := n.Right.(*BinaryNode)
82-
if ok {
79+
if rb, ok := n.Right.(*BinaryNode); ok {
8380
if operator.Less(rb.Operator, n.Operator) {
8481
rwrap = true
8582
}
@@ -88,6 +85,13 @@ func (n *BinaryNode) String() string {
8885
}
8986
}
9087

88+
if _, ok := n.Left.(*ConditionalNode); ok {
89+
lwrap = true
90+
}
91+
if _, ok := n.Right.(*ConditionalNode); ok {
92+
rwrap = true
93+
}
94+
9195
if lwrap {
9296
lhs = fmt.Sprintf("(%s)", n.Left.String())
9397
} else {
@@ -108,20 +112,25 @@ func (n *ChainNode) String() string {
108112
}
109113

110114
func (n *MemberNode) String() string {
115+
node := n.Node.String()
116+
if _, ok := n.Node.(*BinaryNode); ok {
117+
node = fmt.Sprintf("(%s)", node)
118+
}
119+
111120
if n.Optional {
112121
if str, ok := n.Property.(*StringNode); ok && utils.IsValidIdentifier(str.Value) {
113-
return fmt.Sprintf("%s?.%s", n.Node.String(), str.Value)
122+
return fmt.Sprintf("%s?.%s", node, str.Value)
114123
} else {
115-
return fmt.Sprintf("%s?.[%s]", n.Node.String(), n.Property.String())
124+
return fmt.Sprintf("%s?.[%s]", node, n.Property.String())
116125
}
117126
}
118127
if str, ok := n.Property.(*StringNode); ok && utils.IsValidIdentifier(str.Value) {
119128
if _, ok := n.Node.(*PointerNode); ok {
120129
return fmt.Sprintf(".%s", str.Value)
121130
}
122-
return fmt.Sprintf("%s.%s", n.Node.String(), str.Value)
131+
return fmt.Sprintf("%s.%s", node, str.Value)
123132
}
124-
return fmt.Sprintf("%s[%s]", n.Node.String(), n.Property.String())
133+
return fmt.Sprintf("%s[%s]", node, n.Property.String())
125134
}
126135

127136
func (n *SliceNode) String() string {
@@ -153,7 +162,7 @@ func (n *BuiltinNode) String() string {
153162
return fmt.Sprintf("%s(%s)", n.Name, strings.Join(arguments, ", "))
154163
}
155164

156-
func (n *ClosureNode) String() string {
165+
func (n *PredicateNode) String() string {
157166
return n.Node.String()
158167
}
159168

@@ -206,7 +215,7 @@ func (n *PairNode) String() string {
206215
if utils.IsValidIdentifier(str.Value) {
207216
return fmt.Sprintf("%s: %s", str.Value, n.Value.String())
208217
}
209-
return fmt.Sprintf("%q: %s", str.String(), n.Value.String())
218+
return fmt.Sprintf("%s: %s", str.String(), n.Value.String())
210219
}
211220
return fmt.Sprintf("(%s): %s", n.Key.String(), n.Value.String())
212221
}

ast/print_test.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ package ast_test
33
import (
44
"testing"
55

6-
"github.com/stretchr/testify/assert"
7-
"github.com/stretchr/testify/require"
6+
"github.com/expr-lang/expr/internal/testify/assert"
7+
"github.com/expr-lang/expr/internal/testify/require"
88

99
"github.com/expr-lang/expr/ast"
1010
"github.com/expr-lang/expr/parser"
@@ -57,6 +57,10 @@ func TestPrint(t *testing.T) {
5757
{`{}`, `{}`},
5858
{`{a: b}`, `{a: b}`},
5959
{`{a: b, c: d}`, `{a: b, c: d}`},
60+
{`{"a": b, 'c': d}`, `{a: b, c: d}`},
61+
{`{"a": b, c: d}`, `{a: b, c: d}`},
62+
{`{"a": b, 8: 8}`, `{a: b, "8": 8}`},
63+
{`{"9": 9, '8': 8, "foo": d}`, `{"9": 9, "8": 8, foo: d}`},
6064
{`[]`, `[]`},
6165
{`[a]`, `[a]`},
6266
{`[a, b]`, `[a, b]`},
@@ -72,6 +76,7 @@ func TestPrint(t *testing.T) {
7276
{`a[:]`, `a[:]`},
7377
{`(nil ?? 1) > 0`, `(nil ?? 1) > 0`},
7478
{`{("a" + "b"): 42}`, `{("a" + "b"): 42}`},
79+
{`(One == 1 ? true : false) && Two == 2`, `(One == 1 ? true : false) && Two == 2`},
7580
}
7681

7782
for _, tt := range tests {

ast/visitor.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ type Visitor interface {
77
}
88

99
func Walk(node *Node, v Visitor) {
10+
if *node == nil {
11+
return
12+
}
1013
switch n := (*node).(type) {
1114
case *NilNode:
1215
case *IdentifierNode:
@@ -42,7 +45,7 @@ func Walk(node *Node, v Visitor) {
4245
for i := range n.Arguments {
4346
Walk(&n.Arguments[i], v)
4447
}
45-
case *ClosureNode:
48+
case *PredicateNode:
4649
Walk(&n.Node, v)
4750
case *PointerNode:
4851
case *VariableDeclaratorNode:

ast/visitor_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,9 @@ package ast_test
33
import (
44
"testing"
55

6+
"github.com/expr-lang/expr/internal/testify/assert"
7+
68
"github.com/expr-lang/expr/ast"
7-
"github.com/stretchr/testify/assert"
89
)
910

1011
type visitor struct {

0 commit comments

Comments
 (0)