Skip to content

Commit b7ef93d

Browse files
authored
Merge branch 'master' into patch_phases
2 parents 0d416cc + bc77b4b commit b7ef93d

27 files changed

+1461
-125
lines changed

.gitattributes

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
*\[generated\].go linguist-language=txt

.github/workflows/build.yml

+1-1
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.22' ]
14+
go-versions: [ '1.18', '1.22', '1.24' ]
1515
go-arch: [ '386' ]
1616
steps:
1717
- uses: actions/checkout@v3

.github/workflows/diff.yml

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ jobs:
1313
with:
1414
go-version: 1.18
1515
- name: Install benchstat
16-
run: go install golang.org/x/perf/cmd/benchstat@latest
16+
# NOTE: benchstat@latest requires go 1.23 since 2025-02-14 - this is the last go 1.18 ref
17+
# https://cs.opensource.google/go/x/perf/+/c95ad7d5b636f67d322a7e4832e83103d0fdd292
18+
run: go install golang.org/x/perf/cmd/benchstat@884df5810d2850d775c2cb4885a7ea339128a17d
1719

1820
- uses: actions/checkout@v3
1921
- name: Benchmark new code

.github/workflows/fuzz.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,14 @@ jobs:
2121
fuzz-seconds: 600
2222
output-sarif: true
2323
- name: Upload Crash
24-
uses: actions/upload-artifact@v3
24+
uses: actions/upload-artifact@v4
2525
if: failure() && steps.build.outcome == 'success'
2626
with:
2727
name: artifacts
2828
path: ./out/artifacts
2929
- name: Upload Sarif
3030
if: always() && steps.build.outcome == 'success'
31-
uses: github/codeql-action/upload-sarif@v2
31+
uses: github/codeql-action/upload-sarif@v3
3232
with:
3333
# Path to SARIF file relative to the root of the repository
3434
sarif_file: cifuzz-sarif/results.sarif

.github/workflows/test.yml

+1-1
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', '1.22' ]
14+
go-versions: [ '1.18', '1.19', '1.20', '1.21', '1.22', '1.23', '1.24' ]
1515
steps:
1616
- uses: actions/checkout@v3
1717
- name: Setup Go ${{ matrix.go-version }}

