Expressions and pattern matching Pub

There are three kinds of expressions available in different contexts:

  • value expressions, used whenever you're computing values, like for a $send
  • match expressions, used when you're trying to match a value, like in $expect or $when`
  • conditions, used in $if

Value expressions

These are used when you're computing values to pass to messages and generally work as you would expect. Here are some examples. See more working tests with supported expressions at expr story and expr spec.

Principles

The expressions are meant to feel natural, so simple stuff like 1 + 2 > 5 works as expected (yeah, false). You can have named operators, such as 1 contains 5, as the language pretends to be human friendly, for instance $expect (result is number).

Operator precedence:

Associativity is left to right and precedence is as follows:

  • multiplication: *,/
  • addition etc: +,-,||,|
  • conditionals: >,<,>=,<=,==,!=,~=,~path,?=,is,not,in,notIn,contains,containsNot
  • booleans: and,or,xor
  • array ops: map,flatMap,flatten,filter,exists
  • typecast: as

Quick expression guide

This is a quick reference guide for expressions - there are lots of examples with comments in expr story.

// constants and values
$send ctx.set (
  constants = 321, 
  addition = last+first,
  jsonBlock={
    "accountNumber": "1729534",
    "start_num": 123
	},
  qualified = jsonBlock.accountNumber,
  interpolation="is${1+2}",
  url="${HOST}/search?q=dieselapps",
  builtInFunctions1 = sizeOf(x=cart.items),
  builtInFunctions2 = typeOf(x=cart.items),
  
  anArray =[1,"a",3],

  // json operations
  cart=cart || {id:customer, items:[] } ,
  cart=cart+{items:{sku : sku, quantity : quantity} } ,
  
  // embedded Javascript expressions
  simpleJs = js:wix.diesel.env ,
  res78=js:cart.items[0].sku ,
  
  res40=js:email.replace(/(\w+)@(\w+).com/, "$1") ,
  
  later=js:{var d = new Date(); d.setSeconds(d.getSeconds() + 10); d.toISOString();} ,

  a284 = [1,2] + [3] filter (x=> x > 1) map (x=> x + 1),
  
  size = size || 4     // JS style if defined OR default. WARN: Do not use || for "or", use "or" for boolean expr
)

$expect and matches

These are match expressions, used in $expect, $when and $mock. Their purpose is to match a parameter by name with a value expression. Some can also extract more parameters (say using regular expressions wiht named groups).

$expect (name) // name is present
$expect (name == "Jane") // equal
$expect (name is "Jane") // equal
$expect (subId ~= "sub[0-9]*") // matches
$expect (subId matches "sub[0-9]*") // matches
$expect (subId matches "sub[0-9]*") // matches

Type checking

$expect (age is Number)
$expect (persons is Array)
$expect (name is String)
$expect (x1 is Student) // class defined in a domain model
$expect (x1 is Json)

// extract more parameters - will also populate an acctId when matching the regex
$mock diesel.rest (path ~= "/getAccount1/(?<acctId>\\d+)") => (payload={
    "status": "ok",
    "someStats": "67",
    "accountId" : acctId
})

Note that while `diesel.rest` is a reserved message, you can use this implicit group parsing into variable in any message:

$when expr.captureGroups (path ~= "/getAccount1/(?<acctId>\\d+)") => (payload={
    "accountId" : acctId
})

To avoid having to declare a rule just to do regex group decomposition, there is a `ctx.regex`:

$send ctx.set (payload = "/getAccount1/99")
$send ctx.regex(regex = "/getAccount1/(?<acctId3>\\d+)")
$expect (acctId3 is "99")

Or if you don't want to overwrite payload:
$send ctx.set (payload = "pa")
$send ctx.regex(regex = "/getAccount1/(?<acctId4>\\d+)", payload="/getAccount1/999")
$expect (acctId4 is "999")
$expect (payload is "pa")

Here's some other variants, with a more intuitive language:

$expect (name is empty) // name is NOT present OR is present but empty (type-aware)
$expect (name is defined) // name is present but may be empty
$expect (name not defined) // name is NOT present
$expect (name not empty) // name is present AND has some value non-empty (type-aware)

Note that you can also use other values for comparison - so take care to not use common reserved words for values:

$val jane = new Person{name:"Jane"}
$expect (guest is jane) // name is NOT present

Note that $expect uses a match expression, so you can use a list of parameters and the result is composed:

$expect (name is "Jane", subId is defined) // both must match

Basic types

The built-in types understood are:

  • boolean
  • number
  • string
  • json
  • array

For each of these types, the operators behave in a straight forward manner.

Strings

Strings are simple constants, with escaping and natural inline expressions:

  • simple special characters:
    • \b, \n, \t, \r, \f
  • anything preceded by \ is escaped, and not interpreted
    • classic escaped characthers: \\, \", \'
    • this is a difference from JAva, where only certain escape sequences are valid - here anything preceded by \ is escaped
  • natural interpolation "${name}" is expanded - note that the curly braces are mandatory.
    • escaping interpolation: "$${name}" is not expanded - it's escaped

Multi-line strings:

