Supported expr-lang syntax#

Expressions support the full expr-lang feature set, including:

CategoryExamples
Arithmetic+, -, *, /, %, **
Array functionsfilter(), map(), reduce(), sort(), sortBy(), reverse(), first(), last(), take(), flatten(), uniq(), concat(), join(), find(), findIndex(), findLast(), findLastIndex(), all(), any(), one(), none(), groupBy()
Bitwisebitand(), bitor(), bitxor(), bitnand(), bitnot(), bitshl(), bitshr(), bitushr()
Comparison & logic==, !=, <, >, and, or, not
Conditionalsargs[0] > 10 ? 'yes' : 'no', args[0] ?? 0 (nil coalescing), if/else
Languagelet bindings, # predicates (current element in closures), len(), get()
Map functionskeys(), values()
Math functionsabs(), ceil(), floor(), round(), mean(), median()
Operators| (pipe), in (membership), .. (range), [:] (slice), ?. (optional chaining)
String functionsupper(), lower(), trim(), trimPrefix(), trimSuffix(), split(), splitAfter(), replace(), repeat(), indexOf(), lastIndexOf(), hasPrefix(), hasSuffix()
String operatorscontains, startsWith, endsWith, matches (regex)
Type conversionint(), float(), string(), type(), toJSON(), fromJSON(), toBase64(), fromBase64(), toPairs(), fromPairs()

Reference data#

These examples show expr-lang expressions used with edg’s reference data. Each expression can appear anywhere an edg expression is accepted (e.g. in args:, expressions:, or inline globals). Visit the Expressions example for a complete runnable demonstration.

The examples below assume the following in-memory reference data:

ref products [
  {name: "Widget", category: "electronics", price: 29.99, stock: 150, active: true}
  {name: "Gadget", category: "electronics", price: 49.99, stock: 80, active: true}
  {name: "Notebook", category: "stationery", price: 4.99, stock: 500, active: true}
  {name: "Pen", category: "stationery", price: 1.99, stock: 1000, active: true}
  {name: "Cable", category: "electronics", price: 9.99, stock: 0, active: false}
]

ref regions [
  {code: "us-east", zone: "us", cities: ["new_york", "boston", "miami"]}
  {code: "eu-west", zone: "eu", cities: ["london", "paris", "dublin"]}
  {code: "ap-south", zone: "ap", cities: ["mumbai", "singapore", "tokyo"]}
]

Arithmetic#

ExpressionExample output
10 % 3

ref('products').stock % 7
1 -> 3
2 ** 8

len(ref_same('regions').cities) ** 3
256 -> 27
3 + 4 * 2

ref('products').price + ref('products').stock * 2
11 -> 329.99

Comparison & logic#

ExpressionExample output
not (ref('products').category == 'stationery')true
ref('products').stock > 0 and ref('products').activetrue

Conditionals#

ExpressionExample output
nil ?? 'unknown'

ref('products')?.description ?? 'none'
unknown -> none
ref('products').stock > 0 ? 'in_stock' : 'sold_out'in_stock

Operators#

ExpressionExample output
1..5

1..len(ref_same('regions').cities) + 1
[1, 2, 3, 4] -> [1, 2, 3]
ref('products')?.nameWidget
ref('products').price | int29
ref_same('regions').cities[0:2][new_york, boston]
ref_same('regions').zone in ['us', 'eu', 'ap']true

Array functions#