README.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,14 @@ 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.
165+
* [Span Digital](https://spandigital.com/) uses Expr in its Knowledge Management products.
166166
* [Xiaohongshu](https://www.xiaohongshu.com/) combining yaml with Expr for dynamically policies delivery.
167167
* [Melrōse](https://melrōse.org) uses Expr to implement its music programming language.
168168
* [Tork](https://www.tork.run/) integrates Expr into its workflow execution.
169-
* [Critical Moments](https://criticalmoments.io) uses Expr for it's mobile realtime conditional targeting system.
169+
* [Critical Moments](https://criticalmoments.io) uses Expr for its mobile realtime conditional targeting system.
170170
* [WoodpeckerCI](https://woodpecker-ci.org) uses Expr for [filtering workflows/steps](https://woodpecker-ci.org/docs/usage/workflow-syntax#evaluate).
171171
* [FastSchema](https://github.com/fastschema/fastschema) - A BaaS leveraging Expr for its customizable and dynamic Access Control system.
172+
* [WunderGraph Cosmo](https://github.com/wundergraph/cosmo) - GraphQL Federeration Router uses Expr to customize Middleware behaviour
172173

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

conf/config.go

+29-17
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,43 @@ import (
1010
"github.com/expr-lang/expr/vm/runtime"
1111
)
1212

13+
const (
14+
// DefaultMemoryBudget represents an upper limit of memory usage
15+
DefaultMemoryBudget uint = 1e6
16+
17+
// DefaultMaxNodes represents an upper limit of AST nodes
18+
DefaultMaxNodes uint = 10000
19+
)
20+
1321
type FunctionsTable map[string]*builtin.Function
1422

1523
type Config struct {
16-
EnvObject any
17-
Env nature.Nature
18-
Expect reflect.Kind
19-
ExpectAny bool
20-
Optimize bool
21-
Strict bool
22-
Profile bool
23-
ConstFns map[string]reflect.Value
24-
Visitors []ast.Visitor
25-
Functions FunctionsTable
26-
Builtins FunctionsTable
27-
Disabled map[string]bool // disabled builtins
24+
EnvObject any
25+
Env nature.Nature
26+
Expect reflect.Kind
27+
ExpectAny bool
28+
Optimize bool
29+
Strict bool
30+
Profile bool
31+
MaxNodes uint
32+
MemoryBudget uint
33+
ConstFns map[string]reflect.Value
34+
Visitors []ast.Visitor
35+
Functions FunctionsTable
36+
Builtins FunctionsTable
37+
Disabled map[string]bool // disabled builtins
2838
}
2939

3040
// CreateNew creates new config with default values.
3141
func CreateNew() *Config {
3242
c := &Config{
33-
Optimize: true,
34-
ConstFns: make(map[string]reflect.Value),
35-
Functions: make(map[string]*builtin.Function),
36-
Builtins: make(map[string]*builtin.Function),
37-
Disabled: make(map[string]bool),
43+
Optimize: true,
44+
MaxNodes: DefaultMaxNodes,
45+
MemoryBudget: DefaultMemoryBudget,
46+
ConstFns: make(map[string]reflect.Value),
47+
Functions: make(map[string]*builtin.Function),
48+
Builtins: make(map[string]*builtin.Function),
49+
Disabled: make(map[string]bool),
3850
}
3951
for _, f := range builtin.Builtins {
4052
c.Builtins[f.Name] = f

docs/language-definition.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ Backticks strings are raw strings, they do not support escape sequences.
9797
<tr>
9898
<td><strong>Conditional</strong></td>
9999
<td>
100-
<code>?:</code> (ternary), <code>??</code> (nil coalescing)
100+
<code>?:</code> (ternary), <code>??</code> (nil coalescing), <code>if {} else {}</code> (multiline)
101101
</td>
102102
</tr>
103103
<tr>
@@ -691,7 +691,7 @@ concat([1, 2], [3, 4]) == [1, 2, 3, 4]
691691

692692
### flatten(array) {#flatten}
693693

694-
Flattens given array into one-dimentional array.
694+
Flattens given array into one-dimensional array.
695695

696696
```expr
697697
flatten([1, 2, [3, 4]]) == [1, 2, 3, 4]

expr_test.go

+17-2
Original file line numberDiff line numberDiff line change
@@ -312,7 +312,7 @@ func ExampleOperator() {
312312
// Output: true
313313
}
314314

315-
func ExampleOperator_Decimal() {
315+
func ExampleOperator_with_decimal() {
316316
type Decimal struct{ N float64 }
317317
code := `A + B - C`
318318

@@ -585,7 +585,7 @@ func ExampleWithContext() {
585585
// Output: 42
586586
}
587587

588-
func ExampleWithTimezone() {
588+
func ExampleTimezone() {
589589
program, err := expr.Compile(`now().Location().String()`, expr.Timezone("Asia/Kamchatka"))
590590
if err != nil {
591591
fmt.Printf("%v", err)
@@ -1291,6 +1291,21 @@ func TestExpr(t *testing.T) {
12911291
`1 < 2 < 3 == true`,
12921292
true,
12931293
},
1294+
{
1295+
`if 1 > 2 { 333 * 2 + 1 } else { 444 }`,
1296+
444,
1297+
},
1298+
{
1299+
`let a = 3;
1300+
let b = 2;
1301+
if a>b {let c = Add(a, b); c+1} else {Add(10, b)}
1302+
`,
1303+
6,
1304+
},
1305+
{
1306+
`if "a" < "b" {let x = "a"; x} else {"abc"}`,
1307+
"a",
1308+
},
12941309
}
12951310

12961311
for _, tt := range tests {

internal/difflib/difflib_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ group
102102
}
103103
}
104104

105-
func ExampleGetUnifiedDiffCode() {
105+
func ExampleGetUnifiedDiffString() {
106106
a := `one
107107
two
108108
three
@@ -135,7 +135,7 @@ four`
135135
// -fmt.Printf("%s,%T",a,b)
136136
}
137137

138-
func ExampleGetContextDiffCode() {
138+
func ExampleGetContextDiffString() {
139139
a := `one
140140
two
141141
three
@@ -172,7 +172,7 @@ four`
172172
// four
173173
}
174174

175-
func ExampleGetContextDiffString() {
175+
func ExampleGetContextDiffString_second() {
176176
a := `one
177177
two
178178
three

internal/spew/common.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ func catchPanic(w io.Writer, v reflect.Value) {
7878
}
7979

8080
// handleMethods attempts to call the Error and String methods on the underlying
81-
// type the passed reflect.Value represents and outputes the result to Writer w.
81+
// type the passed reflect.Value represents and outputs the result to Writer w.
8282
//
8383
// It handles panics in any called methods by catching and displaying the error
8484
// as the formatted value.
@@ -100,7 +100,7 @@ func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool)
100100
// Choose whether or not to do error and Stringer interface lookups against
101101
// the base type or a pointer to the base type depending on settings.
102102
// Technically calling one of these methods with a pointer receiver can
103-
// mutate the value, however, types which choose to satisify an error or
103+
// mutate the value, however, types which choose to satisfy an error or
104104
// Stringer interface with a pointer receiver should not be mutating their
105105
// state inside these interface methods.
106106
if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() {

internal/spew/common_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ func helpTestSortValues(tests []sortTestCase, cs *spew.ConfigState, t *testing.T
154154
}
155155
}
156156

157-
// TestSortValues ensures the sort functionality for relect.Value based sorting
157+
// TestSortValues ensures the sort functionality for reflect.Value based sorting
158158
// works as intended.
159159
func TestSortValues(t *testing.T) {
160160
v := reflect.ValueOf
@@ -228,7 +228,7 @@ func TestSortValues(t *testing.T) {
228228
helpTestSortValues(tests, &cs, t)
229229
}
230230

231-
// TestSortValuesWithMethods ensures the sort functionality for relect.Value
231+
// TestSortValuesWithMethods ensures the sort functionality for reflect.Value
232232
// based sorting works as intended when using string methods.
233233
func TestSortValuesWithMethods(t *testing.T) {
234234
v := reflect.ValueOf
@@ -263,7 +263,7 @@ func TestSortValuesWithMethods(t *testing.T) {
263263
helpTestSortValues(tests, &cs, t)
264264
}
265265

266-
// TestSortValuesWithSpew ensures the sort functionality for relect.Value
266+
// TestSortValuesWithSpew ensures the sort functionality for reflect.Value
267267
// based sorting works as intended when using spew to stringify keys.
268268
func TestSortValuesWithSpew(t *testing.T) {
269269
v := reflect.ValueOf

internal/spew/config.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ type ConfigState struct {
5959
//
6060
// NOTE: This might be an unsafe action since calling one of these methods
6161
// with a pointer receiver could technically mutate the value, however,
62-
// in practice, types which choose to satisify an error or Stringer
62+
// in practice, types which choose to satisfy an error or Stringer
6363
// interface with a pointer receiver should not be mutating their state
6464
// inside these interface methods. As a result, this option relies on
6565
// access to the unsafe package, so it will not have any effect when
@@ -228,7 +228,7 @@ types similar to the standard %v format specifier.
228228
229229
The custom formatter only responds to the %v (most compact), %+v (adds pointer
230230
addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb
231-
combinations. Any other verbs such as %x and %q will be sent to the the
231+
combinations. Any other verbs such as %x and %q will be sent to the
232232
standard fmt package for formatting. In addition, the custom formatter ignores
233233
the width and precision arguments (however they will still work on the format
234234
specifiers not handled by the custom formatter).

internal/spew/doc.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ standard %v format specifier.
169169
170170
The custom formatter only responds to the %v (most compact), %+v (adds pointer
171171
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
172-
combinations. Any other verbs such as %x and %q will be sent to the the
172+
combinations. Any other verbs such as %x and %q will be sent to the
173173
standard fmt package for formatting. In addition, the custom formatter ignores
174174
the width and precision arguments (however they will still work on the format
175175
specifiers not handled by the custom formatter).

internal/spew/dumpcgo_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
// NOTE: Due to the following build constraints, this file will only be compiled
1616
// when both cgo is supported and "-tags testcgo" is added to the go test
1717
// command line. This means the cgo tests are only added (and hence run) when
18-
// specifially requested. This configuration is used because spew itself
18+
// specifically requested. This configuration is used because spew itself
1919
// does not require cgo to run even though it does handle certain cgo types
2020
// specially. Rather than forcing all clients to require cgo and an external
2121
// C compiler just to run the tests, this scheme makes them optional.
@@ -90,7 +90,7 @@ func addCgoDumpTests() {
9090
addDumpTest(v5, "("+v5t+") "+v5s+"\n", "("+v5t2+") "+v5s+"\n")
9191

9292
// C typedefed unsigned char array.
93-
v6, v6l, v6c := testdata.GetCgoTypdefedUnsignedCharArray()
93+
v6, v6l, v6c := testdata.GetCgoTypedefedUnsignedCharArray()
9494
v6Len := fmt.Sprintf("%d", v6l)
9595
v6Cap := fmt.Sprintf("%d", v6c)
9696
v6t := "[6]testdata._Ctype_custom_uchar_t"

internal/spew/format.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ func (f *formatState) formatPtr(v reflect.Value) {
121121
// Keep list of all dereferenced pointers to possibly show later.
122122
pointerChain := make([]uintptr, 0)
123123

124-
// Figure out how many levels of indirection there are by derferencing
124+
// Figure out how many levels of indirection there are by dereferencing
125125
// pointers and unpacking interfaces down the chain while detecting circular
126126
// references.
127127
nilFound := false
@@ -405,7 +405,7 @@ types similar to the standard %v format specifier.
405405
406406
The custom formatter only responds to the %v (most compact), %+v (adds pointer
407407
addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb
408-
combinations. Any other verbs such as %x and %q will be sent to the the
408+
combinations. Any other verbs such as %x and %q will be sent to the
409409
standard fmt package for formatting. In addition, the custom formatter ignores
410410
the width and precision arguments (however they will still work on the format
411411
specifiers not handled by the custom formatter).

internal/spew/internal_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616

1717
/*
18-
This test file is part of the spew package rather than than the spew_test
18+
This test file is part of the spew package rather than the spew_test
1919
package because it needs access to internals to properly test certain cases
2020
which are not possible via the public interface since they should never happen.
2121
*/

internal/spew/internalunsafe_test.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
// +build !js,!appengine,!safe,!disableunsafe,go1.4
2121

2222
/*
23-
This test file is part of the spew package rather than than the spew_test
23+
This test file is part of the spew package rather than the spew_test
2424
package because it needs access to internals to properly test certain cases
2525
which are not possible via the public interface since they should never happen.
2626
*/

internal/spew/testdata/dumpcgo.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ func GetCgoUint8tArray() (interface{}, int, int) {
7676
return C.ui8ta, len(C.ui8ta), cap(C.ui8ta)
7777
}
7878

79-
// GetCgoTypdefedUnsignedCharArray returns a typedefed unsigned char array via
79+
// GetCgoTypedefedUnsignedCharArray returns a typedefed unsigned char array via
8080
// cgo and the array's len and cap. This is only used for tests.
81-
func GetCgoTypdefedUnsignedCharArray() (interface{}, int, int) {
81+
func GetCgoTypedefedUnsignedCharArray() (interface{}, int, int) {
8282
return C.tuca, len(C.tuca), cap(C.tuca)
8383
}

parser/lexer/lexer_test.go

+36
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,42 @@ func TestLex(t *testing.T) {
239239
{Kind: EOF},
240240
},
241241
},
242+
{
243+
`if a>b {x1+x2} else {x2}`,
244+
[]Token{
245+
{Kind: Operator, Value: "if"},
246+
{Kind: Identifier, Value: "a"},
247+
{Kind: Operator, Value: ">"},
248+
{Kind: Identifier, Value: "b"},
249+
{Kind: Bracket, Value: "{"},
250+
{Kind: Identifier, Value: "x1"},
251+
{Kind: Operator, Value: "+"},
252+
{Kind: Identifier, Value: "x2"},
253+
{Kind: Bracket, Value: "}"},
254+
{Kind: Operator, Value: "else"},
255+
{Kind: Bracket, Value: "{"},
256+
{Kind: Identifier, Value: "x2"},
257+
{Kind: Bracket, Value: "}"},
258+
{Kind: EOF},
259+
},
260+
},
261+
{
262+
`a>b if {x1} else {x2}`,
263+
[]Token{
264+
{Kind: Identifier, Value: "a"},
265+
{Kind: Operator, Value: ">"},
266+
{Kind: Identifier, Value: "b"},
267+
{Kind: Operator, Value: "if"},
268+
{Kind: Bracket, Value: "{"},
269+
{Kind: Identifier, Value: "x1"},
270+
{Kind: Bracket, Value: "}"},
271+
{Kind: Operator, Value: "else"},
272+
{Kind: Bracket, Value: "{"},
273+
{Kind: Identifier, Value: "x2"},
274+
{Kind: Bracket, Value: "}"},
275+
{Kind: EOF},
276+
},
277+
},
242278
}
243279

244280
for _, test := range tests {

0 commit comments

Comments
 (0)