Skip to content

Commit b143c83

Browse files
authored
Merge branch 'master' into u-escape
2 parents 341e906 + 6ed72a2 commit b143c83

File tree

8 files changed

+45
-4
lines changed

8 files changed

+45
-4
lines changed

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

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

conf/config.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ type Config struct {
3636
Builtins FunctionsTable
3737
Disabled map[string]bool // disabled builtins
3838
NtCache nature.Cache
39+
// DisableIfOperator disables the built-in `if ... { } else { }` operator syntax
40+
// so that users can use a custom function named `if(...)` without conflicts.
41+
// When enabled, the lexer treats `if`/`else` as identifiers and the parser
42+
// will not parse `if` statements.
43+
DisableIfOperator bool
3944
}
4045

4146
// CreateNew creates new config with default values.

expr.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,14 @@ func AsFloat64() Option {
109109
}
110110
}
111111

112+
// DisableIfOperator disables the `if ... else ...` operator syntax so a custom
113+
// function named `if(...)` can be used without conflicts.
114+
func DisableIfOperator() Option {
115+
return func(c *conf.Config) {
116+
c.DisableIfOperator = true
117+
}
118+
}
119+
112120
// WarnOnAny tells the compiler to warn if expression return any type.
113121
func WarnOnAny() Option {
114122
return func(c *conf.Config) {

expr_test.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,17 @@ func ExampleCompile() {
7070
// Output: true
7171
}
7272

73+
func TestDisableIfOperator_AllowsIfFunction(t *testing.T) {
74+
env := map[string]any{
75+
"if": func(x int) int { return x + 1 },
76+
}
77+
program, err := expr.Compile("if(41)", expr.Env(env), expr.DisableIfOperator())
78+
require.NoError(t, err)
79+
out, err := expr.Run(program, env)
80+
require.NoError(t, err)
81+
assert.Equal(t, 42, out)
82+
}
83+
7384
func ExampleEnv() {
7485
type Segment struct {
7586
Origin string

parser/lexer/lexer.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ type Lexer struct {
4646
byte, rune int
4747
}
4848
eof bool
49+
// When true, keywords `if`/`else` are not treated as operators and
50+
// will be emitted as identifiers instead (for compatibility with custom if()).
51+
DisableIfOperator bool
4952
}
5053

5154
func (l *Lexer) Reset(source file.Source) {

parser/lexer/state.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,14 @@ loop:
129129
switch l.word() {
130130
case "not":
131131
return not
132-
case "in", "or", "and", "matches", "contains", "startsWith", "endsWith", "let", "if", "else":
132+
case "in", "or", "and", "matches", "contains", "startsWith", "endsWith", "let":
133133
l.emit(Operator)
134+
case "if", "else":
135+
if !l.DisableIfOperator {
136+
l.emit(Operator)
137+
} else {
138+
l.emit(Identifier)
139+
}
134140
default:
135141
l.emit(Identifier)
136142
}

parser/parser.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,14 @@ func (p *Parser) Parse(input string, config *conf.Config) (*Tree, error) {
6262
p.lexer = New()
6363
}
6464
p.config = config
65+
// propagate config flags to lexer
66+
if p.lexer != nil {
67+
if config != nil {
68+
p.lexer.DisableIfOperator = config.DisableIfOperator
69+
} else {
70+
p.lexer.DisableIfOperator = false
71+
}
72+
}
6573
source := file.NewSource(input)
6674
p.lexer.Reset(source)
6775
p.next()
@@ -218,7 +226,7 @@ func (p *Parser) parseExpression(precedence int) Node {
218226
return p.parseVariableDeclaration()
219227
}
220228

221-
if precedence == 0 && p.current.Is(Operator, "if") {
229+
if precedence == 0 && (p.config == nil || !p.config.DisableIfOperator) && p.current.Is(Operator, "if") {
222230
return p.parseConditionalIf()
223231
}
224232

0 commit comments

Comments
 (0)