ExpressionExample output
all(ref_same('regions').cities, {len(#) > 3})true
any(ref_same('regions').cities, {# == 'miami'})true
concat(ref_same('regions').cities, ['london', 'paris'])[new_york, boston, miami, london, paris]
filter(ref_same('regions').cities, {# startsWith 'b'})[boston]
find(ref_same('regions').cities, {# startsWith 'b'})boston
findIndex(ref_same('regions').cities, {# startsWith 'b'})1
findLast(ref_same('regions').cities, {# endsWith 'i'})miami
findLastIndex(ref_same('regions').cities, {# endsWith 'i'})2
first(ref_same('regions').cities)new_york
flatten([['new_york', 'boston'], ['london', 'paris']])[new_york, boston, london, paris]
groupBy(ref_same('regions').cities, {len(#) > 5}){false: [miami, boston], true: [new_york]}
join(ref_same('regions').cities, ', ')new_york, boston, miami
last(ref_same('regions').cities)miami
len(ref_same('regions').cities)3
map(ref_same('regions').cities, {upper(#)})[NEW_YORK, BOSTON, MIAMI]
none(ref_same('regions').cities, {# == 'tokyo'})true
one(ref_same('regions').cities, {# == 'miami'})true
reduce([29.99, 49.99, 4.99, 1.99, 9.99], {#acc + #}, 0)

reduce(ref_same('regions').cities, {#acc + len(#)}, 0)
96.95 -> 19
reverse(ref_same('regions').cities)[miami, boston, new_york]
sort(ref_same('regions').cities)[boston, miami, new_york]
sortBy(['Pen', 'Widget', 'Cable'], {len(#)})

sortBy(ref_same('regions').cities, {len(#)})
[Pen, Cable, Widget]

[miami, boston, new_york]
take(ref_same('regions').cities, 2)[new_york, boston]
uniq(['electronics', 'stationery', 'electronics'])

uniq(concat(ref_same('regions').cities, ref_same('regions').cities))
[electronics, stationery] -> [new_york, boston, miami]

Map functions#

ExpressionExample output
keys(ref('products'))[name, category, price, stock, active]
values(ref('products'))[Widget, electronics, 29.99, 150, true]

Math functions#

ExpressionExample output
abs(-7)

abs(ref('products').stock - 200)
7 -> 50
ceil(3.2)

ceil(ref('products').price)
4 -> 30
floor(ref('products').price)29
mean([29.99, 49.99, 4.99, 1.99, 9.99])

mean(map(ref_same('regions').cities, {len(#)}))
19.39 -> 6.33
median([1.99, 4.99, 9.99, 29.99, 49.99])

median(map(ref_same('regions').cities, {len(#)}))
9.99 -> 6
round(ref('products').price)30

Bitwise#

ExpressionExample output
bitand(0b1100, 0b1010)

bitand(ref('products').stock, 0xFF)
8 -> 150
bitnot(0b1100)

bitnot(ref('products').stock)
-13 -> -151
bitor(0b1100, 0b1010)

bitor(ref('products').stock, 1)
14 -> 151
bitshl(1, 4)

bitshl(1, len(ref_same('regions').cities))
16 -> 8
bitshr(16, 4)

bitshr(ref('products').stock, 1)
1 -> 75
bitxor(0b1100, 0b1010)

bitxor(ref('products').stock, 0xFF)
6 -> 105

String functions#

ExpressionExample output
hasPrefix(ref_same('regions').code, 'us')true
hasSuffix(ref_same('regions').code, 'east')true
indexOf('london', 'on')

indexOf(ref('products').category, 'c')
1 -> 3
lastIndexOf('london', 'on')

lastIndexOf(ref('products').category, 'c')
4 -> 9
lower(ref('products').category)electronics
repeat('*', 5)

repeat(ref_same('regions').zone, 3)
***** -> ususus
replace(ref_same('regions').code, '-', '_')us_east
split('new_york,boston,miami', ',')

split(ref_same('regions').code, '-')
[new_york, boston, miami] -> [us, east]
splitAfter('us,eu,ap', ',')

splitAfter(ref_same('regions').code, '-')
[us,, eu,, ap] -> [us-, east]
trim(' gadget ')

trim(ref('products').name)
gadget -> Widget
trimPrefix(ref_same('regions').code, ref_same('regions').zone + '-')east
trimSuffix(ref_same('regions').code,
'-' + trimPrefix(ref_same('regions').code, ref_same('regions').zone + '-'))
us
upper(ref('products').name)WIDGET

String operators#

ExpressionExample output
ref('products').category contains 'electron'true
ref_same('regions').code endsWith 'east'true
ref_same('regions').code matches '[a-z]+-[a-z]+'true
ref_same('regions').code startsWith 'us'true

Type conversion#

ExpressionExample output
float(ref('products').stock)150.0
fromBase64('V2lkZ2V0')

fromBase64(toBase64(ref('products').name))
Widget -> Widget
fromJSON('{"code":"us-east","zone":"us"}')

fromJSON('{"id":' + string(ref('products').stock) + '}')
{code: us-east, zone: us} -> {id: 150}
fromPairs([['name', 'Widget'], ['price', 29.99]])

fromPairs([['product', ref('products').name],
['zone', ref_same('regions').zone]])
{name: Widget, price: 29.99} -> {product: Widget, zone: us}
int('42')

int(ref('products').price)
42 -> 29
string(ref('products').price)29.99
toBase64(ref('products').name)V2lkZ2V0
toJSON(ref('products')){"active":true,"category":"electronics","name":"Widget","price":29.99,"stock":150}
toPairs({name: 'Widget', price: 29.99})

toPairs(ref('products'))
[[name, Widget], [price, 29.99]] -> [[active, true], [category, electronics], [name, Widget], [price, 29.99], [stock, 150]]
type(ref('products').price)float

Advanced expressions#

These combine multiple functions and reference lookups for more complex use cases.

DescriptionExpressionExample output
Chained array opsjoin(take(sort(map(ref_same('regions').cities, {upper(#)})), 2), ', ')BOSTON, MIAMI
Conditional formattinglet p = ref('products'); p.stock > 100 ? upper(p.name) : lower(p.name)WIDGET
Conditional string opslet r = ref_same('regions'); hasPrefix(r.code, 'us') ? upper(first(r.cities)) : lower(last(r.cities))NEW_YORK
Derived metriclet p = ref('products'); int(ceil(p.price * float(p.stock) / 100))45
Derived sluglet p = ref('products'); replace(lower(p.name + '_' + p.category), ' ', '_')widget_electronics
Discount pricinglet p = ref('products'); p.active ? round(p.price * 0.9) : 027
Filtered countlen(filter(ref_same('regions').cities, {len(#) > 5}))2
JSON from refstoJSON(fromPairs([['product', ref('products').name], ['zone', ref_same('regions').zone]])){"product":"Widget","zone":"us"}
Multi-condition classificationlet p = ref('products'); p.price > 10 and p.stock > 0 ? 'premium' : (p.active ? 'basic' : 'discontinued')premium
Reduce mapped valuesreduce(map(ref_same('regions').cities, {len(#)}), {#acc + #}, 0)19
Switch on env var{'fra': 'eu-central-1', 'sin': 'ap-southeast-1', 'iad': 'us-east-1'}[env('FLY_REGION')] ?? fail('unknown FLY_REGION')eu-central-1