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()

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:

reference:
  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}
  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]}
CategoryExpressionExample output
Arithmetic10 % 3

ref_rand('products').stock % 7
1 -> 3
Arithmetic2 ** 8

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

ref_rand('products').price + ref_rand('products').stock * 2
11 -> 329.99
Array functionsall(ref_same('regions').cities, {len(#) > 3})true
Array functionsany(ref_same('regions').cities, {# == 'miami'})true
Array functionsconcat(ref_same('regions').cities, ['london', 'paris'])[new_york, boston, miami, london, paris]
Array functionsfilter(ref_same('regions').cities, {# startsWith 'b'})[boston]
Array functionsfind(ref_same('regions').cities, {# startsWith 'b'})boston
Array functionsfindIndex(ref_same('regions').cities, {# startsWith 'b'})1
Array functionsfindLast(ref_same('regions').cities, {# endsWith 'i'})miami
Array functionsfindLastIndex(ref_same('regions').cities, {# endsWith 'i'})2
Array functionsfirst(ref_same('regions').cities)new_york
Array functionsflatten([['new_york', 'boston'], ['london', 'paris']])

flatten([ref_same('regions').cities, ['sydney', 'tokyo']])
[new_york, boston, london, paris] -> [new_york, boston, miami, sydney, tokyo]
Array functionsgroupBy(ref_same('regions').cities, {len(#) > 5}){false: [miami, boston], true: [new_york]}
Array functionsjoin(ref_same('regions').cities, ', ')new_york, boston, miami
Array functionslast(ref_same('regions').cities)miami
Array functionsmap(ref_same('regions').cities, {upper(#)})[NEW_YORK, BOSTON, MIAMI]
Array functionsnone(ref_same('regions').cities, {# == 'tokyo'})true
Array functionsone(ref_same('regions').cities, {# == 'miami'})true
Array functionsreduce([29.99, 49.99, 4.99, 1.99, 9.99], {#acc + #}, 0)

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

sortBy(ref_same('regions').cities, {len(#)})
[Pen, Cable, Widget] -> [miami, boston, new_york]
Array functionstake(ref_same('regions').cities, 2)[new_york, boston]
Array functionsuniq(['electronics', 'stationery', 'electronics'])

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

bitand(ref_rand('products').stock, 0xFF)
8 -> 150
Bitwisebitnot(0b1100)

bitnot(ref_rand('products').stock)
-13 -> -151
Bitwisebitor(0b1100, 0b1010)

bitor(ref_rand('products').stock, 1)
14 -> 151
Bitwisebitshl(1, 4)

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

bitshr(ref_rand('products').stock, 1)
1 -> 75
Bitwisebitxor(0b1100, 0b1010)

bitxor(ref_rand('products').stock, 0xFF)
6 -> 105
Comparison & logicnot (ref_rand('products').category == 'stationery')true
Comparison & logicref_rand('products').stock > 0 and ref_rand('products').activetrue
Conditionalsnil ?? 'unknown'

ref_rand('products')?.description ?? 'none'
unknown -> none
Conditionalsref_rand('products').stock > 0 ? 'in_stock' : 'sold_out'in_stock
Languageall(ref_same('regions').cities, {len(#) > 0})true
Languageget(ref_rand('products'), 'name')Widget
Languagelen(ref_same('regions').cities)3
Languagelet p = ref_rand('products'); p.price + 1039.99
Map functionskeys(ref_rand('products'))[name, category, price, stock, active]
Map functionsvalues(ref_rand('products'))[Widget, electronics, 29.99, 150, true]
Math functionsabs(-7)

abs(ref_rand('products').stock - 200)
7 -> 50
Math functionsceil(3.2)

ceil(ref_rand('products').price)
4 -> 30
Math functionsfloor(ref_rand('products').price)29
Math functionsmean([29.99, 49.99, 4.99, 1.99, 9.99])

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

median(map(ref_same('regions').cities, {len(#)}))
9.99 -> 6
Math functionsround(ref_rand('products').price)30
Operators1..5

1..len(ref_same('regions').cities) + 1
[1, 2, 3, 4] -> [1, 2, 3]
Operatorsref_rand('products')?.nameWidget
Operatorsref_rand('products').price | int29
Operatorsref_same('regions').cities[0:2][new_york, boston]
Operatorsref_same('regions').zone in ['us', 'eu', 'ap']true
String functionshasPrefix(ref_same('regions').code, 'us')true
String functionshasSuffix(ref_same('regions').code, 'east')true
String functionsindexOf('london', 'on')

indexOf(ref_rand('products').category, 'c')
1 -> 3
String functionslastIndexOf('london', 'on')

lastIndexOf(ref_rand('products').category, 'c')
4 -> 9
String functionslower(ref_rand('products').category)electronics
String functionsrepeat('*', 5)

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

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

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

trim(ref_rand('products').name)
gadget -> Widget
String functionstrimPrefix(ref_same('regions').code, ref_same('regions').zone + '-')east
String functionstrimSuffix(ref_same('regions').code, '-' + trimPrefix(ref_same('regions').code, ref_same('regions').zone + '-'))us
String functionsupper(ref_rand('products').name)WIDGET
String operatorsref_rand('products').category contains 'electron'true
String operatorsref_same('regions').code endsWith 'east'true
String operatorsref_same('regions').code matches '[a-z]+-[a-z]+'true
String operatorsref_same('regions').code startsWith 'us'true
Type conversionfloat(ref_rand('products').stock)150.0
Type conversionfromBase64('V2lkZ2V0')

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

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

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

int(ref_rand('products').price)
42 -> 29
Type conversionstring(ref_rand('products').price)29.99
Type conversiontoBase64(ref_rand('products').name)V2lkZ2V0
Type conversiontoJSON(ref_rand('products')){"active":true,"category":"electronics","name":"Widget","price":29.99,"stock":150}
Type conversiontoPairs({name: 'Widget', price: 29.99})

toPairs(ref_rand('products'))
[[name, Widget], [price, 29.99]] -> [[active, true], [category, electronics], [name, Widget], [price, 29.99], [stock, 150]]
Type conversiontype(ref_rand('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_rand('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_rand('products'); int(ceil(p.price * float(p.stock) / 100))45
Derived sluglet p = ref_rand('products'); replace(lower(p.name + '_' + p.category), ' ', '_')widget_electronics
Discount pricinglet p = ref_rand('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_rand('products').name], ['zone', ref_same('regions').zone]])){"product":"Widget","zone":"us"}
Multi-condition classificationlet p = ref_rand('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