This document provides a complete reference for AMEL's expression syntax.
- Literals
- Variables and JSONPath
- Operators
- Function Calls
- Lambda Expressions
- Lists
- Operator Precedence
- Reserved Keywords
- Comments
AMEL supports the following literal types:
64-bit signed integers:
42
-17
0
1000000
64-bit floating-point numbers:
3.14
-2.5
0.0
100.0
UTF-8 strings enclosed in double or single quotes:
"hello world"
'single quotes work too'
"with \"escaped\" quotes"
"line\nbreak"
| Sequence | Description |
|---|---|
\\ |
Backslash |
\" |
Double quote |
\' |
Single quote |
\n |
Newline |
\r |
Carriage return |
\t |
Tab |
true
false
null
Access data from the payload using JSONPath expressions starting with $:
$.name // Root-level property
$.user.name // Nested property
$.user.address.city // Deeply nested property
$.items[0] // First element
$.items[1].name // Property of second element
$.matrix[0][1] // Nested array access
$.users[0].addresses[0].street
$.data.results[5].metadata.tags[0]
When a JSONPath points to a non-existent property, it returns null:
$.missing.property // Returns null if path doesn't exist
Use isNull() or coalesce() to handle missing values:
isNull($.optional)
coalesce($.nickname, $.name, "Anonymous")
| Operator | Description | Example |
|---|---|---|
== |
Equal to | $.age == 18 |
!= |
Not equal to | $.status != "deleted" |
> |
Greater than | $.price > 100 |
< |
Less than | $.quantity < 10 |
>= |
Greater than or equal | $.age >= 21 |
<= |
Less than or equal | $.score <= 100 |
$.user.age >= 18
$.status == "active"
$.price != null
$.count > 0
| Operator | Description | Example |
|---|---|---|
&& |
Logical AND | $.a && $.b |
|| |
Logical OR | $.a || $.b |
! |
Logical NOT | !$.active |
$.age >= 18 && $.verified == true
$.role == "admin" || $.role == "moderator"
!$.deleted
!($.status == "inactive")
Short-circuit evaluation: AMEL uses short-circuit evaluation for logical operators:
&&stops evaluating if the left side is false||stops evaluating if the left side is true
| Operator | Description | Example |
|---|---|---|
+ |
Addition | $.a + $.b |
- |
Subtraction | $.a - $.b |
* |
Multiplication | $.a * $.b |
/ |
Division | $.a / $.b |
% |
Modulo | $.a % $.b |
$.price * $.quantity
$.total - $.discount
$.score / $.max * 100
$.index % 2 == 0
String concatenation: The + operator also concatenates strings:
"Hello, " + $.name + "!"
| Operator | Description | Example |
|---|---|---|
- |
Numeric negation | -$.value |
! |
Logical negation | !$.active |
| Operator | Description | Example |
|---|---|---|
IN |
Value in list | $.role IN ["a", "b"] |
NOT IN |
Value not in list | $.status NOT IN ["x", "y"] |
$.user.role IN ["admin", "moderator", "editor"]
$.status NOT IN ["deleted", "archived", "suspended"]
"premium" IN $.user.tags
| Operator | Description | Example |
|---|---|---|
=~ |
Regex match | $.email =~ "@gmail\\.com$" |
!~ |
Regex not match | $.name !~ "^test" |
// Email validation
$.email =~ "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$"
// Phone number pattern
$.phone =~ "^\\+?[0-9]{10,14}$"
// Case insensitive match
$.status =~ "(?i)^active$"
// Word boundary
$.text =~ "\\bword\\b"
// Negated match
$.email !~ "@spam\\.com$"
Access list elements by index:
$.items[0] // First element
$.items[1] // Second element
$.items[-1] // Last element (negative indexing)
[1, 2, 3][0] // Direct list access
Call built-in or user-defined functions:
functionName(arg1, arg2, ...)
// String functions
upper($.name)
lower($.email)
trim($.input)
len($.description)
contains($.text, "keyword")
startsWith($.url, "https://")
endsWith($.file, ".pdf")
// Math functions
abs($.balance)
max($.a, $.b, $.c)
min($.x, $.y)
round($.price)
floor($.value)
ceil($.score)
// Type functions
int($.stringNumber)
float($.value)
string($.count)
bool($.flag)
// Null handling
coalesce($.nickname, $.name, "Unknown")
isNull($.optional)
isNotNull($.required)
// Conditional
ifThenElse($.age >= 18, "adult", "minor")
See Built-in Functions for the complete list.
Lambda expressions are used with array operations:
x => x * 2
x => x > 5
x => x + 1
(acc, x) => acc + x
(sum, item) => sum + item.price
// Map: transform each element
map([1, 2, 3], x => x * 2) // [2, 4, 6]
// Filter: select matching elements
filter([1, 2, 3, 4, 5], x => x > 2) // [3, 4, 5]
// Reduce: aggregate to single value
reduce([1, 2, 3, 4], 0, (acc, x) => acc + x) // 10
// Find: first matching element
find([1, 2, 3, 4], x => x > 2) // 3
// Some: any element matches
some([1, 2, 3], x => x > 2) // true
// Every: all elements match
every([1, 2, 3], x => x > 0) // true
[] // Empty list
[1, 2, 3] // Integer list
["a", "b", "c"] // String list
[true, false, true] // Boolean list
[1, "mixed", true] // Mixed types
[[1, 2], [3, 4]] // Nested lists
[10, 20, 30][0] // 10
[10, 20, 30][1] // 20
[10, 20, 30][-1] // 30 (last element)
// Length
len([1, 2, 3]) // 3
// Membership
5 IN [1, 2, 3, 4, 5] // true
"x" NOT IN ["a", "b", "c"] // true
// Aggregation
sum([1, 2, 3, 4]) // 10
avg([10, 20, 30]) // 20
min([5, 2, 8, 1]) // 1
max([5, 2, 8, 1]) // 8
// Element access
first([1, 2, 3]) // 1
last([1, 2, 3]) // 3
at([10, 20, 30], 1) // 20
// Transformation
reverse([1, 2, 3]) // [3, 2, 1]
unique([1, 2, 2, 3, 3]) // [1, 2, 3]
flatten([[1, 2], [3, 4]]) // [1, 2, 3, 4]
slice([1, 2, 3, 4], 1, 3) // [2, 3]
From lowest to highest precedence:
| Precedence | Operators | Associativity | Description |
|---|---|---|---|
| 1 | || |
Left | Logical OR |
| 2 | && |
Left | Logical AND |
| 3 | ! |
Right | Logical NOT |
| 4 | == != > < >= <= IN NOT IN =~ !~ |
Left | Comparison |
| 5 | + - |
Left | Addition, Subtraction |
| 6 | * / % |
Left | Multiplication, Division, Modulo |
| 7 | Unary - |
Right | Negation |
| 8 | [] () |
- | Index, Function call, Grouping |
// Parsed as: (a && b) || c
a && b || c
// Parsed as: a && (b || c)
a && (b || c)
// Parsed as: (a > b) && (c < d)
a > b && c < d
// Parsed as: a + (b * c)
a + b * c
// Parsed as: (a + b) * c
(a + b) * c
// Parsed as: !(a == b)
!a == b
// Parsed as: (!a) == b (use parentheses)
(!a) == b
The following words are reserved and cannot be used as identifiers:
true
false
null
IN
NOT
AND
OR
Note: AND and OR are reserved but && and || are the actual operators.
AMEL supports both single-line and block comments:
// This is a comment
$.age >= 18 // Check if adult
/* This is a
multi-line comment */
$.user.name
For completeness, here's the formal grammar:
Expression = LogicalOr ;
LogicalOr = LogicalAnd { "||" LogicalAnd } ;
LogicalAnd = LogicalNot { "&&" LogicalNot } ;
LogicalNot = "!" LogicalNot | Comparison ;
Comparison = Arithmetic [ CompOp Arithmetic ] ;
CompOp = "==" | "!=" | ">" | "<" | ">=" | "<="
| "IN" | "NOT IN" | "=~" | "!~" ;
Arithmetic = Term { ("+" | "-") Term } ;
Term = Factor { ("*" | "/" | "%") Factor } ;
Factor = Unary ;
Unary = "-" Unary | Primary ;
Primary = Literal
| Identifier
| JSONPath
| FunctionCall
| ListLiteral
| LambdaExpression
| "(" Expression ")" ;
Literal = Integer | Float | String | Boolean | Null ;
JSONPath = "$" { "." Identifier | "[" (Integer | String) "]" } ;
FunctionCall = Identifier "(" [ ArgList ] ")" ;
ArgList = Expression { "," Expression } ;
ListLiteral = "[" [ Expression { "," Expression } ] "]" ;
LambdaExpression = Identifier "=>" Expression
| "(" Identifier "," Identifier ")" "=>" Expression ;$.user.role IN ["admin", "moderator"] ||
($.user.verified == true && $.user.reputation >= 1000)
$.order.total >= 100 && $.customer.tier == "gold" ||
$.order.total >= 500
isNotNull($.email) &&
$.email =~ "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" &&
len($.password) >= 8
$.feature.enabled == true &&
$.user.id % 100 < $.feature.rolloutPercent
sum(map($.items, x => x.price * x.quantity)) *
(1 - coalesce($.discount, 0) / 100)
- Explore Built-in Functions
- Create Custom Functions