Repository: ovh/cds
Pull request #6549 (2023-05-10 15:09:36)

feat(sdk): add ascode action interpolate
1. Description
1. Related issues
1. About tests
1. Mentions

@ovh/cds


sonarcloud[bot]: Kudos, SonarCloud Quality Gate passed!&nbsp; &nbsp; [![Quality Gate passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/passed-16px.png 'Quality Gate passed')](https://sonarcloud.io/dashboard?id=ovh_cds&pullRequest=6549)

[![Bug](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/bug-16px.png 'Bug')](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=6549&resolved=false&types=BUG) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=6549&resolved=false&types=BUG) [0 Bugs](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=6549&resolved=false&types=BUG)  
[![Vulnerability](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/vulnerability-16px.png 'Vulnerability')](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=6549&resolved=false&types=VULNERABILITY) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=6549&resolved=false&types=VULNERABILITY) [0 Vulnerabilities](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=6549&resolved=false&types=VULNERABILITY)  
[![Security Hotspot](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/security_hotspot-16px.png 'Security Hotspot')](https://sonarcloud.io/project/security_hotspots?id=ovh_cds&pullRequest=6549&resolved=false&types=SECURITY_HOTSPOT) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/security_hotspots?id=ovh_cds&pullRequest=6549&resolved=false&types=SECURITY_HOTSPOT) [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=ovh_cds&pullRequest=6549&resolved=false&types=SECURITY_HOTSPOT)  
[![Code Smell](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/code_smell-16px.png 'Code Smell')](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=6549&resolved=false&types=CODE_SMELL) [![A](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/RatingBadge/A-16px.png 'A')](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=6549&resolved=false&types=CODE_SMELL) [102 Code Smells](https://sonarcloud.io/project/issues?id=ovh_cds&pullRequest=6549&resolved=false&types=CODE_SMELL)

[![No Coverage information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/CoverageChart/NoCoverageInfo-16px.png 'No Coverage information')](https://sonarcloud.io/component_measures?id=ovh_cds&pullRequest=6549&metric=coverage&view=list) No Coverage information  
[![No Duplication information](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/Duplications/NoDuplicationInfo-16px.png 'No Duplication information')](https://sonarcloud.io/component_measures?id=ovh_cds&pullRequest=6549&metric=duplicated_lines_density&view=list) No Duplication information


ovh-cds: 
CDS Report build-all-cds#18818.0 ✘  
* Build
  * Build and Package All ✔ 
  * Build Debpacker ✔ 
  * Build SMTPMock ✔ 
  * Lint UI Translate ✔ 
  * Test CLI ✔ 
  * Test Engine ✔ 
  * Test SDK ✔ 
  * Test UI ✔ 
* Package
  * Docker ✘  

ovh-cds: 
CDS Report build-all-cds#18818.1 ✘  
* Build
  * Build and Package All ✔ 
  * Build Debpacker ✔ 
  * Build SMTPMock ✔ 
  * Lint UI Translate ✔ 
  * Test CLI ✔ 
  * Test Engine ✔ 
  * Test SDK ✔ 
  * Test UI ✔ 
* Package
  * Docker ✘  

ovh-cds: 
CDS Report build-all-cds#18818.2 ✘  
* Build
  * Build and Package All ✔ 
  * Build Debpacker ✔ 
  * Build SMTPMock ✔ 
  * Lint UI Translate ✔ 
  * Test CLI ✔ 
  * Test Engine ✔ 
  * Test SDK ✔ 
  * Test UI ✔ 
* Package
  * Docker ✘  


Patch:
diff --git a/Dockerfile b/Dockerfile
index 11568b3df..ac771805c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,4 +1,4 @@
-FROM alpine:3.17
+FROM alpine:3.18
 RUN apk update && apk update && \
     apk --no-cache add curl && \
     apk --no-cache add gpg && \
@@ -6,7 +6,7 @@ RUN apk update && apk update && \
     apk --no-cache add tzdata && \
     apk --no-cache add openssh-client && \
     apk --no-cache add gpg-agent && \
-    apk --no-cache add ca-certificates && rm -rf /var/cache/apk/* 
+    apk --no-cache add ca-certificates && rm -rf /var/cache/apk/*
 RUN update-ca-certificates
 RUN echo "PubkeyAcceptedKeyTypes +ssh-rsa" >> /etc/ssh/ssh_config
 RUN mkdir -p /app/sql /app/ui_static_files
diff --git a/README.md b/README.md
index 05cbdb9c1..4ef99fb0c 100644
--- a/README.md
+++ b/README.md
@@ -39,9 +39,9 @@ Docker-Compose is your friend, see [Ready To Run Tutorials](https://ovh.github.i
 
 ## Blog posts and talks
 
-- CDS Introduction: https://www.ovh.com/fr/blog/how-does-ovh-manage-the-ci-cd-at-scale/
-- DataBuzzWord Podcast (French) : https://www.ovh.com/fr/blog/understanding-ci-cd-for-big-data-and-machine-learning/
-- Continuous Delivery and Deployment Workflows with CDS: https://www.ovh.com/fr/blog/continuous-delivery-and-deployment-workflows-with-cds/
+- CDS Introduction: https://blog.ovhcloud.com/how-does-ovh-manage-the-ci-cd-at-scale/
+- DataBuzzWord Podcast (French) : https://blog.ovhcloud.com/understanding-ci-cd-for-big-data-and-machine-learning/
+- Continuous Delivery and Deployment Workflows with CDS: https://blog.ovhcloud.com/continuous-delivery-and-deployment-workflows-with-cds/
 - Talk at conference [Breizhcamp](https://www.breizhcamp.org) to introduce CDS (French): https://www.youtube.com/watch?v=JUzEQuOehv4
 
 ## FAQ
diff --git a/contrib/grpcplugins/action/plugin-archive/go.mod b/contrib/grpcplugins/action/plugin-archive/go.mod
index d328d551e..524535650 100644
--- a/contrib/grpcplugins/action/plugin-archive/go.mod
+++ b/contrib/grpcplugins/action/plugin-archive/go.mod
@@ -17,6 +17,7 @@ require (
 	github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
 	github.com/acomagu/bufpipe v1.0.3 // indirect
 	github.com/andybalholm/brotli v1.0.3 // indirect
+	github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1 // indirect
 	github.com/aokoli/goutils v1.1.0 // indirect
 	github.com/blang/semver v3.5.1+incompatible // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
diff --git a/contrib/grpcplugins/action/plugin-archive/go.sum b/contrib/grpcplugins/action/plugin-archive/go.sum
index 6b07b6b3e..b7bae8049 100644
--- a/contrib/grpcplugins/action/plugin-archive/go.sum
+++ b/contrib/grpcplugins/action/plugin-archive/go.sum
@@ -55,6 +55,8 @@ github.com/andybalholm/brotli v1.0.3/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHG
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1 h1:X8MJ0fnN5FPdcGF5Ij2/OW+HgiJrRg3AfHAx1PJtIzM=
+github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
 github.com/aokoli/goutils v1.1.0 h1:jy4ghdcYvs5EIoGssZNslIASX5m+KNMfyyKvRQ0TEVE=
 github.com/aokoli/goutils v1.1.0/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
diff --git a/contrib/grpcplugins/action/plugin-artifactory-release-bundle-create/go.mod b/contrib/grpcplugins/action/plugin-artifactory-release-bundle-create/go.mod
index 678dc1c04..8e537f735 100644
--- a/contrib/grpcplugins/action/plugin-artifactory-release-bundle-create/go.mod
+++ b/contrib/grpcplugins/action/plugin-artifactory-release-bundle-create/go.mod
@@ -20,6 +20,7 @@ require (
 	github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
 	github.com/acomagu/bufpipe v1.0.3 // indirect
 	github.com/andybalholm/brotli v1.0.1 // indirect
+	github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1 // indirect
 	github.com/aokoli/goutils v1.1.0 // indirect
 	github.com/blang/semver v3.5.1+incompatible // indirect
 	github.com/buger/jsonparser v1.1.1 // indirect
@@ -95,6 +96,7 @@ require (
 	go.uber.org/multierr v1.8.0 // indirect
 	go.uber.org/zap v1.23.0 // indirect
 	golang.org/x/crypto v0.7.0 // indirect
+	golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 // indirect
 	golang.org/x/net v0.8.0 // indirect
 	golang.org/x/sys v0.6.0 // indirect
 	golang.org/x/term v0.6.0 // indirect
diff --git a/contrib/grpcplugins/action/plugin-artifactory-release-bundle-create/go.sum b/contrib/grpcplugins/action/plugin-artifactory-release-bundle-create/go.sum
index c25f4b31c..190ee4ab4 100644
--- a/contrib/grpcplugins/action/plugin-artifactory-release-bundle-create/go.sum
+++ b/contrib/grpcplugins/action/plugin-artifactory-release-bundle-create/go.sum
@@ -59,6 +59,8 @@ github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1 h1:X8MJ0fnN5FPdcGF5Ij2/OW+HgiJrRg3AfHAx1PJtIzM=
+github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
 github.com/aokoli/goutils v1.1.0 h1:jy4ghdcYvs5EIoGssZNslIASX5m+KNMfyyKvRQ0TEVE=
 github.com/aokoli/goutils v1.1.0/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
@@ -565,6 +567,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
 golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
 golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
+golang.org/x/exp v0.0.0-20220827204233-334a2380cb91 h1:tnebWN09GYg9OLPss1KXj8txwZc6X6uMr6VFdcGNbHw=
+golang.org/x/exp v0.0.0-20220827204233-334a2380cb91/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
 golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
 golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
 golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
diff --git a/contrib/grpcplugins/action/plugin-artifactory-release-bundle-distribute/go.mod b/contrib/grpcplugins/action/plugin-artifactory-release-bundle-distribute/go.mod
index 755e8344a..a447922f1 100644
--- a/contrib/grpcplugins/action/plugin-artifactory-release-bundle-distribute/go.mod
+++ b/contrib/grpcplugins/action/plugin-artifactory-release-bundle-distribute/go.mod
@@ -18,6 +18,7 @@ require (
 	github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
 	github.com/acomagu/bufpipe v1.0.3 // indirect
 	github.com/andybalholm/brotli v1.0.1 // indirect
+	github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1 // indirect
 	github.com/aokoli/goutils v1.1.0 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
 	github.com/blang/semver v3.5.1+incompatible // indirect
diff --git a/contrib/grpcplugins/action/plugin-artifactory-release-bundle-distribute/go.sum b/contrib/grpcplugins/action/plugin-artifactory-release-bundle-distribute/go.sum
index 1d947ae5e..a20e2d1d6 100644
--- a/contrib/grpcplugins/action/plugin-artifactory-release-bundle-distribute/go.sum
+++ b/contrib/grpcplugins/action/plugin-artifactory-release-bundle-distribute/go.sum
@@ -75,6 +75,8 @@ github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1 h1:X8MJ0fnN5FPdcGF5Ij2/OW+HgiJrRg3AfHAx1PJtIzM=
+github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
 github.com/aokoli/goutils v1.1.0 h1:jy4ghdcYvs5EIoGssZNslIASX5m+KNMfyyKvRQ0TEVE=
 github.com/aokoli/goutils v1.1.0/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
diff --git a/contrib/grpcplugins/action/plugin-npm-audit-parser/go.mod b/contrib/grpcplugins/action/plugin-npm-audit-parser/go.mod
index ec5da1271..3d4bc629e 100644
--- a/contrib/grpcplugins/action/plugin-npm-audit-parser/go.mod
+++ b/contrib/grpcplugins/action/plugin-npm-audit-parser/go.mod
@@ -15,6 +15,7 @@ require (
 	github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
 	github.com/acomagu/bufpipe v1.0.3 // indirect
 	github.com/andybalholm/brotli v1.0.1 // indirect
+	github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1 // indirect
 	github.com/aokoli/goutils v1.1.0 // indirect
 	github.com/blang/semver v3.5.1+incompatible // indirect
 	github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
diff --git a/contrib/grpcplugins/action/plugin-npm-audit-parser/go.sum b/contrib/grpcplugins/action/plugin-npm-audit-parser/go.sum
index 066506696..6c3e04821 100644
--- a/contrib/grpcplugins/action/plugin-npm-audit-parser/go.sum
+++ b/contrib/grpcplugins/action/plugin-npm-audit-parser/go.sum
@@ -54,6 +54,8 @@ github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1 h1:X8MJ0fnN5FPdcGF5Ij2/OW+HgiJrRg3AfHAx1PJtIzM=
+github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
 github.com/aokoli/goutils v1.1.0 h1:jy4ghdcYvs5EIoGssZNslIASX5m+KNMfyyKvRQ0TEVE=
 github.com/aokoli/goutils v1.1.0/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
diff --git a/contrib/grpcplugins/action/plugin-tmpl/go.mod b/contrib/grpcplugins/action/plugin-tmpl/go.mod
index 63e3729a4..d86315912 100644
--- a/contrib/grpcplugins/action/plugin-tmpl/go.mod
+++ b/contrib/grpcplugins/action/plugin-tmpl/go.mod
@@ -15,6 +15,7 @@ require (
 	github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
 	github.com/acomagu/bufpipe v1.0.3 // indirect
 	github.com/andybalholm/brotli v1.0.1 // indirect
+	github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1 // indirect
 	github.com/aokoli/goutils v1.1.0 // indirect
 	github.com/blang/semver v3.5.1+incompatible // indirect
 	github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
diff --git a/contrib/grpcplugins/action/plugin-tmpl/go.sum b/contrib/grpcplugins/action/plugin-tmpl/go.sum
index 066506696..6c3e04821 100644
--- a/contrib/grpcplugins/action/plugin-tmpl/go.sum
+++ b/contrib/grpcplugins/action/plugin-tmpl/go.sum
@@ -54,6 +54,8 @@ github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1 h1:X8MJ0fnN5FPdcGF5Ij2/OW+HgiJrRg3AfHAx1PJtIzM=
+github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
 github.com/aokoli/goutils v1.1.0 h1:jy4ghdcYvs5EIoGssZNslIASX5m+KNMfyyKvRQ0TEVE=
 github.com/aokoli/goutils v1.1.0/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
diff --git a/engine/api/worker/registration.go b/engine/api/worker/registration.go
index dadd8bad1..924c71155 100644
--- a/engine/api/worker/registration.go
+++ b/engine/api/worker/registration.go
@@ -138,9 +138,11 @@ func RegisterWorker(ctx context.Context, db gorpmapper.SqlExecutorWithTx, store
 		OS:           registrationForm.OS,
 		Arch:         registrationForm.Arch,
 	}
+	w.ModelName = &spawnArgs.Model.Name
 	if model != nil {
 		w.ModelID = &spawnArgs.Model.ID
 	}
+
 	if spawnArgs.JobID > 0 {
 		w.JobRunID = &spawnArgs.JobID
 	}
diff --git a/engine/api/workflow/process_requirements.go b/engine/api/workflow/process_requirements.go
index c1899dbeb..873e9af58 100644
--- a/engine/api/workflow/process_requirements.go
+++ b/engine/api/workflow/process_requirements.go
@@ -115,6 +115,9 @@ func processNodeJobRunRequirements(ctx context.Context, store cache.Store, db go
 		sdk.AddRequirement(&requirements, v.ID, name, v.Type, value)
 	}
 	if wm != nil {
+		if wm.Disabled {
+			errm.Append(sdk.NewErrorFrom(sdk.ErrInvalidData, "worker model %s is disabled. Please update your requirement", wm.Name))
+		}
 		modelType = wm.Type
 
 		// Check that the worker model has the binaries capabilitites
diff --git a/engine/api/workflow_queue.go b/engine/api/workflow_queue.go
index ba01bd842..9bbdaef31 100644
--- a/engine/api/workflow_queue.go
+++ b/engine/api/workflow_queue.go
@@ -80,6 +80,8 @@ func (api *API) postTakeWorkflowJobHandler() service.Handler {
 				return err
 			}
 			workerModelName = wm.Name
+		} else if wk.ModelName != nil {
+			workerModelName = *wk.ModelName
 		}
 
 		// Load job run
diff --git a/engine/sql/api/267_plugin.sql b/engine/sql/api/267_plugin.sql
new file mode 100644
index 000000000..79379e120
--- /dev/null
+++ b/engine/sql/api/267_plugin.sql
@@ -0,0 +1,6 @@
+-- +migrate Up
+ALTER TABLE worker ADD COLUMN model_name TEXT DEFAULT '';
+
+-- +migrate Down
+ALTER TABLE worker DROP COLUMN model_name;
+
diff --git a/go.mod b/go.mod
index c22579f5c..e28010beb 100644
--- a/go.mod
+++ b/go.mod
@@ -8,13 +8,14 @@ require (
 	contrib.go.opencensus.io/exporter/prometheus v0.4.2
 	github.com/Shopify/sarama v1.36.0
 	github.com/andygrunwald/go-gerrit v0.0.0-20181207071854-19ef3e9332a4
+	github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1
 	github.com/aws/aws-sdk-go v1.43.21
 	github.com/blang/semver v3.5.1+incompatible
 	github.com/buger/goterm v0.0.0-20170918171949-d443b9114f9c
 	github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e
 	github.com/confluentinc/bincover v0.2.0
 	github.com/coreos/go-oidc v2.2.1+incompatible
-	github.com/docker/distribution v2.8.1+incompatible
+	github.com/docker/distribution v2.8.2+incompatible
 	github.com/docker/docker v23.0.3+incompatible
 	github.com/docker/go-connections v0.4.0
 	github.com/eapache/go-resiliency v1.3.0
diff --git a/go.sum b/go.sum
index 507c7d2dc..e042879c0 100644
--- a/go.sum
+++ b/go.sum
@@ -115,6 +115,8 @@ github.com/andygrunwald/go-gerrit v0.0.0-20181207071854-19ef3e9332a4/go.mod h1:0
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1 h1:X8MJ0fnN5FPdcGF5Ij2/OW+HgiJrRg3AfHAx1PJtIzM=
+github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
 github.com/aokoli/goutils v1.1.0 h1:jy4ghdcYvs5EIoGssZNslIASX5m+KNMfyyKvRQ0TEVE=
 github.com/aokoli/goutils v1.1.0/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
 github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
@@ -191,8 +193,8 @@ github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
 github.com/davecgh/go-xdr v0.0.0-20161123171359-e6a2ba005892/go.mod h1:CTDl0pzVzE5DEzZhPfvhY/9sPFMQIxaJ9VAMs9AagrE=
 github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
 github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
-github.com/docker/distribution v2.8.1+incompatible h1:Q50tZOPR6T/hjNsyc9g8/syEs6bk8XXApsHjKukMl68=
-github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
+github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8=
+github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
 github.com/docker/docker v23.0.3+incompatible h1:9GhVsShNWz1hO//9BNg/dpMnZW25KydO4wtVxWAIbho=
 github.com/docker/docker v23.0.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
diff --git a/sdk/action_parser.go b/sdk/action_parser.go
new file mode 100644
index 000000000..939abbca4
--- /dev/null
+++ b/sdk/action_parser.go
@@ -0,0 +1,589 @@
+package sdk
+
+import (
+	"context"
+	"fmt"
+	"reflect"
+	"regexp"
+	"strconv"
+	"strings"
+
+	"github.com/antlr/antlr4/runtime/Go/antlr/v4"
+	"github.com/rockbears/log"
+
+	"github.com/ovh/cds/sdk/parser"
+)
+
+var extractRegexp = regexp.MustCompile("(\\${{ .+? }})")
+
+type ActionParser struct {
+	contexts map[string]interface{}
+	funcs    map[string]ActionFunc
+}
+
+func NewActionParser(contexts map[string]interface{}, funcs map[string]ActionFunc) *ActionParser {
+	return &ActionParser{
+		contexts: contexts,
+		funcs:    funcs,
+	}
+}
+
+func (a *ActionParser) createLexerAndParser(input string) (*parser.ActionParser, *ParserErrorListener) {
+	lexer := parser.NewActionLexer(antlr.NewInputStream(input))
+	stream := antlr.NewCommonTokenStream(lexer, 0)
+	p := parser.NewActionParser(stream)
+
+	errorLst := NewParserErrorListener()
+	p.RemoveErrorListeners()
+	p.AddErrorListener(errorLst)
+	p.BuildParseTrees = true
+	return p, errorLst
+}
+
+func (a *ActionParser) Validate(ctx context.Context, input string) error {
+	matches := extractRegexp.FindAllString(input, -1)
+	validationInputs := make([]string, 0)
+	for _, match := range matches {
+		log.Debug(ctx, "Parse expression: %s", match)
+		errors := a.checkSyntax(ctx, match)
+		validationInputs = append(validationInputs, errors...)
+	}
+
+	if len(validationInputs) > 0 {
+		return NewErrorFrom(ErrInvalidData, strings.Join(validationInputs, "\n"))
+	}
+	return nil
+}
+
+func (a *ActionParser) checkSyntax(_ context.Context, input string) []string {
+	p, errorLst := a.createLexerAndParser(input)
+	_ = p.Expression()
+	return errorLst.Errors
+}
+
+func (a *ActionParser) Interpolate(ctx context.Context, input string) (string, error) {
+	interpolatedInput := input
+	matches := extractRegexp.FindAllString(input, -1)
+	for _, match := range matches {
+		log.Debug(ctx, "Parse expression: %s", match)
+		result, err := a.parse(ctx, match)
+		if err != nil {
+			return input, err
+		}
+		interpolatedInput = strings.Replace(interpolatedInput, match, result, 1)
+	}
+	return interpolatedInput, nil
+}
+
+func (a *ActionParser) parse(ctx context.Context, elt string) (string, error) {
+	p, errorLst := a.createLexerAndParser(elt)
+	tree := p.Expression()
+	if len(errorLst.Errors) != 0 {
+		return "", fmt.Errorf(strings.Join(errorLst.Errors, "\n"))
+	}
+
+	for i := 0; i < tree.GetChildCount(); i++ {
+		switch t := tree.GetChild(i).(type) {
+		case *parser.OrExpressionContext:
+			return a.parseOrExpressionContext(ctx, t)
+		case *parser.ExpressionStartContext, *parser.ExpressionEndContext:
+			continue
+		default:
+			log.Error(ctx, "Unknown child type %T: %s", t, tree.GetText())
+		}
+	}
+	return "", NewErrorFrom(ErrInvalidData, "No expression found. Gor [%s]", elt)
+}
+
+func (a *ActionParser) parseOrExpressionContext(ctx context.Context, exp *parser.OrExpressionContext) (string, error) {
+	log.Debug(ctx, "Or expression detected: %s", exp.GetText())
+	operands := make([]interface{}, 0, exp.GetChildCount())
+	for i := 0; i < exp.GetChildCount(); i++ {
+		switch t := exp.GetChild(i).(type) {
+		case *parser.AndExpressionContext:
+			result, err := a.parseAndExpressionContext(ctx, t)
+			if err != nil {
+				return "", err
+			}
+			operands = append(operands, result)
+		case *parser.OrOperatorContext:
+			continue
+		default:
+			return "", NewErrorFrom(ErrInvalidData, "unknown type %T in OrExpression", t)
+		}
+	}
+	switch len(operands) {
+	case 1:
+		return fmt.Sprintf("%v", operands[0]), nil
+	default:
+		b, err := a.or(operands)
+		if err != nil {
+			return "", err
+		}
+		return fmt.Sprintf("%v", b), nil
+	}
+}
+
+func (a *ActionParser) parseAndExpressionContext(ctx context.Context, exp *parser.AndExpressionContext) (interface{}, error) {
+	log.Debug(ctx, "And expression detected: %s", exp.GetText())
+	operands := make([]interface{}, 0, exp.GetChildCount())
+	for i := 0; i < exp.GetChildCount(); i++ {
+		switch t := exp.GetChild(i).(type) {
+		case *parser.ComparisonExpressionContext:
+			result, err := a.parseComparisonExpressionContext(ctx, t)
+			if err != nil {
+				return "", err
+			}
+			operands = append(operands, result)
+		case *parser.AndOperatorContext:
+			continue
+		default:
+			return "", NewErrorFrom(ErrInvalidData, "unknown type %T in AndExpression", t)
+		}
+	}
+
+	switch len(operands) {
+	case 1:
+		return operands[0], nil
+	default:
+		return a.and(operands)
+	}
+}
+
+func (a *ActionParser) parseComparisonExpressionContext(ctx context.Context, exp *parser.ComparisonExpressionContext) (interface{}, error) {
+	log.Debug(ctx, "Comparison expression detected: %s", exp.GetText())
+	operands := make([]interface{}, 0, exp.GetChildCount())
+	var operator string
+	for i := 0; i < exp.GetChildCount(); i++ {
+		switch t := exp.GetChild(i).(type) {
+		case *parser.EqualityExpressionContext:
+			result, err := a.parseEqualityExpressionContext(ctx, t)
+			if err != nil {
+				return "", err
+			}
+			operands = append(operands, result)
+		case *parser.ComparisonOperatorContext:
+			operator = t.GetText()
+			continue
+		default:
+			return "", NewErrorFrom(ErrInvalidData, "unknown type %T in ComparisonExpression", t)
+		}
+	}
+
+	switch len(operands) {
+	case 1:
+		return operands[0], nil
+	case 2:
+		return a.compare(operands, operator)
+	}
+	return nil, NewErrorFrom(ErrInvalidData, "invalid comparison expression: %s", exp.GetText())
+}
+
+func (a *ActionParser) parseEqualityExpressionContext(ctx context.Context, exp *parser.EqualityExpressionContext) (interface{}, error) {
+	log.Debug(ctx, "Equality expression detected: %s", exp.GetText())
+	operands := make([]interface{}, 0, exp.GetChildCount())
+	var operator string
+	for i := 0; i < exp.GetChildCount(); i++ {
+		switch t := exp.GetChild(i).(type) {
+		case *parser.PrimaryExpressionContext:
+			result, err := a.parsePrimaryExpressionContext(ctx, t)
+			if err != nil {
+				return "", err
+			}
+			operands = append(operands, result)
+		case *parser.EqualityOperatorContext:
+			operator = t.GetText()
+		default:
+			return "", NewErrorFrom(ErrInvalidData, "unknown type %T in EqualityExpression", t)
+		}
+	}
+
+	switch len(operands) {
+	case 1:
+		return operands[0], nil
+	case 2:
+		return a.equal(operands, operator)
+	}
+	return "", NewErrorFrom(ErrInvalidData, "wrong equality expression. Got [%s]", exp.GetText())
+}
+
+func (a *ActionParser) parsePrimaryExpressionContext(ctx context.Context, exp *parser.PrimaryExpressionContext) (interface{}, error) {
+	log.Debug(ctx, "Primary expression detected: %s", exp.GetText())
+	if exp.GetChildCount() != 1 {
+		return nil, NewErrorFrom(ErrInvalidData, "invalid empty primary expression. Got [%s]", exp.GetText())
+	}
+
+	switch t := exp.GetChild(0).(type) {
+	case *parser.VariableContextContext:
+		return a.parseVariableContextContext(ctx, t)
+	case *parser.FunctionCallContext:
+		return a.parseFunctionCallContext(ctx, t)
+	case *parser.NumberExpressionContext:
+		return a.parseNumberExpression(ctx, t)
+	case *parser.StringExpressionContext:
+		return a.trimString(t.GetText()), nil
+	case *parser.TermExpressionContext:
+		return a.parseTermExpressionContext(ctx, t)
+	case *parser.NotExpressionContext:
+		return a.parseNotExpression(ctx, t)
+	default:
+		return nil, NewErrorFrom(ErrInvalidData, "unknown type %T in PrimaryExpression", t)
+	}
+}
+
+func (a *ActionParser) parseNotExpression(ctx context.Context, exp *parser.NotExpressionContext) (bool, error) {
+	log.Debug(ctx, "Not expression detected: %s", exp.GetText())
+	for i := 0; i < exp.GetChildCount(); i++ {
+		switch t := exp.GetChild(i).(type) {
+		case *parser.NotOperatorContext:
+			continue
+		case *parser.PrimaryExpressionContext:
+			result, err := a.parsePrimaryExpressionContext(ctx, t)
+			if err != nil {
+				return false, err
+			}
+			resultB, ok := result.(bool)
+			if !ok {
+				return false, NewErrorFrom(ErrInvalidData, "expression [%s] need to return a boolean value to be able to use the Not operator. Got [%v]", t.GetText(), result)
+			}
+			return !resultB, nil
+		}
+	}
+	return false, nil
+}
+
+func (a *ActionParser) parseFunctionCallContext(ctx context.Context, exp *parser.FunctionCallContext) (interface{}, error) {
+	log.Debug(ctx, "Function call expression detected: %s", exp.GetText())
+	var funcName string
+	args := make([]interface{}, 0)
+	for i := 0; i < exp.GetChildCount(); i++ {
+		switch t := exp.GetChild(i).(type) {
+		case *parser.FunctionNameContext:
+			funcName = t.GetText()
+		case *parser.FunctionCallArgumentsContext:
+			result, err := a.parseFunctionCallArgumentsContext(ctx, t)
+			if err != nil {
+				return nil, err
+			}
+			args = append(args, result)
+		case *antlr.TerminalNodeImpl:
+			if t.GetText() != "(" && t.GetText() != ")" && t.GetText() != "," {
+				return nil, NewErrorFrom(ErrInvalidData, "unknown string found in functionCall expression: [%s]", t.GetText())
+			}
+		default:
+			return nil, NewErrorFrom(ErrInvalidData, "unknown type %T in FunctionCall", t)
+		}
+	}
+	return a.callFunction(ctx, funcName, args)
+}
+
+func (a *ActionParser) parseFunctionCallArgumentsContext(ctx context.Context, exp *parser.FunctionCallArgumentsContext) (interface{}, error) {
+	log.Debug(ctx, "Function call arguments expression detected: %s", exp.GetText())
+	if exp.GetChildCount() == 0 {
+		return nil, nil
+	}
+
+	switch t := exp.GetChild(0).(type) {
+	case *parser.VariableContextContext:
+		result, err := a.parseVariableContextContext(ctx, t)
+		if err != nil {
+			return nil, err
+		}
+		return result, nil
+	case *parser.StringExpressionContext:
+		return a.trimString(t.GetText()), nil
+	case *parser.NumberExpressionContext:
+		return a.parseNumberExpression(ctx, t)
+	case *parser.BooleanExpressionContext:
+		s := t.GetText()
+		b, err := strconv.ParseBool(s)
+		if err != nil {
+			return nil, NewErrorFrom(ErrInvalidData, "unable to parse boolean %s", s)
+		}
+		return b, nil
+	default:
+		return nil, NewErrorFrom(ErrInvalidData, "unknown type %T in FunctionCall Arguments", t)
+	}
+
+}
+
+func (a *ActionParser) parseNumberExpression(ctx context.Context, t *parser.NumberExpressionContext) (interface{}, error) {
+	log.Debug(ctx, "Number expression detected: %s", t.GetText())
+	intValue, err := strconv.Atoi(t.GetText())
+	if err == nil {
+		return intValue, nil
+	}
+	floatValue, err := strconv.ParseFloat(t.GetText(), 64)
+	if err != nil {
+		return nil, NewErrorFrom(ErrInvalidData, "value %s is not an int neither a float", t.GetText())
+	}
+	return floatValue, nil
+}
+
+func (a *ActionParser) parseTermExpressionContext(ctx context.Context, exp *parser.TermExpressionContext) (bool, error) {
+	log.Debug(ctx, "Term expression detected: %s", exp.GetText())
+	for i := 0; i < exp.GetChildCount(); i++ {
+		switch t := exp.GetChild(i).(type) {
+		case *antlr.TerminalNodeImpl:
+		case *parser.OrExpressionContext:
+			result, err := a.parseOrExpressionContext(ctx, t)
+			if err != nil {
+				return false, err
+			}
+			b, err := strconv.ParseBool(result)
+			if err != nil {
+				return false, NewErrorFrom(ErrInvalidData, "%s is not a boolean", result)
+			}
+			return b, nil
+		default:
+			return false, NewErrorFrom(ErrInvalidData, "unknown type %T in TermExpression", t)
+		}
+	}
+	return false, nil
+}
+
+func (a *ActionParser) parseVariableContextContext(ctx context.Context, exp *parser.VariableContextContext) (interface{}, error) {
+	log.Debug(ctx, "VariableContext expression detected: %s", exp.GetText())
+	var selectedValue interface{}
+	isFilter := false
+	for i := 0; i < exp.GetChildCount(); i++ {
+		switch t := exp.GetChild(i).(type) {
+		case *parser.VariableIdentifierContext:
+			contextName := t.GetText()
+			var has bool
+			selectedValue, has = a.contexts[contextName]
+			if !has {
+				return nil, NewErrorFrom(ErrInvalidData, "unknown context %s", contextName)
+			}
+			continue
+		case *parser.VariablePathContext:
+			key, err := a.parseVariablePathContext(ctx, t)
+			if err != nil {
+				return nil, err
+			}
+			switch kType := key.(type) {
+			case string:
+				if key == "*" {
+					if isFilter {
+						return nil, NewErrorFrom(ErrInvalidData, "unable to filter a filtered object")
+					}
+					isFilter = true
+				} else {
+					selectedValue, err = a.getItemValueFromContext(ctx, selectedValue, kType, isFilter)
+					if err != nil {
+						return nil, err
+					}
+				}
+			case int:
+				selectedValue, err = a.getArrayItemValueFromContext(ctx, selectedValue, kType)
+				if err != nil {
+					return nil, err
+				}
+			}
+		default:
+			return false, NewErrorFrom(ErrInvalidData, "unknown type %T in VariableContext", t)
+		}
+	}
+	return selectedValue, nil
+}
+
+func (a *ActionParser) parseVariablePathContext(ctx context.Context, exp *parser.VariablePathContext) (interface{}, error) {
+	log.Debug(ctx, "VariablePath expression detected: %s", exp.GetText())
+	for i := 0; i < exp.GetChildCount(); i++ {
+		switch t := exp.GetChild(i).(type) {
+		case *antlr.TerminalNodeImpl:
+			continue
+		case *parser.VariableIdentifierContext:
+			return t.GetText(), nil
+		case *parser.ArrayContext:
+			result, err := a.parseArrayContext(ctx, t)
+			if err != nil {
+				return "", err
+			}
+			return result, nil
+		case *parser.FilterExpressionContext:
+			log.Debug(ctx, "Filter expression detected: %s", t.GetText())
+			return "*", nil
+		default:
+			return "", NewErrorFrom(ErrInvalidData, "unknown type %T in VariablePath", t)
+		}
+	}
+	return "", nil
+}
+
+func (a *ActionParser) parseArrayContext(ctx context.Context, exp *parser.ArrayContext) (int, error) {
+	log.Debug(ctx, "ArrayContext expression detected: %s", exp.GetText())
+	for i := 0; i < exp.GetChildCount(); i++ {
+		switch t := exp.GetChild(i).(type) {
+		case *antlr.TerminalNodeImpl:
+			if t.GetText() == "[" || t.GetText() == "]" {
+				continue
+			}
+			return 0, NewErrorFrom(ErrInvalidData, "unknown string found in a array context expression: [%s]", t.GetText())
+		case *parser.ArrayIndexContext:
+			return a.parseArrayIndexContext(ctx, t)
+		default:
+			return 0, NewErrorFrom(ErrInvalidData, "unknown type %T in ArrayContext", t)
+		}
+	}
+	return 0, NewErrorFrom(ErrInvalidData, "array index not found in expression [%s]", exp.GetText())
+}
+
+func (a *ActionParser) parseArrayIndexContext(ctx context.Context, exp *parser.ArrayIndexContext) (int, error) {
+	log.Debug(ctx, "ArrayIndexContext expression detected: %s", exp.GetText())
+	if exp.GetChildCount() != 1 {
+		return 0, NewErrorFrom(ErrInvalidData, "invalid array index expression: [%s]", exp.GetText())
+	}
+	switch t := exp.GetChild(0).(type) {
+	case *parser.PrimaryExpressionContext:
+		result, err := a.parsePrimaryExpressionContext(ctx, t)
+		if err != nil {
+			return 0, err
+		}
+		resultInt, ok := result.(int)
+		if !ok {
+			return 0, NewErrorFrom(ErrInvalidData, "invalid int index [%v]", result)
+		}
+		return resultInt, nil
+	default:
+		return 0, NewErrorFrom(ErrInvalidData, "invalid index expression %T", t)
+	}
+}
+
+func (a *ActionParser) getArrayItemValueFromContext(_ context.Context, currentContext interface{}, key int) (interface{}, error) {
+	items := reflect.ValueOf(currentContext)
+	if items.Kind() != reflect.Slice {
+		return nil, NewErrorFrom(ErrInvalidData, "object is not an array. Got %T [%v]", currentContext, currentContext)
+	}
+	return items.Index(key).Interface(), nil
+}
+
+// Item not found in a context must return empty
+func (a *ActionParser) getItemValueFromContext(ctx context.Context, currentContext interface{}, key string, isFilter bool) (interface{}, error) {
+	if isFilter {
+		varContext, ok := currentContext.([]map[string]interface{})
+		if !ok {
+			return nil, NewErrorFrom(ErrInvalidData, "unable to filter a non array object")
+		}
+		result := make([]interface{}, 0, len(varContext))
+		for _, currentItem := range varContext {
+			result = append(result, currentItem[key])
+		}
+
+		return result, nil
+	}
+	varContext, ok := currentContext.(map[string]interface{})
+	if !ok {
+		log.Debug(ctx, "key [%s] do not exist in current context")
+		return "", nil
+	}
+	value, has := varContext[key]
+	if !has {
+		return "", nil
+	}
+	return value, nil
+}
+
+func (a *ActionParser) compare(operands []interface{}, operator string) (bool, error) {
+	if len(operands) != 2 {
+		return false, NewErrorFrom(ErrInvalidData, "cannot compare more or less than 2 operands")
+	}
+	operand1, ok := operands[0].(float64)
+	if !ok {
+		operand1Int, ok := operands[0].(int)
+		if !ok {
+			return false, NewErrorFrom(ErrInvalidData, "%v must be a float or an int, got [%T]", operands[0], operands[0])
+		}
+		operand1 = float64(operand1Int)
+	}
+	operand2, ok := operands[1].(float64)
+	if !ok {
+		operand2Int, ok := operands[1].(int)
+		if !ok {
+			return false, NewErrorFrom(ErrInvalidData, "%v must be a float or an int, got [%T]", operands[1], operands[1])
+		}
+		operand2 = float64(operand2Int)
+	}
+
+	switch operator {
+	case "<":
+		return operand1 < operand2, nil
+	case ">":
+		return operand1 > operand2, nil
+	case "<=":
+		return operand1 <= operand2, nil
+	case ">=":
+		return operand1 >= operand2, nil
+	default:
+		return false, NewErrorFrom(ErrInvalidData, "unknown comparator operator %s", operator)
+	}
+}
+
+func (a *ActionParser) equal(operands []interface{}, operator string) (bool, error) {
+	if len(operands) != 2 {
+		return false, NewErrorFrom(ErrInvalidData, "cannot equalize more or less than 2 operands")
+	}
+
+	switch operator {
+	case "==":
+		return operands[0] == operands[1], nil
+	case "!=":
+		return operands[0] != operands[1], nil
+	default:
+		return false, NewErrorFrom(ErrInvalidData, "unknown equalizer operator %s", operator)
+	}
+}
+
+func (a *ActionParser) and(operands []interface{}) (bool, error) {
+	if len(operands) == 0 {
+		return false, nil
+	}
+	if len(operands) == 1 {
+		return false, NewErrorFrom(ErrInvalidData, "and expression must have at least 2 operands")
+	}
+	currentResult := true
+	for _, op := range operands {
+		opBool, ok := op.(bool)
+		if !ok {
+			return false, NewErrorFrom(ErrInvalidData, "operator && only work with boolean, got [%v]", op)
+		}
+		currentResult = currentResult && opBool
+		if !currentResult {
+			return currentResult, nil
+		}
+	}
+	return currentResult, nil
+}
+
+func (a *ActionParser) or(operands []interface{}) (bool, error) {
+	if len(operands) == 0 {
+		return false, nil
+	}
+	if len(operands) == 1 {
+		return false, NewErrorFrom(ErrInvalidData, "and expression must have at least 2 operands")
+	}
+	currentResult := false
+	for _, op := range operands {
+		opBool, ok := op.(bool)
+		if !ok {
+			return false, NewErrorFrom(ErrInvalidData, "operator && only work with boolean, got [%v]", opBool)
+		}
+		currentResult = currentResult || opBool
+		if currentResult {
+			return currentResult, nil
+		}
+	}
+	return currentResult, nil
+}
+
+func (a *ActionParser) trimString(value string) string {
+	return strings.TrimPrefix(strings.TrimSuffix(value, "'"), "'")
+}
+
+func (a *ActionParser) callFunction(ctx context.Context, funcName string, args []interface{}) (interface{}, error) {
+	fct, has := a.funcs[funcName]
+	if !has {
+		return nil, NewErrorFrom(ErrInvalidData, "function %s does not exist", funcName)
+	}
+	return fct(ctx, a, args...)
+}
diff --git a/sdk/action_parser_error.go b/sdk/action_parser_error.go
new file mode 100644
index 000000000..b571fb038
--- /dev/null
+++ b/sdk/action_parser_error.go
@@ -0,0 +1,28 @@
+package sdk
+
+import (
+	"fmt"
+
+	"github.com/antlr/antlr4/runtime/Go/antlr/v4"
+)
+
+type ParserErrorListener struct {
+	*antlr.DiagnosticErrorListener
+	Errors []string
+}
+
+func NewParserErrorListener() *ParserErrorListener {
+	del := new(antlr.DiagnosticErrorListener)
+	p := &ParserErrorListener{}
+	p.DiagnosticErrorListener = del
+	return p
+}
+
+func (c *ParserErrorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, line, column int, msg string, e antlr.RecognitionException) {
+	var errorMessage string
+	a, ok := recognizer.(*antlr.BaseParser)
+	if ok {
+		errorMessage += fmt.Sprintf("%v: ", a.Consume().GetInputStream())
+	}
+	c.Errors = append(c.Errors, errorMessage+msg)
+}
diff --git a/sdk/action_parser_funcs.go b/sdk/action_parser_funcs.go
new file mode 100644
index 000000000..e06653937
--- /dev/null
+++ b/sdk/action_parser_funcs.go
@@ -0,0 +1,166 @@
+package sdk
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"strings"
+
+	"github.com/rockbears/log"
+)
+
+var (
+	DefaultFuncs = map[string]ActionFunc{
+		"contains":   contains,
+		"startsWith": startsWith,
+		"endsWith":   endsWith,
+		"format":     format,
+		"join":       join,
+		"toJSON":     toJSON,
+		"fromJSON":   fromJSON,
+		"hashFiles":  hashFiles,
+		"success":    success,
+		"always":     always,
+		"cancelled":  cancelled,
+		"failure":    failure,
+	}
+)
+
+type ActionFunc func(ctx context.Context, a *ActionParser, inputs ...interface{}) (interface{}, error)
+
+// contains(search, item)
+func contains(ctx context.Context, _ *ActionParser, inputs ...interface{}) (interface{}, error) {
+	log.Debug(ctx, "function: contains with args: %v", inputs)
+	if len(inputs) != 2 {
+		return nil, NewErrorFrom(ErrInvalidData, "contains: wrong number of arguments to call contains(search, item)")
+	}
+
+	inputToSearch, ok := inputs[1].(string)
+	if !ok {
+		return nil, NewErrorFrom(ErrInvalidData, "contains: item argument must be a string")
+	}
+
+	inputSearch, ok := inputs[0].(string)
+	if ok {
+		return strings.Contains(strings.ToLower(inputSearch), strings.ToLower(inputToSearch)), nil
+	}
+
+	inputSearchArray, ok := inputs[0].([]interface{})
+	if !ok {
+		return nil, NewErrorFrom(ErrInvalidData, "contains: search argument must be a string or an array")
+	}
+
+	// If search is an array, inputToSearch must be an item of the array
+	for _, i := range inputSearchArray {
+		if fmt.Sprintf("%v", i) == inputToSearch {
+			return true, nil
+		}
+	}
+	return false, nil
+}
+
+func startsWith(ctx context.Context, _ *ActionParser, inputs ...interface{}) (interface{}, error) {
+	log.Debug(ctx, "function: startsWith with args: %v", inputs)
+	searchString, ok := inputs[0].(string)
+	if !ok {
+		return nil, NewErrorFrom(ErrInvalidData, "startsWith: searchString argument must be a string")
+	}
+
+	searchValue, ok := inputs[1].(string)
+	if !ok {
+		return nil, NewErrorFrom(ErrInvalidData, "startsWith: searchValue argument must be a string")
+	}
+	return strings.HasPrefix(strings.ToLower(searchString), strings.ToLower(searchValue)), nil
+}
+
+func endsWith(ctx context.Context, _ *ActionParser, inputs ...interface{}) (interface{}, error) {
+	log.Debug(ctx, "function: endsWith with args: %v", inputs)
+	searchString, ok := inputs[0].(string)
+	if !ok {
+		return nil, NewErrorFrom(ErrInvalidData, "endsWith: searchString argument must be a string")
+	}
+
+	searchValue, ok := inputs[1].(string)
+	if !ok {
+		return nil, NewErrorFrom(ErrInvalidData, "endsWith: searchValue argument must be a string")
+	}
+	return strings.HasSuffix(strings.ToLower(searchString), strings.ToLower(searchValue)), nil
+}
+
+func format(ctx context.Context, _ *ActionParser, inputs ...interface{}) (interface{}, error) {
+	log.Debug(ctx, "function: format with args: %v", inputs)
+	if len(inputs) < 2 {
+		return nil, NewErrorFrom(ErrInvalidData, "format: you must specify at least one replace value")
+	}
+	inputString, ok := inputs[0].(string)
+	if !ok {
+		return nil, NewErrorFrom(ErrInvalidData, "format: first argument must be a string")
+	}
+
+	for i := 1; i < len(inputs); i++ {
+		inputString = strings.Replace(inputString, fmt.Sprintf("{%d}", i-1), fmt.Sprintf("%v", inputs[i]), -1)
+	}
+	return inputString, nil
+}
+
+func join(ctx context.Context, _ *ActionParser, inputs ...interface{}) (interface{}, error) {
+	log.Debug(ctx, "function: join with args: %v", inputs)
+	if len(inputs) < 1 || len(inputs) > 2 {
+		return nil, NewErrorFrom(ErrInvalidData, "join: you must specify an array as first argument, and an optional separator")
+	}
+	separator := ","
+	if len(inputs) == 2 {
+		separator = fmt.Sprintf("%v", inputs[1])
+	}
+
+	var arrayString []string
+
+	arrayInput, ok := inputs[0].([]interface{})
+	if ok {
+		for _, item := range arrayInput {
+			arrayString = append(arrayString, fmt.Sprintf("%v", item))
+		}
+		return strings.Join(arrayString, separator), nil
+	}
+	stringInput, ok := inputs[0].(string)
+	if !ok {
+		return nil, NewErrorFrom(ErrInvalidData, "join: first argument must be an array or a string")
+	}
+	return stringInput, nil
+}
+
+func toJSON(ctx context.Context, _ *ActionParser, inputs ...interface{}) (interface{}, error) {
+	log.Debug(ctx, "function: toJSON with args: %v", inputs)
+	if len(inputs) != 1 {
+		return nil, NewErrorFrom(ErrInvalidData, "toJSON: you must one argument")
+	}
+	bts, err := json.MarshalIndent(inputs[0], "", "  ")
+	if err != nil {
+		return nil, NewErrorFrom(ErrInvalidData, "toJSON: given input cannot be convert to JSON")
+	}
+	return string(bts), nil
+}
+
+func fromJSON(_ context.Context, _ *ActionParser, _ ...interface{}) (interface{}, error) {
+	return nil, NewErrorFrom(ErrNotImplemented, "fromJSON is not implemented yet")
+}
+
+func hashFiles(_ context.Context, _ *ActionParser, _ ...interface{}) (interface{}, error) {
+	return nil, NewErrorFrom(ErrNotImplemented, "hashFiles is not implemented yet")
+}
+
+func success(_ context.Context, _ *ActionParser, _ ...interface{}) (interface{}, error) {
+	return nil, NewErrorFrom(ErrNotImplemented, "success is not implemented yet")
+}
+
+func always(_ context.Context, _ *ActionParser, _ ...interface{}) (interface{}, error) {
+	return nil, NewErrorFrom(ErrNotImplemented, "always is not implemented yet")
+}
+
+func cancelled(_ context.Context, _ *ActionParser, _ ...interface{}) (interface{}, error) {
+	return nil, NewErrorFrom(ErrNotImplemented, "cancelled is not implemented yet")
+}
+
+func failure(_ context.Context, _ *ActionParser, _ ...interface{}) (interface{}, error) {
+	return nil, NewErrorFrom(ErrNotImplemented, "failure is not implemented yet")
+}
diff --git a/sdk/hatchery/types.go b/sdk/hatchery/types.go
index f90c13b8c..10a5447a6 100644
--- a/sdk/hatchery/types.go
+++ b/sdk/hatchery/types.go
@@ -61,7 +61,8 @@ type WorkerJWTClaims struct {
 type SpawnArgumentsJWT struct {
 	WorkerName string `json:"worker_model,omitempty"`
 	Model      struct {
-		ID int64 `json:"id,omitempty"`
+		ID   int64  `json:"id,omitempty"`
+		Name string `json:"name,omitempty"`
 	} `json:"model,omitempty"`
 	JobID        int64  `json:"job_id,omitempty"`
 	RegisterOnly bool   `json:"register_only"`
diff --git a/sdk/hatchery/worker_token.go b/sdk/hatchery/worker_token.go
index be3bbdd55..0a5f654b9 100644
--- a/sdk/hatchery/worker_token.go
+++ b/sdk/hatchery/worker_token.go
@@ -27,6 +27,7 @@ func NewWorkerToken(hatcheryName string, privateKey *rsa.PrivateKey, expiration
 	}
 	if w.Model != nil {
 		claims.Worker.Model.ID = w.Model.ID
+		claims.Worker.Model.Name = w.Model.Name
 	}
 
 	jwtoken := jwt.NewWithClaims(jwt.SigningMethodRS512, claims)
diff --git a/sdk/parser/action_base_listener.go b/sdk/parser/action_base_listener.go
new file mode 100644
index 000000000..bc0fc566a
--- /dev/null
+++ b/sdk/parser/action_base_listener.go
@@ -0,0 +1,190 @@
+// Code generated from Action.g4 by ANTLR 4.12.0. DO NOT EDIT.
+
+package parser // Action
+
+import "github.com/antlr/antlr4/runtime/Go/antlr/v4"
+
+// BaseActionListener is a complete listener for a parse tree produced by ActionParser.
+type BaseActionListener struct{}
+
+var _ ActionListener = &BaseActionListener{}
+
+// VisitTerminal is called when a terminal node is visited.
+func (s *BaseActionListener) VisitTerminal(node antlr.TerminalNode) {}
+
+// VisitErrorNode is called when an error node is visited.
+func (s *BaseActionListener) VisitErrorNode(node antlr.ErrorNode) {}
+
+// EnterEveryRule is called when any rule is entered.
+func (s *BaseActionListener) EnterEveryRule(ctx antlr.ParserRuleContext) {}
+
+// ExitEveryRule is called when any rule is exited.
+func (s *BaseActionListener) ExitEveryRule(ctx antlr.ParserRuleContext) {}
+
+// EnterStart is called when production start is entered.
+func (s *BaseActionListener) EnterStart(ctx *StartContext) {}
+
+// ExitStart is called when production start is exited.
+func (s *BaseActionListener) ExitStart(ctx *StartContext) {}
+
+// EnterExpression is called when production expression is entered.
+func (s *BaseActionListener) EnterExpression(ctx *ExpressionContext) {}
+
+// ExitExpression is called when production expression is exited.
+func (s *BaseActionListener) ExitExpression(ctx *ExpressionContext) {}
+
+// EnterOrExpression is called when production orExpression is entered.
+func (s *BaseActionListener) EnterOrExpression(ctx *OrExpressionContext) {}
+
+// ExitOrExpression is called when production orExpression is exited.
+func (s *BaseActionListener) ExitOrExpression(ctx *OrExpressionContext) {}
+
+// EnterAndExpression is called when production andExpression is entered.
+func (s *BaseActionListener) EnterAndExpression(ctx *AndExpressionContext) {}
+
+// ExitAndExpression is called when production andExpression is exited.
+func (s *BaseActionListener) ExitAndExpression(ctx *AndExpressionContext) {}
+
+// EnterComparisonExpression is called when production comparisonExpression is entered.
+func (s *BaseActionListener) EnterComparisonExpression(ctx *ComparisonExpressionContext) {}
+
+// ExitComparisonExpression is called when production comparisonExpression is exited.
+func (s *BaseActionListener) ExitComparisonExpression(ctx *ComparisonExpressionContext) {}
+
+// EnterEqualityExpression is called when production equalityExpression is entered.
+func (s *BaseActionListener) EnterEqualityExpression(ctx *EqualityExpressionContext) {}
+
+// ExitEqualityExpression is called when production equalityExpression is exited.
+func (s *BaseActionListener) ExitEqualityExpression(ctx *EqualityExpressionContext) {}
+
+// EnterPrimaryExpression is called when production primaryExpression is entered.
+func (s *BaseActionListener) EnterPrimaryExpression(ctx *PrimaryExpressionContext) {}
+
+// ExitPrimaryExpression is called when production primaryExpression is exited.
+func (s *BaseActionListener) ExitPrimaryExpression(ctx *PrimaryExpressionContext) {}
+
+// EnterVariableContext is called when production variableContext is entered.
+func (s *BaseActionListener) EnterVariableContext(ctx *VariableContextContext) {}
+
+// ExitVariableContext is called when production variableContext is exited.
+func (s *BaseActionListener) ExitVariableContext(ctx *VariableContextContext) {}
+
+// EnterVariablePath is called when production variablePath is entered.
+func (s *BaseActionListener) EnterVariablePath(ctx *VariablePathContext) {}
+
+// ExitVariablePath is called when production variablePath is exited.
+func (s *BaseActionListener) ExitVariablePath(ctx *VariablePathContext) {}
+
+// EnterVariableIdentifier is called when production variableIdentifier is entered.
+func (s *BaseActionListener) EnterVariableIdentifier(ctx *VariableIdentifierContext) {}
+
+// ExitVariableIdentifier is called when production variableIdentifier is exited.
+func (s *BaseActionListener) ExitVariableIdentifier(ctx *VariableIdentifierContext) {}
+
+// EnterNumberExpression is called when production numberExpression is entered.
+func (s *BaseActionListener) EnterNumberExpression(ctx *NumberExpressionContext) {}
+
+// ExitNumberExpression is called when production numberExpression is exited.
+func (s *BaseActionListener) ExitNumberExpression(ctx *NumberExpressionContext) {}
+
+// EnterStringExpression is called when production stringExpression is entered.
+func (s *BaseActionListener) EnterStringExpression(ctx *StringExpressionContext) {}
+
+// ExitStringExpression is called when production stringExpression is exited.
+func (s *BaseActionListener) ExitStringExpression(ctx *StringExpressionContext) {}
+
+// EnterTermExpression is called when production termExpression is entered.
+func (s *BaseActionListener) EnterTermExpression(ctx *TermExpressionContext) {}
+
+// ExitTermExpression is called when production termExpression is exited.
+func (s *BaseActionListener) ExitTermExpression(ctx *TermExpressionContext) {}
+
+// EnterNotExpression is called when production notExpression is entered.
+func (s *BaseActionListener) EnterNotExpression(ctx *NotExpressionContext) {}
+
+// ExitNotExpression is called when production notExpression is exited.
+func (s *BaseActionListener) ExitNotExpression(ctx *NotExpressionContext) {}
+
+// EnterNotOperator is called when production notOperator is entered.
+func (s *BaseActionListener) EnterNotOperator(ctx *NotOperatorContext) {}
+
+// ExitNotOperator is called when production notOperator is exited.
+func (s *BaseActionListener) ExitNotOperator(ctx *NotOperatorContext) {}
+
+// EnterFunctionCall is called when production functionCall is entered.
+func (s *BaseActionListener) EnterFunctionCall(ctx *FunctionCallContext) {}
+
+// ExitFunctionCall is called when production functionCall is exited.
+func (s *BaseActionListener) ExitFunctionCall(ctx *FunctionCallContext) {}
+
+// EnterFunctionName is called when production functionName is entered.
+func (s *BaseActionListener) EnterFunctionName(ctx *FunctionNameContext) {}
+
+// ExitFunctionName is called when production functionName is exited.
+func (s *BaseActionListener) ExitFunctionName(ctx *FunctionNameContext) {}
+
+// EnterFunctionCallArguments is called when production functionCallArguments is entered.
+func (s *BaseActionListener) EnterFunctionCallArguments(ctx *FunctionCallArgumentsContext) {}
+
+// ExitFunctionCallArguments is called when production functionCallArguments is exited.
+func (s *BaseActionListener) ExitFunctionCallArguments(ctx *FunctionCallArgumentsContext) {}
+
+// EnterArray is called when production array is entered.
+func (s *BaseActionListener) EnterArray(ctx *ArrayContext) {}
+
+// ExitArray is called when production array is exited.
+func (s *BaseActionListener) ExitArray(ctx *ArrayContext) {}
+
+// EnterArrayIndex is called when production arrayIndex is entered.
+func (s *BaseActionListener) EnterArrayIndex(ctx *ArrayIndexContext) {}
+
+// ExitArrayIndex is called when production arrayIndex is exited.
+func (s *BaseActionListener) ExitArrayIndex(ctx *ArrayIndexContext) {}
+
+// EnterAndOperator is called when production andOperator is entered.
+func (s *BaseActionListener) EnterAndOperator(ctx *AndOperatorContext) {}
+
+// ExitAndOperator is called when production andOperator is exited.
+func (s *BaseActionListener) ExitAndOperator(ctx *AndOperatorContext) {}
+
+// EnterOrOperator is called when production orOperator is entered.
+func (s *BaseActionListener) EnterOrOperator(ctx *OrOperatorContext) {}
+
+// ExitOrOperator is called when production orOperator is exited.
+func (s *BaseActionListener) ExitOrOperator(ctx *OrOperatorContext) {}
+
+// EnterComparisonOperator is called when production comparisonOperator is entered.
+func (s *BaseActionListener) EnterComparisonOperator(ctx *ComparisonOperatorContext) {}
+
+// ExitComparisonOperator is called when production comparisonOperator is exited.
+func (s *BaseActionListener) ExitComparisonOperator(ctx *ComparisonOperatorContext) {}
+
+// EnterEqualityOperator is called when production equalityOperator is entered.
+func (s *BaseActionListener) EnterEqualityOperator(ctx *EqualityOperatorContext) {}
+
+// ExitEqualityOperator is called when production equalityOperator is exited.
+func (s *BaseActionListener) ExitEqualityOperator(ctx *EqualityOperatorContext) {}
+
+// EnterBooleanExpression is called when production booleanExpression is entered.
+func (s *BaseActionListener) EnterBooleanExpression(ctx *BooleanExpressionContext) {}
+
+// ExitBooleanExpression is called when production booleanExpression is exited.
+func (s *BaseActionListener) ExitBooleanExpression(ctx *BooleanExpressionContext) {}
+
+// EnterExpressionStart is called when production expressionStart is entered.
+func (s *BaseActionListener) EnterExpressionStart(ctx *ExpressionStartContext) {}
+
+// ExitExpressionStart is called when production expressionStart is exited.
+func (s *BaseActionListener) ExitExpressionStart(ctx *ExpressionStartContext) {}
+
+// EnterExpressionEnd is called when production expressionEnd is entered.
+func (s *BaseActionListener) EnterExpressionEnd(ctx *ExpressionEndContext) {}
+
+// ExitExpressionEnd is called when production expressionEnd is exited.
+func (s *BaseActionListener) ExitExpressionEnd(ctx *ExpressionEndContext) {}
+
+// EnterFilterExpression is called when production filterExpression is entered.
+func (s *BaseActionListener) EnterFilterExpression(ctx *FilterExpressionContext) {}
+
+// ExitFilterExpression is called when production filterExpression is exited.
+func (s *BaseActionListener) ExitFilterExpression(ctx *FilterExpressionContext) {}
diff --git a/sdk/parser/action_lexer.go b/sdk/parser/action_lexer.go
new file mode 100644
index 000000000..fde145b34
--- /dev/null
+++ b/sdk/parser/action_lexer.go
@@ -0,0 +1,226 @@
+// Code generated from Action.g4 by ANTLR 4.12.0. DO NOT EDIT.
+
+package parser
+
+import (
+	"fmt"
+	"sync"
+	"unicode"
+
+	"github.com/antlr/antlr4/runtime/Go/antlr/v4"
+)
+
+// Suppress unused import error
+var _ = fmt.Printf
+var _ = sync.Once{}
+var _ = unicode.IsLetter
+
+type ActionLexer struct {
+	*antlr.BaseLexer
+	channelNames []string
+	modeNames    []string
+	// TODO: EOF string
+}
+
+var actionlexerLexerStaticData 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
+}
+
+func actionlexerLexerInit() {
+	staticData := &actionlexerLexerStaticData
+	staticData.channelNames = []string{
+		"DEFAULT_TOKEN_CHANNEL", "HIDDEN",
+	}
+	staticData.modeNames = []string{
+		"DEFAULT_MODE",
+	}
+	staticData.literalNames = []string{
+		"", "','", "'['", "']'", "", "", "'null'", "'${{'", "'}}'", "", "'=='",
+		"'!='", "'>'", "'<'", "'>='", "'<='", "", "'('", "')'", "'!'", "'||'",
+		"'&&'", "'.'", "'*'",
+	}
+	staticData.symbolicNames = []string{
+		"", "", "", "", "STRING_INSIDE_EXPRESSION", "BOOLEAN", "NULL", "EXP_START",
+		"EXP_END", "NUMBER", "EQ", "NEQ", "GT", "LT", "GTE", "LTE", "ID", "LPAREN",
+		"RPAREN", "NOT", "OR", "AND", "DOT", "STAR", "WS",
+	}
+	staticData.ruleNames = []string{
+		"T__0", "T__1", "T__2", "STRING_INSIDE_EXPRESSION", "BOOLEAN", "NULL",
+		"EXP_START", "EXP_END", "NUMBER", "EQ", "NEQ", "GT", "LT", "GTE", "LTE",
+		"ID", "LPAREN", "RPAREN", "NOT", "OR", "AND", "DOT", "STAR", "ESC",
+		"INT", "FLOAT", "EXPONENT", "IDENTIFIER", "WS",
+	}
+	staticData.predictionContextCache = antlr.NewPredictionContextCache()
+	staticData.serializedATN = []int32{
+		4, 0, 24, 214, 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, 1, 0, 1, 0, 1, 1, 1, 1, 1, 2,
+		1, 2, 1, 3, 1, 3, 1, 3, 5, 3, 69, 8, 3, 10, 3, 12, 3, 72, 9, 3, 1, 3, 1,
+		3, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 1, 4, 3, 4, 85, 8, 4,
+		1, 5, 1, 5, 1, 5, 1, 5, 1, 5, 1, 6, 1, 6, 1, 6, 1, 6, 1, 7, 1, 7, 1, 7,
+		1, 8, 1, 8, 3, 8, 101, 8, 8, 1, 9, 1, 9, 1, 9, 1, 10, 1, 10, 1, 10, 1,
+		11, 1, 11, 1, 12, 1, 12, 1, 13, 1, 13, 1, 13, 1, 14, 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, 19, 1,
+		20, 1, 20, 1, 20, 1, 21, 1, 21, 1, 22, 1, 22, 1, 23, 1, 23, 1, 23, 1, 24,
+		1, 24, 1, 24, 5, 24, 143, 8, 24, 10, 24, 12, 24, 146, 9, 24, 3, 24, 148,
+		8, 24, 1, 25, 1, 25, 1, 25, 5, 25, 153, 8, 25, 10, 25, 12, 25, 156, 9,
+		25, 3, 25, 158, 8, 25, 1, 25, 1, 25, 5, 25, 162, 8, 25, 10, 25, 12, 25,
+		165, 9, 25, 1, 25, 3, 25, 168, 8, 25, 1, 25, 1, 25, 4, 25, 172, 8, 25,
+		11, 25, 12, 25, 173, 1, 25, 3, 25, 177, 8, 25, 1, 25, 1, 25, 1, 25, 5,
+		25, 182, 8, 25, 10, 25, 12, 25, 185, 9, 25, 3, 25, 187, 8, 25, 1, 25, 3,
+		25, 190, 8, 25, 1, 26, 1, 26, 3, 26, 194, 8, 26, 1, 26, 4, 26, 197, 8,
+		26, 11, 26, 12, 26, 198, 1, 27, 1, 27, 5, 27, 203, 8, 27, 10, 27, 12, 27,
+		206, 9, 27, 1, 28, 4, 28, 209, 8, 28, 11, 28, 12, 28, 210, 1, 28, 1, 28,
+		1, 70, 0, 29, 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, 0, 49, 0, 51, 0, 53, 0, 55,
+		0, 57, 24, 1, 0, 8, 9, 0, 34, 34, 39, 39, 47, 47, 92, 92, 98, 98, 102,
+		102, 110, 110, 114, 114, 116, 116, 1, 0, 49, 57, 1, 0, 48, 57, 2, 0, 69,
+		69, 101, 101, 2, 0, 43, 43, 45, 45, 3, 0, 65, 90, 95, 95, 97, 122, 5, 0,
+		45, 45, 48, 57, 65, 90, 95, 95, 97, 122, 3, 0, 9, 10, 13, 13, 32, 32, 228,
+		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,
+		57, 1, 0, 0, 0, 1, 59, 1, 0, 0, 0, 3, 61, 1, 0, 0, 0, 5, 63, 1, 0, 0, 0,
+		7, 65, 1, 0, 0, 0, 9, 84, 1, 0, 0, 0, 11, 86, 1, 0, 0, 0, 13, 91, 1, 0,
+		0, 0, 15, 95, 1, 0, 0, 0, 17, 100, 1, 0, 0, 0, 19, 102, 1, 0, 0, 0, 21,
+		105, 1, 0, 0, 0, 23, 108, 1, 0, 0, 0, 25, 110, 1, 0, 0, 0, 27, 112, 1,
+		0, 0, 0, 29, 115, 1, 0, 0, 0, 31, 118, 1, 0, 0, 0, 33, 120, 1, 0, 0, 0,
+		35, 122, 1, 0, 0, 0, 37, 124, 1, 0, 0, 0, 39, 126, 1, 0, 0, 0, 41, 129,
+		1, 0, 0, 0, 43, 132, 1, 0, 0, 0, 45, 134, 1, 0, 0, 0, 47, 136, 1, 0, 0,
+		0, 49, 147, 1, 0, 0, 0, 51, 189, 1, 0, 0, 0, 53, 191, 1, 0, 0, 0, 55, 200,
+		1, 0, 0, 0, 57, 208, 1, 0, 0, 0, 59, 60, 5, 44, 0, 0, 60, 2, 1, 0, 0, 0,
+		61, 62, 5, 91, 0, 0, 62, 4, 1, 0, 0, 0, 63, 64, 5, 93, 0, 0, 64, 6, 1,
+		0, 0, 0, 65, 70, 5, 39, 0, 0, 66, 69, 3, 47, 23, 0, 67, 69, 9, 0, 0, 0,
+		68, 66, 1, 0, 0, 0, 68, 67, 1, 0, 0, 0, 69, 72, 1, 0, 0, 0, 70, 71, 1,
+		0, 0, 0, 70, 68, 1, 0, 0, 0, 71, 73, 1, 0, 0, 0, 72, 70, 1, 0, 0, 0, 73,
+		74, 5, 39, 0, 0, 74, 8, 1, 0, 0, 0, 75, 76, 5, 116, 0, 0, 76, 77, 5, 114,
+		0, 0, 77, 78, 5, 117, 0, 0, 78, 85, 5, 101, 0, 0, 79, 80, 5, 102, 0, 0,
+		80, 81, 5, 97, 0, 0, 81, 82, 5, 108, 0, 0, 82, 83, 5, 115, 0, 0, 83, 85,
+		5, 101, 0, 0, 84, 75, 1, 0, 0, 0, 84, 79, 1, 0, 0, 0, 85, 10, 1, 0, 0,
+		0, 86, 87, 5, 110, 0, 0, 87, 88, 5, 117, 0, 0, 88, 89, 5, 108, 0, 0, 89,
+		90, 5, 108, 0, 0, 90, 12, 1, 0, 0, 0, 91, 92, 5, 36, 0, 0, 92, 93, 5, 123,
+		0, 0, 93, 94, 5, 123, 0, 0, 94, 14, 1, 0, 0, 0, 95, 96, 5, 125, 0, 0, 96,
+		97, 5, 125, 0, 0, 97, 16, 1, 0, 0, 0, 98, 101, 3, 49, 24, 0, 99, 101, 3,
+		51, 25, 0, 100, 98, 1, 0, 0, 0, 100, 99, 1, 0, 0, 0, 101, 18, 1, 0, 0,
+		0, 102, 103, 5, 61, 0, 0, 103, 104, 5, 61, 0, 0, 104, 20, 1, 0, 0, 0, 105,
+		106, 5, 33, 0, 0, 106, 107, 5, 61, 0, 0, 107, 22, 1, 0, 0, 0, 108, 109,
+		5, 62, 0, 0, 109, 24, 1, 0, 0, 0, 110, 111, 5, 60, 0, 0, 111, 26, 1, 0,
+		0, 0, 112, 113, 5, 62, 0, 0, 113, 114, 5, 61, 0, 0, 114, 28, 1, 0, 0, 0,
+		115, 116, 5, 60, 0, 0, 116, 117, 5, 61, 0, 0, 117, 30, 1, 0, 0, 0, 118,
+		119, 3, 55, 27, 0, 119, 32, 1, 0, 0, 0, 120, 121, 5, 40, 0, 0, 121, 34,
+		1, 0, 0, 0, 122, 123, 5, 41, 0, 0, 123, 36, 1, 0, 0, 0, 124, 125, 5, 33,
+		0, 0, 125, 38, 1, 0, 0, 0, 126, 127, 5, 124, 0, 0, 127, 128, 5, 124, 0,
+		0, 128, 40, 1, 0, 0, 0, 129, 130, 5, 38, 0, 0, 130, 131, 5, 38, 0, 0, 131,
+		42, 1, 0, 0, 0, 132, 133, 5, 46, 0, 0, 133, 44, 1, 0, 0, 0, 134, 135, 5,
+		42, 0, 0, 135, 46, 1, 0, 0, 0, 136, 137, 5, 92, 0, 0, 137, 138, 7, 0, 0,
+		0, 138, 48, 1, 0, 0, 0, 139, 148, 5, 48, 0, 0, 140, 144, 7, 1, 0, 0, 141,
+		143, 7, 2, 0, 0, 142, 141, 1, 0, 0, 0, 143, 146, 1, 0, 0, 0, 144, 142,
+		1, 0, 0, 0, 144, 145, 1, 0, 0, 0, 145, 148, 1, 0, 0, 0, 146, 144, 1, 0,
+		0, 0, 147, 139, 1, 0, 0, 0, 147, 140, 1, 0, 0, 0, 148, 50, 1, 0, 0, 0,
+		149, 158, 5, 48, 0, 0, 150, 154, 7, 1, 0, 0, 151, 153, 7, 2, 0, 0, 152,
+		151, 1, 0, 0, 0, 153, 156, 1, 0, 0, 0, 154, 152, 1, 0, 0, 0, 154, 155,
+		1, 0, 0, 0, 155, 158, 1, 0, 0, 0, 156, 154, 1, 0, 0, 0, 157, 149, 1, 0,
+		0, 0, 157, 150, 1, 0, 0, 0, 158, 159, 1, 0, 0, 0, 159, 163, 5, 46, 0, 0,
+		160, 162, 7, 2, 0, 0, 161, 160, 1, 0, 0, 0, 162, 165, 1, 0, 0, 0, 163,
+		161, 1, 0, 0, 0, 163, 164, 1, 0, 0, 0, 164, 167, 1, 0, 0, 0, 165, 163,
+		1, 0, 0, 0, 166, 168, 3, 53, 26, 0, 167, 166, 1, 0, 0, 0, 167, 168, 1,
+		0, 0, 0, 168, 190, 1, 0, 0, 0, 169, 171, 5, 46, 0, 0, 170, 172, 7, 2, 0,
+		0, 171, 170, 1, 0, 0, 0, 172, 173, 1, 0, 0, 0, 173, 171, 1, 0, 0, 0, 173,
+		174, 1, 0, 0, 0, 174, 176, 1, 0, 0, 0, 175, 177, 3, 53, 26, 0, 176, 175,
+		1, 0, 0, 0, 176, 177, 1, 0, 0, 0, 177, 190, 1, 0, 0, 0, 178, 187, 5, 48,
+		0, 0, 179, 183, 7, 1, 0, 0, 180, 182, 7, 2, 0, 0, 181, 180, 1, 0, 0, 0,
+		182, 185, 1, 0, 0, 0, 183, 181, 1, 0, 0, 0, 183, 184, 1, 0, 0, 0, 184,
+		187, 1, 0, 0, 0, 185, 183, 1, 0, 0, 0, 186, 178, 1, 0, 0, 0, 186, 179,
+		1, 0, 0, 0, 187, 188, 1, 0, 0, 0, 188, 190, 3, 53, 26, 0, 189, 157, 1,
+		0, 0, 0, 189, 169, 1, 0, 0, 0, 189, 186, 1, 0, 0, 0, 190, 52, 1, 0, 0,
+		0, 191, 193, 7, 3, 0, 0, 192, 194, 7, 4, 0, 0, 193, 192, 1, 0, 0, 0, 193,
+		194, 1, 0, 0, 0, 194, 196, 1, 0, 0, 0, 195, 197, 7, 2, 0, 0, 196, 195,
+		1, 0, 0, 0, 197, 198, 1, 0, 0, 0, 198, 196, 1, 0, 0, 0, 198, 199, 1, 0,
+		0, 0, 199, 54, 1, 0, 0, 0, 200, 204, 7, 5, 0, 0, 201, 203, 7, 6, 0, 0,
+		202, 201, 1, 0, 0, 0, 203, 206, 1, 0, 0, 0, 204, 202, 1, 0, 0, 0, 204,
+		205, 1, 0, 0, 0, 205, 56, 1, 0, 0, 0, 206, 204, 1, 0, 0, 0, 207, 209, 7,
+		7, 0, 0, 208, 207, 1, 0, 0, 0, 209, 210, 1, 0, 0, 0, 210, 208, 1, 0, 0,
+		0, 210, 211, 1, 0, 0, 0, 211, 212, 1, 0, 0, 0, 212, 213, 6, 28, 0, 0, 213,
+		58, 1, 0, 0, 0, 20, 0, 68, 70, 84, 100, 144, 147, 154, 157, 163, 167, 173,
+		176, 183, 186, 189, 193, 198, 204, 210, 1, 6, 0, 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)
+	}
+}
+
+// ActionLexerInit initializes any static state used to implement ActionLexer. By default the
+// static state used to implement the lexer is lazily initialized during the first call to
+// NewActionLexer(). You can call this function if you wish to initialize the static state ahead
+// of time.
+func ActionLexerInit() {
+	staticData := &actionlexerLexerStaticData
+	staticData.once.Do(actionlexerLexerInit)
+}
+
+// NewActionLexer produces a new lexer instance for the optional input antlr.CharStream.
+func NewActionLexer(input antlr.CharStream) *ActionLexer {
+	ActionLexerInit()
+	l := new(ActionLexer)
+	l.BaseLexer = antlr.NewBaseLexer(input)
+	staticData := &actionlexerLexerStaticData
+	l.Interpreter = antlr.NewLexerATNSimulator(l, staticData.atn, staticData.decisionToDFA, staticData.predictionContextCache)
+	l.channelNames = staticData.channelNames
+	l.modeNames = staticData.modeNames
+	l.RuleNames = staticData.ruleNames
+	l.LiteralNames = staticData.literalNames
+	l.SymbolicNames = staticData.symbolicNames
+	l.GrammarFileName = "Action.g4"
+	// TODO: l.EOF = antlr.TokenEOF
+
+	return l
+}
+
+// ActionLexer tokens.
+const (
+	ActionLexerT__0                     = 1
+	ActionLexerT__1                     = 2
+	ActionLexerT__2                     = 3
+	ActionLexerSTRING_INSIDE_EXPRESSION = 4
+	ActionLexerBOOLEAN                  = 5
+	ActionLexerNULL                     = 6
+	ActionLexerEXP_START                = 7
+	ActionLexerEXP_END                  = 8
+	ActionLexerNUMBER                   = 9
+	ActionLexerEQ                       = 10
+	ActionLexerNEQ                      = 11
+	ActionLexerGT                       = 12
+	ActionLexerLT                       = 13
+	ActionLexerGTE                      = 14
+	ActionLexerLTE                      = 15
+	ActionLexerID                       = 16
+	ActionLexerLPAREN                   = 17
+	ActionLexerRPAREN                   = 18
+	ActionLexerNOT                      = 19
+	ActionLexerOR                       = 20
+	ActionLexerAND                      = 21
+	ActionLexerDOT                      = 22
+	ActionLexerSTAR                     = 23
+	ActionLexerWS                       = 24
+)
diff --git a/sdk/parser/action_listener.go b/sdk/parser/action_listener.go
new file mode 100644
index 000000000..68b0094b4
--- /dev/null
+++ b/sdk/parser/action_listener.go
@@ -0,0 +1,178 @@
+// Code generated from Action.g4 by ANTLR 4.12.0. DO NOT EDIT.
+
+package parser // Action
+
+import "github.com/antlr/antlr4/runtime/Go/antlr/v4"
+
+// ActionListener is a complete listener for a parse tree produced by ActionParser.
+type ActionListener interface {
+	antlr.ParseTreeListener
+
+	// EnterStart is called when entering the start production.
+	EnterStart(c *StartContext)
+
+	// EnterExpression is called when entering the expression production.
+	EnterExpression(c *ExpressionContext)
+
+	// EnterOrExpression is called when entering the orExpression production.
+	EnterOrExpression(c *OrExpressionContext)
+
+	// EnterAndExpression is called when entering the andExpression production.
+	EnterAndExpression(c *AndExpressionContext)
+
+	// EnterComparisonExpression is called when entering the comparisonExpression production.
+	EnterComparisonExpression(c *ComparisonExpressionContext)
+
+	// EnterEqualityExpression is called when entering the equalityExpression production.
+	EnterEqualityExpression(c *EqualityExpressionContext)
+
+	// EnterPrimaryExpression is called when entering the primaryExpression production.
+	EnterPrimaryExpression(c *PrimaryExpressionContext)
+
+	// EnterVariableContext is called when entering the variableContext production.
+	EnterVariableContext(c *VariableContextContext)
+
+	// EnterVariablePath is called when entering the variablePath production.
+	EnterVariablePath(c *VariablePathContext)
+
+	// EnterVariableIdentifier is called when entering the variableIdentifier production.
+	EnterVariableIdentifier(c *VariableIdentifierContext)
+
+	// EnterNumberExpression is called when entering the numberExpression production.
+	EnterNumberExpression(c *NumberExpressionContext)
+
+	// EnterStringExpression is called when entering the stringExpression production.
+	EnterStringExpression(c *StringExpressionContext)
+
+	// EnterTermExpression is called when entering the termExpression production.
+	EnterTermExpression(c *TermExpressionContext)
+
+	// EnterNotExpression is called when entering the notExpression production.
+	EnterNotExpression(c *NotExpressionContext)
+
+	// EnterNotOperator is called when entering the notOperator production.
+	EnterNotOperator(c *NotOperatorContext)
+
+	// EnterFunctionCall is called when entering the functionCall production.
+	EnterFunctionCall(c *FunctionCallContext)
+
+	// EnterFunctionName is called when entering the functionName production.
+	EnterFunctionName(c *FunctionNameContext)
+
+	// EnterFunctionCallArguments is called when entering the functionCallArguments production.
+	EnterFunctionCallArguments(c *FunctionCallArgumentsContext)
+
+	// EnterArray is called when entering the array production.
+	EnterArray(c *ArrayContext)
+
+	// EnterArrayIndex is called when entering the arrayIndex production.
+	EnterArrayIndex(c *ArrayIndexContext)
+
+	// EnterAndOperator is called when entering the andOperator production.
+	EnterAndOperator(c *AndOperatorContext)
+
+	// EnterOrOperator is called when entering the orOperator production.
+	EnterOrOperator(c *OrOperatorContext)
+
+	// EnterComparisonOperator is called when entering the comparisonOperator production.
+	EnterComparisonOperator(c *ComparisonOperatorContext)
+
+	// EnterEqualityOperator is called when entering the equalityOperator production.
+	EnterEqualityOperator(c *EqualityOperatorContext)
+
+	// EnterBooleanExpression is called when entering the booleanExpression production.
+	EnterBooleanExpression(c *BooleanExpressionContext)
+
+	// EnterExpressionStart is called when entering the expressionStart production.
+	EnterExpressionStart(c *ExpressionStartContext)
+
+	// EnterExpressionEnd is called when entering the expressionEnd production.
+	EnterExpressionEnd(c *ExpressionEndContext)
+
+	// EnterFilterExpression is called when entering the filterExpression production.
+	EnterFilterExpression(c *FilterExpressionContext)
+
+	// ExitStart is called when exiting the start production.
+	ExitStart(c *StartContext)
+
+	// ExitExpression is called when exiting the expression production.
+	ExitExpression(c *ExpressionContext)
+
+	// ExitOrExpression is called when exiting the orExpression production.
+	ExitOrExpression(c *OrExpressionContext)
+
+	// ExitAndExpression is called when exiting the andExpression production.
+	ExitAndExpression(c *AndExpressionContext)
+
+	// ExitComparisonExpression is called when exiting the comparisonExpression production.
+	ExitComparisonExpression(c *ComparisonExpressionContext)
+
+	// ExitEqualityExpression is called when exiting the equalityExpression production.
+	ExitEqualityExpression(c *EqualityExpressionContext)
+
+	// ExitPrimaryExpression is called when exiting the primaryExpression production.
+	ExitPrimaryExpression(c *PrimaryExpressionContext)
+
+	// ExitVariableContext is called when exiting the variableContext production.
+	ExitVariableContext(c *VariableContextContext)
+
+	// ExitVariablePath is called when exiting the variablePath production.
+	ExitVariablePath(c *VariablePathContext)
+
+	// ExitVariableIdentifier is called when exiting the variableIdentifier production.
+	ExitVariableIdentifier(c *VariableIdentifierContext)
+
+	// ExitNumberExpression is called when exiting the numberExpression production.
+	ExitNumberExpression(c *NumberExpressionContext)
+
+	// ExitStringExpression is called when exiting the stringExpression production.
+	ExitStringExpression(c *StringExpressionContext)
+
+	// ExitTermExpression is called when exiting the termExpression production.
+	ExitTermExpression(c *TermExpressionContext)
+
+	// ExitNotExpression is called when exiting the notExpression production.
+	ExitNotExpression(c *NotExpressionContext)
+
+	// ExitNotOperator is called when exiting the notOperator production.
+	ExitNotOperator(c *NotOperatorContext)
+
+	// ExitFunctionCall is called when exiting the functionCall production.
+	ExitFunctionCall(c *FunctionCallContext)
+
+	// ExitFunctionName is called when exiting the functionName production.
+	ExitFunctionName(c *FunctionNameContext)
+
+	// ExitFunctionCallArguments is called when exiting the functionCallArguments production.
+	ExitFunctionCallArguments(c *FunctionCallArgumentsContext)
+
+	// ExitArray is called when exiting the array production.
+	ExitArray(c *ArrayContext)
+
+	// ExitArrayIndex is called when exiting the arrayIndex production.
+	ExitArrayIndex(c *ArrayIndexContext)
+
+	// ExitAndOperator is called when exiting the andOperator production.
+	ExitAndOperator(c *AndOperatorContext)
+
+	// ExitOrOperator is called when exiting the orOperator production.
+	ExitOrOperator(c *OrOperatorContext)
+
+	// ExitComparisonOperator is called when exiting the comparisonOperator production.
+	ExitComparisonOperator(c *ComparisonOperatorContext)
+
+	// ExitEqualityOperator is called when exiting the equalityOperator production.
+	ExitEqualityOperator(c *EqualityOperatorContext)
+
+	// ExitBooleanExpression is called when exiting the booleanExpression production.
+	ExitBooleanExpression(c *BooleanExpressionContext)
+
+	// ExitExpressionStart is called when exiting the expressionStart production.
+	ExitExpressionStart(c *ExpressionStartContext)
+
+	// ExitExpressionEnd is called when exiting the expressionEnd production.
+	ExitExpressionEnd(c *ExpressionEndContext)
+
+	// ExitFilterExpression is called when exiting the filterExpression production.
+	ExitFilterExpression(c *FilterExpressionContext)
+}
diff --git a/sdk/parser/action_parser.go b/sdk/parser/action_parser.go
new file mode 100644
index 000000000..bfee5cdc6
--- /dev/null
+++ b/sdk/parser/action_parser.go
@@ -0,0 +1,3959 @@
+// Code generated from Action.g4 by ANTLR 4.12.0. DO NOT EDIT.
+
+package parser // Action
+
+import (
+	"fmt"
+	"strconv"
+	"sync"
+
+	"github.com/antlr/antlr4/runtime/Go/antlr/v4"
+)
+
+// Suppress unused import errors
+var _ = fmt.Printf
+var _ = strconv.Itoa
+var _ = sync.Once{}
+
+type ActionParser struct {
+	*antlr.BaseParser
+}
+
+var actionParserStaticData struct {
+	once                   sync.Once
+	serializedATN          []int32
+	literalNames           []string
+	symbolicNames          []string
+	ruleNames              []string
+	predictionContextCache *antlr.PredictionContextCache
+	atn                    *antlr.ATN
+	decisionToDFA          []*antlr.DFA
+}
+
+func actionParserInit() {
+	staticData := &actionParserStaticData
+	staticData.literalNames = []string{
+		"", "','", "'['", "']'", "", "", "'null'", "'${{'", "'}}'", "", "'=='",
+		"'!='", "'>'", "'<'", "'>='", "'<='", "", "'('", "')'", "'!'", "'||'",
+		"'&&'", "'.'", "'*'",
+	}
+	staticData.symbolicNames = []string{
+		"", "", "", "", "STRING_INSIDE_EXPRESSION", "BOOLEAN", "NULL", "EXP_START",
+		"EXP_END", "NUMBER", "EQ", "NEQ", "GT", "LT", "GTE", "LTE", "ID", "LPAREN",
+		"RPAREN", "NOT", "OR", "AND", "DOT", "STAR", "WS",
+	}
+	staticData.ruleNames = []string{
+		"start", "expression", "orExpression", "andExpression", "comparisonExpression",
+		"equalityExpression", "primaryExpression", "variableContext", "variablePath",
+		"variableIdentifier", "numberExpression", "stringExpression", "termExpression",
+		"notExpression", "notOperator", "functionCall", "functionName", "functionCallArguments",
+		"array", "arrayIndex", "andOperator", "orOperator", "comparisonOperator",
+		"equalityOperator", "booleanExpression", "expressionStart", "expressionEnd",
+		"filterExpression",
+	}
+	staticData.predictionContextCache = antlr.NewPredictionContextCache()
+	staticData.serializedATN = []int32{
+		4, 1, 24, 174, 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, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1,
+		2, 1, 2, 1, 2, 5, 2, 68, 8, 2, 10, 2, 12, 2, 71, 9, 2, 1, 3, 1, 3, 1, 3,
+		1, 3, 5, 3, 77, 8, 3, 10, 3, 12, 3, 80, 9, 3, 1, 4, 1, 4, 1, 4, 1, 4, 3,
+		4, 86, 8, 4, 1, 5, 1, 5, 1, 5, 1, 5, 3, 5, 92, 8, 5, 1, 6, 1, 6, 1, 6,
+		1, 6, 1, 6, 1, 6, 3, 6, 100, 8, 6, 1, 7, 1, 7, 5, 7, 104, 8, 7, 10, 7,
+		12, 7, 107, 9, 7, 1, 8, 1, 8, 1, 8, 1, 8, 1, 8, 3, 8, 114, 8, 8, 1, 9,
+		1, 9, 1, 10, 1, 10, 1, 11, 1, 11, 1, 12, 1, 12, 1, 12, 1, 12, 1, 13, 1,
+		13, 1, 13, 1, 14, 1, 14, 1, 15, 1, 15, 1, 15, 1, 15, 1, 15, 5, 15, 136,
+		8, 15, 10, 15, 12, 15, 139, 9, 15, 1, 15, 1, 15, 1, 16, 1, 16, 1, 17, 1,
+		17, 1, 17, 1, 17, 1, 17, 3, 17, 150, 8, 17, 1, 18, 1, 18, 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, 26, 1, 26, 1, 27, 1, 27, 1, 27, 0, 0, 28, 0,
+		2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38,
+		40, 42, 44, 46, 48, 50, 52, 54, 0, 2, 1, 0, 12, 15, 1, 0, 10, 11, 162,
+		0, 56, 1, 0, 0, 0, 2, 59, 1, 0, 0, 0, 4, 63, 1, 0, 0, 0, 6, 72, 1, 0, 0,
+		0, 8, 81, 1, 0, 0, 0, 10, 87, 1, 0, 0, 0, 12, 99, 1, 0, 0, 0, 14, 101,
+		1, 0, 0, 0, 16, 113, 1, 0, 0, 0, 18, 115, 1, 0, 0, 0, 20, 117, 1, 0, 0,
+		0, 22, 119, 1, 0, 0, 0, 24, 121, 1, 0, 0, 0, 26, 125, 1, 0, 0, 0, 28, 128,
+		1, 0, 0, 0, 30, 130, 1, 0, 0, 0, 32, 142, 1, 0, 0, 0, 34, 149, 1, 0, 0,
+		0, 36, 151, 1, 0, 0, 0, 38, 155, 1, 0, 0, 0, 40, 157, 1, 0, 0, 0, 42, 159,
+		1, 0, 0, 0, 44, 161, 1, 0, 0, 0, 46, 163, 1, 0, 0, 0, 48, 165, 1, 0, 0,
+		0, 50, 167, 1, 0, 0, 0, 52, 169, 1, 0, 0, 0, 54, 171, 1, 0, 0, 0, 56, 57,
+		3, 2, 1, 0, 57, 58, 5, 0, 0, 1, 58, 1, 1, 0, 0, 0, 59, 60, 3, 50, 25, 0,
+		60, 61, 3, 4, 2, 0, 61, 62, 3, 52, 26, 0, 62, 3, 1, 0, 0, 0, 63, 69, 3,
+		6, 3, 0, 64, 65, 3, 42, 21, 0, 65, 66, 3, 6, 3, 0, 66, 68, 1, 0, 0, 0,
+		67, 64, 1, 0, 0, 0, 68, 71, 1, 0, 0, 0, 69, 67, 1, 0, 0, 0, 69, 70, 1,
+		0, 0, 0, 70, 5, 1, 0, 0, 0, 71, 69, 1, 0, 0, 0, 72, 78, 3, 8, 4, 0, 73,
+		74, 3, 40, 20, 0, 74, 75, 3, 8, 4, 0, 75, 77, 1, 0, 0, 0, 76, 73, 1, 0,
+		0, 0, 77, 80, 1, 0, 0, 0, 78, 76, 1, 0, 0, 0, 78, 79, 1, 0, 0, 0, 79, 7,
+		1, 0, 0, 0, 80, 78, 1, 0, 0, 0, 81, 85, 3, 10, 5, 0, 82, 83, 3, 44, 22,
+		0, 83, 84, 3, 10, 5, 0, 84, 86, 1, 0, 0, 0, 85, 82, 1, 0, 0, 0, 85, 86,
+		1, 0, 0, 0, 86, 9, 1, 0, 0, 0, 87, 91, 3, 12, 6, 0, 88, 89, 3, 46, 23,
+		0, 89, 90, 3, 12, 6, 0, 90, 92, 1, 0, 0, 0, 91, 88, 1, 0, 0, 0, 91, 92,
+		1, 0, 0, 0, 92, 11, 1, 0, 0, 0, 93, 100, 3, 14, 7, 0, 94, 100, 3, 20, 10,
+		0, 95, 100, 3, 30, 15, 0, 96, 100, 3, 22, 11, 0, 97, 100, 3, 24, 12, 0,
+		98, 100, 3, 26, 13, 0, 99, 93, 1, 0, 0, 0, 99, 94, 1, 0, 0, 0, 99, 95,
+		1, 0, 0, 0, 99, 96, 1, 0, 0, 0, 99, 97, 1, 0, 0, 0, 99, 98, 1, 0, 0, 0,
+		100, 13, 1, 0, 0, 0, 101, 105, 3, 18, 9, 0, 102, 104, 3, 16, 8, 0, 103,
+		102, 1, 0, 0, 0, 104, 107, 1, 0, 0, 0, 105, 103, 1, 0, 0, 0, 105, 106,
+		1, 0, 0, 0, 106, 15, 1, 0, 0, 0, 107, 105, 1, 0, 0, 0, 108, 109, 5, 22,
+		0, 0, 109, 114, 3, 18, 9, 0, 110, 114, 3, 36, 18, 0, 111, 112, 5, 22, 0,
+		0, 112, 114, 3, 54, 27, 0, 113, 108, 1, 0, 0, 0, 113, 110, 1, 0, 0, 0,
+		113, 111, 1, 0, 0, 0, 114, 17, 1, 0, 0, 0, 115, 116, 5, 16, 0, 0, 116,
+		19, 1, 0, 0, 0, 117, 118, 5, 9, 0, 0, 118, 21, 1, 0, 0, 0, 119, 120, 5,
+		4, 0, 0, 120, 23, 1, 0, 0, 0, 121, 122, 5, 17, 0, 0, 122, 123, 3, 4, 2,
+		0, 123, 124, 5, 18, 0, 0, 124, 25, 1, 0, 0, 0, 125, 126, 3, 28, 14, 0,
+		126, 127, 3, 12, 6, 0, 127, 27, 1, 0, 0, 0, 128, 129, 5, 19, 0, 0, 129,
+		29, 1, 0, 0, 0, 130, 131, 3, 32, 16, 0, 131, 132, 5, 17, 0, 0, 132, 137,
+		3, 34, 17, 0, 133, 134, 5, 1, 0, 0, 134, 136, 3, 34, 17, 0, 135, 133, 1,
+		0, 0, 0, 136, 139, 1, 0, 0, 0, 137, 135, 1, 0, 0, 0, 137, 138, 1, 0, 0,
+		0, 138, 140, 1, 0, 0, 0, 139, 137, 1, 0, 0, 0, 140, 141, 5, 18, 0, 0, 141,
+		31, 1, 0, 0, 0, 142, 143, 5, 16, 0, 0, 143, 33, 1, 0, 0, 0, 144, 150, 1,
+		0, 0, 0, 145, 150, 3, 14, 7, 0, 146, 150, 3, 22, 11, 0, 147, 150, 3, 20,
+		10, 0, 148, 150, 3, 48, 24, 0, 149, 144, 1, 0, 0, 0, 149, 145, 1, 0, 0,
+		0, 149, 146, 1, 0, 0, 0, 149, 147, 1, 0, 0, 0, 149, 148, 1, 0, 0, 0, 150,
+		35, 1, 0, 0, 0, 151, 152, 5, 2, 0, 0, 152, 153, 3, 38, 19, 0, 153, 154,
+		5, 3, 0, 0, 154, 37, 1, 0, 0, 0, 155, 156, 3, 12, 6, 0, 156, 39, 1, 0,
+		0, 0, 157, 158, 5, 21, 0, 0, 158, 41, 1, 0, 0, 0, 159, 160, 5, 20, 0, 0,
+		160, 43, 1, 0, 0, 0, 161, 162, 7, 0, 0, 0, 162, 45, 1, 0, 0, 0, 163, 164,
+		7, 1, 0, 0, 164, 47, 1, 0, 0, 0, 165, 166, 5, 5, 0, 0, 166, 49, 1, 0, 0,
+		0, 167, 168, 5, 7, 0, 0, 168, 51, 1, 0, 0, 0, 169, 170, 5, 8, 0, 0, 170,
+		53, 1, 0, 0, 0, 171, 172, 5, 23, 0, 0, 172, 55, 1, 0, 0, 0, 9, 69, 78,
+		85, 91, 99, 105, 113, 137, 149,
+	}
+	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)
+	}
+}
+
+// ActionParserInit initializes any static state used to implement ActionParser. By default the
+// static state used to implement the parser is lazily initialized during the first call to
+// NewActionParser(). You can call this function if you wish to initialize the static state ahead
+// of time.
+func ActionParserInit() {
+	staticData := &actionParserStaticData
+	staticData.once.Do(actionParserInit)
+}
+
+// NewActionParser produces a new parser instance for the optional input antlr.TokenStream.
+func NewActionParser(input antlr.TokenStream) *ActionParser {
+	ActionParserInit()
+	this := new(ActionParser)
+	this.BaseParser = antlr.NewBaseParser(input)
+	staticData := &actionParserStaticData
+	this.Interpreter = antlr.NewParserATNSimulator(this, staticData.atn, staticData.decisionToDFA, staticData.predictionContextCache)
+	this.RuleNames = staticData.ruleNames
+	this.LiteralNames = staticData.literalNames
+	this.SymbolicNames = staticData.symbolicNames
+	this.GrammarFileName = "Action.g4"
+
+	return this
+}
+
+// ActionParser tokens.
+const (
+	ActionParserEOF                      = antlr.TokenEOF
+	ActionParserT__0                     = 1
+	ActionParserT__1                     = 2
+	ActionParserT__2                     = 3
+	ActionParserSTRING_INSIDE_EXPRESSION = 4
+	ActionParserBOOLEAN                  = 5
+	ActionParserNULL                     = 6
+	ActionParserEXP_START                = 7
+	ActionParserEXP_END                  = 8
+	ActionParserNUMBER                   = 9
+	ActionParserEQ                       = 10
+	ActionParserNEQ                      = 11
+	ActionParserGT                       = 12
+	ActionParserLT                       = 13
+	ActionParserGTE                      = 14
+	ActionParserLTE                      = 15
+	ActionParserID                       = 16
+	ActionParserLPAREN                   = 17
+	ActionParserRPAREN                   = 18
+	ActionParserNOT                      = 19
+	ActionParserOR                       = 20
+	ActionParserAND                      = 21
+	ActionParserDOT                      = 22
+	ActionParserSTAR                     = 23
+	ActionParserWS                       = 24
+)
+
+// ActionParser rules.
+const (
+	ActionParserRULE_start                 = 0
+	ActionParserRULE_expression            = 1
+	ActionParserRULE_orExpression          = 2
+	ActionParserRULE_andExpression         = 3
+	ActionParserRULE_comparisonExpression  = 4
+	ActionParserRULE_equalityExpression    = 5
+	ActionParserRULE_primaryExpression     = 6
+	ActionParserRULE_variableContext       = 7
+	ActionParserRULE_variablePath          = 8
+	ActionParserRULE_variableIdentifier    = 9
+	ActionParserRULE_numberExpression      = 10
+	ActionParserRULE_stringExpression      = 11
+	ActionParserRULE_termExpression        = 12
+	ActionParserRULE_notExpression         = 13
+	ActionParserRULE_notOperator           = 14
+	ActionParserRULE_functionCall          = 15
+	ActionParserRULE_functionName          = 16
+	ActionParserRULE_functionCallArguments = 17
+	ActionParserRULE_array                 = 18
+	ActionParserRULE_arrayIndex            = 19
+	ActionParserRULE_andOperator           = 20
+	ActionParserRULE_orOperator            = 21
+	ActionParserRULE_comparisonOperator    = 22
+	ActionParserRULE_equalityOperator      = 23
+	ActionParserRULE_booleanExpression     = 24
+	ActionParserRULE_expressionStart       = 25
+	ActionParserRULE_expressionEnd         = 26
+	ActionParserRULE_filterExpression      = 27
+)
+
+// IStartContext is an interface to support dynamic dispatch.
+type IStartContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	Expression() IExpressionContext
+	EOF() antlr.TerminalNode
+
+	// IsStartContext differentiates from other interfaces.
+	IsStartContext()
+}
+
+type StartContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyStartContext() *StartContext {
+	var p = new(StartContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_start
+	return p
+}
+
+func (*StartContext) IsStartContext() {}
+
+func NewStartContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *StartContext {
+	var p = new(StartContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_start
+
+	return p
+}
+
+func (s *StartContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *StartContext) Expression() IExpressionContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IExpressionContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IExpressionContext)
+}
+
+func (s *StartContext) EOF() antlr.TerminalNode {
+	return s.GetToken(ActionParserEOF, 0)
+}
+
+func (s *StartContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *StartContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *StartContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterStart(s)
+	}
+}
+
+func (s *StartContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitStart(s)
+	}
+}
+
+func (p *ActionParser) Start() (localctx IStartContext) {
+	this := p
+	_ = this
+
+	localctx = NewStartContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 0, ActionParserRULE_start)
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(56)
+		p.Expression()
+	}
+	{
+		p.SetState(57)
+		p.Match(ActionParserEOF)
+	}
+
+	return localctx
+}
+
+// IExpressionContext is an interface to support dynamic dispatch.
+type IExpressionContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	ExpressionStart() IExpressionStartContext
+	OrExpression() IOrExpressionContext
+	ExpressionEnd() IExpressionEndContext
+
+	// IsExpressionContext differentiates from other interfaces.
+	IsExpressionContext()
+}
+
+type ExpressionContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyExpressionContext() *ExpressionContext {
+	var p = new(ExpressionContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_expression
+	return p
+}
+
+func (*ExpressionContext) IsExpressionContext() {}
+
+func NewExpressionContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ExpressionContext {
+	var p = new(ExpressionContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_expression
+
+	return p
+}
+
+func (s *ExpressionContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *ExpressionContext) ExpressionStart() IExpressionStartContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IExpressionStartContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IExpressionStartContext)
+}
+
+func (s *ExpressionContext) OrExpression() IOrExpressionContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IOrExpressionContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IOrExpressionContext)
+}
+
+func (s *ExpressionContext) ExpressionEnd() IExpressionEndContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IExpressionEndContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IExpressionEndContext)
+}
+
+func (s *ExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *ExpressionContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *ExpressionContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterExpression(s)
+	}
+}
+
+func (s *ExpressionContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitExpression(s)
+	}
+}
+
+func (p *ActionParser) Expression() (localctx IExpressionContext) {
+	this := p
+	_ = this
+
+	localctx = NewExpressionContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 2, ActionParserRULE_expression)
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(59)
+		p.ExpressionStart()
+	}
+	{
+		p.SetState(60)
+		p.OrExpression()
+	}
+	{
+		p.SetState(61)
+		p.ExpressionEnd()
+	}
+
+	return localctx
+}
+
+// IOrExpressionContext is an interface to support dynamic dispatch.
+type IOrExpressionContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	AllAndExpression() []IAndExpressionContext
+	AndExpression(i int) IAndExpressionContext
+	AllOrOperator() []IOrOperatorContext
+	OrOperator(i int) IOrOperatorContext
+
+	// IsOrExpressionContext differentiates from other interfaces.
+	IsOrExpressionContext()
+}
+
+type OrExpressionContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyOrExpressionContext() *OrExpressionContext {
+	var p = new(OrExpressionContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_orExpression
+	return p
+}
+
+func (*OrExpressionContext) IsOrExpressionContext() {}
+
+func NewOrExpressionContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *OrExpressionContext {
+	var p = new(OrExpressionContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_orExpression
+
+	return p
+}
+
+func (s *OrExpressionContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *OrExpressionContext) AllAndExpression() []IAndExpressionContext {
+	children := s.GetChildren()
+	len := 0
+	for _, ctx := range children {
+		if _, ok := ctx.(IAndExpressionContext); ok {
+			len++
+		}
+	}
+
+	tst := make([]IAndExpressionContext, len)
+	i := 0
+	for _, ctx := range children {
+		if t, ok := ctx.(IAndExpressionContext); ok {
+			tst[i] = t.(IAndExpressionContext)
+			i++
+		}
+	}
+
+	return tst
+}
+
+func (s *OrExpressionContext) AndExpression(i int) IAndExpressionContext {
+	var t antlr.RuleContext
+	j := 0
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IAndExpressionContext); ok {
+			if j == i {
+				t = ctx.(antlr.RuleContext)
+				break
+			}
+			j++
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IAndExpressionContext)
+}
+
+func (s *OrExpressionContext) AllOrOperator() []IOrOperatorContext {
+	children := s.GetChildren()
+	len := 0
+	for _, ctx := range children {
+		if _, ok := ctx.(IOrOperatorContext); ok {
+			len++
+		}
+	}
+
+	tst := make([]IOrOperatorContext, len)
+	i := 0
+	for _, ctx := range children {
+		if t, ok := ctx.(IOrOperatorContext); ok {
+			tst[i] = t.(IOrOperatorContext)
+			i++
+		}
+	}
+
+	return tst
+}
+
+func (s *OrExpressionContext) OrOperator(i int) IOrOperatorContext {
+	var t antlr.RuleContext
+	j := 0
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IOrOperatorContext); ok {
+			if j == i {
+				t = ctx.(antlr.RuleContext)
+				break
+			}
+			j++
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IOrOperatorContext)
+}
+
+func (s *OrExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *OrExpressionContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *OrExpressionContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterOrExpression(s)
+	}
+}
+
+func (s *OrExpressionContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitOrExpression(s)
+	}
+}
+
+func (p *ActionParser) OrExpression() (localctx IOrExpressionContext) {
+	this := p
+	_ = this
+
+	localctx = NewOrExpressionContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 4, ActionParserRULE_orExpression)
+	var _la int
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(63)
+		p.AndExpression()
+	}
+	p.SetState(69)
+	p.GetErrorHandler().Sync(p)
+	_la = p.GetTokenStream().LA(1)
+
+	for _la == ActionParserOR {
+		{
+			p.SetState(64)
+			p.OrOperator()
+		}
+		{
+			p.SetState(65)
+			p.AndExpression()
+		}
+
+		p.SetState(71)
+		p.GetErrorHandler().Sync(p)
+		_la = p.GetTokenStream().LA(1)
+	}
+
+	return localctx
+}
+
+// IAndExpressionContext is an interface to support dynamic dispatch.
+type IAndExpressionContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	AllComparisonExpression() []IComparisonExpressionContext
+	ComparisonExpression(i int) IComparisonExpressionContext
+	AllAndOperator() []IAndOperatorContext
+	AndOperator(i int) IAndOperatorContext
+
+	// IsAndExpressionContext differentiates from other interfaces.
+	IsAndExpressionContext()
+}
+
+type AndExpressionContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyAndExpressionContext() *AndExpressionContext {
+	var p = new(AndExpressionContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_andExpression
+	return p
+}
+
+func (*AndExpressionContext) IsAndExpressionContext() {}
+
+func NewAndExpressionContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *AndExpressionContext {
+	var p = new(AndExpressionContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_andExpression
+
+	return p
+}
+
+func (s *AndExpressionContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *AndExpressionContext) AllComparisonExpression() []IComparisonExpressionContext {
+	children := s.GetChildren()
+	len := 0
+	for _, ctx := range children {
+		if _, ok := ctx.(IComparisonExpressionContext); ok {
+			len++
+		}
+	}
+
+	tst := make([]IComparisonExpressionContext, len)
+	i := 0
+	for _, ctx := range children {
+		if t, ok := ctx.(IComparisonExpressionContext); ok {
+			tst[i] = t.(IComparisonExpressionContext)
+			i++
+		}
+	}
+
+	return tst
+}
+
+func (s *AndExpressionContext) ComparisonExpression(i int) IComparisonExpressionContext {
+	var t antlr.RuleContext
+	j := 0
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IComparisonExpressionContext); ok {
+			if j == i {
+				t = ctx.(antlr.RuleContext)
+				break
+			}
+			j++
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IComparisonExpressionContext)
+}
+
+func (s *AndExpressionContext) AllAndOperator() []IAndOperatorContext {
+	children := s.GetChildren()
+	len := 0
+	for _, ctx := range children {
+		if _, ok := ctx.(IAndOperatorContext); ok {
+			len++
+		}
+	}
+
+	tst := make([]IAndOperatorContext, len)
+	i := 0
+	for _, ctx := range children {
+		if t, ok := ctx.(IAndOperatorContext); ok {
+			tst[i] = t.(IAndOperatorContext)
+			i++
+		}
+	}
+
+	return tst
+}
+
+func (s *AndExpressionContext) AndOperator(i int) IAndOperatorContext {
+	var t antlr.RuleContext
+	j := 0
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IAndOperatorContext); ok {
+			if j == i {
+				t = ctx.(antlr.RuleContext)
+				break
+			}
+			j++
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IAndOperatorContext)
+}
+
+func (s *AndExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *AndExpressionContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *AndExpressionContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterAndExpression(s)
+	}
+}
+
+func (s *AndExpressionContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitAndExpression(s)
+	}
+}
+
+func (p *ActionParser) AndExpression() (localctx IAndExpressionContext) {
+	this := p
+	_ = this
+
+	localctx = NewAndExpressionContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 6, ActionParserRULE_andExpression)
+	var _la int
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(72)
+		p.ComparisonExpression()
+	}
+	p.SetState(78)
+	p.GetErrorHandler().Sync(p)
+	_la = p.GetTokenStream().LA(1)
+
+	for _la == ActionParserAND {
+		{
+			p.SetState(73)
+			p.AndOperator()
+		}
+		{
+			p.SetState(74)
+			p.ComparisonExpression()
+		}
+
+		p.SetState(80)
+		p.GetErrorHandler().Sync(p)
+		_la = p.GetTokenStream().LA(1)
+	}
+
+	return localctx
+}
+
+// IComparisonExpressionContext is an interface to support dynamic dispatch.
+type IComparisonExpressionContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	AllEqualityExpression() []IEqualityExpressionContext
+	EqualityExpression(i int) IEqualityExpressionContext
+	ComparisonOperator() IComparisonOperatorContext
+
+	// IsComparisonExpressionContext differentiates from other interfaces.
+	IsComparisonExpressionContext()
+}
+
+type ComparisonExpressionContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyComparisonExpressionContext() *ComparisonExpressionContext {
+	var p = new(ComparisonExpressionContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_comparisonExpression
+	return p
+}
+
+func (*ComparisonExpressionContext) IsComparisonExpressionContext() {}
+
+func NewComparisonExpressionContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ComparisonExpressionContext {
+	var p = new(ComparisonExpressionContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_comparisonExpression
+
+	return p
+}
+
+func (s *ComparisonExpressionContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *ComparisonExpressionContext) AllEqualityExpression() []IEqualityExpressionContext {
+	children := s.GetChildren()
+	len := 0
+	for _, ctx := range children {
+		if _, ok := ctx.(IEqualityExpressionContext); ok {
+			len++
+		}
+	}
+
+	tst := make([]IEqualityExpressionContext, len)
+	i := 0
+	for _, ctx := range children {
+		if t, ok := ctx.(IEqualityExpressionContext); ok {
+			tst[i] = t.(IEqualityExpressionContext)
+			i++
+		}
+	}
+
+	return tst
+}
+
+func (s *ComparisonExpressionContext) EqualityExpression(i int) IEqualityExpressionContext {
+	var t antlr.RuleContext
+	j := 0
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IEqualityExpressionContext); ok {
+			if j == i {
+				t = ctx.(antlr.RuleContext)
+				break
+			}
+			j++
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IEqualityExpressionContext)
+}
+
+func (s *ComparisonExpressionContext) ComparisonOperator() IComparisonOperatorContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IComparisonOperatorContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IComparisonOperatorContext)
+}
+
+func (s *ComparisonExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *ComparisonExpressionContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *ComparisonExpressionContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterComparisonExpression(s)
+	}
+}
+
+func (s *ComparisonExpressionContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitComparisonExpression(s)
+	}
+}
+
+func (p *ActionParser) ComparisonExpression() (localctx IComparisonExpressionContext) {
+	this := p
+	_ = this
+
+	localctx = NewComparisonExpressionContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 8, ActionParserRULE_comparisonExpression)
+	var _la int
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(81)
+		p.EqualityExpression()
+	}
+	p.SetState(85)
+	p.GetErrorHandler().Sync(p)
+	_la = p.GetTokenStream().LA(1)
+
+	if (int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&61440) != 0 {
+		{
+			p.SetState(82)
+			p.ComparisonOperator()
+		}
+		{
+			p.SetState(83)
+			p.EqualityExpression()
+		}
+
+	}
+
+	return localctx
+}
+
+// IEqualityExpressionContext is an interface to support dynamic dispatch.
+type IEqualityExpressionContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	AllPrimaryExpression() []IPrimaryExpressionContext
+	PrimaryExpression(i int) IPrimaryExpressionContext
+	EqualityOperator() IEqualityOperatorContext
+
+	// IsEqualityExpressionContext differentiates from other interfaces.
+	IsEqualityExpressionContext()
+}
+
+type EqualityExpressionContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyEqualityExpressionContext() *EqualityExpressionContext {
+	var p = new(EqualityExpressionContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_equalityExpression
+	return p
+}
+
+func (*EqualityExpressionContext) IsEqualityExpressionContext() {}
+
+func NewEqualityExpressionContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *EqualityExpressionContext {
+	var p = new(EqualityExpressionContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_equalityExpression
+
+	return p
+}
+
+func (s *EqualityExpressionContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *EqualityExpressionContext) AllPrimaryExpression() []IPrimaryExpressionContext {
+	children := s.GetChildren()
+	len := 0
+	for _, ctx := range children {
+		if _, ok := ctx.(IPrimaryExpressionContext); ok {
+			len++
+		}
+	}
+
+	tst := make([]IPrimaryExpressionContext, len)
+	i := 0
+	for _, ctx := range children {
+		if t, ok := ctx.(IPrimaryExpressionContext); ok {
+			tst[i] = t.(IPrimaryExpressionContext)
+			i++
+		}
+	}
+
+	return tst
+}
+
+func (s *EqualityExpressionContext) PrimaryExpression(i int) IPrimaryExpressionContext {
+	var t antlr.RuleContext
+	j := 0
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IPrimaryExpressionContext); ok {
+			if j == i {
+				t = ctx.(antlr.RuleContext)
+				break
+			}
+			j++
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IPrimaryExpressionContext)
+}
+
+func (s *EqualityExpressionContext) EqualityOperator() IEqualityOperatorContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IEqualityOperatorContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IEqualityOperatorContext)
+}
+
+func (s *EqualityExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *EqualityExpressionContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *EqualityExpressionContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterEqualityExpression(s)
+	}
+}
+
+func (s *EqualityExpressionContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitEqualityExpression(s)
+	}
+}
+
+func (p *ActionParser) EqualityExpression() (localctx IEqualityExpressionContext) {
+	this := p
+	_ = this
+
+	localctx = NewEqualityExpressionContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 10, ActionParserRULE_equalityExpression)
+	var _la int
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(87)
+		p.PrimaryExpression()
+	}
+	p.SetState(91)
+	p.GetErrorHandler().Sync(p)
+	_la = p.GetTokenStream().LA(1)
+
+	if _la == ActionParserEQ || _la == ActionParserNEQ {
+		{
+			p.SetState(88)
+			p.EqualityOperator()
+		}
+		{
+			p.SetState(89)
+			p.PrimaryExpression()
+		}
+
+	}
+
+	return localctx
+}
+
+// IPrimaryExpressionContext is an interface to support dynamic dispatch.
+type IPrimaryExpressionContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	VariableContext() IVariableContextContext
+	NumberExpression() INumberExpressionContext
+	FunctionCall() IFunctionCallContext
+	StringExpression() IStringExpressionContext
+	TermExpression() ITermExpressionContext
+	NotExpression() INotExpressionContext
+
+	// IsPrimaryExpressionContext differentiates from other interfaces.
+	IsPrimaryExpressionContext()
+}
+
+type PrimaryExpressionContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyPrimaryExpressionContext() *PrimaryExpressionContext {
+	var p = new(PrimaryExpressionContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_primaryExpression
+	return p
+}
+
+func (*PrimaryExpressionContext) IsPrimaryExpressionContext() {}
+
+func NewPrimaryExpressionContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *PrimaryExpressionContext {
+	var p = new(PrimaryExpressionContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_primaryExpression
+
+	return p
+}
+
+func (s *PrimaryExpressionContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *PrimaryExpressionContext) VariableContext() IVariableContextContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IVariableContextContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IVariableContextContext)
+}
+
+func (s *PrimaryExpressionContext) NumberExpression() INumberExpressionContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(INumberExpressionContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(INumberExpressionContext)
+}
+
+func (s *PrimaryExpressionContext) FunctionCall() IFunctionCallContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IFunctionCallContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IFunctionCallContext)
+}
+
+func (s *PrimaryExpressionContext) StringExpression() IStringExpressionContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IStringExpressionContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IStringExpressionContext)
+}
+
+func (s *PrimaryExpressionContext) TermExpression() ITermExpressionContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(ITermExpressionContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(ITermExpressionContext)
+}
+
+func (s *PrimaryExpressionContext) NotExpression() INotExpressionContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(INotExpressionContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(INotExpressionContext)
+}
+
+func (s *PrimaryExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *PrimaryExpressionContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *PrimaryExpressionContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterPrimaryExpression(s)
+	}
+}
+
+func (s *PrimaryExpressionContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitPrimaryExpression(s)
+	}
+}
+
+func (p *ActionParser) PrimaryExpression() (localctx IPrimaryExpressionContext) {
+	this := p
+	_ = this
+
+	localctx = NewPrimaryExpressionContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 12, ActionParserRULE_primaryExpression)
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.SetState(99)
+	p.GetErrorHandler().Sync(p)
+	switch p.GetInterpreter().AdaptivePredict(p.GetTokenStream(), 4, p.GetParserRuleContext()) {
+	case 1:
+		p.EnterOuterAlt(localctx, 1)
+		{
+			p.SetState(93)
+			p.VariableContext()
+		}
+
+	case 2:
+		p.EnterOuterAlt(localctx, 2)
+		{
+			p.SetState(94)
+			p.NumberExpression()
+		}
+
+	case 3:
+		p.EnterOuterAlt(localctx, 3)
+		{
+			p.SetState(95)
+			p.FunctionCall()
+		}
+
+	case 4:
+		p.EnterOuterAlt(localctx, 4)
+		{
+			p.SetState(96)
+			p.StringExpression()
+		}
+
+	case 5:
+		p.EnterOuterAlt(localctx, 5)
+		{
+			p.SetState(97)
+			p.TermExpression()
+		}
+
+	case 6:
+		p.EnterOuterAlt(localctx, 6)
+		{
+			p.SetState(98)
+			p.NotExpression()
+		}
+
+	}
+
+	return localctx
+}
+
+// IVariableContextContext is an interface to support dynamic dispatch.
+type IVariableContextContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	VariableIdentifier() IVariableIdentifierContext
+	AllVariablePath() []IVariablePathContext
+	VariablePath(i int) IVariablePathContext
+
+	// IsVariableContextContext differentiates from other interfaces.
+	IsVariableContextContext()
+}
+
+type VariableContextContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyVariableContextContext() *VariableContextContext {
+	var p = new(VariableContextContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_variableContext
+	return p
+}
+
+func (*VariableContextContext) IsVariableContextContext() {}
+
+func NewVariableContextContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *VariableContextContext {
+	var p = new(VariableContextContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_variableContext
+
+	return p
+}
+
+func (s *VariableContextContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *VariableContextContext) VariableIdentifier() IVariableIdentifierContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IVariableIdentifierContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IVariableIdentifierContext)
+}
+
+func (s *VariableContextContext) AllVariablePath() []IVariablePathContext {
+	children := s.GetChildren()
+	len := 0
+	for _, ctx := range children {
+		if _, ok := ctx.(IVariablePathContext); ok {
+			len++
+		}
+	}
+
+	tst := make([]IVariablePathContext, len)
+	i := 0
+	for _, ctx := range children {
+		if t, ok := ctx.(IVariablePathContext); ok {
+			tst[i] = t.(IVariablePathContext)
+			i++
+		}
+	}
+
+	return tst
+}
+
+func (s *VariableContextContext) VariablePath(i int) IVariablePathContext {
+	var t antlr.RuleContext
+	j := 0
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IVariablePathContext); ok {
+			if j == i {
+				t = ctx.(antlr.RuleContext)
+				break
+			}
+			j++
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IVariablePathContext)
+}
+
+func (s *VariableContextContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *VariableContextContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *VariableContextContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterVariableContext(s)
+	}
+}
+
+func (s *VariableContextContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitVariableContext(s)
+	}
+}
+
+func (p *ActionParser) VariableContext() (localctx IVariableContextContext) {
+	this := p
+	_ = this
+
+	localctx = NewVariableContextContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 14, ActionParserRULE_variableContext)
+	var _la int
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(101)
+		p.VariableIdentifier()
+	}
+	p.SetState(105)
+	p.GetErrorHandler().Sync(p)
+	_la = p.GetTokenStream().LA(1)
+
+	for _la == ActionParserT__1 || _la == ActionParserDOT {
+		{
+			p.SetState(102)
+			p.VariablePath()
+		}
+
+		p.SetState(107)
+		p.GetErrorHandler().Sync(p)
+		_la = p.GetTokenStream().LA(1)
+	}
+
+	return localctx
+}
+
+// IVariablePathContext is an interface to support dynamic dispatch.
+type IVariablePathContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	DOT() antlr.TerminalNode
+	VariableIdentifier() IVariableIdentifierContext
+	Array() IArrayContext
+	FilterExpression() IFilterExpressionContext
+
+	// IsVariablePathContext differentiates from other interfaces.
+	IsVariablePathContext()
+}
+
+type VariablePathContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyVariablePathContext() *VariablePathContext {
+	var p = new(VariablePathContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_variablePath
+	return p
+}
+
+func (*VariablePathContext) IsVariablePathContext() {}
+
+func NewVariablePathContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *VariablePathContext {
+	var p = new(VariablePathContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_variablePath
+
+	return p
+}
+
+func (s *VariablePathContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *VariablePathContext) DOT() antlr.TerminalNode {
+	return s.GetToken(ActionParserDOT, 0)
+}
+
+func (s *VariablePathContext) VariableIdentifier() IVariableIdentifierContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IVariableIdentifierContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IVariableIdentifierContext)
+}
+
+func (s *VariablePathContext) Array() IArrayContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IArrayContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IArrayContext)
+}
+
+func (s *VariablePathContext) FilterExpression() IFilterExpressionContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IFilterExpressionContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IFilterExpressionContext)
+}
+
+func (s *VariablePathContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *VariablePathContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *VariablePathContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterVariablePath(s)
+	}
+}
+
+func (s *VariablePathContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitVariablePath(s)
+	}
+}
+
+func (p *ActionParser) VariablePath() (localctx IVariablePathContext) {
+	this := p
+	_ = this
+
+	localctx = NewVariablePathContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 16, ActionParserRULE_variablePath)
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	p.SetState(113)
+	p.GetErrorHandler().Sync(p)
+	switch p.GetInterpreter().AdaptivePredict(p.GetTokenStream(), 6, p.GetParserRuleContext()) {
+	case 1:
+		{
+			p.SetState(108)
+			p.Match(ActionParserDOT)
+		}
+		{
+			p.SetState(109)
+			p.VariableIdentifier()
+		}
+
+	case 2:
+		{
+			p.SetState(110)
+			p.Array()
+		}
+
+	case 3:
+		{
+			p.SetState(111)
+			p.Match(ActionParserDOT)
+		}
+		{
+			p.SetState(112)
+			p.FilterExpression()
+		}
+
+	}
+
+	return localctx
+}
+
+// IVariableIdentifierContext is an interface to support dynamic dispatch.
+type IVariableIdentifierContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	ID() antlr.TerminalNode
+
+	// IsVariableIdentifierContext differentiates from other interfaces.
+	IsVariableIdentifierContext()
+}
+
+type VariableIdentifierContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyVariableIdentifierContext() *VariableIdentifierContext {
+	var p = new(VariableIdentifierContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_variableIdentifier
+	return p
+}
+
+func (*VariableIdentifierContext) IsVariableIdentifierContext() {}
+
+func NewVariableIdentifierContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *VariableIdentifierContext {
+	var p = new(VariableIdentifierContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_variableIdentifier
+
+	return p
+}
+
+func (s *VariableIdentifierContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *VariableIdentifierContext) ID() antlr.TerminalNode {
+	return s.GetToken(ActionParserID, 0)
+}
+
+func (s *VariableIdentifierContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *VariableIdentifierContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *VariableIdentifierContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterVariableIdentifier(s)
+	}
+}
+
+func (s *VariableIdentifierContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitVariableIdentifier(s)
+	}
+}
+
+func (p *ActionParser) VariableIdentifier() (localctx IVariableIdentifierContext) {
+	this := p
+	_ = this
+
+	localctx = NewVariableIdentifierContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 18, ActionParserRULE_variableIdentifier)
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(115)
+		p.Match(ActionParserID)
+	}
+
+	return localctx
+}
+
+// INumberExpressionContext is an interface to support dynamic dispatch.
+type INumberExpressionContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	NUMBER() antlr.TerminalNode
+
+	// IsNumberExpressionContext differentiates from other interfaces.
+	IsNumberExpressionContext()
+}
+
+type NumberExpressionContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyNumberExpressionContext() *NumberExpressionContext {
+	var p = new(NumberExpressionContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_numberExpression
+	return p
+}
+
+func (*NumberExpressionContext) IsNumberExpressionContext() {}
+
+func NewNumberExpressionContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *NumberExpressionContext {
+	var p = new(NumberExpressionContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_numberExpression
+
+	return p
+}
+
+func (s *NumberExpressionContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *NumberExpressionContext) NUMBER() antlr.TerminalNode {
+	return s.GetToken(ActionParserNUMBER, 0)
+}
+
+func (s *NumberExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *NumberExpressionContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *NumberExpressionContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterNumberExpression(s)
+	}
+}
+
+func (s *NumberExpressionContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitNumberExpression(s)
+	}
+}
+
+func (p *ActionParser) NumberExpression() (localctx INumberExpressionContext) {
+	this := p
+	_ = this
+
+	localctx = NewNumberExpressionContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 20, ActionParserRULE_numberExpression)
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(117)
+		p.Match(ActionParserNUMBER)
+	}
+
+	return localctx
+}
+
+// IStringExpressionContext is an interface to support dynamic dispatch.
+type IStringExpressionContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	STRING_INSIDE_EXPRESSION() antlr.TerminalNode
+
+	// IsStringExpressionContext differentiates from other interfaces.
+	IsStringExpressionContext()
+}
+
+type StringExpressionContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyStringExpressionContext() *StringExpressionContext {
+	var p = new(StringExpressionContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_stringExpression
+	return p
+}
+
+func (*StringExpressionContext) IsStringExpressionContext() {}
+
+func NewStringExpressionContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *StringExpressionContext {
+	var p = new(StringExpressionContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_stringExpression
+
+	return p
+}
+
+func (s *StringExpressionContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *StringExpressionContext) STRING_INSIDE_EXPRESSION() antlr.TerminalNode {
+	return s.GetToken(ActionParserSTRING_INSIDE_EXPRESSION, 0)
+}
+
+func (s *StringExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *StringExpressionContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *StringExpressionContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterStringExpression(s)
+	}
+}
+
+func (s *StringExpressionContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitStringExpression(s)
+	}
+}
+
+func (p *ActionParser) StringExpression() (localctx IStringExpressionContext) {
+	this := p
+	_ = this
+
+	localctx = NewStringExpressionContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 22, ActionParserRULE_stringExpression)
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(119)
+		p.Match(ActionParserSTRING_INSIDE_EXPRESSION)
+	}
+
+	return localctx
+}
+
+// ITermExpressionContext is an interface to support dynamic dispatch.
+type ITermExpressionContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	LPAREN() antlr.TerminalNode
+	OrExpression() IOrExpressionContext
+	RPAREN() antlr.TerminalNode
+
+	// IsTermExpressionContext differentiates from other interfaces.
+	IsTermExpressionContext()
+}
+
+type TermExpressionContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyTermExpressionContext() *TermExpressionContext {
+	var p = new(TermExpressionContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_termExpression
+	return p
+}
+
+func (*TermExpressionContext) IsTermExpressionContext() {}
+
+func NewTermExpressionContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *TermExpressionContext {
+	var p = new(TermExpressionContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_termExpression
+
+	return p
+}
+
+func (s *TermExpressionContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *TermExpressionContext) LPAREN() antlr.TerminalNode {
+	return s.GetToken(ActionParserLPAREN, 0)
+}
+
+func (s *TermExpressionContext) OrExpression() IOrExpressionContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IOrExpressionContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IOrExpressionContext)
+}
+
+func (s *TermExpressionContext) RPAREN() antlr.TerminalNode {
+	return s.GetToken(ActionParserRPAREN, 0)
+}
+
+func (s *TermExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *TermExpressionContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *TermExpressionContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterTermExpression(s)
+	}
+}
+
+func (s *TermExpressionContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitTermExpression(s)
+	}
+}
+
+func (p *ActionParser) TermExpression() (localctx ITermExpressionContext) {
+	this := p
+	_ = this
+
+	localctx = NewTermExpressionContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 24, ActionParserRULE_termExpression)
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(121)
+		p.Match(ActionParserLPAREN)
+	}
+	{
+		p.SetState(122)
+		p.OrExpression()
+	}
+	{
+		p.SetState(123)
+		p.Match(ActionParserRPAREN)
+	}
+
+	return localctx
+}
+
+// INotExpressionContext is an interface to support dynamic dispatch.
+type INotExpressionContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	NotOperator() INotOperatorContext
+	PrimaryExpression() IPrimaryExpressionContext
+
+	// IsNotExpressionContext differentiates from other interfaces.
+	IsNotExpressionContext()
+}
+
+type NotExpressionContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyNotExpressionContext() *NotExpressionContext {
+	var p = new(NotExpressionContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_notExpression
+	return p
+}
+
+func (*NotExpressionContext) IsNotExpressionContext() {}
+
+func NewNotExpressionContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *NotExpressionContext {
+	var p = new(NotExpressionContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_notExpression
+
+	return p
+}
+
+func (s *NotExpressionContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *NotExpressionContext) NotOperator() INotOperatorContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(INotOperatorContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(INotOperatorContext)
+}
+
+func (s *NotExpressionContext) PrimaryExpression() IPrimaryExpressionContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IPrimaryExpressionContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IPrimaryExpressionContext)
+}
+
+func (s *NotExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *NotExpressionContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *NotExpressionContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterNotExpression(s)
+	}
+}
+
+func (s *NotExpressionContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitNotExpression(s)
+	}
+}
+
+func (p *ActionParser) NotExpression() (localctx INotExpressionContext) {
+	this := p
+	_ = this
+
+	localctx = NewNotExpressionContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 26, ActionParserRULE_notExpression)
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(125)
+		p.NotOperator()
+	}
+	{
+		p.SetState(126)
+		p.PrimaryExpression()
+	}
+
+	return localctx
+}
+
+// INotOperatorContext is an interface to support dynamic dispatch.
+type INotOperatorContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	NOT() antlr.TerminalNode
+
+	// IsNotOperatorContext differentiates from other interfaces.
+	IsNotOperatorContext()
+}
+
+type NotOperatorContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyNotOperatorContext() *NotOperatorContext {
+	var p = new(NotOperatorContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_notOperator
+	return p
+}
+
+func (*NotOperatorContext) IsNotOperatorContext() {}
+
+func NewNotOperatorContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *NotOperatorContext {
+	var p = new(NotOperatorContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_notOperator
+
+	return p
+}
+
+func (s *NotOperatorContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *NotOperatorContext) NOT() antlr.TerminalNode {
+	return s.GetToken(ActionParserNOT, 0)
+}
+
+func (s *NotOperatorContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *NotOperatorContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *NotOperatorContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterNotOperator(s)
+	}
+}
+
+func (s *NotOperatorContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitNotOperator(s)
+	}
+}
+
+func (p *ActionParser) NotOperator() (localctx INotOperatorContext) {
+	this := p
+	_ = this
+
+	localctx = NewNotOperatorContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 28, ActionParserRULE_notOperator)
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(128)
+		p.Match(ActionParserNOT)
+	}
+
+	return localctx
+}
+
+// IFunctionCallContext is an interface to support dynamic dispatch.
+type IFunctionCallContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	FunctionName() IFunctionNameContext
+	LPAREN() antlr.TerminalNode
+	AllFunctionCallArguments() []IFunctionCallArgumentsContext
+	FunctionCallArguments(i int) IFunctionCallArgumentsContext
+	RPAREN() antlr.TerminalNode
+
+	// IsFunctionCallContext differentiates from other interfaces.
+	IsFunctionCallContext()
+}
+
+type FunctionCallContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyFunctionCallContext() *FunctionCallContext {
+	var p = new(FunctionCallContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_functionCall
+	return p
+}
+
+func (*FunctionCallContext) IsFunctionCallContext() {}
+
+func NewFunctionCallContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *FunctionCallContext {
+	var p = new(FunctionCallContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_functionCall
+
+	return p
+}
+
+func (s *FunctionCallContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *FunctionCallContext) FunctionName() IFunctionNameContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IFunctionNameContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IFunctionNameContext)
+}
+
+func (s *FunctionCallContext) LPAREN() antlr.TerminalNode {
+	return s.GetToken(ActionParserLPAREN, 0)
+}
+
+func (s *FunctionCallContext) AllFunctionCallArguments() []IFunctionCallArgumentsContext {
+	children := s.GetChildren()
+	len := 0
+	for _, ctx := range children {
+		if _, ok := ctx.(IFunctionCallArgumentsContext); ok {
+			len++
+		}
+	}
+
+	tst := make([]IFunctionCallArgumentsContext, len)
+	i := 0
+	for _, ctx := range children {
+		if t, ok := ctx.(IFunctionCallArgumentsContext); ok {
+			tst[i] = t.(IFunctionCallArgumentsContext)
+			i++
+		}
+	}
+
+	return tst
+}
+
+func (s *FunctionCallContext) FunctionCallArguments(i int) IFunctionCallArgumentsContext {
+	var t antlr.RuleContext
+	j := 0
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IFunctionCallArgumentsContext); ok {
+			if j == i {
+				t = ctx.(antlr.RuleContext)
+				break
+			}
+			j++
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IFunctionCallArgumentsContext)
+}
+
+func (s *FunctionCallContext) RPAREN() antlr.TerminalNode {
+	return s.GetToken(ActionParserRPAREN, 0)
+}
+
+func (s *FunctionCallContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *FunctionCallContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *FunctionCallContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterFunctionCall(s)
+	}
+}
+
+func (s *FunctionCallContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitFunctionCall(s)
+	}
+}
+
+func (p *ActionParser) FunctionCall() (localctx IFunctionCallContext) {
+	this := p
+	_ = this
+
+	localctx = NewFunctionCallContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 30, ActionParserRULE_functionCall)
+	var _la int
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(130)
+		p.FunctionName()
+	}
+	{
+		p.SetState(131)
+		p.Match(ActionParserLPAREN)
+	}
+	{
+		p.SetState(132)
+		p.FunctionCallArguments()
+	}
+	p.SetState(137)
+	p.GetErrorHandler().Sync(p)
+	_la = p.GetTokenStream().LA(1)
+
+	for _la == ActionParserT__0 {
+		{
+			p.SetState(133)
+			p.Match(ActionParserT__0)
+		}
+		{
+			p.SetState(134)
+			p.FunctionCallArguments()
+		}
+
+		p.SetState(139)
+		p.GetErrorHandler().Sync(p)
+		_la = p.GetTokenStream().LA(1)
+	}
+	{
+		p.SetState(140)
+		p.Match(ActionParserRPAREN)
+	}
+
+	return localctx
+}
+
+// IFunctionNameContext is an interface to support dynamic dispatch.
+type IFunctionNameContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	ID() antlr.TerminalNode
+
+	// IsFunctionNameContext differentiates from other interfaces.
+	IsFunctionNameContext()
+}
+
+type FunctionNameContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyFunctionNameContext() *FunctionNameContext {
+	var p = new(FunctionNameContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_functionName
+	return p
+}
+
+func (*FunctionNameContext) IsFunctionNameContext() {}
+
+func NewFunctionNameContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *FunctionNameContext {
+	var p = new(FunctionNameContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_functionName
+
+	return p
+}
+
+func (s *FunctionNameContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *FunctionNameContext) ID() antlr.TerminalNode {
+	return s.GetToken(ActionParserID, 0)
+}
+
+func (s *FunctionNameContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *FunctionNameContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *FunctionNameContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterFunctionName(s)
+	}
+}
+
+func (s *FunctionNameContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitFunctionName(s)
+	}
+}
+
+func (p *ActionParser) FunctionName() (localctx IFunctionNameContext) {
+	this := p
+	_ = this
+
+	localctx = NewFunctionNameContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 32, ActionParserRULE_functionName)
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(142)
+		p.Match(ActionParserID)
+	}
+
+	return localctx
+}
+
+// IFunctionCallArgumentsContext is an interface to support dynamic dispatch.
+type IFunctionCallArgumentsContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	VariableContext() IVariableContextContext
+	StringExpression() IStringExpressionContext
+	NumberExpression() INumberExpressionContext
+	BooleanExpression() IBooleanExpressionContext
+
+	// IsFunctionCallArgumentsContext differentiates from other interfaces.
+	IsFunctionCallArgumentsContext()
+}
+
+type FunctionCallArgumentsContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyFunctionCallArgumentsContext() *FunctionCallArgumentsContext {
+	var p = new(FunctionCallArgumentsContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_functionCallArguments
+	return p
+}
+
+func (*FunctionCallArgumentsContext) IsFunctionCallArgumentsContext() {}
+
+func NewFunctionCallArgumentsContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *FunctionCallArgumentsContext {
+	var p = new(FunctionCallArgumentsContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_functionCallArguments
+
+	return p
+}
+
+func (s *FunctionCallArgumentsContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *FunctionCallArgumentsContext) VariableContext() IVariableContextContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IVariableContextContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IVariableContextContext)
+}
+
+func (s *FunctionCallArgumentsContext) StringExpression() IStringExpressionContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IStringExpressionContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IStringExpressionContext)
+}
+
+func (s *FunctionCallArgumentsContext) NumberExpression() INumberExpressionContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(INumberExpressionContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(INumberExpressionContext)
+}
+
+func (s *FunctionCallArgumentsContext) BooleanExpression() IBooleanExpressionContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IBooleanExpressionContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IBooleanExpressionContext)
+}
+
+func (s *FunctionCallArgumentsContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *FunctionCallArgumentsContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *FunctionCallArgumentsContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterFunctionCallArguments(s)
+	}
+}
+
+func (s *FunctionCallArgumentsContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitFunctionCallArguments(s)
+	}
+}
+
+func (p *ActionParser) FunctionCallArguments() (localctx IFunctionCallArgumentsContext) {
+	this := p
+	_ = this
+
+	localctx = NewFunctionCallArgumentsContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 34, ActionParserRULE_functionCallArguments)
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.SetState(149)
+	p.GetErrorHandler().Sync(p)
+
+	switch p.GetTokenStream().LA(1) {
+	case ActionParserT__0, ActionParserRPAREN:
+		p.EnterOuterAlt(localctx, 1)
+
+	case ActionParserID:
+		p.EnterOuterAlt(localctx, 2)
+		{
+			p.SetState(145)
+			p.VariableContext()
+		}
+
+	case ActionParserSTRING_INSIDE_EXPRESSION:
+		p.EnterOuterAlt(localctx, 3)
+		{
+			p.SetState(146)
+			p.StringExpression()
+		}
+
+	case ActionParserNUMBER:
+		p.EnterOuterAlt(localctx, 4)
+		{
+			p.SetState(147)
+			p.NumberExpression()
+		}
+
+	case ActionParserBOOLEAN:
+		p.EnterOuterAlt(localctx, 5)
+		{
+			p.SetState(148)
+			p.BooleanExpression()
+		}
+
+	default:
+		panic(antlr.NewNoViableAltException(p, nil, nil, nil, nil, nil))
+	}
+
+	return localctx
+}
+
+// IArrayContext is an interface to support dynamic dispatch.
+type IArrayContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	ArrayIndex() IArrayIndexContext
+
+	// IsArrayContext differentiates from other interfaces.
+	IsArrayContext()
+}
+
+type ArrayContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyArrayContext() *ArrayContext {
+	var p = new(ArrayContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_array
+	return p
+}
+
+func (*ArrayContext) IsArrayContext() {}
+
+func NewArrayContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ArrayContext {
+	var p = new(ArrayContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_array
+
+	return p
+}
+
+func (s *ArrayContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *ArrayContext) ArrayIndex() IArrayIndexContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IArrayIndexContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IArrayIndexContext)
+}
+
+func (s *ArrayContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *ArrayContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *ArrayContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterArray(s)
+	}
+}
+
+func (s *ArrayContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitArray(s)
+	}
+}
+
+func (p *ActionParser) Array() (localctx IArrayContext) {
+	this := p
+	_ = this
+
+	localctx = NewArrayContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 36, ActionParserRULE_array)
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(151)
+		p.Match(ActionParserT__1)
+	}
+	{
+		p.SetState(152)
+		p.ArrayIndex()
+	}
+	{
+		p.SetState(153)
+		p.Match(ActionParserT__2)
+	}
+
+	return localctx
+}
+
+// IArrayIndexContext is an interface to support dynamic dispatch.
+type IArrayIndexContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	PrimaryExpression() IPrimaryExpressionContext
+
+	// IsArrayIndexContext differentiates from other interfaces.
+	IsArrayIndexContext()
+}
+
+type ArrayIndexContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyArrayIndexContext() *ArrayIndexContext {
+	var p = new(ArrayIndexContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_arrayIndex
+	return p
+}
+
+func (*ArrayIndexContext) IsArrayIndexContext() {}
+
+func NewArrayIndexContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ArrayIndexContext {
+	var p = new(ArrayIndexContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_arrayIndex
+
+	return p
+}
+
+func (s *ArrayIndexContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *ArrayIndexContext) PrimaryExpression() IPrimaryExpressionContext {
+	var t antlr.RuleContext
+	for _, ctx := range s.GetChildren() {
+		if _, ok := ctx.(IPrimaryExpressionContext); ok {
+			t = ctx.(antlr.RuleContext)
+			break
+		}
+	}
+
+	if t == nil {
+		return nil
+	}
+
+	return t.(IPrimaryExpressionContext)
+}
+
+func (s *ArrayIndexContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *ArrayIndexContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *ArrayIndexContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterArrayIndex(s)
+	}
+}
+
+func (s *ArrayIndexContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitArrayIndex(s)
+	}
+}
+
+func (p *ActionParser) ArrayIndex() (localctx IArrayIndexContext) {
+	this := p
+	_ = this
+
+	localctx = NewArrayIndexContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 38, ActionParserRULE_arrayIndex)
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(155)
+		p.PrimaryExpression()
+	}
+
+	return localctx
+}
+
+// IAndOperatorContext is an interface to support dynamic dispatch.
+type IAndOperatorContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	AND() antlr.TerminalNode
+
+	// IsAndOperatorContext differentiates from other interfaces.
+	IsAndOperatorContext()
+}
+
+type AndOperatorContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyAndOperatorContext() *AndOperatorContext {
+	var p = new(AndOperatorContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_andOperator
+	return p
+}
+
+func (*AndOperatorContext) IsAndOperatorContext() {}
+
+func NewAndOperatorContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *AndOperatorContext {
+	var p = new(AndOperatorContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_andOperator
+
+	return p
+}
+
+func (s *AndOperatorContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *AndOperatorContext) AND() antlr.TerminalNode {
+	return s.GetToken(ActionParserAND, 0)
+}
+
+func (s *AndOperatorContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *AndOperatorContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *AndOperatorContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterAndOperator(s)
+	}
+}
+
+func (s *AndOperatorContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitAndOperator(s)
+	}
+}
+
+func (p *ActionParser) AndOperator() (localctx IAndOperatorContext) {
+	this := p
+	_ = this
+
+	localctx = NewAndOperatorContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 40, ActionParserRULE_andOperator)
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(157)
+		p.Match(ActionParserAND)
+	}
+
+	return localctx
+}
+
+// IOrOperatorContext is an interface to support dynamic dispatch.
+type IOrOperatorContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	OR() antlr.TerminalNode
+
+	// IsOrOperatorContext differentiates from other interfaces.
+	IsOrOperatorContext()
+}
+
+type OrOperatorContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyOrOperatorContext() *OrOperatorContext {
+	var p = new(OrOperatorContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_orOperator
+	return p
+}
+
+func (*OrOperatorContext) IsOrOperatorContext() {}
+
+func NewOrOperatorContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *OrOperatorContext {
+	var p = new(OrOperatorContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_orOperator
+
+	return p
+}
+
+func (s *OrOperatorContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *OrOperatorContext) OR() antlr.TerminalNode {
+	return s.GetToken(ActionParserOR, 0)
+}
+
+func (s *OrOperatorContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *OrOperatorContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *OrOperatorContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterOrOperator(s)
+	}
+}
+
+func (s *OrOperatorContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitOrOperator(s)
+	}
+}
+
+func (p *ActionParser) OrOperator() (localctx IOrOperatorContext) {
+	this := p
+	_ = this
+
+	localctx = NewOrOperatorContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 42, ActionParserRULE_orOperator)
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(159)
+		p.Match(ActionParserOR)
+	}
+
+	return localctx
+}
+
+// IComparisonOperatorContext is an interface to support dynamic dispatch.
+type IComparisonOperatorContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	GT() antlr.TerminalNode
+	LT() antlr.TerminalNode
+	GTE() antlr.TerminalNode
+	LTE() antlr.TerminalNode
+
+	// IsComparisonOperatorContext differentiates from other interfaces.
+	IsComparisonOperatorContext()
+}
+
+type ComparisonOperatorContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyComparisonOperatorContext() *ComparisonOperatorContext {
+	var p = new(ComparisonOperatorContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_comparisonOperator
+	return p
+}
+
+func (*ComparisonOperatorContext) IsComparisonOperatorContext() {}
+
+func NewComparisonOperatorContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ComparisonOperatorContext {
+	var p = new(ComparisonOperatorContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_comparisonOperator
+
+	return p
+}
+
+func (s *ComparisonOperatorContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *ComparisonOperatorContext) GT() antlr.TerminalNode {
+	return s.GetToken(ActionParserGT, 0)
+}
+
+func (s *ComparisonOperatorContext) LT() antlr.TerminalNode {
+	return s.GetToken(ActionParserLT, 0)
+}
+
+func (s *ComparisonOperatorContext) GTE() antlr.TerminalNode {
+	return s.GetToken(ActionParserGTE, 0)
+}
+
+func (s *ComparisonOperatorContext) LTE() antlr.TerminalNode {
+	return s.GetToken(ActionParserLTE, 0)
+}
+
+func (s *ComparisonOperatorContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *ComparisonOperatorContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *ComparisonOperatorContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterComparisonOperator(s)
+	}
+}
+
+func (s *ComparisonOperatorContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitComparisonOperator(s)
+	}
+}
+
+func (p *ActionParser) ComparisonOperator() (localctx IComparisonOperatorContext) {
+	this := p
+	_ = this
+
+	localctx = NewComparisonOperatorContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 44, ActionParserRULE_comparisonOperator)
+	var _la int
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(161)
+		_la = p.GetTokenStream().LA(1)
+
+		if !((int64(_la) & ^0x3f) == 0 && ((int64(1)<<_la)&61440) != 0) {
+			p.GetErrorHandler().RecoverInline(p)
+		} else {
+			p.GetErrorHandler().ReportMatch(p)
+			p.Consume()
+		}
+	}
+
+	return localctx
+}
+
+// IEqualityOperatorContext is an interface to support dynamic dispatch.
+type IEqualityOperatorContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	EQ() antlr.TerminalNode
+	NEQ() antlr.TerminalNode
+
+	// IsEqualityOperatorContext differentiates from other interfaces.
+	IsEqualityOperatorContext()
+}
+
+type EqualityOperatorContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyEqualityOperatorContext() *EqualityOperatorContext {
+	var p = new(EqualityOperatorContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_equalityOperator
+	return p
+}
+
+func (*EqualityOperatorContext) IsEqualityOperatorContext() {}
+
+func NewEqualityOperatorContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *EqualityOperatorContext {
+	var p = new(EqualityOperatorContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_equalityOperator
+
+	return p
+}
+
+func (s *EqualityOperatorContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *EqualityOperatorContext) EQ() antlr.TerminalNode {
+	return s.GetToken(ActionParserEQ, 0)
+}
+
+func (s *EqualityOperatorContext) NEQ() antlr.TerminalNode {
+	return s.GetToken(ActionParserNEQ, 0)
+}
+
+func (s *EqualityOperatorContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *EqualityOperatorContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *EqualityOperatorContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterEqualityOperator(s)
+	}
+}
+
+func (s *EqualityOperatorContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitEqualityOperator(s)
+	}
+}
+
+func (p *ActionParser) EqualityOperator() (localctx IEqualityOperatorContext) {
+	this := p
+	_ = this
+
+	localctx = NewEqualityOperatorContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 46, ActionParserRULE_equalityOperator)
+	var _la int
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(163)
+		_la = p.GetTokenStream().LA(1)
+
+		if !(_la == ActionParserEQ || _la == ActionParserNEQ) {
+			p.GetErrorHandler().RecoverInline(p)
+		} else {
+			p.GetErrorHandler().ReportMatch(p)
+			p.Consume()
+		}
+	}
+
+	return localctx
+}
+
+// IBooleanExpressionContext is an interface to support dynamic dispatch.
+type IBooleanExpressionContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	BOOLEAN() antlr.TerminalNode
+
+	// IsBooleanExpressionContext differentiates from other interfaces.
+	IsBooleanExpressionContext()
+}
+
+type BooleanExpressionContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyBooleanExpressionContext() *BooleanExpressionContext {
+	var p = new(BooleanExpressionContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_booleanExpression
+	return p
+}
+
+func (*BooleanExpressionContext) IsBooleanExpressionContext() {}
+
+func NewBooleanExpressionContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *BooleanExpressionContext {
+	var p = new(BooleanExpressionContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_booleanExpression
+
+	return p
+}
+
+func (s *BooleanExpressionContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *BooleanExpressionContext) BOOLEAN() antlr.TerminalNode {
+	return s.GetToken(ActionParserBOOLEAN, 0)
+}
+
+func (s *BooleanExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *BooleanExpressionContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *BooleanExpressionContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterBooleanExpression(s)
+	}
+}
+
+func (s *BooleanExpressionContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitBooleanExpression(s)
+	}
+}
+
+func (p *ActionParser) BooleanExpression() (localctx IBooleanExpressionContext) {
+	this := p
+	_ = this
+
+	localctx = NewBooleanExpressionContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 48, ActionParserRULE_booleanExpression)
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(165)
+		p.Match(ActionParserBOOLEAN)
+	}
+
+	return localctx
+}
+
+// IExpressionStartContext is an interface to support dynamic dispatch.
+type IExpressionStartContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	EXP_START() antlr.TerminalNode
+
+	// IsExpressionStartContext differentiates from other interfaces.
+	IsExpressionStartContext()
+}
+
+type ExpressionStartContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyExpressionStartContext() *ExpressionStartContext {
+	var p = new(ExpressionStartContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_expressionStart
+	return p
+}
+
+func (*ExpressionStartContext) IsExpressionStartContext() {}
+
+func NewExpressionStartContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ExpressionStartContext {
+	var p = new(ExpressionStartContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_expressionStart
+
+	return p
+}
+
+func (s *ExpressionStartContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *ExpressionStartContext) EXP_START() antlr.TerminalNode {
+	return s.GetToken(ActionParserEXP_START, 0)
+}
+
+func (s *ExpressionStartContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *ExpressionStartContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *ExpressionStartContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterExpressionStart(s)
+	}
+}
+
+func (s *ExpressionStartContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitExpressionStart(s)
+	}
+}
+
+func (p *ActionParser) ExpressionStart() (localctx IExpressionStartContext) {
+	this := p
+	_ = this
+
+	localctx = NewExpressionStartContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 50, ActionParserRULE_expressionStart)
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(167)
+		p.Match(ActionParserEXP_START)
+	}
+
+	return localctx
+}
+
+// IExpressionEndContext is an interface to support dynamic dispatch.
+type IExpressionEndContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	EXP_END() antlr.TerminalNode
+
+	// IsExpressionEndContext differentiates from other interfaces.
+	IsExpressionEndContext()
+}
+
+type ExpressionEndContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyExpressionEndContext() *ExpressionEndContext {
+	var p = new(ExpressionEndContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_expressionEnd
+	return p
+}
+
+func (*ExpressionEndContext) IsExpressionEndContext() {}
+
+func NewExpressionEndContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *ExpressionEndContext {
+	var p = new(ExpressionEndContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_expressionEnd
+
+	return p
+}
+
+func (s *ExpressionEndContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *ExpressionEndContext) EXP_END() antlr.TerminalNode {
+	return s.GetToken(ActionParserEXP_END, 0)
+}
+
+func (s *ExpressionEndContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *ExpressionEndContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *ExpressionEndContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterExpressionEnd(s)
+	}
+}
+
+func (s *ExpressionEndContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitExpressionEnd(s)
+	}
+}
+
+func (p *ActionParser) ExpressionEnd() (localctx IExpressionEndContext) {
+	this := p
+	_ = this
+
+	localctx = NewExpressionEndContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 52, ActionParserRULE_expressionEnd)
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(169)
+		p.Match(ActionParserEXP_END)
+	}
+
+	return localctx
+}
+
+// IFilterExpressionContext is an interface to support dynamic dispatch.
+type IFilterExpressionContext interface {
+	antlr.ParserRuleContext
+
+	// GetParser returns the parser.
+	GetParser() antlr.Parser
+
+	// Getter signatures
+	STAR() antlr.TerminalNode
+
+	// IsFilterExpressionContext differentiates from other interfaces.
+	IsFilterExpressionContext()
+}
+
+type FilterExpressionContext struct {
+	*antlr.BaseParserRuleContext
+	parser antlr.Parser
+}
+
+func NewEmptyFilterExpressionContext() *FilterExpressionContext {
+	var p = new(FilterExpressionContext)
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(nil, -1)
+	p.RuleIndex = ActionParserRULE_filterExpression
+	return p
+}
+
+func (*FilterExpressionContext) IsFilterExpressionContext() {}
+
+func NewFilterExpressionContext(parser antlr.Parser, parent antlr.ParserRuleContext, invokingState int) *FilterExpressionContext {
+	var p = new(FilterExpressionContext)
+
+	p.BaseParserRuleContext = antlr.NewBaseParserRuleContext(parent, invokingState)
+
+	p.parser = parser
+	p.RuleIndex = ActionParserRULE_filterExpression
+
+	return p
+}
+
+func (s *FilterExpressionContext) GetParser() antlr.Parser { return s.parser }
+
+func (s *FilterExpressionContext) STAR() antlr.TerminalNode {
+	return s.GetToken(ActionParserSTAR, 0)
+}
+
+func (s *FilterExpressionContext) GetRuleContext() antlr.RuleContext {
+	return s
+}
+
+func (s *FilterExpressionContext) ToStringTree(ruleNames []string, recog antlr.Recognizer) string {
+	return antlr.TreesStringTree(s, ruleNames, recog)
+}
+
+func (s *FilterExpressionContext) EnterRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.EnterFilterExpression(s)
+	}
+}
+
+func (s *FilterExpressionContext) ExitRule(listener antlr.ParseTreeListener) {
+	if listenerT, ok := listener.(ActionListener); ok {
+		listenerT.ExitFilterExpression(s)
+	}
+}
+
+func (p *ActionParser) FilterExpression() (localctx IFilterExpressionContext) {
+	this := p
+	_ = this
+
+	localctx = NewFilterExpressionContext(p, p.GetParserRuleContext(), p.GetState())
+	p.EnterRule(localctx, 54, ActionParserRULE_filterExpression)
+
+	defer func() {
+		p.ExitRule()
+	}()
+
+	defer func() {
+		if err := recover(); err != nil {
+			if v, ok := err.(antlr.RecognitionException); ok {
+				localctx.SetException(v)
+				p.GetErrorHandler().ReportError(p, v)
+				p.GetErrorHandler().Recover(p, v)
+			} else {
+				panic(err)
+			}
+		}
+	}()
+
+	p.EnterOuterAlt(localctx, 1)
+	{
+		p.SetState(171)
+		p.Match(ActionParserSTAR)
+	}
+
+	return localctx
+}
diff --git a/sdk/worker.go b/sdk/worker.go
index c458cea68..859c2e082 100644
--- a/sdk/worker.go
+++ b/sdk/worker.go
@@ -15,6 +15,7 @@ type Worker struct {
 	Name         string    `json:"name" cli:"name,key" db:"name"`
 	LastBeat     time.Time `json:"lastbeat" cli:"lastbeat" db:"last_beat"`
 	ModelID      *int64    `json:"model_id" cli:"-"  db:"model_id"`
+	ModelName    *string   `json:"model_name" cli:"-"  db:"model_name"`
 	JobRunID     *int64    `json:"job_run_id" cli:"-"  db:"job_run_id"`
 	Status       string    `json:"status" cli:"status" db:"status"` // Waiting, Building, Disabled, Unknown
 	HatcheryID   *int64    `json:"hatchery_id,omitempty" cli:"-" db:"hatchery_id"`
diff --git a/tools/smtpmock/Dockerfile b/tools/smtpmock/Dockerfile
index cb8477a26..1f7fed117 100644
--- a/tools/smtpmock/Dockerfile
+++ b/tools/smtpmock/Dockerfile
@@ -1,5 +1,5 @@
 # build stage
-FROM golang:1.17 AS builder
+FROM golang:1.20 AS builder
 ADD . $GOPATH/src/github.com/ovh/cds/tools/smtpmock
 WORKDIR $GOPATH/src/github.com/ovh/cds/tools/smtpmock
 RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o /tmp/smtpmocksrv github.com/ovh/cds/tools/smtpmock/server
diff --git a/tools/smtpmock/go.mod b/tools/smtpmock/go.mod
index 396004c7d..ef7350644 100644
--- a/tools/smtpmock/go.mod
+++ b/tools/smtpmock/go.mod
@@ -1,13 +1,13 @@
 module github.com/ovh/cds/tools/smtpmock
 
-go 1.19
+go 1.20
 
 require (
 	github.com/fsamin/smtp v0.0.0-20190904085838-54a7c428d4f9
 	github.com/gofrs/uuid v4.2.0+incompatible
 	github.com/golang-jwt/jwt v3.2.2+incompatible
 	github.com/labstack/echo/v4 v4.9.0
-	github.com/pkg/errors v0.8.1
+	github.com/pkg/errors v0.9.1
 	github.com/urfave/cli/v2 v2.3.0
 )
 
diff --git a/tools/smtpmock/go.sum b/tools/smtpmock/go.sum
index d5f1dab84..ba68cb5da 100644
--- a/tools/smtpmock/go.sum
+++ b/tools/smtpmock/go.sum
@@ -20,8 +20,8 @@ github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZb
 github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
 github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
 github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
-github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=


Test patch:
diff --git a/sdk/action_parser_test.go b/sdk/action_parser_test.go
new file mode 100644
index 000000000..9a72de6d3
--- /dev/null
+++ b/sdk/action_parser_test.go
@@ -0,0 +1,788 @@
+package sdk
+
+import (
+	"context"
+	"github.com/rockbears/log"
+	"github.com/stretchr/testify/require"
+	"testing"
+)
+
+func TestParserValidate(t *testing.T) {
+	log.Factory = log.NewTestingWrapper(t)
+	log.UnregisterField(log.FieldCaller, log.FieldSourceFile, log.FieldSourceLine)
+
+	input := `echo ${{ git.author }}
+if [[ "${{ ${{git.branch }}" == 'master' ]]; then
+  echo 'Master branch'
+fi;
+echo "Job in progress: ${{ job.status == 'in progress' }}"
+echo "First job: ${{ job.buildNumber < 2 }}"
+echo "Commit message contains foobar: ${{ contains(git.message, 'foobar' }}"
+`
+
+	ap := NewActionParser(nil, nil)
+	err := ap.Validate(context.TODO(), input)
+	t.Logf("%v", err)
+	require.Error(t, err)
+}
+
+func TestParserVariables(t *testing.T) {
+	log.Factory = log.NewTestingWrapper(t)
+	log.UnregisterField(log.FieldCaller, log.FieldSourceFile, log.FieldSourceLine)
+
+	tests := []struct {
+		name         string
+		context      map[string]interface{}
+		input        string
+		result       string
+		containError string
+	}{
+		{
+			name:   "simple string variable",
+			input:  "${{ git.branch }}",
+			result: "master",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"branch": "master",
+				},
+			},
+		},
+		{
+			name:   "deeper string variable",
+			input:  "${{ job.step.line.column }}",
+			result: "foo",
+			context: map[string]interface{}{
+				"job": map[string]interface{}{
+					"step": map[string]interface{}{
+						"line": map[string]interface{}{
+							"column": "foo",
+						},
+					},
+				},
+			},
+		},
+		{
+			name:   "simple number variable",
+			input:  "${{ git.commitID }}",
+			result: "1",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"commitID": 1,
+				},
+			},
+		},
+		{
+			name:   "simple boolean variable",
+			input:  "${{ git.closed }}",
+			result: "true",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"closed": true,
+				},
+			},
+		},
+		{
+			name:   "simple array variable",
+			input:  "${{ git.changes[0].hash }}",
+			result: "123456",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"changes": []map[string]interface{}{
+						{
+							"hash": "123456",
+						},
+					},
+				},
+			},
+		},
+		{
+			name:         "unknown context",
+			input:        "${{ job.id }}",
+			result:       "",
+			containError: "unknown context job",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"branch": "master",
+				},
+			},
+		},
+		{
+			name:         "unknown variable in context must return empty",
+			input:        "${{ job.id }}",
+			result:       "",
+			containError: "",
+			context: map[string]interface{}{
+				"job": map[string]interface{}{},
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			ap := NewActionParser(tt.context, DefaultFuncs)
+			result, err := ap.parse(context.TODO(), tt.input)
+			if tt.containError != "" {
+				require.Error(t, err)
+				require.Contains(t, err.Error(), tt.containError)
+			} else {
+				require.NoError(t, err)
+				require.Equal(t, tt.result, result)
+			}
+		})
+	}
+}
+
+func TestParserOperations(t *testing.T) {
+	log.Factory = log.NewTestingWrapper(t)
+	log.UnregisterField(log.FieldCaller, log.FieldSourceFile, log.FieldSourceLine)
+	tests := []struct {
+		name         string
+		context      map[string]interface{}
+		input        string
+		result       string
+		containError string
+	}{
+		{
+			name:   "simple equality",
+			input:  "${{ git.branch == 'master' }}",
+			result: "true",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"branch": "master",
+				},
+			},
+		},
+		{
+			name:   "full variable equality",
+			input:  "${{ git.branch == git.ref.branch }}",
+			result: "true",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"branch": "master",
+					"ref": map[string]interface{}{
+						"branch": "master",
+					},
+				},
+			},
+		},
+		{
+			name:   "simple variable not equals",
+			input:  "${{ git.branch != 'dev/myfeature' }}",
+			result: "true",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"branch": "master",
+				},
+			},
+		},
+		{
+			name:   "full variable not equals",
+			input:  "${{ git.branch != git.ref.branch }}",
+			result: "true",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"branch": "master",
+					"ref": map[string]interface{}{
+						"branch": "dev/myfeature",
+					},
+				},
+			},
+		},
+		{
+			name:   "simple greater than",
+			input:  "${{ job.num > 1 }}",
+			result: "true",
+			context: map[string]interface{}{
+				"job": map[string]interface{}{
+					"num": 5,
+				},
+			},
+		},
+		{
+			name:   "simple greater than",
+			input:  "${{ job.num > job.num2 }}",
+			result: "true",
+			context: map[string]interface{}{
+				"job": map[string]interface{}{
+					"num":  5,
+					"num2": 1,
+				},
+			},
+		},
+		{
+			name:   "simple greater or equal than",
+			input:  "${{ job.num >= job.num2 }}",
+			result: "true",
+			context: map[string]interface{}{
+				"job": map[string]interface{}{
+					"num":  5,
+					"num2": 5,
+				},
+			},
+		},
+		{
+			name:   "simple less than",
+			input:  "${{ job.num < job.num2 }}",
+			result: "true",
+			context: map[string]interface{}{
+				"job": map[string]interface{}{
+					"num":  1,
+					"num2": 5,
+				},
+			},
+		},
+		{
+			name:   "simple less or equal than",
+			input:  "${{ job.num <= job.num2 }}",
+			result: "true",
+			context: map[string]interface{}{
+				"job": map[string]interface{}{
+					"num":  1,
+					"num2": 1,
+				},
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			ap := NewActionParser(tt.context, DefaultFuncs)
+			result, err := ap.parse(context.TODO(), tt.input)
+			if tt.containError != "" {
+				require.Error(t, err)
+				require.Contains(t, err.Error(), tt.containError)
+			} else {
+				require.NoError(t, err)
+				require.Equal(t, tt.result, result)
+			}
+		})
+	}
+}
+
+func TestParserBooleanExpression(t *testing.T) {
+	log.Factory = log.NewTestingWrapper(t)
+	log.UnregisterField(log.FieldCaller, log.FieldSourceFile, log.FieldSourceLine)
+	tests := []struct {
+		name         string
+		context      map[string]interface{}
+		input        string
+		result       string
+		containError string
+	}{
+		{
+			name:   "or expression true",
+			input:  "${{ git.branch == 'master' || git.ref == 'testing' }}",
+			result: "true",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"branch": "master",
+					"ref":    "prod",
+				},
+			},
+		},
+		{
+			name:   "or expression false",
+			input:  "${{ git.branch == 'master' || git.ref == 'testing' }}",
+			result: "false",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"branch": "dev",
+					"ref":    "prod",
+				},
+			},
+		},
+
+		{
+			name:   "and expression true",
+			input:  "${{ git.branch == 'master' && git.ref == 'testing' }}",
+			result: "true",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"branch": "master",
+					"ref":    "testing",
+				},
+			},
+		},
+		{
+			name:   "and expression false",
+			input:  "${{ git.branch == 'master' && git.ref == 'testing' }}",
+			result: "false",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"branch": "master",
+					"ref":    "prod",
+				},
+			},
+		},
+		{
+			name:   "term expression or - true",
+			input:  "${{ (git.branch == 'master' && git.ref == 'testing') || (git.branch == 'master' && git.ref == 'prod') }}",
+			result: "true",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"branch": "master",
+					"ref":    "prod",
+				},
+			},
+		},
+		{
+			name:   "term expression or - false",
+			input:  "${{ (git.branch == 'dev' && git.id < 2) || (git.branch == 'dev' && git.ref == 'prod') }}",
+			result: "false",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"branch": "master",
+					"ref":    "testing",
+					"id":     3,
+				},
+			},
+		},
+		{
+			name:   "term expression and - true",
+			input:  "${{ (git.branch == 'master' || git.branch == 'testing') && (git.ref == 'testing' || git.id == 2) }}",
+			result: "true",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"branch": "master",
+					"ref":    "prod",
+					"id":     2,
+				},
+			},
+		},
+		{
+			name:   "term expression and - false",
+			input:  "${{ (git.branch == 'master' || git.branch == 'testing') && (git.ref == 'testing' || git.id == 2) }}",
+			result: "false",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"branch": "master",
+					"ref":    "prod",
+					"id":     3,
+				},
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			ap := NewActionParser(tt.context, DefaultFuncs)
+			result, err := ap.parse(context.TODO(), tt.input)
+			if tt.containError != "" {
+				require.Error(t, err)
+				require.Contains(t, err.Error(), tt.containError)
+			} else {
+				require.NoError(t, err)
+				require.Equal(t, tt.result, result)
+			}
+		})
+	}
+}
+
+func TestInterpolate(t *testing.T) {
+	log.Factory = log.NewTestingWrapper(t)
+	log.UnregisterField(log.FieldCaller, log.FieldSourceFile, log.FieldSourceLine)
+	input := `echo ${{ git.author }}
+if [[ "${{ git.branch }}" == 'master' ]]; then
+  echo 'Master branch'
+fi;
+echo "Job in progress: ${{ job.status == 'in progress' }}"
+echo "First job: ${{ job.buildNumber < 2 }}"
+echo "Commit message contains foobar: ${{ contains(git.message, 'foobar') }}"
+`
+
+	result := `echo Steven
+if [[ "master" == 'master' ]]; then
+  echo 'Master branch'
+fi;
+echo "Job in progress: true"
+echo "First job: true"
+echo "Commit message contains foobar: true"
+`
+
+	actionCtx := map[string]interface{}{
+		"git": map[string]interface{}{
+			"branch":  "master",
+			"author":  "Steven",
+			"message": "Message foobar, feature ascode",
+		},
+		"job": map[string]interface{}{
+			"status":      "in progress",
+			"buildNumber": 1,
+		},
+	}
+
+	app := NewActionParser(actionCtx, DefaultFuncs)
+	interpolatedInput, err := app.Interpolate(context.TODO(), input)
+	require.NoError(t, err)
+	require.Equal(t, result, interpolatedInput)
+
+}
+
+func TestInterpolateError(t *testing.T) {
+	log.Factory = log.NewTestingWrapper(t)
+	log.UnregisterField(log.FieldCaller, log.FieldSourceFile, log.FieldSourceLine)
+	input := `if [[ "${{ ${{git.branch }}" == 'master' ]]; then`
+
+	app := NewActionParser(nil, DefaultFuncs)
+	_, err := app.Interpolate(context.TODO(), input)
+	t.Logf("%v", err)
+	require.Error(t, err)
+}
+
+func TestParserFuncContains(t *testing.T) {
+	log.Factory = log.NewTestingWrapper(t)
+	log.UnregisterField(log.FieldCaller, log.FieldSourceFile, log.FieldSourceLine)
+	tests := []struct {
+		name         string
+		context      map[string]interface{}
+		input        string
+		result       string
+		containError string
+	}{
+		{
+			name:   "simple contains function",
+			input:  "${{ contains(git.branch, 'ast') }}",
+			result: "true",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"branch": "master",
+				},
+			},
+		},
+		{
+			name:   "simple contains function with string filter",
+			input:  "${{ contains(git.changes.*.message, 'foo') }}",
+			result: "true",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"changes": []map[string]interface{}{
+						{
+							"message": "not me",
+						},
+						{
+							"message": "foo",
+						},
+						{
+							"message": "not me",
+						},
+					},
+				},
+			},
+		},
+		{
+			name:   "simple contains function with int filter",
+			input:  "${{ contains(git.changes.*.message, '2') }}",
+			result: "true",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"changes": []map[string]interface{}{
+						{
+							"message": 0,
+						},
+						{
+							"message": 2,
+						},
+						{
+							"message": 1,
+						},
+					},
+				},
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			ap := NewActionParser(tt.context, DefaultFuncs)
+			result, err := ap.parse(context.TODO(), tt.input)
+			if tt.containError != "" {
+				require.Error(t, err)
+				require.Contains(t, err.Error(), tt.containError)
+			} else {
+				require.NoError(t, err)
+				require.Equal(t, tt.result, result)
+			}
+		})
+	}
+}
+
+func TestParserFuncStartsWith(t *testing.T) {
+	log.Factory = log.NewTestingWrapper(t)
+	log.UnregisterField(log.FieldCaller, log.FieldSourceFile, log.FieldSourceLine)
+	tests := []struct {
+		name         string
+		context      map[string]interface{}
+		input        string
+		result       string
+		containError string
+	}{
+		{
+			name:   "simple startsWith function",
+			input:  "${{ startsWith(git.branch, 'Mas') }}",
+			result: "true",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"branch": "master",
+				},
+			},
+		},
+		{
+			name:   "complex startsWith function",
+			input:  "${{ startsWith(git.branch, git.prefix) }}",
+			result: "true",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"branch": "Master",
+					"prefix": "mas",
+				},
+			},
+		},
+		{
+			name:         "startsWith wrong input",
+			input:        "${{ startsWith(git.branch, 2) }}",
+			result:       "",
+			containError: "searchValue argument must be a string",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"branch": "Master",
+				},
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			ap := NewActionParser(tt.context, DefaultFuncs)
+			result, err := ap.parse(context.TODO(), tt.input)
+			if tt.containError != "" {
+				require.Error(t, err)
+				require.Contains(t, err.Error(), tt.containError)
+			} else {
+				require.NoError(t, err)
+				require.Equal(t, tt.result, result)
+			}
+		})
+	}
+}
+
+func TestParserFuncEndsWith(t *testing.T) {
+	log.Factory = log.NewTestingWrapper(t)
+	log.UnregisterField(log.FieldCaller, log.FieldSourceFile, log.FieldSourceLine)
+	tests := []struct {
+		name         string
+		context      map[string]interface{}
+		input        string
+		result       string
+		containError string
+	}{
+		{
+			name:   "simple endsWith function",
+			input:  "${{ endsWith(git.branch, 'Ter') }}",
+			result: "true",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"branch": "master",
+				},
+			},
+		},
+		{
+			name:   "complex endsWith function",
+			input:  "${{ endsWith(git.branch, git.prefix) }}",
+			result: "true",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"branch": "mastEr",
+					"prefix": "ter",
+				},
+			},
+		},
+		{
+			name:         "endsWith wrong input",
+			input:        "${{ endsWith(git.branch, 2) }}",
+			result:       "",
+			containError: "searchValue argument must be a string",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"branch": "Master",
+				},
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			ap := NewActionParser(tt.context, DefaultFuncs)
+			result, err := ap.parse(context.TODO(), tt.input)
+			if tt.containError != "" {
+				require.Error(t, err)
+				require.Contains(t, err.Error(), tt.containError)
+			} else {
+				require.NoError(t, err)
+				require.Equal(t, tt.result, result)
+			}
+		})
+	}
+}
+
+func TestParserFuncFormat(t *testing.T) {
+	log.Factory = log.NewTestingWrapper(t)
+	log.UnregisterField(log.FieldCaller, log.FieldSourceFile, log.FieldSourceLine)
+	tests := []struct {
+		name         string
+		context      map[string]interface{}
+		input        string
+		result       string
+		containError string
+	}{
+		{
+			name:    "simple string format",
+			input:   "${{ format('Hello {0} {1}', 'foo', 'bar') }}",
+			result:  "Hello foo bar",
+			context: map[string]interface{}{},
+		},
+		{
+			name:   "complex string format with variable",
+			input:  "${{ format(job.message, job.replace1, job.replace2) }}",
+			result: "Hello foo bar",
+			context: map[string]interface{}{
+				"job": map[string]interface{}{
+					"message":  "Hello {0} {1}",
+					"replace1": "foo",
+					"replace2": "bar",
+				},
+			},
+		},
+		{
+			name:   "complex string and int format with variable",
+			input:  "${{ format(job.message, job.replace1, job.replace2) }}",
+			result: "Hello foo 2",
+			context: map[string]interface{}{
+				"job": map[string]interface{}{
+					"message":  "Hello {0} {1}",
+					"replace1": "foo",
+					"replace2": 2,
+				},
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			ap := NewActionParser(tt.context, DefaultFuncs)
+			result, err := ap.parse(context.TODO(), tt.input)
+			if tt.containError != "" {
+				require.Error(t, err)
+				require.Contains(t, err.Error(), tt.containError)
+			} else {
+				require.NoError(t, err)
+				require.Equal(t, tt.result, result)
+			}
+		})
+	}
+}
+
+func TestParserFuncJoin(t *testing.T) {
+	log.Factory = log.NewTestingWrapper(t)
+	log.UnregisterField(log.FieldCaller, log.FieldSourceFile, log.FieldSourceLine)
+	tests := []struct {
+		name         string
+		context      map[string]interface{}
+		input        string
+		result       string
+		containError string
+	}{
+		{
+			name:   "object filter join ",
+			input:  "${{ join(git.changes.*.id, '-') }}",
+			result: "1-2-3",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"changes": []map[string]interface{}{
+						{
+							"id": 1,
+						},
+						{
+							"id": 2,
+						},
+						{
+							"id": 3,
+						},
+					},
+				},
+			},
+		},
+		{
+			name:   "join with no separator",
+			input:  "${{ join(git.changes.*.id) }}",
+			result: "1,2,3",
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"changes": []map[string]interface{}{
+						{
+							"id": 1,
+						},
+						{
+							"id": 2,
+						},
+						{
+							"id": 3,
+						},
+					},
+				},
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			ap := NewActionParser(tt.context, DefaultFuncs)
+			result, err := ap.parse(context.TODO(), tt.input)
+			if tt.containError != "" {
+				require.Error(t, err)
+				require.Contains(t, err.Error(), tt.containError)
+			} else {
+				require.NoError(t, err)
+				require.Equal(t, tt.result, result)
+			}
+		})
+	}
+}
+
+func TestParserFuncToJSON(t *testing.T) {
+	log.Factory = log.NewTestingWrapper(t)
+	log.UnregisterField(log.FieldCaller, log.FieldSourceFile, log.FieldSourceLine)
+	tests := []struct {
+		name         string
+		context      map[string]interface{}
+		input        string
+		result       string
+		containError string
+	}{
+		{
+			name:  "toJSON",
+			input: "${{ toJSON(git) }}",
+			result: `{
+  "branch": "master"
+}`,
+			context: map[string]interface{}{
+				"git": map[string]interface{}{
+					"branch": "master",
+				},
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			ap := NewActionParser(tt.context, DefaultFuncs)
+			result, err := ap.parse(context.TODO(), tt.input)
+			if tt.containError != "" {
+				require.Error(t, err)
+				require.Contains(t, err.Error(), tt.containError)
+			} else {
+				require.NoError(t, err)
+				require.Equal(t, tt.result, result)
+			}
+		})
+	}
+}
diff --git a/tests/fixtures/04SCWorkflowRunSimplePlugin/go.mod b/tests/fixtures/04SCWorkflowRunSimplePlugin/go.mod
index 86482f730..0a346064a 100644
--- a/tests/fixtures/04SCWorkflowRunSimplePlugin/go.mod
+++ b/tests/fixtures/04SCWorkflowRunSimplePlugin/go.mod
@@ -13,6 +13,7 @@ require (
 	github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
 	github.com/acomagu/bufpipe v1.0.3 // indirect
 	github.com/andybalholm/brotli v1.0.1 // indirect
+	github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1 // indirect
 	github.com/aokoli/goutils v1.1.0 // indirect
 	github.com/blang/semver v3.5.1+incompatible // indirect
 	github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 // indirect
diff --git a/tests/fixtures/04SCWorkflowRunSimplePlugin/go.sum b/tests/fixtures/04SCWorkflowRunSimplePlugin/go.sum
index 066506696..6c3e04821 100644
--- a/tests/fixtures/04SCWorkflowRunSimplePlugin/go.sum
+++ b/tests/fixtures/04SCWorkflowRunSimplePlugin/go.sum
@@ -54,6 +54,8 @@ github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239 h1:kFOfPq6dUM1hTo4JG6LR5AXSUEsOjtdm0kw0FtQtMJA=
 github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
+github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1 h1:X8MJ0fnN5FPdcGF5Ij2/OW+HgiJrRg3AfHAx1PJtIzM=
+github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230321174746-8dcc6526cfb1/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM=
 github.com/aokoli/goutils v1.1.0 h1:jy4ghdcYvs5EIoGssZNslIASX5m+KNMfyyKvRQ0TEVE=
 github.com/aokoli/goutils v1.1.0/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ=
 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=