Use """ to delimitate multi-line strings - same rules above apply. This is quite good for simple templates, as interpolation works anywhere.

Arrays

You can test if an element is in or not in an array:

$when test.diesel.isin(x in ["123", "234"], y not in [1,2])

Conditions

Conditions are used in $ifc and here are some examples:

=> if (not false) (b=b+1, a02)
=> if (not a ~= b ) (b=b+1, a07)
=> if ( a < b or a > b ) (b=b+1, a12)
=> if ( a < b and (a > b or a < b) ) (b=b+1, a13)

There is also another if, using the match expressions: $ifm. The default for $if is the $ifc with boolean expressions.

See expr story, which is a live test of all expressions supported.

Arrays

$when test.diesel.isin(x in ["123", "234"], y not in [1,2])

$expect (["123", "234"] contains x, [1,2] containsNot y)

Note that you can't use contains with $when - the limitation there is that the named parameter has to be first element in the expression.

Complex types

Complex types can be defined and used like so:

$class DieselTestStudent (name, address:DieselTestAddress)
$class DieselTestAddress (street, no)

$send ctx.echo(m = new DieselTestStudent {
  name: "John",
  address: new DieselTestAddress {
    street: "Baker St",
    no: "121b"
  }
})

Object functions

map will work on objects too - it takes a function with one argument which will be an object with two attributes: key and value:

$val query={key:"elf", age:1000}

$send ctx.set (y = query map (x => x.key + ":\"" + x.value + "\""))
$send ctx.echo (x = y mkString " AND ")

Arrays

Arrays (or lists) are dynamic in size and look and feel much like in JS:

  a284 = [1,2] + [3] filter (x=> x > 1) map (x=> x + 1)
  x in [1,2]
  x not in [1,2]
  [3,4] contains y
  [1,2] containsNot y
  sizeOf([1,2,3])

Array functions

The array operations are: map,flatMap,flatten,filter,exists. The right side must be a lambda function:

  • map will transform the values
  • flatten will flatten an array of arrays into a single array
  • flatMap transform and flatten
  • filter keeps only the items meeting the predicate
  • exists true if an element meets the criteria
  • mkString takes a right-hand constant and will create a nice string
  a284 = [1,2] + [3] filter (x=> x > 1) map (x=> x + 1)
  a284 = [1,2] + [3] filter (x=> x > 1) map (x=> x + 1)

as - typecasts

You can typecast an expression, to change the type or make sure it's of a certain type. The right side expression can be:

  • a name representing a basic type x as number
  • a string constant representing an extended type `x as "application/pdf")
  • a name representing a class in the current domain x as Student
  • if none of the above works, the right side is then evaluated as an expression and we'll try again: x as avalue where avalue is "Student" will be re-evaluated as x as Student

asAttrs

This is a special transformation that can be applied to a json document, to flatten it into a list of parameters that can be used to call a message:

$when something.hapenned ()
=> (j = {a:1, b:2})
=> something.else (j.asAttrs)

j would be flattened and a and b would be parameters to the new message something.else.

Properties

You can get system properties with diesel.props.system which will populate the payload with an object containing all system properties, IF running in self-hosted mode.

Likewise, you can load a property file with diesel.props.file only in self-hosted mode. Or a JSON file via diesel.props.jsonFile message, also only in self-hosted mode.

Rule pattern matching

Matching rules for decomposition is done according to these pattern matching rules.

Simple match

$when dieseltest.rule1(a,b)
=> (rule1ab=a+b)

This matches the message by name and list of values, no ambiguity.

Regex match

You can match the entity with a * - this will match a.sendtest as well as a.b.c.sendtest:

$when *.sendtest
=> (rule12=true)

Or, you can match the action with a * - but note that this matches only the last word, i.e. the "action" - so it will match dieseltestsendtest.dothis, but not dieseltestsendtest.a.b.c.dothis:

$when dieseltestsendtest.*
=> (rule16=true)

$when dieseltest.send.multiple.*
=> (rule18=true)

JS Full match

You can use a full and more complex js-like match for both entity and action together:

$when /dieseltest.*/
=> (rule19=true)

$when /dieseltest\..*/
=> (rule19a=true)

The second rule above will match dieseltest.a.b.c.action but not dieseltests.a.b.c.action.

Conditions

...can be builtinto the list of arguments, like diesel.isLocalhost is false below - that is not an argument that is passed in, but a general condition:

$when diesel.realm.configure(realm is "devnetlinq", diesel.isLocalhost is false)

Built-in functions

There are a few built-in functions you can use in a variety of situations in a common manner x=sizeOf(anArray)

sizeOf

Calculates the size of an array/list (number of elements) or of an object (number of attributes).

typeOf

Returns the type of a parameter.

$val  builtInFunctions1 = sizeOf(x=cart.items),
$val  builtInFunctions2 = typeOf(x=cart.items),

See as operator as well, for typecasts.

accessor

Use it to access a JSON object with an expression:

$send ctx.echo(x5=accessor(obj={a:{a:{a:1}}, b:{b:{b:2}}}, accessor="a.a.a"))

urlencode

Encodes a string with url conventions.

flatten

Flatten a lists of lists (or array of arrays).

now

Returns current time in ISO format

uuid

Returns a UUID using Mongo helpers (see object id in Mongo) - it is shorter than other UUIDs, when you don't need to encode the world!

Types

The expressions are type-aware. Common sense type casts are done (string plus number is a string).

Use as to force a typecast 5 as string.

Use typeOf to get the type of something.

Use is or not to check the type of something age is Number.

Domains - see Domain Modelling. All defined types are derived from Json and look at feel like Json.

Accessing objects

Objects are JSON and are used much like a sensical Javascript would:

$val x = {a:1,b:"asdf"}
$val y = {a:1,b:"asdf"}

$send ctx.echo(z = x + y) //  merge two objects

$val n = x.a


Was this useful?    

By: Razie | 2017-08-31 .. 2021-04-02 | Tags: academy , reference


Viewed 189 times ( | Print ) this page.

You need to log in to post a comment!

© Copyright DieselApps, 2012-2021, all rights reserved.