1
0
mirror of https://github.com/kubernetes/kubernetes.git synced 2025-02-06 10:03:10 +00:00

Bump cel-go to v0.23.2.

This commit is contained in:
Cici Huang 2025-01-27 18:43:12 +00:00
parent e07aeb7c8b
commit c1e0443232
72 changed files with 2803 additions and 1954 deletions

4
go.mod
View File

@ -35,7 +35,7 @@ require (
github.com/godbus/dbus/v5 v5.1.0
github.com/gogo/protobuf v1.3.2
github.com/google/cadvisor v0.51.0
github.com/google/cel-go v0.22.0
github.com/google/cel-go v0.23.2
github.com/google/gnostic-models v0.6.9
github.com/google/go-cmp v0.6.0
github.com/google/gofuzz v1.2.0
@ -126,7 +126,7 @@ require (
)
require (
cel.dev/expr v0.18.0 // indirect
cel.dev/expr v0.19.1 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/MakeNowJust/heredoc v1.0.0 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect

8
go.sum
View File

@ -1,7 +1,7 @@
bitbucket.org/bertimus9/systemstat v0.5.0 h1:n0aLnh2Jo4nBUBym9cE5PJDG8GT6g+4VuS2Ya2jYYpA=
bitbucket.org/bertimus9/systemstat v0.5.0/go.mod h1:EkUWPp8lKFPMXP8vnbpT5JDI0W/sTiLZAvN8ONWErHY=
cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo=
cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4=
cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4=
@ -285,8 +285,8 @@ github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cadvisor v0.51.0 h1:BspqSPdZoLKrnvuZNOvM/KiJ/A+RdixwagN20n+2H8k=
github.com/google/cadvisor v0.51.0/go.mod h1:czGE/c/P/i0QFpVNKTFrIEzord9Y10YfpwuaSWXELc0=
github.com/google/cel-go v0.22.0 h1:b3FJZxpiv1vTMo2/5RDUqAHPxkT8mmMfJIrq1llbf7g=
github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8=
github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4=
github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo=
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=

View File

@ -12,7 +12,7 @@ require (
github.com/emicklei/go-restful/v3 v3.11.0
github.com/fxamacker/cbor/v2 v2.7.0
github.com/gogo/protobuf v1.3.2
github.com/google/cel-go v0.22.0
github.com/google/cel-go v0.23.2
github.com/google/gnostic-models v0.6.9
github.com/google/go-cmp v0.6.0
github.com/google/gofuzz v1.2.0
@ -42,7 +42,7 @@ require (
)
require (
cel.dev/expr v0.18.0 // indirect
cel.dev/expr v0.19.1 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect

View File

@ -1,5 +1,5 @@
cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo=
cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4=
cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4=
@ -211,8 +211,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.22.0 h1:b3FJZxpiv1vTMo2/5RDUqAHPxkT8mmMfJIrq1llbf7g=
github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8=
github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4=
github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo=
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=

View File

@ -17,7 +17,7 @@ require (
github.com/go-logr/logr v1.4.2
github.com/gogo/protobuf v1.3.2
github.com/google/btree v1.1.3
github.com/google/cel-go v0.22.0
github.com/google/cel-go v0.23.2
github.com/google/gnostic-models v0.6.9
github.com/google/go-cmp v0.6.0
github.com/google/gofuzz v1.2.0
@ -66,7 +66,7 @@ require (
)
require (
cel.dev/expr v0.18.0 // indirect
cel.dev/expr v0.19.1 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect

View File

@ -1,5 +1,5 @@
cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo=
cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4=
cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.112.0/go.mod h1:3jEEVwZ/MHU4djK5t5RHuKOA/GbLddgTdVubX1qnPD4=
@ -212,8 +212,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.22.0 h1:b3FJZxpiv1vTMo2/5RDUqAHPxkT8mmMfJIrq1llbf7g=
github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8=
github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4=
github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo=
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=

View File

@ -25,7 +25,7 @@ require (
)
require (
cel.dev/expr v0.18.0 // indirect
cel.dev/expr v0.19.1 // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
@ -49,7 +49,7 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/cel-go v0.22.0 // indirect
github.com/google/cel-go v0.23.2 // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.6.0 // indirect

View File

@ -1,5 +1,5 @@
cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo=
cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4=
cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
@ -74,8 +74,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.22.0 h1:b3FJZxpiv1vTMo2/5RDUqAHPxkT8mmMfJIrq1llbf7g=
github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8=
github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4=
github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo=
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=

View File

@ -22,7 +22,7 @@ require (
)
require (
cel.dev/expr v0.18.0 // indirect
cel.dev/expr v0.19.1 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
@ -43,7 +43,7 @@ require (
github.com/go-openapi/swag v0.23.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/cel-go v0.22.0 // indirect
github.com/google/cel-go v0.23.2 // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect

View File

@ -1,5 +1,5 @@
cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo=
cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4=
cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
@ -71,8 +71,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.22.0 h1:b3FJZxpiv1vTMo2/5RDUqAHPxkT8mmMfJIrq1llbf7g=
github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8=
github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4=
github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo=
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=

View File

@ -10,7 +10,7 @@ godebug winsymlink=0
require (
github.com/blang/semver/v4 v4.0.0
github.com/google/cel-go v0.22.0
github.com/google/cel-go v0.23.2
github.com/google/go-cmp v0.6.0
github.com/onsi/gomega v1.35.1
github.com/stretchr/testify v1.9.0
@ -26,7 +26,7 @@ require (
)
require (
cel.dev/expr v0.18.0 // indirect
cel.dev/expr v0.19.1 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect

View File

@ -1,5 +1,5 @@
cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo=
cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4=
cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
@ -56,8 +56,8 @@ github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w
github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.22.0 h1:b3FJZxpiv1vTMo2/5RDUqAHPxkT8mmMfJIrq1llbf7g=
github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8=
github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4=
github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo=
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=

View File

@ -33,7 +33,7 @@ require (
)
require (
cel.dev/expr v0.18.0 // indirect
cel.dev/expr v0.19.1 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
@ -53,7 +53,7 @@ require (
github.com/go-openapi/swag v0.23.0 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/cel-go v0.22.0 // indirect
github.com/google/cel-go v0.23.2 // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect

View File

@ -1,5 +1,5 @@
cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo=
cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4=
cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
@ -72,8 +72,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.22.0 h1:b3FJZxpiv1vTMo2/5RDUqAHPxkT8mmMfJIrq1llbf7g=
github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8=
github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4=
github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo=
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=

View File

@ -1,4 +1,4 @@
cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
@ -30,7 +30,7 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8=
github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo=
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=

View File

@ -1,4 +1,4 @@
cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c=
@ -53,7 +53,7 @@ github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w
github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8=
github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo=
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=

View File

@ -25,7 +25,7 @@ require (
)
require (
cel.dev/expr v0.18.0 // indirect
cel.dev/expr v0.19.1 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
@ -47,7 +47,7 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/cel-go v0.22.0 // indirect
github.com/google/cel-go v0.23.2 // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.6.0 // indirect

View File

@ -1,5 +1,5 @@
cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo=
cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4=
cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
@ -71,8 +71,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.22.0 h1:b3FJZxpiv1vTMo2/5RDUqAHPxkT8mmMfJIrq1llbf7g=
github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8=
github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4=
github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo=
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=

View File

@ -23,7 +23,7 @@ require (
)
require (
cel.dev/expr v0.18.0 // indirect
cel.dev/expr v0.19.1 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
@ -45,7 +45,7 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/cel-go v0.22.0 // indirect
github.com/google/cel-go v0.23.2 // indirect
github.com/google/gnostic-models v0.6.9 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/uuid v1.6.0 // indirect

View File

@ -1,5 +1,5 @@
cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo=
cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4=
cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw=
cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
@ -71,8 +71,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=
github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
github.com/google/cel-go v0.22.0 h1:b3FJZxpiv1vTMo2/5RDUqAHPxkT8mmMfJIrq1llbf7g=
github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8=
github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4=
github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo=
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=

View File

@ -217,7 +217,7 @@ func (e *Env) Check(ast *Ast) (*Ast, *Issues) {
chk, err := e.initChecker()
if err != nil {
errs := common.NewErrors(ast.Source())
errs.ReportError(common.NoLocation, err.Error())
errs.ReportErrorString(common.NoLocation, err.Error())
return nil, NewIssuesWithSourceInfo(errs, ast.NativeRep().SourceInfo())
}
@ -556,7 +556,8 @@ func (e *Env) PartialVars(vars any) (interpreter.PartialActivation, error) {
// TODO: Consider adding an option to generate a Program.Residual to avoid round-tripping to an
// Ast format and then Program again.
func (e *Env) ResidualAst(a *Ast, details *EvalDetails) (*Ast, error) {
pruned := interpreter.PruneAst(a.impl.Expr(), a.impl.SourceInfo().MacroCalls(), details.State())
ast := a.NativeRep()
pruned := interpreter.PruneAst(ast.Expr(), ast.SourceInfo().MacroCalls(), details.State())
newAST := &Ast{source: a.Source(), impl: pruned}
expr, err := AstToString(newAST)
if err != nil {
@ -582,7 +583,7 @@ func (e *Env) EstimateCost(ast *Ast, estimator checker.CostEstimator, opts ...ch
extendedOpts := make([]checker.CostOption, 0, len(e.costOptions))
extendedOpts = append(extendedOpts, opts...)
extendedOpts = append(extendedOpts, e.costOptions...)
return checker.Cost(ast.impl, estimator, extendedOpts...)
return checker.Cost(ast.NativeRep(), estimator, extendedOpts...)
}
// configure applies a series of EnvOptions to the current environment.
@ -614,6 +615,9 @@ func (e *Env) configure(opts []EnvOption) (*Env, error) {
if e.HasFeature(featureVariadicLogicalASTs) {
prsrOpts = append(prsrOpts, parser.EnableVariadicOperatorASTs(true))
}
if e.HasFeature(featureIdentEscapeSyntax) {
prsrOpts = append(prsrOpts, parser.EnableIdentEscapeSyntax(true))
}
e.prsr, err = parser.NewParser(prsrOpts...)
if err != nil {
return nil, err

View File

@ -60,7 +60,7 @@ func NewInlineVariable(name string, definition *Ast) *InlineVariable {
// If the variable occurs more than once, the provided alias will be used to replace the expressions
// where the variable name occurs.
func NewInlineVariableWithAlias(name, alias string, definition *Ast) *InlineVariable {
return &InlineVariable{name: name, alias: alias, def: definition.impl}
return &InlineVariable{name: name, alias: alias, def: definition.NativeRep()}
}
// NewInliningOptimizer creates and optimizer which replaces variables with expression definitions.

View File

@ -62,7 +62,7 @@ func AstToCheckedExpr(a *Ast) (*exprpb.CheckedExpr, error) {
if !a.IsChecked() {
return nil, fmt.Errorf("cannot convert unchecked ast")
}
return ast.ToProto(a.impl)
return ast.ToProto(a.NativeRep())
}
// ParsedExprToAst converts a parsed expression proto message to an Ast.
@ -99,15 +99,17 @@ func AstToParsedExpr(a *Ast) (*exprpb.ParsedExpr, error) {
// Note, the conversion may not be an exact replica of the original expression, but will produce
// a string that is semantically equivalent and whose textual representation is stable.
func AstToString(a *Ast) (string, error) {
return parser.Unparse(a.impl.Expr(), a.impl.SourceInfo())
return parser.Unparse(a.NativeRep().Expr(), a.NativeRep().SourceInfo())
}
// RefValueToValue converts between ref.Val and api.expr.Value.
// RefValueToValue converts between ref.Val and google.api.expr.v1alpha1.Value.
// The result Value is the serialized proto form. The ref.Val must not be error or unknown.
func RefValueToValue(res ref.Val) (*exprpb.Value, error) {
return ValueAsAlphaProto(res)
}
// ValueAsAlphaProto converts between ref.Val and google.api.expr.v1alpha1.Value.
// The result Value is the serialized proto form. The ref.Val must not be error or unknown.
func ValueAsAlphaProto(res ref.Val) (*exprpb.Value, error) {
canonical, err := ValueAsProto(res)
if err != nil {
@ -118,6 +120,8 @@ func ValueAsAlphaProto(res ref.Val) (*exprpb.Value, error) {
return alpha, err
}
// ValueAsProto converts between ref.Val and cel.expr.Value.
// The result Value is the serialized proto form. The ref.Val must not be error or unknown.
func ValueAsProto(res ref.Val) (*celpb.Value, error) {
switch res.Type() {
case types.BoolType:
@ -205,11 +209,12 @@ var (
anyPbType = reflect.TypeOf(&anypb.Any{})
)
// ValueToRefValue converts between exprpb.Value and ref.Val.
// ValueToRefValue converts between google.api.expr.v1alpha1.Value and ref.Val.
func ValueToRefValue(adapter types.Adapter, v *exprpb.Value) (ref.Val, error) {
return AlphaProtoAsValue(adapter, v)
}
// AlphaProtoAsValue converts between google.api.expr.v1alpha1.Value and ref.Val.
func AlphaProtoAsValue(adapter types.Adapter, v *exprpb.Value) (ref.Val, error) {
canonical := &celpb.Value{}
if err := convertProto(v, canonical); err != nil {
@ -218,6 +223,7 @@ func AlphaProtoAsValue(adapter types.Adapter, v *exprpb.Value) (ref.Val, error)
return ProtoAsValue(adapter, canonical)
}
// ProtoAsValue converts between cel.expr.Value and ref.Val.
func ProtoAsValue(adapter types.Adapter, v *celpb.Value) (ref.Val, error) {
switch v.Kind.(type) {
case *celpb.Value_NullValue:

View File

@ -15,6 +15,7 @@
package cel
import (
"fmt"
"math"
"strconv"
"strings"
@ -35,9 +36,11 @@ const (
optMapMacro = "optMap"
optFlatMapMacro = "optFlatMap"
hasValueFunc = "hasValue"
unwrapOptFunc = "unwrapOpt"
optionalNoneFunc = "optional.none"
optionalOfFunc = "optional.of"
optionalOfNonZeroValueFunc = "optional.ofNonZeroValue"
optionalUnwrapFunc = "optional.unwrap"
valueFunc = "value"
unusedIterVar = "#unused"
)
@ -260,6 +263,37 @@ func (stdLibrary) ProgramOptions() []ProgramOption {
// be expressed with `optMap`.
//
// msg.?elements.optFlatMap(e, e[?0]) // return the first element if present.
// # First
//
// Introduced in version: 2
//
// Returns an optional with the first value from the right hand list, or
// optional.None.
//
// [1, 2, 3].first().value() == 1
// # Last
//
// Introduced in version: 2
//
// Returns an optional with the last value from the right hand list, or
// optional.None.
//
// [1, 2, 3].last().value() == 3
//
// This is syntactic sugar for msg.elements[msg.elements.size()-1].
// # Unwrap / UnwrapOpt
//
// Introduced in version: 2
//
// Returns a list of all the values that are not none in the input list of optional values.
// Can be used as optional.unwrap(List[T]) or with postfix notation: List[T].unwrapOpt()
//
// optional.unwrap([optional.of(42), optional.none()]) == [42]
// [optional.of(42), optional.none()].unwrapOpt() == [42]
func OptionalTypes(opts ...OptionalTypesOption) EnvOption {
lib := &optionalLib{version: math.MaxUint32}
for _, opt := range opts {
@ -303,6 +337,7 @@ func (lib *optionalLib) CompileOptions() []EnvOption {
optionalTypeV := OptionalType(paramTypeV)
listTypeV := ListType(paramTypeV)
mapTypeKV := MapType(paramTypeK, paramTypeV)
listOptionalTypeV := ListType(optionalTypeV)
opts := []EnvOption{
// Enable the optional syntax in the parser.
@ -375,6 +410,46 @@ func (lib *optionalLib) CompileOptions() []EnvOption {
if lib.version >= 1 {
opts = append(opts, Macros(ReceiverMacro(optFlatMapMacro, 2, optFlatMap)))
}
if lib.version >= 2 {
opts = append(opts, Function("last",
MemberOverload("list_last", []*Type{listTypeV}, optionalTypeV,
UnaryBinding(func(v ref.Val) ref.Val {
list := v.(traits.Lister)
sz := list.Size().Value().(int64)
if sz == 0 {
return types.OptionalNone
}
return types.OptionalOf(list.Get(types.Int(sz - 1)))
}),
),
))
opts = append(opts, Function("first",
MemberOverload("list_first", []*Type{listTypeV}, optionalTypeV,
UnaryBinding(func(v ref.Val) ref.Val {
list := v.(traits.Lister)
sz := list.Size().Value().(int64)
if sz == 0 {
return types.OptionalNone
}
return types.OptionalOf(list.Get(types.Int(0)))
}),
),
))
opts = append(opts, Function(optionalUnwrapFunc,
Overload("optional_unwrap", []*Type{listOptionalTypeV}, listTypeV,
UnaryBinding(optUnwrap))))
opts = append(opts, Function(unwrapOptFunc,
MemberOverload("optional_unwrapOpt", []*Type{listOptionalTypeV}, listTypeV,
UnaryBinding(optUnwrap))))
}
return opts
}
@ -439,6 +514,23 @@ func optFlatMap(meh MacroExprFactory, target ast.Expr, args []ast.Expr) (ast.Exp
), nil
}
func optUnwrap(value ref.Val) ref.Val {
list := value.(traits.Lister)
var unwrappedList []ref.Val
iter := list.Iterator()
for iter.HasNext() == types.True {
val := iter.Next()
opt, isOpt := val.(*types.Optional)
if !isOpt {
return types.WrapErr(fmt.Errorf("value %v is not optional", val))
}
if opt.HasValue() {
unwrappedList = append(unwrappedList, opt.GetValue())
}
}
return types.DefaultTypeAdapter.NativeToValue(unwrappedList)
}
func enableOptionalSyntax() EnvOption {
return func(e *Env) (*Env, error) {
e.prsrOpts = append(e.prsrOpts, parser.EnableOptionalSyntax(true))
@ -677,7 +769,7 @@ var (
func timestampGetFullYear(ts, tz ref.Val) ref.Val {
t, err := inTimeZone(ts, tz)
if err != nil {
return types.NewErr(err.Error())
return types.NewErrFromString(err.Error())
}
return types.Int(t.Year())
}
@ -685,7 +777,7 @@ func timestampGetFullYear(ts, tz ref.Val) ref.Val {
func timestampGetMonth(ts, tz ref.Val) ref.Val {
t, err := inTimeZone(ts, tz)
if err != nil {
return types.NewErr(err.Error())
return types.NewErrFromString(err.Error())
}
// CEL spec indicates that the month should be 0-based, but the Time value
// for Month() is 1-based.
@ -695,7 +787,7 @@ func timestampGetMonth(ts, tz ref.Val) ref.Val {
func timestampGetDayOfYear(ts, tz ref.Val) ref.Val {
t, err := inTimeZone(ts, tz)
if err != nil {
return types.NewErr(err.Error())
return types.NewErrFromString(err.Error())
}
return types.Int(t.YearDay() - 1)
}
@ -703,7 +795,7 @@ func timestampGetDayOfYear(ts, tz ref.Val) ref.Val {
func timestampGetDayOfMonthZeroBased(ts, tz ref.Val) ref.Val {
t, err := inTimeZone(ts, tz)
if err != nil {
return types.NewErr(err.Error())
return types.NewErrFromString(err.Error())
}
return types.Int(t.Day() - 1)
}
@ -711,7 +803,7 @@ func timestampGetDayOfMonthZeroBased(ts, tz ref.Val) ref.Val {
func timestampGetDayOfMonthOneBased(ts, tz ref.Val) ref.Val {
t, err := inTimeZone(ts, tz)
if err != nil {
return types.NewErr(err.Error())
return types.NewErrFromString(err.Error())
}
return types.Int(t.Day())
}
@ -719,7 +811,7 @@ func timestampGetDayOfMonthOneBased(ts, tz ref.Val) ref.Val {
func timestampGetDayOfWeek(ts, tz ref.Val) ref.Val {
t, err := inTimeZone(ts, tz)
if err != nil {
return types.NewErr(err.Error())
return types.NewErrFromString(err.Error())
}
return types.Int(t.Weekday())
}
@ -727,7 +819,7 @@ func timestampGetDayOfWeek(ts, tz ref.Val) ref.Val {
func timestampGetHours(ts, tz ref.Val) ref.Val {
t, err := inTimeZone(ts, tz)
if err != nil {
return types.NewErr(err.Error())
return types.NewErrFromString(err.Error())
}
return types.Int(t.Hour())
}
@ -735,7 +827,7 @@ func timestampGetHours(ts, tz ref.Val) ref.Val {
func timestampGetMinutes(ts, tz ref.Val) ref.Val {
t, err := inTimeZone(ts, tz)
if err != nil {
return types.NewErr(err.Error())
return types.NewErrFromString(err.Error())
}
return types.Int(t.Minute())
}
@ -743,7 +835,7 @@ func timestampGetMinutes(ts, tz ref.Val) ref.Val {
func timestampGetSeconds(ts, tz ref.Val) ref.Val {
t, err := inTimeZone(ts, tz)
if err != nil {
return types.NewErr(err.Error())
return types.NewErrFromString(err.Error())
}
return types.Int(t.Second())
}
@ -751,7 +843,7 @@ func timestampGetSeconds(ts, tz ref.Val) ref.Val {
func timestampGetMilliseconds(ts, tz ref.Val) ref.Val {
t, err := inTimeZone(ts, tz)
if err != nil {
return types.NewErr(err.Error())
return types.NewErrFromString(err.Error())
}
return types.Int(t.Nanosecond() / 1000000)
}

View File

@ -48,8 +48,8 @@ func NewStaticOptimizer(optimizers ...ASTOptimizer) *StaticOptimizer {
// If issues are encountered, the Issues.Err() return value will be non-nil.
func (opt *StaticOptimizer) Optimize(env *Env, a *Ast) (*Ast, *Issues) {
// Make a copy of the AST to be optimized.
optimized := ast.Copy(a.impl)
ids := newIDGenerator(ast.MaxID(a.impl))
optimized := ast.Copy(a.NativeRep())
ids := newIDGenerator(ast.MaxID(a.NativeRep()))
// Create the optimizer context, could be pooled in the future.
issues := NewIssues(common.NewErrors(a.Source()))
@ -86,7 +86,7 @@ func (opt *StaticOptimizer) Optimize(env *Env, a *Ast) (*Ast, *Issues) {
if iss.Err() != nil {
return nil, iss
}
optimized = checked.impl
optimized = checked.NativeRep()
}
// Return the optimized result.

View File

@ -65,6 +65,9 @@ const (
// Enable error generation when a presence test or optional field selection is
// performed on a primitive type.
featureEnableErrorOnBadPresenceTest
// Enable escape syntax for field identifiers (`).
featureIdentEscapeSyntax
)
// EnvOption is a functional interface for configuring the environment.
@ -618,6 +621,12 @@ func EnableMacroCallTracking() EnvOption {
return features(featureEnableMacroCallTracking, true)
}
// EnableIdentifierEscapeSyntax enables identifier escaping (`) syntax for
// fields.
func EnableIdentifierEscapeSyntax() EnvOption {
return features(featureIdentEscapeSyntax, true)
}
// CrossTypeNumericComparisons makes it possible to compare across numeric types, e.g. double < int
func CrossTypeNumericComparisons(enabled bool) EnvOption {
return features(featureCrossTypeNumericComparisons, enabled)
@ -655,6 +664,15 @@ func ParserExpressionSizeLimit(limit int) EnvOption {
}
}
// EnableHiddenAccumulatorName sets the parser to use the identifier '@result' for accumulators
// which is not normally accessible from CEL source.
func EnableHiddenAccumulatorName(enabled bool) EnvOption {
return func(e *Env) (*Env, error) {
e.prsrOpts = append(e.prsrOpts, parser.EnableHiddenAccumulatorName(enabled))
return e, nil
}
}
func maybeInteropProvider(provider any) (types.Provider, error) {
switch p := provider.(type) {
case types.Provider:

View File

@ -100,6 +100,9 @@ type EvalDetails struct {
// State of the evaluation, non-nil if the OptTrackState or OptExhaustiveEval is specified
// within EvalOptions.
func (ed *EvalDetails) State() interpreter.EvalState {
if ed == nil {
return interpreter.NewEvalState()
}
return ed.state
}

View File

@ -529,9 +529,15 @@ func (c *checker) checkComprehension(e ast.Expr) {
c.isAssignable(types.DynType, rangeType)
// Set the range iteration variable to type DYN as well.
varType = types.DynType
if comp.HasIterVar2() {
var2Type = types.DynType
}
default:
c.errors.notAComprehensionRange(comp.IterRange().ID(), c.location(comp.IterRange()), rangeType)
varType = types.ErrorType
if comp.HasIterVar2() {
var2Type = types.ErrorType
}
}
// Create a block scope for the loop.

View File

@ -28,15 +28,20 @@ import (
// CostEstimator estimates the sizes of variable length input data and the costs of functions.
type CostEstimator interface {
// EstimateSize returns a SizeEstimate for the given AstNode, or nil if
// the estimator has no estimate to provide. The size is equivalent to the result of the CEL `size()` function:
// length of strings and bytes, number of map entries or number of list items.
// EstimateSize is only called for AstNodes where
// CEL does not know the size; EstimateSize is not called for values defined inline in CEL where the size
// is already obvious to CEL.
// EstimateSize returns a SizeEstimate for the given AstNode, or nil if the estimator has no
// estimate to provide.
//
// The size is equivalent to the result of the CEL `size()` function:
// * Number of unicode characters in a string
// * Number of bytes in a sequence
// * Number of map entries or number of list items.
//
// EstimateSize is only called for AstNodes where CEL does not know the size; EstimateSize is not
// called for values defined inline in CEL where the size is already obvious to CEL.
EstimateSize(element AstNode) *SizeEstimate
// EstimateCallCost returns the estimated cost of an invocation, or nil if
// the estimator has no estimate to provide.
// EstimateCallCost returns the estimated cost of an invocation, or nil if the estimator has no
// estimate to provide.
EstimateCallCost(function, overloadID string, target *AstNode, args []AstNode) *CallEstimate
}
@ -44,6 +49,7 @@ type CostEstimator interface {
// The ResultSize should only be provided if the call results in a map, list, string or bytes.
type CallEstimate struct {
CostEstimate
ResultSize *SizeEstimate
}
@ -53,10 +59,13 @@ type AstNode interface {
// represent type directly reachable from the provided type declarations.
// The first path element is a variable. All subsequent path elements are one of: field name, '@items', '@keys', '@values'.
Path() []string
// Type returns the deduced type of the AstNode.
Type() *types.Type
// Expr returns the expression of the AstNode.
Expr() ast.Expr
// ComputedSize returns a size estimate of the AstNode derived from information available in the CEL expression.
// For constants and inline list and map declarations, the exact size is returned. For concatenated list, strings
// and bytes, the size is derived from the size estimates of the operands. nil is returned if there is no
@ -84,36 +93,7 @@ func (e astNode) Expr() ast.Expr {
}
func (e astNode) ComputedSize() *SizeEstimate {
if e.derivedSize != nil {
return e.derivedSize
}
var v uint64
switch e.expr.Kind() {
case ast.LiteralKind:
switch ck := e.expr.AsLiteral().(type) {
case types.String:
// converting to runes here is an O(n) operation, but
// this is consistent with how size is computed at runtime,
// and how the language definition defines string size
v = uint64(len([]rune(ck)))
case types.Bytes:
v = uint64(len(ck))
case types.Bool, types.Double, types.Duration,
types.Int, types.Timestamp, types.Uint,
types.Null:
v = uint64(1)
default:
return nil
}
case ast.ListKind:
v = uint64(e.expr.AsList().Size())
case ast.MapKind:
v = uint64(e.expr.AsMap().Size())
default:
return nil
}
return &SizeEstimate{Min: v, Max: v}
return e.derivedSize
}
// SizeEstimate represents an estimated size of a variable length string, bytes, map or list.
@ -121,6 +101,16 @@ type SizeEstimate struct {
Min, Max uint64
}
// UnknownSizeEstimate returns a size between 0 and max uint
func UnknownSizeEstimate() SizeEstimate {
return unknownSizeEstimate
}
// FixedSizeEstimate returns a size estimate with a fixed min and max range.
func FixedSizeEstimate(size uint64) SizeEstimate {
return SizeEstimate{Min: size, Max: size}
}
// Add adds to another SizeEstimate and returns the sum.
// If add would result in an uint64 overflow, the result is math.MaxUint64.
func (se SizeEstimate) Add(sizeEstimate SizeEstimate) SizeEstimate {
@ -175,12 +165,22 @@ type CostEstimate struct {
Min, Max uint64
}
// UnknownCostEstimate returns a cost with an unknown impact.
func UnknownCostEstimate() CostEstimate {
return unknownCostEstimate
}
// FixedCostEstimate returns a cost with a fixed min and max range.
func FixedCostEstimate(cost uint64) CostEstimate {
return CostEstimate{Min: cost, Max: cost}
}
// Add adds the costs and returns the sum.
// If add would result in an uint64 overflow for the min or max, the value is set to math.MaxUint64.
func (ce CostEstimate) Add(cost CostEstimate) CostEstimate {
return CostEstimate{
addUint64NoOverflow(ce.Min, cost.Min),
addUint64NoOverflow(ce.Max, cost.Max),
Min: addUint64NoOverflow(ce.Min, cost.Min),
Max: addUint64NoOverflow(ce.Max, cost.Max),
}
}
@ -188,8 +188,8 @@ func (ce CostEstimate) Add(cost CostEstimate) CostEstimate {
// If multiply would result in an uint64 overflow, the result is math.MaxUint64.
func (ce CostEstimate) Multiply(cost CostEstimate) CostEstimate {
return CostEstimate{
multiplyUint64NoOverflow(ce.Min, cost.Min),
multiplyUint64NoOverflow(ce.Max, cost.Max),
Min: multiplyUint64NoOverflow(ce.Min, cost.Min),
Max: multiplyUint64NoOverflow(ce.Max, cost.Max),
}
}
@ -197,8 +197,8 @@ func (ce CostEstimate) Multiply(cost CostEstimate) CostEstimate {
// nearest integer of the result, rounded up.
func (ce CostEstimate) MultiplyByCostFactor(costPerUnit float64) CostEstimate {
return CostEstimate{
multiplyByCostFactor(ce.Min, costPerUnit),
multiplyByCostFactor(ce.Max, costPerUnit),
Min: multiplyByCostFactor(ce.Min, costPerUnit),
Max: multiplyByCostFactor(ce.Max, costPerUnit),
}
}
@ -245,49 +245,6 @@ func multiplyByCostFactor(x uint64, y float64) uint64 {
return uint64(ceil)
}
var (
selectAndIdentCost = CostEstimate{Min: common.SelectAndIdentCost, Max: common.SelectAndIdentCost}
constCost = CostEstimate{Min: common.ConstCost, Max: common.ConstCost}
createListBaseCost = CostEstimate{Min: common.ListCreateBaseCost, Max: common.ListCreateBaseCost}
createMapBaseCost = CostEstimate{Min: common.MapCreateBaseCost, Max: common.MapCreateBaseCost}
createMessageBaseCost = CostEstimate{Min: common.StructCreateBaseCost, Max: common.StructCreateBaseCost}
)
type coster struct {
// exprPath maps from Expr Id to field path.
exprPath map[int64][]string
// iterRanges tracks the iterRange of each iterVar.
iterRanges iterRangeScopes
// computedSizes tracks the computed sizes of call results.
computedSizes map[int64]SizeEstimate
checkedAST *ast.AST
estimator CostEstimator
overloadEstimators map[string]FunctionEstimator
// presenceTestCost will either be a zero or one based on whether has() macros count against cost computations.
presenceTestCost CostEstimate
}
// Use a stack of iterVar -> iterRange Expr Ids to handle shadowed variable names.
type iterRangeScopes map[string][]int64
func (vs iterRangeScopes) push(varName string, expr ast.Expr) {
vs[varName] = append(vs[varName], expr.ID())
}
func (vs iterRangeScopes) pop(varName string) {
varStack := vs[varName]
vs[varName] = varStack[:len(varStack)-1]
}
func (vs iterRangeScopes) peek(varName string) (int64, bool) {
varStack := vs[varName]
if len(varStack) > 0 {
return varStack[len(varStack)-1], true
}
return 0, false
}
// CostOption configures flags which affect cost computations.
type CostOption func(*coster) error
@ -300,7 +257,7 @@ func PresenceTestHasCost(hasCost bool) CostOption {
c.presenceTestCost = selectAndIdentCost
return nil
}
c.presenceTestCost = CostEstimate{Min: 0, Max: 0}
c.presenceTestCost = FixedCostEstimate(0)
return nil
}
}
@ -325,10 +282,11 @@ func Cost(checked *ast.AST, estimator CostEstimator, opts ...CostOption) (CostEs
checkedAST: checked,
estimator: estimator,
overloadEstimators: map[string]FunctionEstimator{},
exprPath: map[int64][]string{},
iterRanges: map[string][]int64{},
exprPaths: map[int64][]string{},
localVars: make(scopes),
computedSizes: map[int64]SizeEstimate{},
presenceTestCost: CostEstimate{Min: 1, Max: 1},
computedEntrySizes: map[int64]entrySizeEstimate{},
presenceTestCost: FixedCostEstimate(1),
}
for _, opt := range opts {
err := opt(c)
@ -339,6 +297,165 @@ func Cost(checked *ast.AST, estimator CostEstimator, opts ...CostOption) (CostEs
return c.cost(checked.Expr()), nil
}
type coster struct {
// exprPaths maps from Expr Id to field path.
exprPaths map[int64][]string
// localVars tracks the local and iteration variables assigned during evaluation.
localVars scopes
// computedSizes tracks the computed sizes of call results.
computedSizes map[int64]SizeEstimate
// computedEntrySizes tracks the size of list and map entries
computedEntrySizes map[int64]entrySizeEstimate
checkedAST *ast.AST
estimator CostEstimator
overloadEstimators map[string]FunctionEstimator
// presenceTestCost will either be a zero or one based on whether has() macros count against cost computations.
presenceTestCost CostEstimate
}
// entrySizeEstimate captures the container kind and associated key/index and value SizeEstimate values.
//
// An entrySizeEstimate only exists if both the key/index and the value have SizeEstimate values, otherwise
// a nil entrySizeEstimate should be used.
type entrySizeEstimate struct {
containerKind types.Kind
key SizeEstimate
val SizeEstimate
}
// container returns the container kind (list or map) of the entry.
func (s *entrySizeEstimate) container() types.Kind {
if s == nil {
return types.UnknownKind
}
return s.containerKind
}
// keySize returns the SizeEstimate for the key if one exists.
func (s *entrySizeEstimate) keySize() *SizeEstimate {
if s == nil {
return nil
}
return &s.key
}
// valSize returns the SizeEstimate for the value if one exists.
func (s *entrySizeEstimate) valSize() *SizeEstimate {
if s == nil {
return nil
}
return &s.val
}
func (s *entrySizeEstimate) union(other *entrySizeEstimate) *entrySizeEstimate {
if s == nil || other == nil {
return nil
}
sk := s.key.Union(other.key)
sv := s.val.Union(other.val)
return &entrySizeEstimate{
containerKind: s.containerKind,
key: sk,
val: sv,
}
}
// localVar captures the local variable size and entrySize estimates if they exist for variables
type localVar struct {
exprID int64
path []string
size *SizeEstimate
entrySize *entrySizeEstimate
}
// scopes is a stack of variable name to integer id stack to handle scopes created by cel.bind() like macros
type scopes map[string][]*localVar
func (s scopes) push(varName string, expr ast.Expr, path []string, size *SizeEstimate, entrySize *entrySizeEstimate) {
s[varName] = append(s[varName], &localVar{
exprID: expr.ID(),
path: path,
size: size,
entrySize: entrySize,
})
}
func (s scopes) pop(varName string) {
varStack := s[varName]
s[varName] = varStack[:len(varStack)-1]
}
func (s scopes) peek(varName string) (*localVar, bool) {
varStack := s[varName]
if len(varStack) > 0 {
return varStack[len(varStack)-1], true
}
return nil, false
}
func (c *coster) pushIterKey(varName string, rangeExpr ast.Expr) {
entrySize := c.computeEntrySize(rangeExpr)
size := entrySize.keySize()
path := c.getPath(rangeExpr)
container := entrySize.container()
if container == types.UnknownKind {
container = c.getType(rangeExpr).Kind()
}
subpath := "@keys"
if container == types.ListKind {
subpath = "@indices"
}
c.localVars.push(varName, rangeExpr, append(path, subpath), size, nil)
}
func (c *coster) pushIterValue(varName string, rangeExpr ast.Expr) {
entrySize := c.computeEntrySize(rangeExpr)
size := entrySize.valSize()
path := c.getPath(rangeExpr)
container := entrySize.container()
if container == types.UnknownKind {
container = c.getType(rangeExpr).Kind()
}
subpath := "@values"
if container == types.ListKind {
subpath = "@items"
}
c.localVars.push(varName, rangeExpr, append(path, subpath), size, nil)
}
func (c *coster) pushIterSingle(varName string, rangeExpr ast.Expr) {
entrySize := c.computeEntrySize(rangeExpr)
size := entrySize.keySize()
subpath := "@keys"
container := entrySize.container()
if container == types.UnknownKind {
container = c.getType(rangeExpr).Kind()
}
if container == types.ListKind {
size = entrySize.valSize()
subpath = "@items"
}
path := c.getPath(rangeExpr)
c.localVars.push(varName, rangeExpr, append(path, subpath), size, nil)
}
func (c *coster) pushLocalVar(varName string, e ast.Expr) {
path := c.getPath(e)
// note: retrieve the entry size for the local variable based on the size of the binding expression
// since the binding expression could be a list or map, the entry size should also be propagated
entrySize := c.computeEntrySize(e)
c.localVars.push(varName, e, path, c.computeSize(e), entrySize)
}
func (c *coster) peekLocalVar(varName string) (*localVar, bool) {
return c.localVars.peek(varName)
}
func (c *coster) popLocalVar(varName string) {
c.localVars.pop(varName)
}
func (c *coster) cost(e ast.Expr) CostEstimate {
if e == nil {
return CostEstimate{}
@ -360,7 +477,11 @@ func (c *coster) cost(e ast.Expr) CostEstimate {
case ast.StructKind:
cost = c.costCreateStruct(e)
case ast.ComprehensionKind:
cost = c.costComprehension(e)
if c.isBind(e) {
cost = c.costBind(e)
} else {
cost = c.costComprehension(e)
}
default:
return CostEstimate{}
}
@ -370,17 +491,11 @@ func (c *coster) cost(e ast.Expr) CostEstimate {
func (c *coster) costIdent(e ast.Expr) CostEstimate {
identName := e.AsIdent()
// build and track the field path
if iterRange, ok := c.iterRanges.peek(identName); ok {
switch c.checkedAST.GetType(iterRange).Kind() {
case types.ListKind:
c.addPath(e, append(c.exprPath[iterRange], "@items"))
case types.MapKind:
c.addPath(e, append(c.exprPath[iterRange], "@keys"))
}
if v, ok := c.peekLocalVar(identName); ok {
c.addPath(e, v.path)
} else {
c.addPath(e, []string{identName})
}
return selectAndIdentCost
}
@ -405,14 +520,18 @@ func (c *coster) costSelect(e ast.Expr) CostEstimate {
// build and track the field path
c.addPath(e, append(c.getPath(sel.Operand()), sel.FieldName()))
return sum
}
func (c *coster) costCall(e ast.Expr) CostEstimate {
// Dyn is just a way to disable type-checking, so return the cost of 1 with the cost of the argument
if dynEstimate := c.maybeUnwrapDynCall(e); dynEstimate != nil {
return *dynEstimate
}
// Continue estimating the cost of all other calls.
call := e.AsCall()
args := call.Args()
var sum CostEstimate
argTypes := make([]AstNode, len(args))
@ -435,7 +554,7 @@ func (c *coster) costCall(e ast.Expr) CostEstimate {
fnCost := CostEstimate{Min: uint64(math.MaxUint64), Max: 0}
var resultSize *SizeEstimate
for _, overload := range overloadIDs {
overloadCost := c.functionCost(call.FunctionName(), overload, &targetType, argTypes, argCosts)
overloadCost := c.functionCost(e, call.FunctionName(), overload, &targetType, argTypes, argCosts)
fnCost = fnCost.Union(overloadCost.CostEstimate)
if overloadCost.ResultSize != nil {
if resultSize == nil {
@ -449,37 +568,73 @@ func (c *coster) costCall(e ast.Expr) CostEstimate {
switch overload {
case overloads.IndexList:
if len(args) > 0 {
// note: assigning resultSize here could be redundant with the path-based lookup later
resultSize = c.computeEntrySize(args[0]).valSize()
c.addPath(e, append(c.getPath(args[0]), "@items"))
}
case overloads.IndexMap:
if len(args) > 0 {
resultSize = c.computeEntrySize(args[0]).valSize()
c.addPath(e, append(c.getPath(args[0]), "@values"))
}
}
if resultSize == nil {
resultSize = c.computeSize(e)
}
}
if resultSize != nil {
c.computedSizes[e.ID()] = *resultSize
}
c.setSize(e, resultSize)
return sum.Add(fnCost)
}
func (c *coster) maybeUnwrapDynCall(e ast.Expr) *CostEstimate {
call := e.AsCall()
if call.FunctionName() != "dyn" {
return nil
}
arg := call.Args()[0]
argCost := c.cost(arg)
c.copySizeEstimates(e, arg)
callCost := FixedCostEstimate(1).Add(argCost)
return &callCost
}
func (c *coster) costCreateList(e ast.Expr) CostEstimate {
create := e.AsList()
var sum CostEstimate
itemSize := SizeEstimate{Min: math.MaxUint64, Max: 0}
if create.Size() == 0 {
itemSize.Min = 0
}
for _, e := range create.Elements() {
sum = sum.Add(c.cost(e))
is := c.sizeOrUnknown(e)
itemSize = itemSize.Union(is)
}
c.setEntrySize(e, &entrySizeEstimate{containerKind: types.ListKind, key: FixedSizeEstimate(1), val: itemSize})
return sum.Add(createListBaseCost)
}
func (c *coster) costCreateMap(e ast.Expr) CostEstimate {
mapVal := e.AsMap()
var sum CostEstimate
keySize := SizeEstimate{Min: math.MaxUint64, Max: 0}
valSize := SizeEstimate{Min: math.MaxUint64, Max: 0}
if mapVal.Size() == 0 {
valSize.Min = 0
keySize.Min = 0
}
for _, ent := range mapVal.Entries() {
entry := ent.AsMapEntry()
sum = sum.Add(c.cost(entry.Key()))
sum = sum.Add(c.cost(entry.Value()))
// Compute the key size range
ks := c.sizeOrUnknown(entry.Key())
keySize = keySize.Union(ks)
// Compute the value size range
vs := c.sizeOrUnknown(entry.Value())
valSize = valSize.Union(vs)
}
c.setEntrySize(e, &entrySizeEstimate{containerKind: types.MapKind, key: keySize, val: valSize})
return sum.Add(createMapBaseCost)
}
@ -498,43 +653,76 @@ func (c *coster) costComprehension(e ast.Expr) CostEstimate {
var sum CostEstimate
sum = sum.Add(c.cost(comp.IterRange()))
sum = sum.Add(c.cost(comp.AccuInit()))
c.pushLocalVar(comp.AccuVar(), comp.AccuInit())
// Track the iterRange of each IterVar for field path construction
c.iterRanges.push(comp.IterVar(), comp.IterRange())
// Track the iterRange of each IterVar and AccuVar for field path construction
if comp.HasIterVar2() {
c.pushIterKey(comp.IterVar(), comp.IterRange())
c.pushIterValue(comp.IterVar2(), comp.IterRange())
} else {
c.pushIterSingle(comp.IterVar(), comp.IterRange())
}
// Determine the cost for each element in the loop
loopCost := c.cost(comp.LoopCondition())
stepCost := c.cost(comp.LoopStep())
c.iterRanges.pop(comp.IterVar())
// Clear the intermediate variable tracking.
c.popLocalVar(comp.IterVar())
if comp.HasIterVar2() {
c.popLocalVar(comp.IterVar2())
}
// Determine the result cost.
sum = sum.Add(c.cost(comp.Result()))
rangeCnt := c.sizeEstimate(c.newAstNode(comp.IterRange()))
c.computedSizes[e.ID()] = rangeCnt
c.localVars.pop(comp.AccuVar())
// Estimate the cost of the loop.
rangeCnt := c.sizeOrUnknown(comp.IterRange())
rangeCost := rangeCnt.MultiplyByCost(stepCost.Add(loopCost))
sum = sum.Add(rangeCost)
switch k := comp.AccuInit().Kind(); k {
case ast.LiteralKind:
c.setSize(e, c.computeSize(comp.AccuInit()))
case ast.ListKind, ast.MapKind:
c.setSize(e, &rangeCnt)
// For a step which produces a container value, it will have an entry size associated
// with its expression id.
if stepEntrySize := c.computeEntrySize(comp.LoopStep()); stepEntrySize != nil {
c.setEntrySize(e, stepEntrySize)
break
}
}
return sum
}
func (c *coster) sizeEstimate(t AstNode) SizeEstimate {
if l := t.ComputedSize(); l != nil {
return *l
}
if l := c.estimator.EstimateSize(t); l != nil {
return *l
}
// return an estimate of 1 for return types of set
// lengths, since strings/bytes/more complex objects could be of
// variable length
if isScalar(t.Type()) {
// TODO: since the logic for size estimation is split between
// ComputedSize and isScalar, changing one will likely require changing
// the other, so they should be merged in the future if possible
return SizeEstimate{Min: 1, Max: 1}
}
return SizeEstimate{Min: 0, Max: math.MaxUint64}
func (c *coster) isBind(e ast.Expr) bool {
comp := e.AsComprehension()
iterRange := comp.IterRange()
loopCond := comp.LoopCondition()
return iterRange.Kind() == ast.ListKind && iterRange.AsList().Size() == 0 &&
loopCond.Kind() == ast.LiteralKind && loopCond.AsLiteral() == types.False &&
comp.AccuVar() != parser.AccumulatorName
}
func (c *coster) functionCost(function, overloadID string, target *AstNode, args []AstNode, argCosts []CostEstimate) CallEstimate {
func (c *coster) costBind(e ast.Expr) CostEstimate {
comp := e.AsComprehension()
var sum CostEstimate
// Binds are lazily initialized, so we retain the cost of an empty iteration range.
sum = sum.Add(c.cost(comp.IterRange()))
sum = sum.Add(c.cost(comp.AccuInit()))
c.pushLocalVar(comp.AccuVar(), comp.AccuInit())
sum = sum.Add(c.cost(comp.Result()))
c.popLocalVar(comp.AccuVar())
// Associate the bind output size with the result size.
c.copySizeEstimates(e, comp.Result())
return sum
}
func (c *coster) functionCost(e ast.Expr, function, overloadID string, target *AstNode, args []AstNode, argCosts []CostEstimate) CallEstimate {
argCostSum := func() CostEstimate {
var sum CostEstimate
for _, a := range argCosts {
@ -559,35 +747,42 @@ func (c *coster) functionCost(function, overloadID string, target *AstNode, args
case overloads.ExtFormatString:
if target != nil {
// ResultSize not calculated because we can't bound the max size.
return CallEstimate{CostEstimate: c.sizeEstimate(*target).MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum())}
return CallEstimate{
CostEstimate: c.sizeOrUnknown(*target).MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum())}
}
case overloads.StringToBytes:
if len(args) == 1 {
sz := c.sizeEstimate(args[0])
sz := c.sizeOrUnknown(args[0])
// ResultSize max is when each char converts to 4 bytes.
return CallEstimate{CostEstimate: sz.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum()), ResultSize: &SizeEstimate{Min: sz.Min, Max: sz.Max * 4}}
return CallEstimate{
CostEstimate: sz.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum()),
ResultSize: &SizeEstimate{Min: sz.Min, Max: sz.Max * 4}}
}
case overloads.BytesToString:
if len(args) == 1 {
sz := c.sizeEstimate(args[0])
sz := c.sizeOrUnknown(args[0])
// ResultSize min is when 4 bytes convert to 1 char.
return CallEstimate{CostEstimate: sz.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum()), ResultSize: &SizeEstimate{Min: sz.Min / 4, Max: sz.Max}}
return CallEstimate{
CostEstimate: sz.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum()),
ResultSize: &SizeEstimate{Min: sz.Min / 4, Max: sz.Max}}
}
case overloads.ExtQuoteString:
if len(args) == 1 {
sz := c.sizeEstimate(args[0])
sz := c.sizeOrUnknown(args[0])
// ResultSize max is when each char is escaped. 2 quote chars always added.
return CallEstimate{CostEstimate: sz.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum()), ResultSize: &SizeEstimate{Min: sz.Min + 2, Max: sz.Max*2 + 2}}
return CallEstimate{
CostEstimate: sz.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum()),
ResultSize: &SizeEstimate{Min: sz.Min + 2, Max: sz.Max*2 + 2}}
}
case overloads.StartsWithString, overloads.EndsWithString:
if len(args) == 1 {
return CallEstimate{CostEstimate: c.sizeEstimate(args[0]).MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum())}
return CallEstimate{CostEstimate: c.sizeOrUnknown(args[0]).MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum())}
}
case overloads.InList:
// If a list is composed entirely of constant values this is O(1), but we don't account for that here.
// We just assume all list containment checks are O(n).
if len(args) == 2 {
return CallEstimate{CostEstimate: c.sizeEstimate(args[1]).MultiplyByCostFactor(1).Add(argCostSum())}
return CallEstimate{CostEstimate: c.sizeOrUnknown(args[1]).MultiplyByCostFactor(1).Add(argCostSum())}
}
// O(nm) functions
case overloads.MatchesString:
@ -595,19 +790,19 @@ func (c *coster) functionCost(function, overloadID string, target *AstNode, args
if target != nil && len(args) == 1 {
// Add one to string length for purposes of cost calculation to prevent product of string and regex to be 0
// in case where string is empty but regex is still expensive.
strCost := c.sizeEstimate(*target).Add(SizeEstimate{Min: 1, Max: 1}).MultiplyByCostFactor(common.StringTraversalCostFactor)
strCost := c.sizeOrUnknown(*target).Add(SizeEstimate{Min: 1, Max: 1}).MultiplyByCostFactor(common.StringTraversalCostFactor)
// We don't know how many expressions are in the regex, just the string length (a huge
// improvement here would be to somehow get a count the number of expressions in the regex or
// how many states are in the regex state machine and use that to measure regex cost).
// For now, we're making a guess that each expression in a regex is typically at least 4 chars
// in length.
regexCost := c.sizeEstimate(args[0]).MultiplyByCostFactor(common.RegexStringLengthCostFactor)
regexCost := c.sizeOrUnknown(args[0]).MultiplyByCostFactor(common.RegexStringLengthCostFactor)
return CallEstimate{CostEstimate: strCost.Multiply(regexCost).Add(argCostSum())}
}
case overloads.ContainsString:
if target != nil && len(args) == 1 {
strCost := c.sizeEstimate(*target).MultiplyByCostFactor(common.StringTraversalCostFactor)
substrCost := c.sizeEstimate(args[0]).MultiplyByCostFactor(common.StringTraversalCostFactor)
strCost := c.sizeOrUnknown(*target).MultiplyByCostFactor(common.StringTraversalCostFactor)
substrCost := c.sizeOrUnknown(args[0]).MultiplyByCostFactor(common.StringTraversalCostFactor)
return CallEstimate{CostEstimate: strCost.Multiply(substrCost).Add(argCostSum())}
}
case overloads.LogicalOr, overloads.LogicalAnd:
@ -617,7 +812,9 @@ func (c *coster) functionCost(function, overloadID string, target *AstNode, args
argCost := CostEstimate{Min: lhs.Min, Max: lhs.Add(rhs).Max}
return CallEstimate{CostEstimate: argCost}
case overloads.Conditional:
size := c.sizeEstimate(args[1]).Union(c.sizeEstimate(args[2]))
size := c.sizeOrUnknown(args[1]).Union(c.sizeOrUnknown(args[2]))
resultEntrySize := c.computeEntrySize(args[1].Expr()).union(c.computeEntrySize(args[2].Expr()))
c.setEntrySize(e, resultEntrySize)
conditionalCost := argCosts[0]
ifTrueCost := argCosts[1]
ifFalseCost := argCosts[2]
@ -625,13 +822,19 @@ func (c *coster) functionCost(function, overloadID string, target *AstNode, args
return CallEstimate{CostEstimate: argCost, ResultSize: &size}
case overloads.AddString, overloads.AddBytes, overloads.AddList:
if len(args) == 2 {
lhsSize := c.sizeEstimate(args[0])
rhsSize := c.sizeEstimate(args[1])
lhsSize := c.sizeOrUnknown(args[0])
rhsSize := c.sizeOrUnknown(args[1])
resultSize := lhsSize.Add(rhsSize)
rhsEntrySize := c.computeEntrySize(args[0].Expr())
lhsEntrySize := c.computeEntrySize(args[1].Expr())
resultEntrySize := rhsEntrySize.union(lhsEntrySize)
if resultEntrySize != nil {
c.setEntrySize(e, resultEntrySize)
}
switch overloadID {
case overloads.AddList:
// list concatenation is O(1), but we handle it here to track size
return CallEstimate{CostEstimate: CostEstimate{Min: 1, Max: 1}.Add(argCostSum()), ResultSize: &resultSize}
return CallEstimate{CostEstimate: FixedCostEstimate(1).Add(argCostSum()), ResultSize: &resultSize}
default:
return CallEstimate{CostEstimate: resultSize.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum()), ResultSize: &resultSize}
}
@ -639,8 +842,8 @@ func (c *coster) functionCost(function, overloadID string, target *AstNode, args
case overloads.LessString, overloads.GreaterString, overloads.LessEqualsString, overloads.GreaterEqualsString,
overloads.LessBytes, overloads.GreaterBytes, overloads.LessEqualsBytes, overloads.GreaterEqualsBytes,
overloads.Equals, overloads.NotEquals:
lhsCost := c.sizeEstimate(args[0])
rhsCost := c.sizeEstimate(args[1])
lhsCost := c.sizeOrUnknown(args[0])
rhsCost := c.sizeOrUnknown(args[1])
min := uint64(0)
smallestMax := lhsCost.Max
if rhsCost.Max < smallestMax {
@ -650,14 +853,16 @@ func (c *coster) functionCost(function, overloadID string, target *AstNode, args
min = 1
}
// equality of 2 scalar values results in a cost of 1
return CallEstimate{CostEstimate: CostEstimate{Min: min, Max: smallestMax}.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum())}
return CallEstimate{
CostEstimate: CostEstimate{Min: min, Max: smallestMax}.MultiplyByCostFactor(common.StringTraversalCostFactor).Add(argCostSum()),
}
}
// O(1) functions
// See CostTracker.costCall for more details about O(1) cost calculations
// Benchmarks suggest that most of the other operations take +/- 50% of a base cost unit
// which on an Intel xeon 2.20GHz CPU is 50ns.
return CallEstimate{CostEstimate: CostEstimate{Min: 1, Max: 1}.Add(argCostSum())}
return CallEstimate{CostEstimate: FixedCostEstimate(1).Add(argCostSum())}
}
func (c *coster) getType(e ast.Expr) *types.Type {
@ -665,28 +870,145 @@ func (c *coster) getType(e ast.Expr) *types.Type {
}
func (c *coster) getPath(e ast.Expr) []string {
return c.exprPath[e.ID()]
if e.Kind() == ast.IdentKind {
if v, found := c.peekLocalVar(e.AsIdent()); found {
return v.path[:]
}
}
return c.exprPaths[e.ID()][:]
}
func (c *coster) addPath(e ast.Expr, path []string) {
c.exprPath[e.ID()] = path
c.exprPaths[e.ID()] = path
}
func isAccumulatorVar(name string) bool {
return name == parser.AccumulatorName || name == parser.HiddenAccumulatorName
}
func (c *coster) newAstNode(e ast.Expr) *astNode {
path := c.getPath(e)
if len(path) > 0 && path[0] == parser.AccumulatorName {
if len(path) > 0 && isAccumulatorVar(path[0]) {
// only provide paths to root vars; omit accumulator vars
path = nil
}
var derivedSize *SizeEstimate
if size, ok := c.computedSizes[e.ID()]; ok {
derivedSize = &size
}
return &astNode{
path: path,
t: c.getType(e),
expr: e,
derivedSize: derivedSize}
derivedSize: c.computeSize(e)}
}
func (c *coster) setSize(e ast.Expr, size *SizeEstimate) {
if size == nil {
return
}
// Store the computed size with the expression
c.computedSizes[e.ID()] = *size
}
func (c *coster) sizeOrUnknown(node any) SizeEstimate {
switch v := node.(type) {
case ast.Expr:
if sz := c.computeSize(v); sz != nil {
return *sz
}
case AstNode:
if sz := v.ComputedSize(); sz != nil {
return *sz
}
}
return UnknownSizeEstimate()
}
func (c *coster) copySizeEstimates(dst, src ast.Expr) {
c.setSize(dst, c.computeSize(src))
c.setEntrySize(dst, c.computeEntrySize(src))
}
func (c *coster) computeSize(e ast.Expr) *SizeEstimate {
if size, ok := c.computedSizes[e.ID()]; ok {
return &size
}
if size := computeExprSize(e); size != nil {
return size
}
// Ensure size estimates are computed first as users may choose to override the costs that
// CEL would otherwise ascribe to the type.
node := astNode{expr: e, path: c.getPath(e), t: c.getType(e)}
if size := c.estimator.EstimateSize(node); size != nil {
// storing the computed size should reduce calls to EstimateSize()
c.computedSizes[e.ID()] = *size
return size
}
if size := computeTypeSize(c.getType(e)); size != nil {
return size
}
if e.Kind() == ast.IdentKind {
varName := e.AsIdent()
if v, ok := c.peekLocalVar(varName); ok && v.size != nil {
return v.size
}
}
return nil
}
func (c *coster) setEntrySize(e ast.Expr, size *entrySizeEstimate) {
if size == nil {
return
}
c.computedEntrySizes[e.ID()] = *size
}
func (c *coster) computeEntrySize(e ast.Expr) *entrySizeEstimate {
if sz, found := c.computedEntrySizes[e.ID()]; found {
return &sz
}
if e.Kind() == ast.IdentKind {
varName := e.AsIdent()
if v, ok := c.peekLocalVar(varName); ok && v.entrySize != nil {
return v.entrySize
}
}
return nil
}
func computeExprSize(expr ast.Expr) *SizeEstimate {
var v uint64
switch expr.Kind() {
case ast.LiteralKind:
switch ck := expr.AsLiteral().(type) {
case types.String:
// converting to runes here is an O(n) operation, but
// this is consistent with how size is computed at runtime,
// and how the language definition defines string size
v = uint64(len([]rune(ck)))
case types.Bytes:
v = uint64(len(ck))
case types.Bool, types.Double, types.Duration,
types.Int, types.Timestamp, types.Uint,
types.Null:
v = uint64(1)
default:
return nil
}
case ast.ListKind:
v = uint64(expr.AsList().Size())
case ast.MapKind:
v = uint64(expr.AsMap().Size())
default:
return nil
}
cost := FixedSizeEstimate(v)
return &cost
}
func computeTypeSize(t *types.Type) *SizeEstimate {
if isScalar(t) {
cost := FixedSizeEstimate(1)
return &cost
}
return nil
}
// isScalar returns true if the given type is known to be of a constant size at
@ -696,10 +1018,24 @@ func isScalar(t *types.Type) bool {
switch t.Kind() {
case types.BoolKind, types.DoubleKind, types.DurationKind, types.IntKind, types.TimestampKind, types.UintKind:
return true
case types.OpaqueKind:
if t.TypeName() == "optional_type" {
return isScalar(t.Parameters()[0])
}
}
return false
}
var (
doubleTwoTo64 = math.Ldexp(1.0, 64)
unknownSizeEstimate = SizeEstimate{Min: 0, Max: math.MaxUint64}
unknownCostEstimate = unknownSizeEstimate.MultiplyByCostFactor(1)
selectAndIdentCost = FixedCostEstimate(common.SelectAndIdentCost)
constCost = FixedCostEstimate(common.ConstCost)
createListBaseCost = FixedCostEstimate(common.ListCreateBaseCost)
createMapBaseCost = FixedCostEstimate(common.MapCreateBaseCost)
createMessageBaseCost = FixedCostEstimate(common.StructCreateBaseCost)
)

View File

@ -40,15 +40,18 @@ type ExprFactory interface {
NewIdent(id int64, name string) Expr
// NewAccuIdent creates an Expr value representing an accumulator identifier within a
//comprehension.
// comprehension.
NewAccuIdent(id int64) Expr
// AccuIdentName reports the name of the accumulator variable to be used within a comprehension.
AccuIdentName() string
// NewLiteral creates an Expr value representing a literal value, such as a string or integer.
NewLiteral(id int64, value ref.Val) Expr
// NewList creates an Expr value representing a list literal expression with optional indices.
//
// Optional indicies will typically be empty unless the CEL optional types are enabled.
// Optional indices will typically be empty unless the CEL optional types are enabled.
NewList(id int64, elems []Expr, optIndices []int32) Expr
// NewMap creates an Expr value representing a map literal expression
@ -78,11 +81,23 @@ type ExprFactory interface {
isExprFactory()
}
type baseExprFactory struct{}
type baseExprFactory struct {
accumulatorName string
}
// NewExprFactory creates an ExprFactory instance.
func NewExprFactory() ExprFactory {
return &baseExprFactory{}
return &baseExprFactory{
"@result",
}
}
// NewExprFactoryWithAccumulator creates an ExprFactory instance with a custom
// accumulator identifier name.
func NewExprFactoryWithAccumulator(id string) ExprFactory {
return &baseExprFactory{
id,
}
}
func (fac *baseExprFactory) NewCall(id int64, function string, args ...Expr) Expr {
@ -138,7 +153,11 @@ func (fac *baseExprFactory) NewIdent(id int64, name string) Expr {
}
func (fac *baseExprFactory) NewAccuIdent(id int64) Expr {
return fac.NewIdent(id, "__result__")
return fac.NewIdent(id, fac.AccuIdentName())
}
func (fac *baseExprFactory) AccuIdentName() string {
return fac.accumulatorName
}
func (fac *baseExprFactory) NewLiteral(id int64, value ref.Val) Expr {

View File

@ -257,7 +257,7 @@ func formatLiteral(c ref.Val) string {
case types.Bool:
return fmt.Sprintf("%t", v)
case types.Bytes:
return fmt.Sprintf("b\"%s\"", string(v))
return fmt.Sprintf("b%s", strconv.Quote(string(v)))
case types.Double:
return fmt.Sprintf("%v", float64(v))
case types.Int:

View File

@ -782,6 +782,11 @@ func TypeVariable(t *types.Type) *VariableDecl {
return NewVariable(t.TypeName(), types.NewTypeTypeWithParam(t))
}
// VariableDeclToExprDecl converts a go-native variable declaration into a protobuf-type variable declaration.
func VariableDeclToExprDecl(v *VariableDecl) (*exprpb.Decl, error) {
return variableDeclToExprDecl(v)
}
// variableDeclToExprDecl converts a go-native variable declaration into a protobuf-type variable declaration.
func variableDeclToExprDecl(v *VariableDecl) (*exprpb.Decl, error) {
varType, err := types.TypeToExprType(v.Type())
@ -791,6 +796,11 @@ func variableDeclToExprDecl(v *VariableDecl) (*exprpb.Decl, error) {
return chkdecls.NewVar(v.Name(), varType), nil
}
// FunctionDeclToExprDecl converts a go-native function declaration into a protobuf-typed function declaration.
func FunctionDeclToExprDecl(f *FunctionDecl) (*exprpb.Decl, error) {
return functionDeclToExprDecl(f)
}
// functionDeclToExprDecl converts a go-native function declaration into a protobuf-typed function declaration.
func functionDeclToExprDecl(f *FunctionDecl) (*exprpb.Decl, error) {
overloads := make([]*exprpb.Decl_FunctionDecl_Overload, len(f.overloads))

View File

@ -30,9 +30,13 @@ type Errors struct {
// NewErrors creates a new instance of the Errors type.
func NewErrors(source Source) *Errors {
src := source
if src == nil {
src = NewTextSource("")
}
return &Errors{
errors: []*Error{},
source: source,
source: src,
maxErrorsToReport: 100,
}
}
@ -42,6 +46,11 @@ func (e *Errors) ReportError(l Location, format string, args ...any) {
e.ReportErrorAtID(0, l, format, args...)
}
// ReportErrorString records an error at a source location.
func (e *Errors) ReportErrorString(l Location, message string) {
e.ReportErrorAtID(0, l, "%s", message)
}
// ReportErrorAtID records an error at a source location and expression id.
func (e *Errors) ReportErrorAtID(id int64, l Location, format string, args ...any) {
e.numErrors++

View File

@ -62,6 +62,12 @@ func NewErr(format string, args ...any) ref.Val {
return &Err{error: fmt.Errorf(format, args...)}
}
// NewErrFromString creates a new Err with the provided message.
// TODO: Audit the use of this function and standardize the error messages and codes.
func NewErrFromString(message string) ref.Val {
return &Err{error: errors.New(message)}
}
// NewErrWithNodeID creates a new Err described by the format string and args.
// TODO: Audit the use of this function and standardize the error messages and codes.
func NewErrWithNodeID(id int64, format string, args ...any) ref.Val {

View File

@ -243,7 +243,7 @@ func (l *baseList) Equal(other ref.Val) ref.Val {
func (l *baseList) Get(index ref.Val) ref.Val {
ind, err := IndexOrError(index)
if err != nil {
return ValOrErr(index, err.Error())
return ValOrErr(index, "%v", err)
}
if ind < 0 || ind >= l.size {
return NewErr("index '%d' out of range in list size '%d'", ind, l.Size())
@ -427,7 +427,7 @@ func (l *concatList) Equal(other ref.Val) ref.Val {
func (l *concatList) Get(index ref.Val) ref.Val {
ind, err := IndexOrError(index)
if err != nil {
return ValOrErr(index, err.Error())
return ValOrErr(index, "%v", err)
}
i := Int(ind)
if i < l.prevList.Size().(Int) {

View File

@ -151,7 +151,7 @@ func (o *protoObj) Get(index ref.Val) ref.Val {
}
fv, err := fd.GetFrom(o.value)
if err != nil {
return NewErr(err.Error())
return NewErrFromString(err.Error())
}
return o.NativeToValue(fv)
}

View File

@ -768,6 +768,19 @@ func ProtoAsType(t *celpb.Type) (*Type, error) {
}
}
// TypeToProto converts from a CEL-native type representation to canonical CEL celpb.Type protobuf type.
func TypeToProto(t *Type) (*celpb.Type, error) {
exprType, err := TypeToExprType(t)
if err != nil {
return nil, err
}
var pbtype celpb.Type
if err = convertProto(exprType, &pbtype); err != nil {
return nil, err
}
return &pbtype, nil
}
func maybeWrapper(t *Type, pbType *exprpb.Type) *exprpb.Type {
if t.IsAssignableType(NullType) {
return chkdecls.NewWrapperType(pbType)

View File

@ -8,6 +8,7 @@ go_library(
name = "go_default_library",
srcs = [
"bindings.go",
"comprehensions.go",
"encoders.go",
"formatting.go",
"guards.go",
@ -45,7 +46,9 @@ go_test(
name = "go_default_test",
size = "small",
srcs = [
"encoders_test.go",
"bindings_test.go",
"comprehensions_test.go",
"encoders_test.go",
"lists_test.go",
"math_test.go",
"native_test.go",

View File

@ -11,7 +11,7 @@ in expressions.
### Cel.Bind
Binds a simple identifier to an initialization expression which may be used
in a subsequenct result expression. Bindings may also be nested within each
in a subsequent result expression. Bindings may also be nested within each
other.
cel.bind(<varName>, <initExpr>, <resultExpr>)
@ -29,7 +29,7 @@ Local bindings are not guaranteed to be evaluated before use.
## Encoders
Encoding utilies for marshalling data into standardized representations.
Encoding utilities for marshalling data into standardized representations.
### Base64.Decode
@ -500,6 +500,36 @@ Examples:
].sortBy(e, e.score).map(e, e.name)
== ["bar", "foo", "baz"]
### Last
**Introduced in the OptionalTypes library version 2**
Returns an optional with the last value from the list or `optional.None` if the
list is empty.
<list(T)>.last() -> <Optional(T)>
Examples:
[1, 2, 3].last().value() == 3
[].last().orValue('test') == 'test'
This is syntactic sugar for list[list.size()-1].
### First
**Introduced in the OptionalTypes library version 2**
Returns an optional with the first value from the list or `optional.None` if the
list is empty.
<list(T)>.first() -> <Optional(T)>
Examples:
[1, 2, 3].first().value() == 1
[].first().orValue('test') == 'test'
## Sets
Sets provides set relationship tests.

View File

@ -16,6 +16,7 @@ package ext
import (
"fmt"
"math"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/ast"
@ -159,19 +160,36 @@ const (
//
// {'greeting': 'aloha', 'farewell': 'aloha'}
// .transformMapEntry(keyVar, valueVar, {valueVar: keyVar}) // error, duplicate key
func TwoVarComprehensions() cel.EnvOption {
return cel.Lib(compreV2Lib{})
func TwoVarComprehensions(options ...TwoVarComprehensionsOption) cel.EnvOption {
l := &compreV2Lib{version: math.MaxUint32}
for _, o := range options {
l = o(l)
}
return cel.Lib(l)
}
type compreV2Lib struct{}
// TwoVarComprehensionsOption declares a functional operator for configuring two-variable comprehensions.
type TwoVarComprehensionsOption func(*compreV2Lib) *compreV2Lib
// TwoVarComprehensionsVersion sets the library version for two-variable comprehensions.
func TwoVarComprehensionsVersion(version uint32) TwoVarComprehensionsOption {
return func(lib *compreV2Lib) *compreV2Lib {
lib.version = version
return lib
}
}
type compreV2Lib struct {
version uint32
}
// LibraryName implements that SingletonLibrary interface method.
func (compreV2Lib) LibraryName() string {
func (*compreV2Lib) LibraryName() string {
return "cel.lib.ext.comprev2"
}
// CompileOptions implements the cel.Library interface method.
func (compreV2Lib) CompileOptions() []cel.EnvOption {
func (*compreV2Lib) CompileOptions() []cel.EnvOption {
kType := cel.TypeParamType("K")
vType := cel.TypeParamType("V")
mapKVType := cel.MapType(kType, vType)
@ -217,7 +235,7 @@ func (compreV2Lib) CompileOptions() []cel.EnvOption {
}
// ProgramOptions implements the cel.Library interface method
func (compreV2Lib) ProgramOptions() []cel.ProgramOption {
func (*compreV2Lib) ProgramOptions() []cel.ProgramOption {
return []cel.ProgramOption{}
}
@ -231,7 +249,7 @@ func quantifierAll(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (
target,
iterVar1,
iterVar2,
parser.AccumulatorName,
mef.AccuIdentName(),
/*accuInit=*/ mef.NewLiteral(types.True),
/*condition=*/ mef.NewCall(operators.NotStrictlyFalse, mef.NewAccuIdent()),
/*step=*/ mef.NewCall(operators.LogicalAnd, mef.NewAccuIdent(), args[2]),
@ -249,7 +267,7 @@ func quantifierExists(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr
target,
iterVar1,
iterVar2,
parser.AccumulatorName,
mef.AccuIdentName(),
/*accuInit=*/ mef.NewLiteral(types.False),
/*condition=*/ mef.NewCall(operators.NotStrictlyFalse, mef.NewCall(operators.LogicalNot, mef.NewAccuIdent())),
/*step=*/ mef.NewCall(operators.LogicalOr, mef.NewAccuIdent(), args[2]),
@ -267,7 +285,7 @@ func quantifierExistsOne(mef cel.MacroExprFactory, target ast.Expr, args []ast.E
target,
iterVar1,
iterVar2,
parser.AccumulatorName,
mef.AccuIdentName(),
/*accuInit=*/ mef.NewLiteral(types.Int(0)),
/*condition=*/ mef.NewLiteral(types.True),
/*step=*/ mef.NewCall(operators.Conditional, args[2],
@ -293,10 +311,10 @@ func transformList(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (
transform = args[2]
}
// __result__ = __result__ + [transform]
// accumulator = accumulator + [transform]
step := mef.NewCall(operators.Add, mef.NewAccuIdent(), mef.NewList(transform))
if filter != nil {
// __result__ = (filter) ? __result__ + [transform] : __result__
// accumulator = (filter) ? accumulator + [transform] : accumulator
step = mef.NewCall(operators.Conditional, filter, step, mef.NewAccuIdent())
}
@ -304,7 +322,7 @@ func transformList(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (
target,
iterVar1,
iterVar2,
parser.AccumulatorName,
mef.AccuIdentName(),
/*accuInit=*/ mef.NewList(),
/*condition=*/ mef.NewLiteral(types.True),
step,
@ -328,17 +346,17 @@ func transformMap(mef cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (a
transform = args[2]
}
// __result__ = cel.@mapInsert(__result__, iterVar1, transform)
// accumulator = cel.@mapInsert(accumulator, iterVar1, transform)
step := mef.NewCall(mapInsert, mef.NewAccuIdent(), mef.NewIdent(iterVar1), transform)
if filter != nil {
// __result__ = (filter) ? cel.@mapInsert(__result__, iterVar1, transform) : __result__
// accumulator = (filter) ? cel.@mapInsert(accumulator, iterVar1, transform) : accumulator
step = mef.NewCall(operators.Conditional, filter, step, mef.NewAccuIdent())
}
return mef.NewComprehensionTwoVar(
target,
iterVar1,
iterVar2,
parser.AccumulatorName,
mef.AccuIdentName(),
/*accuInit=*/ mef.NewMap(),
/*condition=*/ mef.NewLiteral(types.True),
step,
@ -362,17 +380,17 @@ func transformMapEntry(mef cel.MacroExprFactory, target ast.Expr, args []ast.Exp
transform = args[2]
}
// __result__ = cel.@mapInsert(__result__, transform)
// accumulator = cel.@mapInsert(accumulator, transform)
step := mef.NewCall(mapInsert, mef.NewAccuIdent(), transform)
if filter != nil {
// __result__ = (filter) ? cel.@mapInsert(__result__, transform) : __result__
// accumulator = (filter) ? cel.@mapInsert(accumulator, transform) : accumulator
step = mef.NewCall(operators.Conditional, filter, step, mef.NewAccuIdent())
}
return mef.NewComprehensionTwoVar(
target,
iterVar1,
iterVar2,
parser.AccumulatorName,
mef.AccuIdentName(),
/*accuInit=*/ mef.NewMap(),
/*condition=*/ mef.NewLiteral(types.True),
step,
@ -392,10 +410,10 @@ func extractIterVars(mef cel.MacroExprFactory, arg0, arg1 ast.Expr) (string, str
if iterVar1 == iterVar2 {
return "", "", mef.NewError(arg1.ID(), fmt.Sprintf("duplicate variable name: %s", iterVar1))
}
if iterVar1 == parser.AccumulatorName {
if iterVar1 == mef.AccuIdentName() || iterVar1 == parser.AccumulatorName {
return "", "", mef.NewError(arg0.ID(), "iteration variable overwrites accumulator variable")
}
if iterVar2 == parser.AccumulatorName {
if iterVar2 == mef.AccuIdentName() || iterVar2 == parser.AccumulatorName {
return "", "", mef.NewError(arg1.ID(), "iteration variable overwrites accumulator variable")
}
return iterVar1, iterVar2, nil

View File

@ -16,6 +16,7 @@ package ext
import (
"encoding/base64"
"math"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/types"
@ -47,17 +48,34 @@ import (
// Examples:
//
// base64.encode(b'hello') // return b'aGVsbG8='
func Encoders() cel.EnvOption {
return cel.Lib(encoderLib{})
func Encoders(options ...EncodersOption) cel.EnvOption {
l := &encoderLib{version: math.MaxUint32}
for _, o := range options {
l = o(l)
}
return cel.Lib(l)
}
type encoderLib struct{}
// EncodersOption declares a functional operator for configuring encoder extensions.
type EncodersOption func(*encoderLib) *encoderLib
func (encoderLib) LibraryName() string {
// EncodersVersion sets the library version for encoder extensions.
func EncodersVersion(version uint32) EncodersOption {
return func(lib *encoderLib) *encoderLib {
lib.version = version
return lib
}
}
type encoderLib struct {
version uint32
}
func (*encoderLib) LibraryName() string {
return "cel.lib.ext.encoders"
}
func (encoderLib) CompileOptions() []cel.EnvOption {
func (*encoderLib) CompileOptions() []cel.EnvOption {
return []cel.EnvOption{
cel.Function("base64.decode",
cel.Overload("base64_decode_string", []*cel.Type{cel.StringType}, cel.BytesType,
@ -74,7 +92,7 @@ func (encoderLib) CompileOptions() []cel.EnvOption {
}
}
func (encoderLib) ProgramOptions() []cel.ProgramOption {
func (*encoderLib) ProgramOptions() []cel.ProgramOption {
return []cel.ProgramOption{}
}

View File

@ -434,7 +434,7 @@ func (stringFormatValidator) Validate(env *cel.Env, _ cel.ValidatorConfig, a *as
// use a placeholder locale, since locale doesn't affect syntax
_, err := parseFormatString(formatStr, formatCheck, formatCheck, "en_US")
if err != nil {
iss.ReportErrorAtID(getErrorExprID(e.ID(), err), err.Error())
iss.ReportErrorAtID(getErrorExprID(e.ID(), err), "%v", err)
continue
}
seenArgs := formatCheck.argsRequested

View File

@ -24,28 +24,28 @@ import (
func intOrError(i int64, err error) ref.Val {
if err != nil {
return types.NewErr(err.Error())
return types.NewErrFromString(err.Error())
}
return types.Int(i)
}
func bytesOrError(bytes []byte, err error) ref.Val {
if err != nil {
return types.NewErr(err.Error())
return types.NewErrFromString(err.Error())
}
return types.Bytes(bytes)
}
func stringOrError(str string, err error) ref.Val {
if err != nil {
return types.NewErr(err.Error())
return types.NewErrFromString(err.Error())
}
return types.String(str)
}
func listStringOrError(strs []string, err error) ref.Val {
if err != nil {
return types.NewErr(err.Error())
return types.NewErrFromString(err.Error())
}
return types.DefaultTypeAdapter.NativeToValue(strs)
}

View File

@ -145,13 +145,10 @@ var comparableTypes = []*cel.Type{
// == ["bar", "foo", "baz"]
func Lists(options ...ListsOption) cel.EnvOption {
l := &listsLib{
version: math.MaxUint32,
}
l := &listsLib{version: math.MaxUint32}
for _, o := range options {
l = o(l)
}
return cel.Lib(l)
}
@ -211,9 +208,10 @@ func (lib listsLib) CompileOptions() []cel.EnvOption {
cel.MemberOverload("list_flatten",
[]*cel.Type{listListType}, listType,
cel.UnaryBinding(func(arg ref.Val) ref.Val {
// double-check as type-guards disabled
list, ok := arg.(traits.Lister)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
return types.ValOrErr(arg, "no such overload: %v.flatten()", arg.Type())
}
flatList, err := flatten(list, 1)
if err != nil {
@ -226,13 +224,14 @@ func (lib listsLib) CompileOptions() []cel.EnvOption {
cel.MemberOverload("list_flatten_int",
[]*cel.Type{listDyn, types.IntType}, listDyn,
cel.BinaryBinding(func(arg1, arg2 ref.Val) ref.Val {
// double-check as type-guards disabled
list, ok := arg1.(traits.Lister)
if !ok {
return types.MaybeNoSuchOverloadErr(arg1)
return types.ValOrErr(arg1, "no such overload: %v.flatten(%v)", arg1.Type(), arg2.Type())
}
depth, ok := arg2.(types.Int)
if !ok {
return types.MaybeNoSuchOverloadErr(arg2)
return types.ValOrErr(arg1, "no such overload: %v.flatten(%v)", arg1.Type(), arg2.Type())
}
flatList, err := flatten(list, int64(depth))
if err != nil {
@ -260,10 +259,8 @@ func (lib listsLib) CompileOptions() []cel.EnvOption {
}),
cel.SingletonUnaryBinding(
func(arg ref.Val) ref.Val {
list, ok := arg.(traits.Lister)
if !ok {
return types.MaybeNoSuchOverloadErr(arg)
}
// validated by type-guards
list := arg.(traits.Lister)
sorted, err := sortList(list)
if err != nil {
return types.WrapErr(err)
@ -287,15 +284,10 @@ func (lib listsLib) CompileOptions() []cel.EnvOption {
)
}),
cel.SingletonBinaryBinding(
func(arg1 ref.Val, arg2 ref.Val) ref.Val {
list, ok := arg1.(traits.Lister)
if !ok {
return types.MaybeNoSuchOverloadErr(arg1)
}
keys, ok := arg2.(traits.Lister)
if !ok {
return types.MaybeNoSuchOverloadErr(arg2)
}
func(arg1, arg2 ref.Val) ref.Val {
// validated by type-guards
list := arg1.(traits.Lister)
keys := arg2.(traits.Lister)
sorted, err := sortListByAssociatedKeys(list, keys)
if err != nil {
return types.WrapErr(err)
@ -498,8 +490,9 @@ func sortByMacro(meh cel.MacroExprFactory, target ast.Expr, args []ast.Expr) (as
if targetKind != ast.ListKind &&
targetKind != ast.SelectKind &&
targetKind != ast.IdentKind &&
targetKind != ast.ComprehensionKind && targetKind != ast.CallKind {
return nil, meh.NewError(target.ID(), fmt.Sprintf("sortBy can only be applied to a list, identifier, comprehension, call or select expression"))
targetKind != ast.ComprehensionKind &&
targetKind != ast.CallKind {
return nil, meh.NewError(target.ID(), "sortBy can only be applied to a list, identifier, comprehension, call or select expression")
}
mapCompr, err := parser.MakeMap(meh, meh.Copy(varIdent), args)

View File

@ -17,6 +17,7 @@ package ext
import (
"errors"
"fmt"
"math"
"reflect"
"strings"
"time"
@ -98,7 +99,9 @@ var (
func NativeTypes(args ...any) cel.EnvOption {
return func(env *cel.Env) (*cel.Env, error) {
nativeTypes := make([]any, 0, len(args))
tpOptions := nativeTypeOptions{}
tpOptions := nativeTypeOptions{
version: math.MaxUint32,
}
for _, v := range args {
switch v := v.(type) {
@ -128,6 +131,14 @@ func NativeTypes(args ...any) cel.EnvOption {
// NativeTypesOption is a functional interface for configuring handling of native types.
type NativeTypesOption func(*nativeTypeOptions) error
// NativeTypesVersion sets the native types version support for native extensions functions.
func NativeTypesVersion(version uint32) NativeTypesOption {
return func(opts *nativeTypeOptions) error {
opts.version = version
return nil
}
}
// NativeTypesFieldNameHandler is a handler for mapping a reflect.StructField to a CEL field name.
// This can be used to override the default Go struct field to CEL field name mapping.
type NativeTypesFieldNameHandler = func(field reflect.StructField) string
@ -158,6 +169,9 @@ type nativeTypeOptions struct {
// This is most commonly used for switching to parsing based off the struct field tag,
// such as "cel" or "json".
fieldNameHandler NativeTypesFieldNameHandler
// version is the native types library version.
version uint32
}
// ParseStructTags configures if native types field names should be overridable by CEL struct tags.
@ -329,7 +343,7 @@ func (tp *nativeTypeProvider) NewValue(typeName string, fields map[string]ref.Va
}
fieldVal, err := val.ConvertToNative(refFieldDef.Type)
if err != nil {
return types.NewErr(err.Error())
return types.NewErrFromString(err.Error())
}
refField := refVal.FieldByIndex(refFieldDef.Index)
refFieldVal := reflect.ValueOf(fieldVal)
@ -436,7 +450,7 @@ func convertToCelType(refType reflect.Type) (*cel.Type, bool) {
func (tp *nativeTypeProvider) newNativeObject(val any, refValue reflect.Value) ref.Val {
valType, err := newNativeType(tp.options.fieldNameHandler, refValue.Type())
if err != nil {
return types.NewErr(err.Error())
return types.NewErrFromString(err.Error())
}
return &nativeObj{
Adapter: tp,

View File

@ -15,6 +15,8 @@
package ext
import (
"math"
"github.com/google/cel-go/cel"
"github.com/google/cel-go/common/ast"
)
@ -49,8 +51,23 @@ import (
// Examples:
//
// proto.hasExt(msg, google.expr.proto2.test.int32_ext) // returns true || false
func Protos() cel.EnvOption {
return cel.Lib(protoLib{})
func Protos(options ...ProtosOption) cel.EnvOption {
l := &protoLib{version: math.MaxUint32}
for _, o := range options {
l = o(l)
}
return cel.Lib(l)
}
// ProtosOption declares a functional operator for configuring protobuf utilities.
type ProtosOption func(*protoLib) *protoLib
// ProtosVersion sets the library version for extensions for protobuf utilities.
func ProtosVersion(version uint32) ProtosOption {
return func(lib *protoLib) *protoLib {
lib.version = version
return lib
}
}
var (
@ -59,7 +76,9 @@ var (
getExtension = "getExt"
)
type protoLib struct{}
type protoLib struct {
version uint32
}
// LibraryName implements the SingletonLibrary interface method.
func (protoLib) LibraryName() string {

View File

@ -77,11 +77,28 @@ import (
// sets.intersects([1], []) // false
// sets.intersects([1], [1, 2]) // true
// sets.intersects([[1], [2, 3]], [[1, 2], [2, 3.0]]) // true
func Sets() cel.EnvOption {
return cel.Lib(setsLib{})
func Sets(options ...SetsOption) cel.EnvOption {
l := &setsLib{}
for _, o := range options {
l = o(l)
}
return cel.Lib(l)
}
type setsLib struct{}
// SetsOption declares a functional operator for configuring set extensions.
type SetsOption func(*setsLib) *setsLib
// SetsVersion sets the library version for set extensions.
func SetsVersion(version uint32) SetsOption {
return func(lib *setsLib) *setsLib {
lib.version = version
return lib
}
}
type setsLib struct {
version uint32
}
// LibraryName implements the SingletonLibrary interface method.
func (setsLib) LibraryName() string {

View File

@ -156,6 +156,11 @@ type PartialActivation interface {
UnknownAttributePatterns() []*AttributePattern
}
// partialActivationConverter indicates whether an Activation implementation supports conversion to a PartialActivation
type partialActivationConverter interface {
asPartialActivation() (PartialActivation, bool)
}
// partActivation is the default implementations of the PartialActivation interface.
type partActivation struct {
Activation
@ -166,3 +171,20 @@ type partActivation struct {
func (a *partActivation) UnknownAttributePatterns() []*AttributePattern {
return a.unknowns
}
// asPartialActivation returns the partActivation as a PartialActivation interface.
func (a *partActivation) asPartialActivation() (PartialActivation, bool) {
return a, true
}
func asPartialActivation(vars Activation) (PartialActivation, bool) {
// Only internal activation instances may implement this interface
if pv, ok := vars.(partialActivationConverter); ok {
return pv.asPartialActivation()
}
// Since Activations may be hierarchical, test whether a parent converts to a PartialActivation
if vars.Parent() != nil {
return asPartialActivation(vars.Parent())
}
return nil, false
}

View File

@ -358,7 +358,7 @@ func (m *attributeMatcher) AddQualifier(qual Qualifier) (Attribute, error) {
func (m *attributeMatcher) Resolve(vars Activation) (any, error) {
id := m.NamespacedAttribute.ID()
// Bug in how partial activation is resolved, should search parents as well.
partial, isPartial := toPartialActivation(vars)
partial, isPartial := asPartialActivation(vars)
if isPartial {
unk, err := m.fac.matchesUnknownPatterns(
partial,
@ -384,14 +384,3 @@ func (m *attributeMatcher) Qualify(vars Activation, obj any) (any, error) {
func (m *attributeMatcher) QualifyIfPresent(vars Activation, obj any, presenceOnly bool) (any, bool, error) {
return attrQualifyIfPresent(m.fac, vars, obj, m, presenceOnly)
}
func toPartialActivation(vars Activation) (PartialActivation, bool) {
pv, ok := vars.(PartialActivation)
if ok {
return pv, true
}
if vars.Parent() != nil {
return toPartialActivation(vars.Parent())
}
return nil, false
}

View File

@ -762,6 +762,9 @@ func (fold *evalFold) Eval(ctx Activation) ref.Val {
defer releaseFolder(f)
foldRange := fold.iterRange.Eval(ctx)
if types.IsUnknownOrError(foldRange) {
return foldRange
}
if fold.iterVar2 != "" {
var foldable traits.Foldable
switch r := foldRange.(type) {
@ -1241,7 +1244,7 @@ func invalidOptionalElementInit(value ref.Val) ref.Val {
func newFolder(eval *evalFold, ctx Activation) *folder {
f := folderPool.Get().(*folder)
f.evalFold = eval
f.Activation = ctx
f.activation = ctx
return f
}
@ -1262,7 +1265,7 @@ func releaseFolder(f *folder) {
// cel.bind or cel.@block.
type folder struct {
*evalFold
Activation
activation Activation
// fold state objects.
accuVal ref.Val
@ -1290,7 +1293,7 @@ func (f *folder) foldIterable(iterable traits.Iterable) ref.Val {
// Update the accumulation value and check for eval interuption.
f.accuVal = f.step.Eval(f)
f.initialized = true
if f.interruptable && checkInterrupt(f.Activation) {
if f.interruptable && checkInterrupt(f.activation) {
f.interrupted = true
return f.evalResult()
}
@ -1316,7 +1319,7 @@ func (f *folder) FoldEntry(key, val any) bool {
// Update the accumulation value and check for eval interuption.
f.accuVal = f.step.Eval(f)
f.initialized = true
if f.interruptable && checkInterrupt(f.Activation) {
if f.interruptable && checkInterrupt(f.activation) {
f.interrupted = true
return false
}
@ -1330,7 +1333,7 @@ func (f *folder) ResolveName(name string) (any, bool) {
if name == f.accuVar {
if !f.initialized {
f.initialized = true
initVal := f.accu.Eval(f.Activation)
initVal := f.accu.Eval(f.activation)
if !f.exhaustive {
if l, isList := initVal.(traits.Lister); isList && l.Size() == types.IntZero {
initVal = types.NewMutableList(f.adapter)
@ -1355,7 +1358,32 @@ func (f *folder) ResolveName(name string) (any, bool) {
return f.iterVar2Val, true
}
}
return f.Activation.ResolveName(name)
return f.activation.ResolveName(name)
}
// Parent returns the activation embedded into the folder.
func (f *folder) Parent() Activation {
return f.activation
}
// UnknownAttributePatterns implements the PartialActivation interface returning the unknown patterns
// if they were provided to the input activation, or an empty set if the proxied activation is not partial.
func (f *folder) UnknownAttributePatterns() []*AttributePattern {
if pv, ok := f.activation.(partialActivationConverter); ok {
if partial, isPartial := pv.asPartialActivation(); isPartial {
return partial.UnknownAttributePatterns()
}
}
return []*AttributePattern{}
}
func (f *folder) asPartialActivation() (PartialActivation, bool) {
if pv, ok := f.activation.(partialActivationConverter); ok {
if _, isPartial := pv.asPartialActivation(); isPartial {
return f, true
}
}
return nil, false
}
// evalResult computes the final result of the fold after all entries have been folded and accumulated.
@ -1381,7 +1409,7 @@ func (f *folder) evalResult() ref.Val {
// reset clears any state associated with folder evaluation.
func (f *folder) reset() {
f.evalFold = nil
f.Activation = nil
f.activation = nil
f.accuVal = nil
f.iterVar1Val = nil
f.iterVar2Val = nil

View File

@ -506,7 +506,7 @@ func (p *planner) planCreateList(expr ast.Expr) (Interpretable, error) {
id: expr.ID(),
elems: elems,
optionals: optionals,
hasOptionals: len(optionals) != 0,
hasOptionals: len(optionalIndices) != 0,
adapter: p.adapter,
}, nil
}
@ -518,6 +518,7 @@ func (p *planner) planCreateMap(expr ast.Expr) (Interpretable, error) {
optionals := make([]bool, len(entries))
keys := make([]Interpretable, len(entries))
vals := make([]Interpretable, len(entries))
hasOptionals := false
for i, e := range entries {
entry := e.AsMapEntry()
keyVal, err := p.Plan(entry.Key())
@ -532,13 +533,14 @@ func (p *planner) planCreateMap(expr ast.Expr) (Interpretable, error) {
}
vals[i] = valVal
optionals[i] = entry.IsOptional()
hasOptionals = hasOptionals || entry.IsOptional()
}
return &evalMap{
id: expr.ID(),
keys: keys,
vals: vals,
optionals: optionals,
hasOptionals: len(optionals) != 0,
hasOptionals: hasOptionals,
adapter: p.adapter,
}, nil
}
@ -554,6 +556,7 @@ func (p *planner) planCreateStruct(expr ast.Expr) (Interpretable, error) {
optionals := make([]bool, len(objFields))
fields := make([]string, len(objFields))
vals := make([]Interpretable, len(objFields))
hasOptionals := false
for i, f := range objFields {
field := f.AsStructField()
fields[i] = field.Name()
@ -563,6 +566,7 @@ func (p *planner) planCreateStruct(expr ast.Expr) (Interpretable, error) {
}
vals[i] = val
optionals[i] = field.IsOptional()
hasOptionals = hasOptionals || field.IsOptional()
}
return &evalObj{
id: expr.ID(),
@ -570,7 +574,7 @@ func (p *planner) planCreateStruct(expr ast.Expr) (Interpretable, error) {
fields: fields,
vals: vals,
optionals: optionals,
hasOptionals: len(optionals) != 0,
hasOptionals: hasOptionals,
provider: p.provider,
}, nil
}

View File

@ -88,7 +88,7 @@ func PruneAst(expr ast.Expr, macroCalls map[int64]ast.Expr, state EvalState) *as
func (p *astPruner) maybeCreateLiteral(id int64, val ref.Val) (ast.Expr, bool) {
switch v := val.(type) {
case types.Bool, types.Bytes, types.Double, types.Int, types.Null, types.String, types.Uint:
case types.Bool, types.Bytes, types.Double, types.Int, types.Null, types.String, types.Uint, *types.Optional:
p.state.SetValue(id, val)
return p.NewLiteral(id, val), true
case types.Duration:
@ -281,13 +281,29 @@ func (p *astPruner) prune(node ast.Expr) (ast.Expr, bool) {
}
if macro, found := p.macroCalls[node.ID()]; found {
// Ensure that intermediate values for the comprehension are cleared during pruning
pruneMacroCall := node.Kind() != ast.UnspecifiedExprKind
if node.Kind() == ast.ComprehensionKind {
compre := node.AsComprehension()
visit(macro, clearIterVarVisitor(compre.IterVar(), p.state))
// Only prune cel.bind() calls since the variables of the comprehension are all
// visible to the user, so there's no chance of an incorrect value being observed
// as a result of looking at intermediate computations within a comprehension.
pruneMacroCall = isCelBindMacro(macro)
}
// prune the expression in terms of the macro call instead of the expanded form.
if newMacro, pruned := p.prune(macro); pruned {
p.macroCalls[node.ID()] = newMacro
if pruneMacroCall {
// prune the expression in terms of the macro call instead of the expanded form when
// dealing with macro call tracking references.
if newMacro, pruned := p.prune(macro); pruned {
p.macroCalls[node.ID()] = newMacro
}
} else {
// Otherwise just prune the macro target in keeping with the pruning behavior of the
// comprehensions later in the call graph.
macroCall := macro.AsCall()
if macroCall.Target() != nil {
if newTarget, pruned := p.prune(macroCall.Target()); pruned {
macro = p.NewMemberCall(macro.ID(), macroCall.FunctionName(), newTarget, macroCall.Args()...)
p.macroCalls[node.ID()] = macro
}
}
}
}
@ -421,6 +437,19 @@ func (p *astPruner) prune(node ast.Expr) (ast.Expr, bool) {
// the last iteration of the comprehension and not each step in the evaluation which
// means that the any residuals computed in between might be inaccurate.
if newRange, pruned := p.maybePrune(compre.IterRange()); pruned {
if compre.HasIterVar2() {
return p.NewComprehensionTwoVar(
node.ID(),
newRange,
compre.IterVar(),
compre.IterVar2(),
compre.AccuVar(),
compre.AccuInit(),
compre.LoopCondition(),
compre.LoopStep(),
compre.Result(),
), true
}
return p.NewComprehension(
node.ID(),
newRange,
@ -468,16 +497,6 @@ func getMaxID(expr ast.Expr) int64 {
return maxID
}
func clearIterVarVisitor(varName string, state EvalState) astVisitor {
return astVisitor{
visitExpr: func(e ast.Expr) {
if e.Kind() == ast.IdentKind && e.AsIdent() == varName {
state.SetValue(e.ID(), nil)
}
},
}
}
func maxIDVisitor(maxID *int64) astVisitor {
return astVisitor{
visitExpr: func(e ast.Expr) {
@ -541,3 +560,15 @@ func visit(expr ast.Expr, visitor astVisitor) {
}
}
}
func isCelBindMacro(macro ast.Expr) bool {
if macro.Kind() != ast.CallKind {
return false
}
macroCall := macro.AsCall()
target := macroCall.Target()
return macroCall.FunctionName() == "bind" &&
macroCall.IsMemberFunction() &&
target.Kind() == ast.IdentKind &&
target.AsIdent() == "cel"
}

View File

@ -198,20 +198,20 @@ func (c *CostTracker) costCall(call InterpretableCall, args []ref.Val, result re
switch call.OverloadID() {
// O(n) functions
case overloads.StartsWithString, overloads.EndsWithString, overloads.StringToBytes, overloads.BytesToString, overloads.ExtQuoteString, overloads.ExtFormatString:
cost += uint64(math.Ceil(float64(c.actualSize(args[0])) * common.StringTraversalCostFactor))
cost += uint64(math.Ceil(float64(actualSize(args[0])) * common.StringTraversalCostFactor))
case overloads.InList:
// If a list is composed entirely of constant values this is O(1), but we don't account for that here.
// We just assume all list containment checks are O(n).
cost += c.actualSize(args[1])
cost += actualSize(args[1])
// O(min(m, n)) functions
case overloads.LessString, overloads.GreaterString, overloads.LessEqualsString, overloads.GreaterEqualsString,
overloads.LessBytes, overloads.GreaterBytes, overloads.LessEqualsBytes, overloads.GreaterEqualsBytes,
overloads.Equals, overloads.NotEquals:
// When we check the equality of 2 scalar values (e.g. 2 integers, 2 floating-point numbers, 2 booleans etc.),
// the CostTracker.actualSize() function by definition returns 1 for each operand, resulting in an overall cost
// the CostTracker.ActualSize() function by definition returns 1 for each operand, resulting in an overall cost
// of 1.
lhsSize := c.actualSize(args[0])
rhsSize := c.actualSize(args[1])
lhsSize := actualSize(args[0])
rhsSize := actualSize(args[1])
minSize := lhsSize
if rhsSize < minSize {
minSize = rhsSize
@ -220,23 +220,23 @@ func (c *CostTracker) costCall(call InterpretableCall, args []ref.Val, result re
// O(m+n) functions
case overloads.AddString, overloads.AddBytes:
// In the worst case scenario, we would need to reallocate a new backing store and copy both operands over.
cost += uint64(math.Ceil(float64(c.actualSize(args[0])+c.actualSize(args[1])) * common.StringTraversalCostFactor))
cost += uint64(math.Ceil(float64(actualSize(args[0])+actualSize(args[1])) * common.StringTraversalCostFactor))
// O(nm) functions
case overloads.MatchesString:
// https://swtch.com/~rsc/regexp/regexp1.html applies to RE2 implementation supported by CEL
// Add one to string length for purposes of cost calculation to prevent product of string and regex to be 0
// in case where string is empty but regex is still expensive.
strCost := uint64(math.Ceil((1.0 + float64(c.actualSize(args[0]))) * common.StringTraversalCostFactor))
strCost := uint64(math.Ceil((1.0 + float64(actualSize(args[0]))) * common.StringTraversalCostFactor))
// We don't know how many expressions are in the regex, just the string length (a huge
// improvement here would be to somehow get a count the number of expressions in the regex or
// how many states are in the regex state machine and use that to measure regex cost).
// For now, we're making a guess that each expression in a regex is typically at least 4 chars
// in length.
regexCost := uint64(math.Ceil(float64(c.actualSize(args[1])) * common.RegexStringLengthCostFactor))
regexCost := uint64(math.Ceil(float64(actualSize(args[1])) * common.RegexStringLengthCostFactor))
cost += strCost * regexCost
case overloads.ContainsString:
strCost := uint64(math.Ceil(float64(c.actualSize(args[0])) * common.StringTraversalCostFactor))
substrCost := uint64(math.Ceil(float64(c.actualSize(args[1])) * common.StringTraversalCostFactor))
strCost := uint64(math.Ceil(float64(actualSize(args[0])) * common.StringTraversalCostFactor))
substrCost := uint64(math.Ceil(float64(actualSize(args[1])) * common.StringTraversalCostFactor))
cost += strCost * substrCost
default:
@ -253,11 +253,15 @@ func (c *CostTracker) costCall(call InterpretableCall, args []ref.Val, result re
return cost
}
// actualSize returns the size of value
func (c *CostTracker) actualSize(value ref.Val) uint64 {
// actualSize returns the size of the value for all traits.Sizer values, a fixed size for all proto-based
// objects, and a size of 1 for all other value types.
func actualSize(value ref.Val) uint64 {
if sz, ok := value.(traits.Sizer); ok {
return uint64(sz.Size().(types.Int))
}
if opt, ok := value.(*types.Optional); ok && opt.HasValue() {
return actualSize(opt.GetValue())
}
return 1
}

View File

@ -15,8 +15,6 @@
package parser
import (
"fmt"
"github.com/google/cel-go/common"
)
@ -31,11 +29,11 @@ func (e *parseErrors) errorCount() int {
}
func (e *parseErrors) internalError(message string) {
e.errs.ReportErrorAtID(0, common.NoLocation, message)
e.errs.ReportErrorAtID(0, common.NoLocation, "%s", message)
}
func (e *parseErrors) syntaxError(l common.Location, message string) {
e.errs.ReportErrorAtID(0, l, fmt.Sprintf("Syntax error: %s", message))
e.errs.ReportErrorAtID(0, l, "Syntax error: %s", message)
}
func (e *parseErrors) reportErrorAtID(id int64, l common.Location, message string, args ...any) {

View File

@ -52,13 +52,14 @@ unary
member
: primary # PrimaryExpr
| member op='.' (opt='?')? id=IDENTIFIER # Select
| member op='.' (opt='?')? id=escapeIdent # Select
| member op='.' id=IDENTIFIER open='(' args=exprList? ')' # MemberCall
| member op='[' (opt='?')? index=expr ']' # Index
;
primary
: leadingDot='.'? id=IDENTIFIER (op='(' args=exprList? ')')? # IdentOrGlobalCall
: leadingDot='.'? id=IDENTIFIER # Ident
| leadingDot='.'? id=IDENTIFIER (op='(' args=exprList? ')') # GlobalCall
| '(' e=expr ')' # Nested
| op='[' elems=listInit? ','? ']' # CreateList
| op='{' entries=mapInitializerList? ','? '}' # CreateStruct
@ -80,13 +81,18 @@ fieldInitializerList
;
optField
: (opt='?')? IDENTIFIER
: (opt='?')? escapeIdent
;
mapInitializerList
: keys+=optExpr cols+=':' values+=expr (',' keys+=optExpr cols+=':' values+=expr)*
;
escapeIdent
: id=IDENTIFIER # SimpleIdentifier
| id=ESC_IDENTIFIER # EscapedIdentifier
;
optExpr
: (opt='?')? e=expr
;
@ -198,3 +204,4 @@ STRING
BYTES : ('b' | 'B') STRING;
IDENTIFIER : (LETTER | '_') ( LETTER | DIGIT | '_')*;
ESC_IDENTIFIER : '`' (LETTER | DIGIT | '_' | '.' | '-' | '/' | ' ')+ '`';

File diff suppressed because one or more lines are too long

View File

@ -34,6 +34,7 @@ NUM_UINT=33
STRING=34
BYTES=35
IDENTIFIER=36
ESC_IDENTIFIER=37
'=='=1
'!='=2
'in'=3

File diff suppressed because one or more lines are too long

View File

@ -34,6 +34,7 @@ NUM_UINT=33
STRING=34
BYTES=35
IDENTIFIER=36
ESC_IDENTIFIER=37
'=='=1
'!='=2
'in'=3

View File

@ -1,4 +1,4 @@
// Code generated from /usr/local/google/home/tswadell/go/src/github.com/google/cel-go/parser/gen/CEL.g4 by ANTLR 4.13.1. DO NOT EDIT.
// Code generated from /usr/local/google/home/jdtatum/github/cel-go/parser/gen/CEL.g4 by ANTLR 4.13.1. DO NOT EDIT.
package gen // CEL
import "github.com/antlr4-go/antlr/v4"
@ -98,11 +98,17 @@ func (s *BaseCELListener) EnterIndex(ctx *IndexContext) {}
// ExitIndex is called when production Index is exited.
func (s *BaseCELListener) ExitIndex(ctx *IndexContext) {}
// EnterIdentOrGlobalCall is called when production IdentOrGlobalCall is entered.
func (s *BaseCELListener) EnterIdentOrGlobalCall(ctx *IdentOrGlobalCallContext) {}
// EnterIdent is called when production Ident is entered.
func (s *BaseCELListener) EnterIdent(ctx *IdentContext) {}
// ExitIdentOrGlobalCall is called when production IdentOrGlobalCall is exited.
func (s *BaseCELListener) ExitIdentOrGlobalCall(ctx *IdentOrGlobalCallContext) {}
// ExitIdent is called when production Ident is exited.
func (s *BaseCELListener) ExitIdent(ctx *IdentContext) {}
// EnterGlobalCall is called when production GlobalCall is entered.
func (s *BaseCELListener) EnterGlobalCall(ctx *GlobalCallContext) {}
// ExitGlobalCall is called when production GlobalCall is exited.
func (s *BaseCELListener) ExitGlobalCall(ctx *GlobalCallContext) {}
// EnterNested is called when production Nested is entered.
func (s *BaseCELListener) EnterNested(ctx *NestedContext) {}
@ -164,6 +170,18 @@ func (s *BaseCELListener) EnterMapInitializerList(ctx *MapInitializerListContext
// ExitMapInitializerList is called when production mapInitializerList is exited.
func (s *BaseCELListener) ExitMapInitializerList(ctx *MapInitializerListContext) {}
// EnterSimpleIdentifier is called when production SimpleIdentifier is entered.
func (s *BaseCELListener) EnterSimpleIdentifier(ctx *SimpleIdentifierContext) {}
// ExitSimpleIdentifier is called when production SimpleIdentifier is exited.
func (s *BaseCELListener) ExitSimpleIdentifier(ctx *SimpleIdentifierContext) {}
// EnterEscapedIdentifier is called when production EscapedIdentifier is entered.
func (s *BaseCELListener) EnterEscapedIdentifier(ctx *EscapedIdentifierContext) {}
// ExitEscapedIdentifier is called when production EscapedIdentifier is exited.
func (s *BaseCELListener) ExitEscapedIdentifier(ctx *EscapedIdentifierContext) {}
// EnterOptExpr is called when production optExpr is entered.
func (s *BaseCELListener) EnterOptExpr(ctx *OptExprContext) {}

View File

@ -1,9 +1,8 @@
// Code generated from /usr/local/google/home/tswadell/go/src/github.com/google/cel-go/parser/gen/CEL.g4 by ANTLR 4.13.1. DO NOT EDIT.
// Code generated from /usr/local/google/home/jdtatum/github/cel-go/parser/gen/CEL.g4 by ANTLR 4.13.1. DO NOT EDIT.
package gen // CEL
import "github.com/antlr4-go/antlr/v4"
type BaseCELVisitor struct {
*antlr.BaseParseTreeVisitor
}
@ -60,7 +59,11 @@ func (v *BaseCELVisitor) VisitIndex(ctx *IndexContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitIdentOrGlobalCall(ctx *IdentOrGlobalCallContext) interface{} {
func (v *BaseCELVisitor) VisitIdent(ctx *IdentContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitGlobalCall(ctx *GlobalCallContext) interface{} {
return v.VisitChildren(ctx)
}
@ -104,6 +107,14 @@ func (v *BaseCELVisitor) VisitMapInitializerList(ctx *MapInitializerListContext)
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitSimpleIdentifier(ctx *SimpleIdentifierContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitEscapedIdentifier(ctx *EscapedIdentifierContext) interface{} {
return v.VisitChildren(ctx)
}
func (v *BaseCELVisitor) VisitOptExpr(ctx *OptExprContext) interface{} {
return v.VisitChildren(ctx)
}

View File

@ -1,278 +1,285 @@
// Code generated from /usr/local/google/home/tswadell/go/src/github.com/google/cel-go/parser/gen/CEL.g4 by ANTLR 4.13.1. DO NOT EDIT.
// Code generated from /usr/local/google/home/jdtatum/github/cel-go/parser/gen/CEL.g4 by ANTLR 4.13.1. DO NOT EDIT.
package gen
import (
"fmt"
"sync"
"unicode"
"github.com/antlr4-go/antlr/v4"
"sync"
"unicode"
)
// Suppress unused import error
var _ = fmt.Printf
var _ = sync.Once{}
var _ = unicode.IsLetter
type CELLexer struct {
*antlr.BaseLexer
channelNames []string
modeNames []string
modeNames []string
// TODO: EOF string
}
var CELLexerLexerStaticData struct {
once sync.Once
serializedATN []int32
ChannelNames []string
ModeNames []string
LiteralNames []string
SymbolicNames []string
RuleNames []string
PredictionContextCache *antlr.PredictionContextCache
atn *antlr.ATN
decisionToDFA []*antlr.DFA
once sync.Once
serializedATN []int32
ChannelNames []string
ModeNames []string
LiteralNames []string
SymbolicNames []string
RuleNames []string
PredictionContextCache *antlr.PredictionContextCache
atn *antlr.ATN
decisionToDFA []*antlr.DFA
}
func cellexerLexerInit() {
staticData := &CELLexerLexerStaticData
staticData.ChannelNames = []string{
"DEFAULT_TOKEN_CHANNEL", "HIDDEN",
}
staticData.ModeNames = []string{
"DEFAULT_MODE",
}
staticData.LiteralNames = []string{
"", "'=='", "'!='", "'in'", "'<'", "'<='", "'>='", "'>'", "'&&'", "'||'",
"'['", "']'", "'{'", "'}'", "'('", "')'", "'.'", "','", "'-'", "'!'",
"'?'", "':'", "'+'", "'*'", "'/'", "'%'", "'true'", "'false'", "'null'",
}
staticData.SymbolicNames = []string{
"", "EQUALS", "NOT_EQUALS", "IN", "LESS", "LESS_EQUALS", "GREATER_EQUALS",
"GREATER", "LOGICAL_AND", "LOGICAL_OR", "LBRACKET", "RPRACKET", "LBRACE",
"RBRACE", "LPAREN", "RPAREN", "DOT", "COMMA", "MINUS", "EXCLAM", "QUESTIONMARK",
"COLON", "PLUS", "STAR", "SLASH", "PERCENT", "CEL_TRUE", "CEL_FALSE",
"NUL", "WHITESPACE", "COMMENT", "NUM_FLOAT", "NUM_INT", "NUM_UINT",
"STRING", "BYTES", "IDENTIFIER",
}
staticData.RuleNames = []string{
"EQUALS", "NOT_EQUALS", "IN", "LESS", "LESS_EQUALS", "GREATER_EQUALS",
"GREATER", "LOGICAL_AND", "LOGICAL_OR", "LBRACKET", "RPRACKET", "LBRACE",
"RBRACE", "LPAREN", "RPAREN", "DOT", "COMMA", "MINUS", "EXCLAM", "QUESTIONMARK",
"COLON", "PLUS", "STAR", "SLASH", "PERCENT", "CEL_TRUE", "CEL_FALSE",
"NUL", "BACKSLASH", "LETTER", "DIGIT", "EXPONENT", "HEXDIGIT", "RAW",
"ESC_SEQ", "ESC_CHAR_SEQ", "ESC_OCT_SEQ", "ESC_BYTE_SEQ", "ESC_UNI_SEQ",
"WHITESPACE", "COMMENT", "NUM_FLOAT", "NUM_INT", "NUM_UINT", "STRING",
"BYTES", "IDENTIFIER",
}
staticData.PredictionContextCache = antlr.NewPredictionContextCache()
staticData.serializedATN = []int32{
4, 0, 36, 423, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2,
4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2,
10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15,
7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7,
20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25,
2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2,
31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36,
7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7,
41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46,
1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1, 3, 1, 3, 1, 4,
1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7, 1, 8, 1, 8,
1, 8, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13,
1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 17, 1, 17, 1, 18, 1, 18, 1,
19, 1, 19, 1, 20, 1, 20, 1, 21, 1, 21, 1, 22, 1, 22, 1, 23, 1, 23, 1, 24,
1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26, 1, 26, 1, 26, 1,
26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 28, 1, 28, 1, 29, 1, 29,
1, 30, 1, 30, 1, 31, 1, 31, 3, 31, 177, 8, 31, 1, 31, 4, 31, 180, 8, 31,
11, 31, 12, 31, 181, 1, 32, 1, 32, 1, 33, 1, 33, 1, 34, 1, 34, 1, 34, 1,
34, 3, 34, 192, 8, 34, 1, 35, 1, 35, 1, 35, 1, 36, 1, 36, 1, 36, 1, 36,
1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 38, 1, 38, 1, 38, 1, 38, 1,
38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38,
1, 38, 1, 38, 1, 38, 3, 38, 225, 8, 38, 1, 39, 4, 39, 228, 8, 39, 11, 39,
12, 39, 229, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40, 1, 40, 5, 40, 238, 8, 40,
10, 40, 12, 40, 241, 9, 40, 1, 40, 1, 40, 1, 41, 4, 41, 246, 8, 41, 11,
41, 12, 41, 247, 1, 41, 1, 41, 4, 41, 252, 8, 41, 11, 41, 12, 41, 253,
1, 41, 3, 41, 257, 8, 41, 1, 41, 4, 41, 260, 8, 41, 11, 41, 12, 41, 261,
1, 41, 1, 41, 1, 41, 1, 41, 4, 41, 268, 8, 41, 11, 41, 12, 41, 269, 1,
41, 3, 41, 273, 8, 41, 3, 41, 275, 8, 41, 1, 42, 4, 42, 278, 8, 42, 11,
42, 12, 42, 279, 1, 42, 1, 42, 1, 42, 1, 42, 4, 42, 286, 8, 42, 11, 42,
12, 42, 287, 3, 42, 290, 8, 42, 1, 43, 4, 43, 293, 8, 43, 11, 43, 12, 43,
294, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 4, 43, 303, 8, 43, 11, 43,
12, 43, 304, 1, 43, 1, 43, 3, 43, 309, 8, 43, 1, 44, 1, 44, 1, 44, 5, 44,
314, 8, 44, 10, 44, 12, 44, 317, 9, 44, 1, 44, 1, 44, 1, 44, 1, 44, 5,
44, 323, 8, 44, 10, 44, 12, 44, 326, 9, 44, 1, 44, 1, 44, 1, 44, 1, 44,
1, 44, 1, 44, 1, 44, 5, 44, 335, 8, 44, 10, 44, 12, 44, 338, 9, 44, 1,
44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 5, 44, 349,
8, 44, 10, 44, 12, 44, 352, 9, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1,
44, 5, 44, 360, 8, 44, 10, 44, 12, 44, 363, 9, 44, 1, 44, 1, 44, 1, 44,
1, 44, 1, 44, 5, 44, 370, 8, 44, 10, 44, 12, 44, 373, 9, 44, 1, 44, 1,
44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 5, 44, 383, 8, 44, 10, 44,
12, 44, 386, 9, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1,
44, 1, 44, 1, 44, 5, 44, 398, 8, 44, 10, 44, 12, 44, 401, 9, 44, 1, 44,
1, 44, 1, 44, 1, 44, 3, 44, 407, 8, 44, 1, 45, 1, 45, 1, 45, 1, 46, 1,
46, 3, 46, 414, 8, 46, 1, 46, 1, 46, 1, 46, 5, 46, 419, 8, 46, 10, 46,
12, 46, 422, 9, 46, 4, 336, 350, 384, 399, 0, 47, 1, 1, 3, 2, 5, 3, 7,
4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10, 21, 11, 23, 12, 25, 13, 27,
14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19, 39, 20, 41, 21, 43, 22, 45,
23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28, 57, 0, 59, 0, 61, 0, 63, 0,
65, 0, 67, 0, 69, 0, 71, 0, 73, 0, 75, 0, 77, 0, 79, 29, 81, 30, 83, 31,
85, 32, 87, 33, 89, 34, 91, 35, 93, 36, 1, 0, 16, 2, 0, 65, 90, 97, 122,
2, 0, 69, 69, 101, 101, 2, 0, 43, 43, 45, 45, 3, 0, 48, 57, 65, 70, 97,
102, 2, 0, 82, 82, 114, 114, 10, 0, 34, 34, 39, 39, 63, 63, 92, 92, 96,
98, 102, 102, 110, 110, 114, 114, 116, 116, 118, 118, 2, 0, 88, 88, 120,
120, 3, 0, 9, 10, 12, 13, 32, 32, 1, 0, 10, 10, 2, 0, 85, 85, 117, 117,
4, 0, 10, 10, 13, 13, 34, 34, 92, 92, 4, 0, 10, 10, 13, 13, 39, 39, 92,
92, 1, 0, 92, 92, 3, 0, 10, 10, 13, 13, 34, 34, 3, 0, 10, 10, 13, 13, 39,
39, 2, 0, 66, 66, 98, 98, 456, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5,
1, 0, 0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13,
1, 0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0,
21, 1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0,
0, 29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0,
0, 0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0,
0, 0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1,
0, 0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, 81,
1, 0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0,
89, 1, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 93, 1, 0, 0, 0, 1, 95, 1, 0, 0, 0,
3, 98, 1, 0, 0, 0, 5, 101, 1, 0, 0, 0, 7, 104, 1, 0, 0, 0, 9, 106, 1, 0,
0, 0, 11, 109, 1, 0, 0, 0, 13, 112, 1, 0, 0, 0, 15, 114, 1, 0, 0, 0, 17,
117, 1, 0, 0, 0, 19, 120, 1, 0, 0, 0, 21, 122, 1, 0, 0, 0, 23, 124, 1,
0, 0, 0, 25, 126, 1, 0, 0, 0, 27, 128, 1, 0, 0, 0, 29, 130, 1, 0, 0, 0,
31, 132, 1, 0, 0, 0, 33, 134, 1, 0, 0, 0, 35, 136, 1, 0, 0, 0, 37, 138,
1, 0, 0, 0, 39, 140, 1, 0, 0, 0, 41, 142, 1, 0, 0, 0, 43, 144, 1, 0, 0,
0, 45, 146, 1, 0, 0, 0, 47, 148, 1, 0, 0, 0, 49, 150, 1, 0, 0, 0, 51, 152,
1, 0, 0, 0, 53, 157, 1, 0, 0, 0, 55, 163, 1, 0, 0, 0, 57, 168, 1, 0, 0,
0, 59, 170, 1, 0, 0, 0, 61, 172, 1, 0, 0, 0, 63, 174, 1, 0, 0, 0, 65, 183,
1, 0, 0, 0, 67, 185, 1, 0, 0, 0, 69, 191, 1, 0, 0, 0, 71, 193, 1, 0, 0,
0, 73, 196, 1, 0, 0, 0, 75, 201, 1, 0, 0, 0, 77, 224, 1, 0, 0, 0, 79, 227,
1, 0, 0, 0, 81, 233, 1, 0, 0, 0, 83, 274, 1, 0, 0, 0, 85, 289, 1, 0, 0,
0, 87, 308, 1, 0, 0, 0, 89, 406, 1, 0, 0, 0, 91, 408, 1, 0, 0, 0, 93, 413,
1, 0, 0, 0, 95, 96, 5, 61, 0, 0, 96, 97, 5, 61, 0, 0, 97, 2, 1, 0, 0, 0,
98, 99, 5, 33, 0, 0, 99, 100, 5, 61, 0, 0, 100, 4, 1, 0, 0, 0, 101, 102,
5, 105, 0, 0, 102, 103, 5, 110, 0, 0, 103, 6, 1, 0, 0, 0, 104, 105, 5,
60, 0, 0, 105, 8, 1, 0, 0, 0, 106, 107, 5, 60, 0, 0, 107, 108, 5, 61, 0,
0, 108, 10, 1, 0, 0, 0, 109, 110, 5, 62, 0, 0, 110, 111, 5, 61, 0, 0, 111,
12, 1, 0, 0, 0, 112, 113, 5, 62, 0, 0, 113, 14, 1, 0, 0, 0, 114, 115, 5,
38, 0, 0, 115, 116, 5, 38, 0, 0, 116, 16, 1, 0, 0, 0, 117, 118, 5, 124,
0, 0, 118, 119, 5, 124, 0, 0, 119, 18, 1, 0, 0, 0, 120, 121, 5, 91, 0,
0, 121, 20, 1, 0, 0, 0, 122, 123, 5, 93, 0, 0, 123, 22, 1, 0, 0, 0, 124,
125, 5, 123, 0, 0, 125, 24, 1, 0, 0, 0, 126, 127, 5, 125, 0, 0, 127, 26,
1, 0, 0, 0, 128, 129, 5, 40, 0, 0, 129, 28, 1, 0, 0, 0, 130, 131, 5, 41,
0, 0, 131, 30, 1, 0, 0, 0, 132, 133, 5, 46, 0, 0, 133, 32, 1, 0, 0, 0,
134, 135, 5, 44, 0, 0, 135, 34, 1, 0, 0, 0, 136, 137, 5, 45, 0, 0, 137,
36, 1, 0, 0, 0, 138, 139, 5, 33, 0, 0, 139, 38, 1, 0, 0, 0, 140, 141, 5,
63, 0, 0, 141, 40, 1, 0, 0, 0, 142, 143, 5, 58, 0, 0, 143, 42, 1, 0, 0,
0, 144, 145, 5, 43, 0, 0, 145, 44, 1, 0, 0, 0, 146, 147, 5, 42, 0, 0, 147,
46, 1, 0, 0, 0, 148, 149, 5, 47, 0, 0, 149, 48, 1, 0, 0, 0, 150, 151, 5,
37, 0, 0, 151, 50, 1, 0, 0, 0, 152, 153, 5, 116, 0, 0, 153, 154, 5, 114,
0, 0, 154, 155, 5, 117, 0, 0, 155, 156, 5, 101, 0, 0, 156, 52, 1, 0, 0,
0, 157, 158, 5, 102, 0, 0, 158, 159, 5, 97, 0, 0, 159, 160, 5, 108, 0,
0, 160, 161, 5, 115, 0, 0, 161, 162, 5, 101, 0, 0, 162, 54, 1, 0, 0, 0,
163, 164, 5, 110, 0, 0, 164, 165, 5, 117, 0, 0, 165, 166, 5, 108, 0, 0,
166, 167, 5, 108, 0, 0, 167, 56, 1, 0, 0, 0, 168, 169, 5, 92, 0, 0, 169,
58, 1, 0, 0, 0, 170, 171, 7, 0, 0, 0, 171, 60, 1, 0, 0, 0, 172, 173, 2,
48, 57, 0, 173, 62, 1, 0, 0, 0, 174, 176, 7, 1, 0, 0, 175, 177, 7, 2, 0,
0, 176, 175, 1, 0, 0, 0, 176, 177, 1, 0, 0, 0, 177, 179, 1, 0, 0, 0, 178,
180, 3, 61, 30, 0, 179, 178, 1, 0, 0, 0, 180, 181, 1, 0, 0, 0, 181, 179,
1, 0, 0, 0, 181, 182, 1, 0, 0, 0, 182, 64, 1, 0, 0, 0, 183, 184, 7, 3,
0, 0, 184, 66, 1, 0, 0, 0, 185, 186, 7, 4, 0, 0, 186, 68, 1, 0, 0, 0, 187,
192, 3, 71, 35, 0, 188, 192, 3, 75, 37, 0, 189, 192, 3, 77, 38, 0, 190,
192, 3, 73, 36, 0, 191, 187, 1, 0, 0, 0, 191, 188, 1, 0, 0, 0, 191, 189,
1, 0, 0, 0, 191, 190, 1, 0, 0, 0, 192, 70, 1, 0, 0, 0, 193, 194, 3, 57,
28, 0, 194, 195, 7, 5, 0, 0, 195, 72, 1, 0, 0, 0, 196, 197, 3, 57, 28,
0, 197, 198, 2, 48, 51, 0, 198, 199, 2, 48, 55, 0, 199, 200, 2, 48, 55,
0, 200, 74, 1, 0, 0, 0, 201, 202, 3, 57, 28, 0, 202, 203, 7, 6, 0, 0, 203,
204, 3, 65, 32, 0, 204, 205, 3, 65, 32, 0, 205, 76, 1, 0, 0, 0, 206, 207,
3, 57, 28, 0, 207, 208, 5, 117, 0, 0, 208, 209, 3, 65, 32, 0, 209, 210,
3, 65, 32, 0, 210, 211, 3, 65, 32, 0, 211, 212, 3, 65, 32, 0, 212, 225,
1, 0, 0, 0, 213, 214, 3, 57, 28, 0, 214, 215, 5, 85, 0, 0, 215, 216, 3,
65, 32, 0, 216, 217, 3, 65, 32, 0, 217, 218, 3, 65, 32, 0, 218, 219, 3,
65, 32, 0, 219, 220, 3, 65, 32, 0, 220, 221, 3, 65, 32, 0, 221, 222, 3,
65, 32, 0, 222, 223, 3, 65, 32, 0, 223, 225, 1, 0, 0, 0, 224, 206, 1, 0,
0, 0, 224, 213, 1, 0, 0, 0, 225, 78, 1, 0, 0, 0, 226, 228, 7, 7, 0, 0,
227, 226, 1, 0, 0, 0, 228, 229, 1, 0, 0, 0, 229, 227, 1, 0, 0, 0, 229,
230, 1, 0, 0, 0, 230, 231, 1, 0, 0, 0, 231, 232, 6, 39, 0, 0, 232, 80,
1, 0, 0, 0, 233, 234, 5, 47, 0, 0, 234, 235, 5, 47, 0, 0, 235, 239, 1,
0, 0, 0, 236, 238, 8, 8, 0, 0, 237, 236, 1, 0, 0, 0, 238, 241, 1, 0, 0,
0, 239, 237, 1, 0, 0, 0, 239, 240, 1, 0, 0, 0, 240, 242, 1, 0, 0, 0, 241,
239, 1, 0, 0, 0, 242, 243, 6, 40, 0, 0, 243, 82, 1, 0, 0, 0, 244, 246,
3, 61, 30, 0, 245, 244, 1, 0, 0, 0, 246, 247, 1, 0, 0, 0, 247, 245, 1,
0, 0, 0, 247, 248, 1, 0, 0, 0, 248, 249, 1, 0, 0, 0, 249, 251, 5, 46, 0,
0, 250, 252, 3, 61, 30, 0, 251, 250, 1, 0, 0, 0, 252, 253, 1, 0, 0, 0,
253, 251, 1, 0, 0, 0, 253, 254, 1, 0, 0, 0, 254, 256, 1, 0, 0, 0, 255,
257, 3, 63, 31, 0, 256, 255, 1, 0, 0, 0, 256, 257, 1, 0, 0, 0, 257, 275,
1, 0, 0, 0, 258, 260, 3, 61, 30, 0, 259, 258, 1, 0, 0, 0, 260, 261, 1,
0, 0, 0, 261, 259, 1, 0, 0, 0, 261, 262, 1, 0, 0, 0, 262, 263, 1, 0, 0,
0, 263, 264, 3, 63, 31, 0, 264, 275, 1, 0, 0, 0, 265, 267, 5, 46, 0, 0,
266, 268, 3, 61, 30, 0, 267, 266, 1, 0, 0, 0, 268, 269, 1, 0, 0, 0, 269,
267, 1, 0, 0, 0, 269, 270, 1, 0, 0, 0, 270, 272, 1, 0, 0, 0, 271, 273,
3, 63, 31, 0, 272, 271, 1, 0, 0, 0, 272, 273, 1, 0, 0, 0, 273, 275, 1,
0, 0, 0, 274, 245, 1, 0, 0, 0, 274, 259, 1, 0, 0, 0, 274, 265, 1, 0, 0,
0, 275, 84, 1, 0, 0, 0, 276, 278, 3, 61, 30, 0, 277, 276, 1, 0, 0, 0, 278,
279, 1, 0, 0, 0, 279, 277, 1, 0, 0, 0, 279, 280, 1, 0, 0, 0, 280, 290,
1, 0, 0, 0, 281, 282, 5, 48, 0, 0, 282, 283, 5, 120, 0, 0, 283, 285, 1,
0, 0, 0, 284, 286, 3, 65, 32, 0, 285, 284, 1, 0, 0, 0, 286, 287, 1, 0,
0, 0, 287, 285, 1, 0, 0, 0, 287, 288, 1, 0, 0, 0, 288, 290, 1, 0, 0, 0,
289, 277, 1, 0, 0, 0, 289, 281, 1, 0, 0, 0, 290, 86, 1, 0, 0, 0, 291, 293,
3, 61, 30, 0, 292, 291, 1, 0, 0, 0, 293, 294, 1, 0, 0, 0, 294, 292, 1,
0, 0, 0, 294, 295, 1, 0, 0, 0, 295, 296, 1, 0, 0, 0, 296, 297, 7, 9, 0,
0, 297, 309, 1, 0, 0, 0, 298, 299, 5, 48, 0, 0, 299, 300, 5, 120, 0, 0,
300, 302, 1, 0, 0, 0, 301, 303, 3, 65, 32, 0, 302, 301, 1, 0, 0, 0, 303,
304, 1, 0, 0, 0, 304, 302, 1, 0, 0, 0, 304, 305, 1, 0, 0, 0, 305, 306,
1, 0, 0, 0, 306, 307, 7, 9, 0, 0, 307, 309, 1, 0, 0, 0, 308, 292, 1, 0,
0, 0, 308, 298, 1, 0, 0, 0, 309, 88, 1, 0, 0, 0, 310, 315, 5, 34, 0, 0,
311, 314, 3, 69, 34, 0, 312, 314, 8, 10, 0, 0, 313, 311, 1, 0, 0, 0, 313,
312, 1, 0, 0, 0, 314, 317, 1, 0, 0, 0, 315, 313, 1, 0, 0, 0, 315, 316,
1, 0, 0, 0, 316, 318, 1, 0, 0, 0, 317, 315, 1, 0, 0, 0, 318, 407, 5, 34,
0, 0, 319, 324, 5, 39, 0, 0, 320, 323, 3, 69, 34, 0, 321, 323, 8, 11, 0,
0, 322, 320, 1, 0, 0, 0, 322, 321, 1, 0, 0, 0, 323, 326, 1, 0, 0, 0, 324,
322, 1, 0, 0, 0, 324, 325, 1, 0, 0, 0, 325, 327, 1, 0, 0, 0, 326, 324,
1, 0, 0, 0, 327, 407, 5, 39, 0, 0, 328, 329, 5, 34, 0, 0, 329, 330, 5,
34, 0, 0, 330, 331, 5, 34, 0, 0, 331, 336, 1, 0, 0, 0, 332, 335, 3, 69,
34, 0, 333, 335, 8, 12, 0, 0, 334, 332, 1, 0, 0, 0, 334, 333, 1, 0, 0,
0, 335, 338, 1, 0, 0, 0, 336, 337, 1, 0, 0, 0, 336, 334, 1, 0, 0, 0, 337,
339, 1, 0, 0, 0, 338, 336, 1, 0, 0, 0, 339, 340, 5, 34, 0, 0, 340, 341,
5, 34, 0, 0, 341, 407, 5, 34, 0, 0, 342, 343, 5, 39, 0, 0, 343, 344, 5,
39, 0, 0, 344, 345, 5, 39, 0, 0, 345, 350, 1, 0, 0, 0, 346, 349, 3, 69,
34, 0, 347, 349, 8, 12, 0, 0, 348, 346, 1, 0, 0, 0, 348, 347, 1, 0, 0,
0, 349, 352, 1, 0, 0, 0, 350, 351, 1, 0, 0, 0, 350, 348, 1, 0, 0, 0, 351,
353, 1, 0, 0, 0, 352, 350, 1, 0, 0, 0, 353, 354, 5, 39, 0, 0, 354, 355,
5, 39, 0, 0, 355, 407, 5, 39, 0, 0, 356, 357, 3, 67, 33, 0, 357, 361, 5,
34, 0, 0, 358, 360, 8, 13, 0, 0, 359, 358, 1, 0, 0, 0, 360, 363, 1, 0,
0, 0, 361, 359, 1, 0, 0, 0, 361, 362, 1, 0, 0, 0, 362, 364, 1, 0, 0, 0,
363, 361, 1, 0, 0, 0, 364, 365, 5, 34, 0, 0, 365, 407, 1, 0, 0, 0, 366,
367, 3, 67, 33, 0, 367, 371, 5, 39, 0, 0, 368, 370, 8, 14, 0, 0, 369, 368,
1, 0, 0, 0, 370, 373, 1, 0, 0, 0, 371, 369, 1, 0, 0, 0, 371, 372, 1, 0,
0, 0, 372, 374, 1, 0, 0, 0, 373, 371, 1, 0, 0, 0, 374, 375, 5, 39, 0, 0,
375, 407, 1, 0, 0, 0, 376, 377, 3, 67, 33, 0, 377, 378, 5, 34, 0, 0, 378,
379, 5, 34, 0, 0, 379, 380, 5, 34, 0, 0, 380, 384, 1, 0, 0, 0, 381, 383,
9, 0, 0, 0, 382, 381, 1, 0, 0, 0, 383, 386, 1, 0, 0, 0, 384, 385, 1, 0,
0, 0, 384, 382, 1, 0, 0, 0, 385, 387, 1, 0, 0, 0, 386, 384, 1, 0, 0, 0,
387, 388, 5, 34, 0, 0, 388, 389, 5, 34, 0, 0, 389, 390, 5, 34, 0, 0, 390,
407, 1, 0, 0, 0, 391, 392, 3, 67, 33, 0, 392, 393, 5, 39, 0, 0, 393, 394,
5, 39, 0, 0, 394, 395, 5, 39, 0, 0, 395, 399, 1, 0, 0, 0, 396, 398, 9,
0, 0, 0, 397, 396, 1, 0, 0, 0, 398, 401, 1, 0, 0, 0, 399, 400, 1, 0, 0,
0, 399, 397, 1, 0, 0, 0, 400, 402, 1, 0, 0, 0, 401, 399, 1, 0, 0, 0, 402,
403, 5, 39, 0, 0, 403, 404, 5, 39, 0, 0, 404, 405, 5, 39, 0, 0, 405, 407,
1, 0, 0, 0, 406, 310, 1, 0, 0, 0, 406, 319, 1, 0, 0, 0, 406, 328, 1, 0,
0, 0, 406, 342, 1, 0, 0, 0, 406, 356, 1, 0, 0, 0, 406, 366, 1, 0, 0, 0,
406, 376, 1, 0, 0, 0, 406, 391, 1, 0, 0, 0, 407, 90, 1, 0, 0, 0, 408, 409,
7, 15, 0, 0, 409, 410, 3, 89, 44, 0, 410, 92, 1, 0, 0, 0, 411, 414, 3,
59, 29, 0, 412, 414, 5, 95, 0, 0, 413, 411, 1, 0, 0, 0, 413, 412, 1, 0,
0, 0, 414, 420, 1, 0, 0, 0, 415, 419, 3, 59, 29, 0, 416, 419, 3, 61, 30,
0, 417, 419, 5, 95, 0, 0, 418, 415, 1, 0, 0, 0, 418, 416, 1, 0, 0, 0, 418,
417, 1, 0, 0, 0, 419, 422, 1, 0, 0, 0, 420, 418, 1, 0, 0, 0, 420, 421,
1, 0, 0, 0, 421, 94, 1, 0, 0, 0, 422, 420, 1, 0, 0, 0, 36, 0, 176, 181,
191, 224, 229, 239, 247, 253, 256, 261, 269, 272, 274, 279, 287, 289, 294,
304, 308, 313, 315, 322, 324, 334, 336, 348, 350, 361, 371, 384, 399, 406,
413, 418, 420, 1, 0, 1, 0,
}
deserializer := antlr.NewATNDeserializer(nil)
staticData.atn = deserializer.Deserialize(staticData.serializedATN)
atn := staticData.atn
staticData.decisionToDFA = make([]*antlr.DFA, len(atn.DecisionToState))
decisionToDFA := staticData.decisionToDFA
for index, state := range atn.DecisionToState {
decisionToDFA[index] = antlr.NewDFA(state, index)
}
staticData := &CELLexerLexerStaticData
staticData.ChannelNames = []string{
"DEFAULT_TOKEN_CHANNEL", "HIDDEN",
}
staticData.ModeNames = []string{
"DEFAULT_MODE",
}
staticData.LiteralNames = []string{
"", "'=='", "'!='", "'in'", "'<'", "'<='", "'>='", "'>'", "'&&'", "'||'",
"'['", "']'", "'{'", "'}'", "'('", "')'", "'.'", "','", "'-'", "'!'",
"'?'", "':'", "'+'", "'*'", "'/'", "'%'", "'true'", "'false'", "'null'",
}
staticData.SymbolicNames = []string{
"", "EQUALS", "NOT_EQUALS", "IN", "LESS", "LESS_EQUALS", "GREATER_EQUALS",
"GREATER", "LOGICAL_AND", "LOGICAL_OR", "LBRACKET", "RPRACKET", "LBRACE",
"RBRACE", "LPAREN", "RPAREN", "DOT", "COMMA", "MINUS", "EXCLAM", "QUESTIONMARK",
"COLON", "PLUS", "STAR", "SLASH", "PERCENT", "CEL_TRUE", "CEL_FALSE",
"NUL", "WHITESPACE", "COMMENT", "NUM_FLOAT", "NUM_INT", "NUM_UINT",
"STRING", "BYTES", "IDENTIFIER", "ESC_IDENTIFIER",
}
staticData.RuleNames = []string{
"EQUALS", "NOT_EQUALS", "IN", "LESS", "LESS_EQUALS", "GREATER_EQUALS",
"GREATER", "LOGICAL_AND", "LOGICAL_OR", "LBRACKET", "RPRACKET", "LBRACE",
"RBRACE", "LPAREN", "RPAREN", "DOT", "COMMA", "MINUS", "EXCLAM", "QUESTIONMARK",
"COLON", "PLUS", "STAR", "SLASH", "PERCENT", "CEL_TRUE", "CEL_FALSE",
"NUL", "BACKSLASH", "LETTER", "DIGIT", "EXPONENT", "HEXDIGIT", "RAW",
"ESC_SEQ", "ESC_CHAR_SEQ", "ESC_OCT_SEQ", "ESC_BYTE_SEQ", "ESC_UNI_SEQ",
"WHITESPACE", "COMMENT", "NUM_FLOAT", "NUM_INT", "NUM_UINT", "STRING",
"BYTES", "IDENTIFIER", "ESC_IDENTIFIER",
}
staticData.PredictionContextCache = antlr.NewPredictionContextCache()
staticData.serializedATN = []int32{
4, 0, 37, 435, 6, -1, 2, 0, 7, 0, 2, 1, 7, 1, 2, 2, 7, 2, 2, 3, 7, 3, 2,
4, 7, 4, 2, 5, 7, 5, 2, 6, 7, 6, 2, 7, 7, 7, 2, 8, 7, 8, 2, 9, 7, 9, 2,
10, 7, 10, 2, 11, 7, 11, 2, 12, 7, 12, 2, 13, 7, 13, 2, 14, 7, 14, 2, 15,
7, 15, 2, 16, 7, 16, 2, 17, 7, 17, 2, 18, 7, 18, 2, 19, 7, 19, 2, 20, 7,
20, 2, 21, 7, 21, 2, 22, 7, 22, 2, 23, 7, 23, 2, 24, 7, 24, 2, 25, 7, 25,
2, 26, 7, 26, 2, 27, 7, 27, 2, 28, 7, 28, 2, 29, 7, 29, 2, 30, 7, 30, 2,
31, 7, 31, 2, 32, 7, 32, 2, 33, 7, 33, 2, 34, 7, 34, 2, 35, 7, 35, 2, 36,
7, 36, 2, 37, 7, 37, 2, 38, 7, 38, 2, 39, 7, 39, 2, 40, 7, 40, 2, 41, 7,
41, 2, 42, 7, 42, 2, 43, 7, 43, 2, 44, 7, 44, 2, 45, 7, 45, 2, 46, 7, 46,
2, 47, 7, 47, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 1, 2, 1,
3, 1, 3, 1, 4, 1, 4, 1, 4, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 7, 1, 7, 1,
7, 1, 8, 1, 8, 1, 8, 1, 9, 1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 12, 1,
12, 1, 13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, 16, 1, 16, 1, 17, 1, 17,
1, 18, 1, 18, 1, 19, 1, 19, 1, 20, 1, 20, 1, 21, 1, 21, 1, 22, 1, 22, 1,
23, 1, 23, 1, 24, 1, 24, 1, 25, 1, 25, 1, 25, 1, 25, 1, 25, 1, 26, 1, 26,
1, 26, 1, 26, 1, 26, 1, 26, 1, 27, 1, 27, 1, 27, 1, 27, 1, 27, 1, 28, 1,
28, 1, 29, 1, 29, 1, 30, 1, 30, 1, 31, 1, 31, 3, 31, 179, 8, 31, 1, 31,
4, 31, 182, 8, 31, 11, 31, 12, 31, 183, 1, 32, 1, 32, 1, 33, 1, 33, 1,
34, 1, 34, 1, 34, 1, 34, 3, 34, 194, 8, 34, 1, 35, 1, 35, 1, 35, 1, 36,
1, 36, 1, 36, 1, 36, 1, 36, 1, 37, 1, 37, 1, 37, 1, 37, 1, 37, 1, 38, 1,
38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38,
1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 1, 38, 3, 38, 227, 8, 38, 1, 39, 4,
39, 230, 8, 39, 11, 39, 12, 39, 231, 1, 39, 1, 39, 1, 40, 1, 40, 1, 40,
1, 40, 5, 40, 240, 8, 40, 10, 40, 12, 40, 243, 9, 40, 1, 40, 1, 40, 1,
41, 4, 41, 248, 8, 41, 11, 41, 12, 41, 249, 1, 41, 1, 41, 4, 41, 254, 8,
41, 11, 41, 12, 41, 255, 1, 41, 3, 41, 259, 8, 41, 1, 41, 4, 41, 262, 8,
41, 11, 41, 12, 41, 263, 1, 41, 1, 41, 1, 41, 1, 41, 4, 41, 270, 8, 41,
11, 41, 12, 41, 271, 1, 41, 3, 41, 275, 8, 41, 3, 41, 277, 8, 41, 1, 42,
4, 42, 280, 8, 42, 11, 42, 12, 42, 281, 1, 42, 1, 42, 1, 42, 1, 42, 4,
42, 288, 8, 42, 11, 42, 12, 42, 289, 3, 42, 292, 8, 42, 1, 43, 4, 43, 295,
8, 43, 11, 43, 12, 43, 296, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 1, 43, 4,
43, 305, 8, 43, 11, 43, 12, 43, 306, 1, 43, 1, 43, 3, 43, 311, 8, 43, 1,
44, 1, 44, 1, 44, 5, 44, 316, 8, 44, 10, 44, 12, 44, 319, 9, 44, 1, 44,
1, 44, 1, 44, 1, 44, 5, 44, 325, 8, 44, 10, 44, 12, 44, 328, 9, 44, 1,
44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 5, 44, 337, 8, 44, 10, 44,
12, 44, 340, 9, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1,
44, 1, 44, 5, 44, 351, 8, 44, 10, 44, 12, 44, 354, 9, 44, 1, 44, 1, 44,
1, 44, 1, 44, 1, 44, 1, 44, 5, 44, 362, 8, 44, 10, 44, 12, 44, 365, 9,
44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 5, 44, 372, 8, 44, 10, 44, 12, 44,
375, 9, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 5,
44, 385, 8, 44, 10, 44, 12, 44, 388, 9, 44, 1, 44, 1, 44, 1, 44, 1, 44,
1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 5, 44, 400, 8, 44, 10, 44, 12,
44, 403, 9, 44, 1, 44, 1, 44, 1, 44, 1, 44, 3, 44, 409, 8, 44, 1, 45, 1,
45, 1, 45, 1, 46, 1, 46, 3, 46, 416, 8, 46, 1, 46, 1, 46, 1, 46, 5, 46,
421, 8, 46, 10, 46, 12, 46, 424, 9, 46, 1, 47, 1, 47, 1, 47, 1, 47, 4,
47, 430, 8, 47, 11, 47, 12, 47, 431, 1, 47, 1, 47, 4, 338, 352, 386, 401,
0, 48, 1, 1, 3, 2, 5, 3, 7, 4, 9, 5, 11, 6, 13, 7, 15, 8, 17, 9, 19, 10,
21, 11, 23, 12, 25, 13, 27, 14, 29, 15, 31, 16, 33, 17, 35, 18, 37, 19,
39, 20, 41, 21, 43, 22, 45, 23, 47, 24, 49, 25, 51, 26, 53, 27, 55, 28,
57, 0, 59, 0, 61, 0, 63, 0, 65, 0, 67, 0, 69, 0, 71, 0, 73, 0, 75, 0, 77,
0, 79, 29, 81, 30, 83, 31, 85, 32, 87, 33, 89, 34, 91, 35, 93, 36, 95,
37, 1, 0, 17, 2, 0, 65, 90, 97, 122, 2, 0, 69, 69, 101, 101, 2, 0, 43,
43, 45, 45, 3, 0, 48, 57, 65, 70, 97, 102, 2, 0, 82, 82, 114, 114, 10,
0, 34, 34, 39, 39, 63, 63, 92, 92, 96, 98, 102, 102, 110, 110, 114, 114,
116, 116, 118, 118, 2, 0, 88, 88, 120, 120, 3, 0, 9, 10, 12, 13, 32, 32,
1, 0, 10, 10, 2, 0, 85, 85, 117, 117, 4, 0, 10, 10, 13, 13, 34, 34, 92,
92, 4, 0, 10, 10, 13, 13, 39, 39, 92, 92, 1, 0, 92, 92, 3, 0, 10, 10, 13,
13, 34, 34, 3, 0, 10, 10, 13, 13, 39, 39, 2, 0, 66, 66, 98, 98, 3, 0, 32,
32, 45, 47, 95, 95, 471, 0, 1, 1, 0, 0, 0, 0, 3, 1, 0, 0, 0, 0, 5, 1, 0,
0, 0, 0, 7, 1, 0, 0, 0, 0, 9, 1, 0, 0, 0, 0, 11, 1, 0, 0, 0, 0, 13, 1,
0, 0, 0, 0, 15, 1, 0, 0, 0, 0, 17, 1, 0, 0, 0, 0, 19, 1, 0, 0, 0, 0, 21,
1, 0, 0, 0, 0, 23, 1, 0, 0, 0, 0, 25, 1, 0, 0, 0, 0, 27, 1, 0, 0, 0, 0,
29, 1, 0, 0, 0, 0, 31, 1, 0, 0, 0, 0, 33, 1, 0, 0, 0, 0, 35, 1, 0, 0, 0,
0, 37, 1, 0, 0, 0, 0, 39, 1, 0, 0, 0, 0, 41, 1, 0, 0, 0, 0, 43, 1, 0, 0,
0, 0, 45, 1, 0, 0, 0, 0, 47, 1, 0, 0, 0, 0, 49, 1, 0, 0, 0, 0, 51, 1, 0,
0, 0, 0, 53, 1, 0, 0, 0, 0, 55, 1, 0, 0, 0, 0, 79, 1, 0, 0, 0, 0, 81, 1,
0, 0, 0, 0, 83, 1, 0, 0, 0, 0, 85, 1, 0, 0, 0, 0, 87, 1, 0, 0, 0, 0, 89,
1, 0, 0, 0, 0, 91, 1, 0, 0, 0, 0, 93, 1, 0, 0, 0, 0, 95, 1, 0, 0, 0, 1,
97, 1, 0, 0, 0, 3, 100, 1, 0, 0, 0, 5, 103, 1, 0, 0, 0, 7, 106, 1, 0, 0,
0, 9, 108, 1, 0, 0, 0, 11, 111, 1, 0, 0, 0, 13, 114, 1, 0, 0, 0, 15, 116,
1, 0, 0, 0, 17, 119, 1, 0, 0, 0, 19, 122, 1, 0, 0, 0, 21, 124, 1, 0, 0,
0, 23, 126, 1, 0, 0, 0, 25, 128, 1, 0, 0, 0, 27, 130, 1, 0, 0, 0, 29, 132,
1, 0, 0, 0, 31, 134, 1, 0, 0, 0, 33, 136, 1, 0, 0, 0, 35, 138, 1, 0, 0,
0, 37, 140, 1, 0, 0, 0, 39, 142, 1, 0, 0, 0, 41, 144, 1, 0, 0, 0, 43, 146,
1, 0, 0, 0, 45, 148, 1, 0, 0, 0, 47, 150, 1, 0, 0, 0, 49, 152, 1, 0, 0,
0, 51, 154, 1, 0, 0, 0, 53, 159, 1, 0, 0, 0, 55, 165, 1, 0, 0, 0, 57, 170,
1, 0, 0, 0, 59, 172, 1, 0, 0, 0, 61, 174, 1, 0, 0, 0, 63, 176, 1, 0, 0,
0, 65, 185, 1, 0, 0, 0, 67, 187, 1, 0, 0, 0, 69, 193, 1, 0, 0, 0, 71, 195,
1, 0, 0, 0, 73, 198, 1, 0, 0, 0, 75, 203, 1, 0, 0, 0, 77, 226, 1, 0, 0,
0, 79, 229, 1, 0, 0, 0, 81, 235, 1, 0, 0, 0, 83, 276, 1, 0, 0, 0, 85, 291,
1, 0, 0, 0, 87, 310, 1, 0, 0, 0, 89, 408, 1, 0, 0, 0, 91, 410, 1, 0, 0,
0, 93, 415, 1, 0, 0, 0, 95, 425, 1, 0, 0, 0, 97, 98, 5, 61, 0, 0, 98, 99,
5, 61, 0, 0, 99, 2, 1, 0, 0, 0, 100, 101, 5, 33, 0, 0, 101, 102, 5, 61,
0, 0, 102, 4, 1, 0, 0, 0, 103, 104, 5, 105, 0, 0, 104, 105, 5, 110, 0,
0, 105, 6, 1, 0, 0, 0, 106, 107, 5, 60, 0, 0, 107, 8, 1, 0, 0, 0, 108,
109, 5, 60, 0, 0, 109, 110, 5, 61, 0, 0, 110, 10, 1, 0, 0, 0, 111, 112,
5, 62, 0, 0, 112, 113, 5, 61, 0, 0, 113, 12, 1, 0, 0, 0, 114, 115, 5, 62,
0, 0, 115, 14, 1, 0, 0, 0, 116, 117, 5, 38, 0, 0, 117, 118, 5, 38, 0, 0,
118, 16, 1, 0, 0, 0, 119, 120, 5, 124, 0, 0, 120, 121, 5, 124, 0, 0, 121,
18, 1, 0, 0, 0, 122, 123, 5, 91, 0, 0, 123, 20, 1, 0, 0, 0, 124, 125, 5,
93, 0, 0, 125, 22, 1, 0, 0, 0, 126, 127, 5, 123, 0, 0, 127, 24, 1, 0, 0,
0, 128, 129, 5, 125, 0, 0, 129, 26, 1, 0, 0, 0, 130, 131, 5, 40, 0, 0,
131, 28, 1, 0, 0, 0, 132, 133, 5, 41, 0, 0, 133, 30, 1, 0, 0, 0, 134, 135,
5, 46, 0, 0, 135, 32, 1, 0, 0, 0, 136, 137, 5, 44, 0, 0, 137, 34, 1, 0,
0, 0, 138, 139, 5, 45, 0, 0, 139, 36, 1, 0, 0, 0, 140, 141, 5, 33, 0, 0,
141, 38, 1, 0, 0, 0, 142, 143, 5, 63, 0, 0, 143, 40, 1, 0, 0, 0, 144, 145,
5, 58, 0, 0, 145, 42, 1, 0, 0, 0, 146, 147, 5, 43, 0, 0, 147, 44, 1, 0,
0, 0, 148, 149, 5, 42, 0, 0, 149, 46, 1, 0, 0, 0, 150, 151, 5, 47, 0, 0,
151, 48, 1, 0, 0, 0, 152, 153, 5, 37, 0, 0, 153, 50, 1, 0, 0, 0, 154, 155,
5, 116, 0, 0, 155, 156, 5, 114, 0, 0, 156, 157, 5, 117, 0, 0, 157, 158,
5, 101, 0, 0, 158, 52, 1, 0, 0, 0, 159, 160, 5, 102, 0, 0, 160, 161, 5,
97, 0, 0, 161, 162, 5, 108, 0, 0, 162, 163, 5, 115, 0, 0, 163, 164, 5,
101, 0, 0, 164, 54, 1, 0, 0, 0, 165, 166, 5, 110, 0, 0, 166, 167, 5, 117,
0, 0, 167, 168, 5, 108, 0, 0, 168, 169, 5, 108, 0, 0, 169, 56, 1, 0, 0,
0, 170, 171, 5, 92, 0, 0, 171, 58, 1, 0, 0, 0, 172, 173, 7, 0, 0, 0, 173,
60, 1, 0, 0, 0, 174, 175, 2, 48, 57, 0, 175, 62, 1, 0, 0, 0, 176, 178,
7, 1, 0, 0, 177, 179, 7, 2, 0, 0, 178, 177, 1, 0, 0, 0, 178, 179, 1, 0,
0, 0, 179, 181, 1, 0, 0, 0, 180, 182, 3, 61, 30, 0, 181, 180, 1, 0, 0,
0, 182, 183, 1, 0, 0, 0, 183, 181, 1, 0, 0, 0, 183, 184, 1, 0, 0, 0, 184,
64, 1, 0, 0, 0, 185, 186, 7, 3, 0, 0, 186, 66, 1, 0, 0, 0, 187, 188, 7,
4, 0, 0, 188, 68, 1, 0, 0, 0, 189, 194, 3, 71, 35, 0, 190, 194, 3, 75,
37, 0, 191, 194, 3, 77, 38, 0, 192, 194, 3, 73, 36, 0, 193, 189, 1, 0,
0, 0, 193, 190, 1, 0, 0, 0, 193, 191, 1, 0, 0, 0, 193, 192, 1, 0, 0, 0,
194, 70, 1, 0, 0, 0, 195, 196, 3, 57, 28, 0, 196, 197, 7, 5, 0, 0, 197,
72, 1, 0, 0, 0, 198, 199, 3, 57, 28, 0, 199, 200, 2, 48, 51, 0, 200, 201,
2, 48, 55, 0, 201, 202, 2, 48, 55, 0, 202, 74, 1, 0, 0, 0, 203, 204, 3,
57, 28, 0, 204, 205, 7, 6, 0, 0, 205, 206, 3, 65, 32, 0, 206, 207, 3, 65,
32, 0, 207, 76, 1, 0, 0, 0, 208, 209, 3, 57, 28, 0, 209, 210, 5, 117, 0,
0, 210, 211, 3, 65, 32, 0, 211, 212, 3, 65, 32, 0, 212, 213, 3, 65, 32,
0, 213, 214, 3, 65, 32, 0, 214, 227, 1, 0, 0, 0, 215, 216, 3, 57, 28, 0,
216, 217, 5, 85, 0, 0, 217, 218, 3, 65, 32, 0, 218, 219, 3, 65, 32, 0,
219, 220, 3, 65, 32, 0, 220, 221, 3, 65, 32, 0, 221, 222, 3, 65, 32, 0,
222, 223, 3, 65, 32, 0, 223, 224, 3, 65, 32, 0, 224, 225, 3, 65, 32, 0,
225, 227, 1, 0, 0, 0, 226, 208, 1, 0, 0, 0, 226, 215, 1, 0, 0, 0, 227,
78, 1, 0, 0, 0, 228, 230, 7, 7, 0, 0, 229, 228, 1, 0, 0, 0, 230, 231, 1,
0, 0, 0, 231, 229, 1, 0, 0, 0, 231, 232, 1, 0, 0, 0, 232, 233, 1, 0, 0,
0, 233, 234, 6, 39, 0, 0, 234, 80, 1, 0, 0, 0, 235, 236, 5, 47, 0, 0, 236,
237, 5, 47, 0, 0, 237, 241, 1, 0, 0, 0, 238, 240, 8, 8, 0, 0, 239, 238,
1, 0, 0, 0, 240, 243, 1, 0, 0, 0, 241, 239, 1, 0, 0, 0, 241, 242, 1, 0,
0, 0, 242, 244, 1, 0, 0, 0, 243, 241, 1, 0, 0, 0, 244, 245, 6, 40, 0, 0,
245, 82, 1, 0, 0, 0, 246, 248, 3, 61, 30, 0, 247, 246, 1, 0, 0, 0, 248,
249, 1, 0, 0, 0, 249, 247, 1, 0, 0, 0, 249, 250, 1, 0, 0, 0, 250, 251,
1, 0, 0, 0, 251, 253, 5, 46, 0, 0, 252, 254, 3, 61, 30, 0, 253, 252, 1,
0, 0, 0, 254, 255, 1, 0, 0, 0, 255, 253, 1, 0, 0, 0, 255, 256, 1, 0, 0,
0, 256, 258, 1, 0, 0, 0, 257, 259, 3, 63, 31, 0, 258, 257, 1, 0, 0, 0,
258, 259, 1, 0, 0, 0, 259, 277, 1, 0, 0, 0, 260, 262, 3, 61, 30, 0, 261,
260, 1, 0, 0, 0, 262, 263, 1, 0, 0, 0, 263, 261, 1, 0, 0, 0, 263, 264,
1, 0, 0, 0, 264, 265, 1, 0, 0, 0, 265, 266, 3, 63, 31, 0, 266, 277, 1,
0, 0, 0, 267, 269, 5, 46, 0, 0, 268, 270, 3, 61, 30, 0, 269, 268, 1, 0,
0, 0, 270, 271, 1, 0, 0, 0, 271, 269, 1, 0, 0, 0, 271, 272, 1, 0, 0, 0,
272, 274, 1, 0, 0, 0, 273, 275, 3, 63, 31, 0, 274, 273, 1, 0, 0, 0, 274,
275, 1, 0, 0, 0, 275, 277, 1, 0, 0, 0, 276, 247, 1, 0, 0, 0, 276, 261,
1, 0, 0, 0, 276, 267, 1, 0, 0, 0, 277, 84, 1, 0, 0, 0, 278, 280, 3, 61,
30, 0, 279, 278, 1, 0, 0, 0, 280, 281, 1, 0, 0, 0, 281, 279, 1, 0, 0, 0,
281, 282, 1, 0, 0, 0, 282, 292, 1, 0, 0, 0, 283, 284, 5, 48, 0, 0, 284,
285, 5, 120, 0, 0, 285, 287, 1, 0, 0, 0, 286, 288, 3, 65, 32, 0, 287, 286,
1, 0, 0, 0, 288, 289, 1, 0, 0, 0, 289, 287, 1, 0, 0, 0, 289, 290, 1, 0,
0, 0, 290, 292, 1, 0, 0, 0, 291, 279, 1, 0, 0, 0, 291, 283, 1, 0, 0, 0,
292, 86, 1, 0, 0, 0, 293, 295, 3, 61, 30, 0, 294, 293, 1, 0, 0, 0, 295,
296, 1, 0, 0, 0, 296, 294, 1, 0, 0, 0, 296, 297, 1, 0, 0, 0, 297, 298,
1, 0, 0, 0, 298, 299, 7, 9, 0, 0, 299, 311, 1, 0, 0, 0, 300, 301, 5, 48,
0, 0, 301, 302, 5, 120, 0, 0, 302, 304, 1, 0, 0, 0, 303, 305, 3, 65, 32,
0, 304, 303, 1, 0, 0, 0, 305, 306, 1, 0, 0, 0, 306, 304, 1, 0, 0, 0, 306,
307, 1, 0, 0, 0, 307, 308, 1, 0, 0, 0, 308, 309, 7, 9, 0, 0, 309, 311,
1, 0, 0, 0, 310, 294, 1, 0, 0, 0, 310, 300, 1, 0, 0, 0, 311, 88, 1, 0,
0, 0, 312, 317, 5, 34, 0, 0, 313, 316, 3, 69, 34, 0, 314, 316, 8, 10, 0,
0, 315, 313, 1, 0, 0, 0, 315, 314, 1, 0, 0, 0, 316, 319, 1, 0, 0, 0, 317,
315, 1, 0, 0, 0, 317, 318, 1, 0, 0, 0, 318, 320, 1, 0, 0, 0, 319, 317,
1, 0, 0, 0, 320, 409, 5, 34, 0, 0, 321, 326, 5, 39, 0, 0, 322, 325, 3,
69, 34, 0, 323, 325, 8, 11, 0, 0, 324, 322, 1, 0, 0, 0, 324, 323, 1, 0,
0, 0, 325, 328, 1, 0, 0, 0, 326, 324, 1, 0, 0, 0, 326, 327, 1, 0, 0, 0,
327, 329, 1, 0, 0, 0, 328, 326, 1, 0, 0, 0, 329, 409, 5, 39, 0, 0, 330,
331, 5, 34, 0, 0, 331, 332, 5, 34, 0, 0, 332, 333, 5, 34, 0, 0, 333, 338,
1, 0, 0, 0, 334, 337, 3, 69, 34, 0, 335, 337, 8, 12, 0, 0, 336, 334, 1,
0, 0, 0, 336, 335, 1, 0, 0, 0, 337, 340, 1, 0, 0, 0, 338, 339, 1, 0, 0,
0, 338, 336, 1, 0, 0, 0, 339, 341, 1, 0, 0, 0, 340, 338, 1, 0, 0, 0, 341,
342, 5, 34, 0, 0, 342, 343, 5, 34, 0, 0, 343, 409, 5, 34, 0, 0, 344, 345,
5, 39, 0, 0, 345, 346, 5, 39, 0, 0, 346, 347, 5, 39, 0, 0, 347, 352, 1,
0, 0, 0, 348, 351, 3, 69, 34, 0, 349, 351, 8, 12, 0, 0, 350, 348, 1, 0,
0, 0, 350, 349, 1, 0, 0, 0, 351, 354, 1, 0, 0, 0, 352, 353, 1, 0, 0, 0,
352, 350, 1, 0, 0, 0, 353, 355, 1, 0, 0, 0, 354, 352, 1, 0, 0, 0, 355,
356, 5, 39, 0, 0, 356, 357, 5, 39, 0, 0, 357, 409, 5, 39, 0, 0, 358, 359,
3, 67, 33, 0, 359, 363, 5, 34, 0, 0, 360, 362, 8, 13, 0, 0, 361, 360, 1,
0, 0, 0, 362, 365, 1, 0, 0, 0, 363, 361, 1, 0, 0, 0, 363, 364, 1, 0, 0,
0, 364, 366, 1, 0, 0, 0, 365, 363, 1, 0, 0, 0, 366, 367, 5, 34, 0, 0, 367,
409, 1, 0, 0, 0, 368, 369, 3, 67, 33, 0, 369, 373, 5, 39, 0, 0, 370, 372,
8, 14, 0, 0, 371, 370, 1, 0, 0, 0, 372, 375, 1, 0, 0, 0, 373, 371, 1, 0,
0, 0, 373, 374, 1, 0, 0, 0, 374, 376, 1, 0, 0, 0, 375, 373, 1, 0, 0, 0,
376, 377, 5, 39, 0, 0, 377, 409, 1, 0, 0, 0, 378, 379, 3, 67, 33, 0, 379,
380, 5, 34, 0, 0, 380, 381, 5, 34, 0, 0, 381, 382, 5, 34, 0, 0, 382, 386,
1, 0, 0, 0, 383, 385, 9, 0, 0, 0, 384, 383, 1, 0, 0, 0, 385, 388, 1, 0,
0, 0, 386, 387, 1, 0, 0, 0, 386, 384, 1, 0, 0, 0, 387, 389, 1, 0, 0, 0,
388, 386, 1, 0, 0, 0, 389, 390, 5, 34, 0, 0, 390, 391, 5, 34, 0, 0, 391,
392, 5, 34, 0, 0, 392, 409, 1, 0, 0, 0, 393, 394, 3, 67, 33, 0, 394, 395,
5, 39, 0, 0, 395, 396, 5, 39, 0, 0, 396, 397, 5, 39, 0, 0, 397, 401, 1,
0, 0, 0, 398, 400, 9, 0, 0, 0, 399, 398, 1, 0, 0, 0, 400, 403, 1, 0, 0,
0, 401, 402, 1, 0, 0, 0, 401, 399, 1, 0, 0, 0, 402, 404, 1, 0, 0, 0, 403,
401, 1, 0, 0, 0, 404, 405, 5, 39, 0, 0, 405, 406, 5, 39, 0, 0, 406, 407,
5, 39, 0, 0, 407, 409, 1, 0, 0, 0, 408, 312, 1, 0, 0, 0, 408, 321, 1, 0,
0, 0, 408, 330, 1, 0, 0, 0, 408, 344, 1, 0, 0, 0, 408, 358, 1, 0, 0, 0,
408, 368, 1, 0, 0, 0, 408, 378, 1, 0, 0, 0, 408, 393, 1, 0, 0, 0, 409,
90, 1, 0, 0, 0, 410, 411, 7, 15, 0, 0, 411, 412, 3, 89, 44, 0, 412, 92,
1, 0, 0, 0, 413, 416, 3, 59, 29, 0, 414, 416, 5, 95, 0, 0, 415, 413, 1,
0, 0, 0, 415, 414, 1, 0, 0, 0, 416, 422, 1, 0, 0, 0, 417, 421, 3, 59, 29,
0, 418, 421, 3, 61, 30, 0, 419, 421, 5, 95, 0, 0, 420, 417, 1, 0, 0, 0,
420, 418, 1, 0, 0, 0, 420, 419, 1, 0, 0, 0, 421, 424, 1, 0, 0, 0, 422,
420, 1, 0, 0, 0, 422, 423, 1, 0, 0, 0, 423, 94, 1, 0, 0, 0, 424, 422, 1,
0, 0, 0, 425, 429, 5, 96, 0, 0, 426, 430, 3, 59, 29, 0, 427, 430, 3, 61,
30, 0, 428, 430, 7, 16, 0, 0, 429, 426, 1, 0, 0, 0, 429, 427, 1, 0, 0,
0, 429, 428, 1, 0, 0, 0, 430, 431, 1, 0, 0, 0, 431, 429, 1, 0, 0, 0, 431,
432, 1, 0, 0, 0, 432, 433, 1, 0, 0, 0, 433, 434, 5, 96, 0, 0, 434, 96,
1, 0, 0, 0, 38, 0, 178, 183, 193, 226, 231, 241, 249, 255, 258, 263, 271,
274, 276, 281, 289, 291, 296, 306, 310, 315, 317, 324, 326, 336, 338, 350,
352, 363, 373, 386, 401, 408, 415, 420, 422, 429, 431, 1, 0, 1, 0,
}
deserializer := antlr.NewATNDeserializer(nil)
staticData.atn = deserializer.Deserialize(staticData.serializedATN)
atn := staticData.atn
staticData.decisionToDFA = make([]*antlr.DFA, len(atn.DecisionToState))
decisionToDFA := staticData.decisionToDFA
for index, state := range atn.DecisionToState {
decisionToDFA[index] = antlr.NewDFA(state, index)
}
}
// CELLexerInit initializes any static state used to implement CELLexer. By default the
@ -280,16 +287,16 @@ func cellexerLexerInit() {
// NewCELLexer(). You can call this function if you wish to initialize the static state ahead
// of time.
func CELLexerInit() {
staticData := &CELLexerLexerStaticData
staticData.once.Do(cellexerLexerInit)
staticData := &CELLexerLexerStaticData
staticData.once.Do(cellexerLexerInit)
}
// NewCELLexer produces a new lexer instance for the optional input antlr.CharStream.
func NewCELLexer(input antlr.CharStream) *CELLexer {
CELLexerInit()
CELLexerInit()
l := new(CELLexer)
l.BaseLexer = antlr.NewBaseLexer(input)
staticData := &CELLexerLexerStaticData
staticData := &CELLexerLexerStaticData
l.Interpreter = antlr.NewLexerATNSimulator(l, staticData.atn, staticData.decisionToDFA, staticData.PredictionContextCache)
l.channelNames = staticData.ChannelNames
l.modeNames = staticData.ModeNames
@ -304,41 +311,41 @@ func NewCELLexer(input antlr.CharStream) *CELLexer {
// CELLexer tokens.
const (
CELLexerEQUALS = 1
CELLexerNOT_EQUALS = 2
CELLexerIN = 3
CELLexerLESS = 4
CELLexerLESS_EQUALS = 5
CELLexerEQUALS = 1
CELLexerNOT_EQUALS = 2
CELLexerIN = 3
CELLexerLESS = 4
CELLexerLESS_EQUALS = 5
CELLexerGREATER_EQUALS = 6
CELLexerGREATER = 7
CELLexerLOGICAL_AND = 8
CELLexerLOGICAL_OR = 9
CELLexerLBRACKET = 10
CELLexerRPRACKET = 11
CELLexerLBRACE = 12
CELLexerRBRACE = 13
CELLexerLPAREN = 14
CELLexerRPAREN = 15
CELLexerDOT = 16
CELLexerCOMMA = 17
CELLexerMINUS = 18
CELLexerEXCLAM = 19
CELLexerQUESTIONMARK = 20
CELLexerCOLON = 21
CELLexerPLUS = 22
CELLexerSTAR = 23
CELLexerSLASH = 24
CELLexerPERCENT = 25
CELLexerCEL_TRUE = 26
CELLexerCEL_FALSE = 27
CELLexerNUL = 28
CELLexerWHITESPACE = 29
CELLexerCOMMENT = 30
CELLexerNUM_FLOAT = 31
CELLexerNUM_INT = 32
CELLexerNUM_UINT = 33
CELLexerSTRING = 34
CELLexerBYTES = 35
CELLexerIDENTIFIER = 36
CELLexerGREATER = 7
CELLexerLOGICAL_AND = 8
CELLexerLOGICAL_OR = 9
CELLexerLBRACKET = 10
CELLexerRPRACKET = 11
CELLexerLBRACE = 12
CELLexerRBRACE = 13
CELLexerLPAREN = 14
CELLexerRPAREN = 15
CELLexerDOT = 16
CELLexerCOMMA = 17
CELLexerMINUS = 18
CELLexerEXCLAM = 19
CELLexerQUESTIONMARK = 20
CELLexerCOLON = 21
CELLexerPLUS = 22
CELLexerSTAR = 23
CELLexerSLASH = 24
CELLexerPERCENT = 25
CELLexerCEL_TRUE = 26
CELLexerCEL_FALSE = 27
CELLexerNUL = 28
CELLexerWHITESPACE = 29
CELLexerCOMMENT = 30
CELLexerNUM_FLOAT = 31
CELLexerNUM_INT = 32
CELLexerNUM_UINT = 33
CELLexerSTRING = 34
CELLexerBYTES = 35
CELLexerIDENTIFIER = 36
CELLexerESC_IDENTIFIER = 37
)

View File

@ -1,9 +1,8 @@
// Code generated from /usr/local/google/home/tswadell/go/src/github.com/google/cel-go/parser/gen/CEL.g4 by ANTLR 4.13.1. DO NOT EDIT.
// Code generated from /usr/local/google/home/jdtatum/github/cel-go/parser/gen/CEL.g4 by ANTLR 4.13.1. DO NOT EDIT.
package gen // CEL
import "github.com/antlr4-go/antlr/v4"
// CELListener is a complete listener for a parse tree produced by CELParser.
type CELListener interface {
antlr.ParseTreeListener
@ -47,8 +46,11 @@ type CELListener interface {
// EnterIndex is called when entering the Index production.
EnterIndex(c *IndexContext)
// EnterIdentOrGlobalCall is called when entering the IdentOrGlobalCall production.
EnterIdentOrGlobalCall(c *IdentOrGlobalCallContext)
// EnterIdent is called when entering the Ident production.
EnterIdent(c *IdentContext)
// EnterGlobalCall is called when entering the GlobalCall production.
EnterGlobalCall(c *GlobalCallContext)
// EnterNested is called when entering the Nested production.
EnterNested(c *NestedContext)
@ -80,6 +82,12 @@ type CELListener interface {
// EnterMapInitializerList is called when entering the mapInitializerList production.
EnterMapInitializerList(c *MapInitializerListContext)
// EnterSimpleIdentifier is called when entering the SimpleIdentifier production.
EnterSimpleIdentifier(c *SimpleIdentifierContext)
// EnterEscapedIdentifier is called when entering the EscapedIdentifier production.
EnterEscapedIdentifier(c *EscapedIdentifierContext)
// EnterOptExpr is called when entering the optExpr production.
EnterOptExpr(c *OptExprContext)
@ -146,8 +154,11 @@ type CELListener interface {
// ExitIndex is called when exiting the Index production.
ExitIndex(c *IndexContext)
// ExitIdentOrGlobalCall is called when exiting the IdentOrGlobalCall production.
ExitIdentOrGlobalCall(c *IdentOrGlobalCallContext)
// ExitIdent is called when exiting the Ident production.
ExitIdent(c *IdentContext)
// ExitGlobalCall is called when exiting the GlobalCall production.
ExitGlobalCall(c *GlobalCallContext)
// ExitNested is called when exiting the Nested production.
ExitNested(c *NestedContext)
@ -179,6 +190,12 @@ type CELListener interface {
// ExitMapInitializerList is called when exiting the mapInitializerList production.
ExitMapInitializerList(c *MapInitializerListContext)
// ExitSimpleIdentifier is called when exiting the SimpleIdentifier production.
ExitSimpleIdentifier(c *SimpleIdentifierContext)
// ExitEscapedIdentifier is called when exiting the EscapedIdentifier production.
ExitEscapedIdentifier(c *EscapedIdentifierContext)
// ExitOptExpr is called when exiting the optExpr production.
ExitOptExpr(c *OptExprContext)

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,8 @@
// Code generated from /usr/local/google/home/tswadell/go/src/github.com/google/cel-go/parser/gen/CEL.g4 by ANTLR 4.13.1. DO NOT EDIT.
// Code generated from /usr/local/google/home/jdtatum/github/cel-go/parser/gen/CEL.g4 by ANTLR 4.13.1. DO NOT EDIT.
package gen // CEL
import "github.com/antlr4-go/antlr/v4"
// A complete Visitor for a parse tree produced by CELParser.
type CELVisitor interface {
antlr.ParseTreeVisitor
@ -47,8 +46,11 @@ type CELVisitor interface {
// Visit a parse tree produced by CELParser#Index.
VisitIndex(ctx *IndexContext) interface{}
// Visit a parse tree produced by CELParser#IdentOrGlobalCall.
VisitIdentOrGlobalCall(ctx *IdentOrGlobalCallContext) interface{}
// Visit a parse tree produced by CELParser#Ident.
VisitIdent(ctx *IdentContext) interface{}
// Visit a parse tree produced by CELParser#GlobalCall.
VisitGlobalCall(ctx *GlobalCallContext) interface{}
// Visit a parse tree produced by CELParser#Nested.
VisitNested(ctx *NestedContext) interface{}
@ -80,6 +82,12 @@ type CELVisitor interface {
// Visit a parse tree produced by CELParser#mapInitializerList.
VisitMapInitializerList(ctx *MapInitializerListContext) interface{}
// Visit a parse tree produced by CELParser#SimpleIdentifier.
VisitSimpleIdentifier(ctx *SimpleIdentifierContext) interface{}
// Visit a parse tree produced by CELParser#EscapedIdentifier.
VisitEscapedIdentifier(ctx *EscapedIdentifierContext) interface{}
// Visit a parse tree produced by CELParser#optExpr.
VisitOptExpr(ctx *OptExprContext) interface{}
@ -106,5 +114,4 @@ type CELVisitor interface {
// Visit a parse tree produced by CELParser#Null.
VisitNull(ctx *NullContext) interface{}
}
}

View File

@ -470,6 +470,11 @@ func (e *exprHelper) NewAccuIdent() ast.Expr {
return e.exprFactory.NewAccuIdent(e.nextMacroID())
}
// AccuIdentName implements the ExprHelper interface method.
func (e *exprHelper) AccuIdentName() string {
return e.exprFactory.AccuIdentName()
}
// NewGlobalCall implements the ExprHelper interface method.
func (e *exprHelper) NewCall(function string, args ...ast.Expr) ast.Expr {
return e.exprFactory.NewCall(e.nextMacroID(), function, args...)

View File

@ -225,6 +225,9 @@ type ExprHelper interface {
// NewAccuIdent returns an accumulator identifier for use with comprehension results.
NewAccuIdent() ast.Expr
// AccuIdentName returns the name of the accumulator identifier.
AccuIdentName() string
// NewCall creates a function call Expr value for a global (free) function.
NewCall(function string, args ...ast.Expr) ast.Expr
@ -259,8 +262,13 @@ var (
// ExistsOneMacro expands "range.exists_one(var, predicate)", which is true if for exactly one
// element in range the predicate holds.
// Deprecated: Use ExistsOneMacroNew
ExistsOneMacro = NewReceiverMacro(operators.ExistsOne, 2, MakeExistsOne)
// ExistsOneMacroNew expands "range.existsOne(var, predicate)", which is true if for exactly one
// element in range the predicate holds.
ExistsOneMacroNew = NewReceiverMacro("existsOne", 2, MakeExistsOne)
// MapMacro expands "range.map(var, function)" into a comprehension which applies the function
// to each element in the range to produce a new list.
MapMacro = NewReceiverMacro(operators.Map, 2, MakeMap)
@ -280,6 +288,7 @@ var (
AllMacro,
ExistsMacro,
ExistsOneMacro,
ExistsOneMacroNew,
MapMacro,
MapFilterMacro,
FilterMacro,
@ -292,6 +301,11 @@ var (
// AccumulatorName is the traditional variable name assigned to the fold accumulator variable.
const AccumulatorName = "__result__"
// HiddenAccumulatorName is a proposed update to the default fold accumlator variable.
// @result is not normally accessible from source, preventing accidental or intentional collisions
// in user expressions.
const HiddenAccumulatorName = "@result"
type quantifierKind int
const (
@ -336,6 +350,10 @@ func MakeMap(eh ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *common
if !found {
return nil, eh.NewError(args[0].ID(), "argument is not an identifier")
}
accu := eh.AccuIdentName()
if v == accu || v == AccumulatorName {
return nil, eh.NewError(args[0].ID(), "iteration variable overwrites accumulator variable")
}
var fn ast.Expr
var filter ast.Expr
@ -355,7 +373,7 @@ func MakeMap(eh ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *common
if filter != nil {
step = eh.NewCall(operators.Conditional, filter, step, eh.NewAccuIdent())
}
return eh.NewComprehension(target, v, AccumulatorName, init, condition, step, eh.NewAccuIdent()), nil
return eh.NewComprehension(target, v, accu, init, condition, step, eh.NewAccuIdent()), nil
}
// MakeFilter expands the input call arguments into a comprehension which produces a list which contains
@ -366,13 +384,17 @@ func MakeFilter(eh ExprHelper, target ast.Expr, args []ast.Expr) (ast.Expr, *com
if !found {
return nil, eh.NewError(args[0].ID(), "argument is not an identifier")
}
accu := eh.AccuIdentName()
if v == accu || v == AccumulatorName {
return nil, eh.NewError(args[0].ID(), "iteration variable overwrites accumulator variable")
}
filter := args[1]
init := eh.NewList()
condition := eh.NewLiteral(types.True)
step := eh.NewCall(operators.Add, eh.NewAccuIdent(), eh.NewList(args[0]))
step = eh.NewCall(operators.Conditional, filter, step, eh.NewAccuIdent())
return eh.NewComprehension(target, v, AccumulatorName, init, condition, step, eh.NewAccuIdent()), nil
return eh.NewComprehension(target, v, accu, init, condition, step, eh.NewAccuIdent()), nil
}
// MakeHas expands the input call arguments into a presence test, e.g. has(<operand>.field)
@ -389,6 +411,10 @@ func makeQuantifier(kind quantifierKind, eh ExprHelper, target ast.Expr, args []
if !found {
return nil, eh.NewError(args[0].ID(), "argument must be a simple name")
}
accu := eh.AccuIdentName()
if v == accu || v == AccumulatorName {
return nil, eh.NewError(args[0].ID(), "iteration variable overwrites accumulator variable")
}
var init ast.Expr
var condition ast.Expr
@ -416,7 +442,7 @@ func makeQuantifier(kind quantifierKind, eh ExprHelper, target ast.Expr, args []
default:
return nil, eh.NewError(args[0].ID(), fmt.Sprintf("unrecognized quantifier '%v'", kind))
}
return eh.NewComprehension(target, v, AccumulatorName, init, condition, step, result), nil
return eh.NewComprehension(target, v, accu, init, condition, step, result), nil
}
func extractIdent(e ast.Expr) (string, bool) {

View File

@ -26,6 +26,8 @@ type options struct {
populateMacroCalls bool
enableOptionalSyntax bool
enableVariadicOperatorASTs bool
enableIdentEscapeSyntax bool
enableHiddenAccumulatorName bool
}
// Option configures the behavior of the parser.
@ -127,6 +129,27 @@ func EnableOptionalSyntax(optionalSyntax bool) Option {
}
}
// EnableIdentEscapeSyntax enables backtick (`) escaped field identifiers. This
// supports extended types of characters in identifiers, e.g. foo.`baz-bar`.
func EnableIdentEscapeSyntax(enableIdentEscapeSyntax bool) Option {
return func(opts *options) error {
opts.enableIdentEscapeSyntax = enableIdentEscapeSyntax
return nil
}
}
// EnableHiddenAccumulatorName uses an accumulator variable name that is not a
// normally accessible identifier in source for comprehension macros. Compatibility notes:
// with this option enabled, a parsed AST would be semantically the same as if disabled, but would
// have different internal identifiers in any of the built-in comprehension sub-expressions. When
// disabled, it is possible but almost certainly a logic error to access the accumulator variable.
func EnableHiddenAccumulatorName(enabled bool) Option {
return func(opts *options) error {
opts.enableHiddenAccumulatorName = enabled
return nil
}
}
// EnableVariadicOperatorASTs enables a compact representation of chained like-kind commutative
// operators. e.g. `a || b || c || d` -> `call(op='||', args=[a, b, c, d])`
//

View File

@ -17,6 +17,7 @@
package parser
import (
"errors"
"fmt"
"regexp"
"strconv"
@ -40,6 +41,7 @@ type Parser struct {
// NewParser builds and returns a new Parser using the provided options.
func NewParser(opts ...Option) (*Parser, error) {
p := &Parser{}
p.enableHiddenAccumulatorName = true
for _, opt := range opts {
if err := opt(&p.options); err != nil {
return nil, err
@ -88,7 +90,11 @@ func mustNewParser(opts ...Option) *Parser {
// Parse parses the expression represented by source and returns the result.
func (p *Parser) Parse(source common.Source) (*ast.AST, *common.Errors) {
errs := common.NewErrors(source)
fac := ast.NewExprFactory()
accu := AccumulatorName
if p.enableHiddenAccumulatorName {
accu = HiddenAccumulatorName
}
fac := ast.NewExprFactoryWithAccumulator(accu)
impl := parser{
errors: &parseErrors{errs},
exprFactory: fac,
@ -101,6 +107,7 @@ func (p *Parser) Parse(source common.Source) (*ast.AST, *common.Errors) {
populateMacroCalls: p.populateMacroCalls,
enableOptionalSyntax: p.enableOptionalSyntax,
enableVariadicOperatorASTs: p.enableVariadicOperatorASTs,
enableIdentEscapeSyntax: p.enableIdentEscapeSyntax,
}
buf, ok := source.(runes.Buffer)
if !ok {
@ -143,6 +150,27 @@ var reservedIds = map[string]struct{}{
"while": {},
}
func unescapeIdent(in string) (string, error) {
if len(in) <= 2 {
return "", errors.New("invalid escaped identifier: underflow")
}
return in[1 : len(in)-1], nil
}
// normalizeIdent returns the interpreted identifier.
func (p *parser) normalizeIdent(ctx gen.IEscapeIdentContext) (string, error) {
switch ident := ctx.(type) {
case *gen.SimpleIdentifierContext:
return ident.GetId().GetText(), nil
case *gen.EscapedIdentifierContext:
if !p.enableIdentEscapeSyntax {
return "", errors.New("unsupported syntax: '`'")
}
return unescapeIdent(ident.GetId().GetText())
}
return "", errors.New("unsupported ident kind")
}
// Parse converts a source input a parsed expression.
// This function calls ParseWithMacros with AllMacros.
//
@ -296,6 +324,7 @@ type parser struct {
populateMacroCalls bool
enableOptionalSyntax bool
enableVariadicOperatorASTs bool
enableIdentEscapeSyntax bool
}
var _ gen.CELVisitor = (*parser)(nil)
@ -369,8 +398,10 @@ func (p *parser) Visit(tree antlr.ParseTree) any {
return out
case *gen.LogicalNotContext:
return p.VisitLogicalNot(tree)
case *gen.IdentOrGlobalCallContext:
return p.VisitIdentOrGlobalCall(tree)
case *gen.IdentContext:
return p.VisitIdent(tree)
case *gen.GlobalCallContext:
return p.VisitGlobalCall(tree)
case *gen.SelectContext:
p.checkAndIncrementRecursionDepth()
out := p.VisitSelect(tree)
@ -538,7 +569,10 @@ func (p *parser) VisitSelect(ctx *gen.SelectContext) any {
if ctx.GetId() == nil || ctx.GetOp() == nil {
return p.helper.newExpr(ctx)
}
id := ctx.GetId().GetText()
id, err := p.normalizeIdent(ctx.GetId())
if err != nil {
p.reportError(ctx.GetId(), "%v", err)
}
if ctx.GetOpt() != nil {
if !p.enableOptionalSyntax {
return p.reportError(ctx.GetOp(), "unsupported syntax '.?'")
@ -622,12 +656,14 @@ func (p *parser) VisitIFieldInitializerList(ctx gen.IFieldInitializerListContext
p.reportError(optField, "unsupported syntax '?'")
continue
}
// The field may be empty due to a prior error.
id := optField.IDENTIFIER()
if id == nil {
return []ast.EntryExpr{}
fieldName, err := p.normalizeIdent(optField.EscapeIdent())
if err != nil {
p.reportError(ctx, "%v", err)
continue
}
fieldName := id.GetText()
value := p.Visit(vals[i]).(ast.Expr)
field := p.helper.newObjectField(initID, fieldName, value, optional)
result[i] = field
@ -635,8 +671,8 @@ func (p *parser) VisitIFieldInitializerList(ctx gen.IFieldInitializerListContext
return result
}
// Visit a parse tree produced by CELParser#IdentOrGlobalCall.
func (p *parser) VisitIdentOrGlobalCall(ctx *gen.IdentOrGlobalCallContext) any {
// Visit a parse tree produced by CELParser#Ident.
func (p *parser) VisitIdent(ctx *gen.IdentContext) any {
identName := ""
if ctx.GetLeadingDot() != nil {
identName = "."
@ -651,13 +687,30 @@ func (p *parser) VisitIdentOrGlobalCall(ctx *gen.IdentOrGlobalCallContext) any {
return p.reportError(ctx, "reserved identifier: %s", id)
}
identName += id
if ctx.GetOp() != nil {
opID := p.helper.id(ctx.GetOp())
return p.globalCallOrMacro(opID, identName, p.visitExprList(ctx.GetArgs())...)
}
return p.helper.newIdent(ctx.GetId(), identName)
}
// Visit a parse tree produced by CELParser#GlobalCallContext.
func (p *parser) VisitGlobalCall(ctx *gen.GlobalCallContext) any {
identName := ""
if ctx.GetLeadingDot() != nil {
identName = "."
}
// Handle the error case where no valid identifier is specified.
if ctx.GetId() == nil {
return p.helper.newExpr(ctx)
}
// Handle reserved identifiers.
id := ctx.GetId().GetText()
if _, ok := reservedIds[id]; ok {
return p.reportError(ctx, "reserved identifier: %s", id)
}
identName += id
opID := p.helper.id(ctx.GetOp())
return p.globalCallOrMacro(opID, identName, p.visitExprList(ctx.GetArgs())...)
}
// Visit a parse tree produced by CELParser#CreateList.
func (p *parser) VisitCreateList(ctx *gen.CreateListContext) any {
listID := p.helper.id(ctx.GetOp())
@ -756,7 +809,7 @@ func (p *parser) VisitDouble(ctx *gen.DoubleContext) any {
// Visit a parse tree produced by CELParser#String.
func (p *parser) VisitString(ctx *gen.StringContext) any {
s := p.unquote(ctx, ctx.GetText(), false)
s := p.unquote(ctx, ctx.GetTok().GetText(), false)
return p.helper.newLiteralString(ctx, s)
}
@ -922,7 +975,7 @@ func (p *parser) expandMacro(exprID int64, function string, target ast.Expr, arg
loc = p.helper.getLocation(exprID)
}
p.helper.deleteID(exprID)
return p.reportError(loc, err.Message), true
return p.reportError(loc, "%s", err.Message), true
}
// A nil value from the macro indicates that the macro implementation decided that
// an expansion should not be performed.

View File

@ -15,7 +15,7 @@
package parser
import (
"fmt"
"errors"
"strings"
"unicode/utf8"
)
@ -30,7 +30,7 @@ func unescape(value string, isBytes bool) (string, error) {
// Nothing to unescape / decode.
if n < 2 {
return value, fmt.Errorf("unable to unescape string")
return value, errors.New("unable to unescape string")
}
// Raw string preceded by the 'r|R' prefix.
@ -43,7 +43,7 @@ func unescape(value string, isBytes bool) (string, error) {
// Quoted string of some form, must have same first and last char.
if value[0] != value[n-1] || (value[0] != '"' && value[0] != '\'') {
return value, fmt.Errorf("unable to unescape string")
return value, errors.New("unable to unescape string")
}
// Normalize the multi-line CEL string representation to a standard
@ -51,12 +51,12 @@ func unescape(value string, isBytes bool) (string, error) {
if n >= 6 {
if strings.HasPrefix(value, "'''") {
if !strings.HasSuffix(value, "'''") {
return value, fmt.Errorf("unable to unescape string")
return value, errors.New("unable to unescape string")
}
value = "\"" + value[3:n-3] + "\""
} else if strings.HasPrefix(value, `"""`) {
if !strings.HasSuffix(value, `"""`) {
return value, fmt.Errorf("unable to unescape string")
return value, errors.New("unable to unescape string")
}
value = "\"" + value[3:n-3] + "\""
}
@ -90,10 +90,10 @@ func unescape(value string, isBytes bool) (string, error) {
// unescapeChar takes a string input and returns the following info:
//
// value - the escaped unicode rune at the front of the string.
// encode - the value should be unicode-encoded
// tail - the remainder of the input string.
// err - error value, if the character could not be unescaped.
// value - the escaped unicode rune at the front of the string.
// encode - the value should be unicode-encoded
// tail - the remainder of the input string.
// err - error value, if the character could not be unescaped.
//
// When encode is true the return value may still fit within a single byte,
// but unicode encoding is attempted which is more expensive than when the
@ -113,7 +113,7 @@ func unescapeChar(s string, isBytes bool) (value rune, encode bool, tail string,
// 2. Last character is the start of an escape sequence.
if len(s) <= 1 {
err = fmt.Errorf("unable to unescape string, found '\\' as last character")
err = errors.New("unable to unescape string, found '\\' as last character")
return
}
@ -157,32 +157,32 @@ func unescapeChar(s string, isBytes bool) (value rune, encode bool, tail string,
case 'u':
n = 4
if isBytes {
err = fmt.Errorf("unable to unescape string")
err = errors.New("unable to unescape string")
return
}
case 'U':
n = 8
if isBytes {
err = fmt.Errorf("unable to unescape string")
err = errors.New("unable to unescape string")
return
}
}
var v rune
if len(s) < n {
err = fmt.Errorf("unable to unescape string")
err = errors.New("unable to unescape string")
return
}
for j := 0; j < n; j++ {
x, ok := unhex(s[j])
if !ok {
err = fmt.Errorf("unable to unescape string")
err = errors.New("unable to unescape string")
return
}
v = v<<4 | x
}
s = s[n:]
if !isBytes && v > utf8.MaxRune {
err = fmt.Errorf("unable to unescape string")
if !isBytes && !utf8.ValidRune(v) {
err = errors.New("invalid unicode code point")
return
}
value = v
@ -190,20 +190,20 @@ func unescapeChar(s string, isBytes bool) (value rune, encode bool, tail string,
// 5. Octal escape sequences, must be three digits \[0-3][0-7][0-7]
case '0', '1', '2', '3':
if len(s) < 2 {
err = fmt.Errorf("unable to unescape octal sequence in string")
err = errors.New("unable to unescape octal sequence in string")
return
}
v := rune(c - '0')
for j := 0; j < 2; j++ {
x := s[j]
if x < '0' || x > '7' {
err = fmt.Errorf("unable to unescape octal sequence in string")
err = errors.New("unable to unescape octal sequence in string")
return
}
v = v*8 + rune(x-'0')
}
if !isBytes && v > utf8.MaxRune {
err = fmt.Errorf("unable to unescape string")
if !isBytes && !utf8.ValidRune(v) {
err = errors.New("invalid unicode code point")
return
}
value = v
@ -212,7 +212,7 @@ func unescapeChar(s string, isBytes bool) (value rune, encode bool, tail string,
// Unknown escape sequence.
default:
err = fmt.Errorf("unable to unescape string")
err = errors.New("unable to unescape string")
}
tail = s

View File

@ -17,12 +17,14 @@ package parser
import (
"errors"
"fmt"
"regexp"
"strconv"
"strings"
"github.com/google/cel-go/common/ast"
"github.com/google/cel-go/common/operators"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
)
// Unparse takes an input expression and source position information and generates a human-readable
@ -65,6 +67,15 @@ func Unparse(expr ast.Expr, info *ast.SourceInfo, opts ...UnparserOption) (strin
return un.str.String(), nil
}
var identifierPartPattern *regexp.Regexp = regexp.MustCompile(`^[A-Za-z_][0-9A-Za-z_]*$`)
func maybeQuoteField(field string) string {
if !identifierPartPattern.MatchString(field) || field == "in" {
return "`" + field + "`"
}
return field
}
// unparser visits an expression to reconstruct a human-readable string from an AST.
type unparser struct {
str strings.Builder
@ -263,8 +274,17 @@ func (un *unparser) visitCallUnary(expr ast.Expr) error {
return un.visitMaybeNested(args[0], nested)
}
func (un *unparser) visitConst(expr ast.Expr) error {
val := expr.AsLiteral()
func (un *unparser) visitConstVal(val ref.Val) error {
optional := false
if optVal, ok := val.(*types.Optional); ok {
if !optVal.HasValue() {
un.str.WriteString("optional.none()")
return nil
}
optional = true
un.str.WriteString("optional.of(")
val = optVal.GetValue()
}
switch val := val.(type) {
case types.Bool:
un.str.WriteString(strconv.FormatBool(bool(val)))
@ -293,7 +313,21 @@ func (un *unparser) visitConst(expr ast.Expr) error {
ui := strconv.FormatUint(uint64(val), 10)
un.str.WriteString(ui)
un.str.WriteString("u")
case *types.Optional:
if err := un.visitConstVal(val); err != nil {
return err
}
default:
return errors.New("unsupported constant")
}
if optional {
un.str.WriteString(")")
}
return nil
}
func (un *unparser) visitConst(expr ast.Expr) error {
val := expr.AsLiteral()
if err := un.visitConstVal(val); err != nil {
return fmt.Errorf("unsupported constant: %v", expr)
}
return nil
@ -352,7 +386,7 @@ func (un *unparser) visitSelectInternal(operand ast.Expr, testOnly bool, op stri
return err
}
un.str.WriteString(op)
un.str.WriteString(field)
un.str.WriteString(maybeQuoteField(field))
if testOnly {
un.str.WriteString(")")
}
@ -370,7 +404,7 @@ func (un *unparser) visitStructMsg(expr ast.Expr) error {
if field.IsOptional() {
un.str.WriteString("?")
}
un.str.WriteString(f)
un.str.WriteString(maybeQuoteField(f))
un.str.WriteString(": ")
v := field.Value()
err := un.visit(v)

4
vendor/modules.txt vendored
View File

@ -2,7 +2,7 @@
# bitbucket.org/bertimus9/systemstat v0.5.0
## explicit; go 1.17
bitbucket.org/bertimus9/systemstat
# cel.dev/expr v0.18.0
# cel.dev/expr v0.19.1
## explicit; go 1.21.1
cel.dev/expr
# github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161
@ -256,7 +256,7 @@ github.com/google/cadvisor/utils/sysfs
github.com/google/cadvisor/utils/sysinfo
github.com/google/cadvisor/version
github.com/google/cadvisor/watcher
# github.com/google/cel-go v0.22.0
# github.com/google/cel-go v0.23.2
## explicit; go 1.21.1
github.com/google/cel-go/cel
github.com/google/cel-go/checker