spiralcraft.lang Syntax Reference

Summary

The syntax of Expressions in ELaine (spiralcraft.lang) is roughly inspired by Java(tm) language expressions, with some major enhancements to support the contextual model and other advanced functionality.

The below presentation is intended to convey common usage patterns for the language constructs, and is not a formal specification of the grammar.

Literals

String x = "Hi"
x = " \"Escaped quotes \" "
x = " Escaped backslash \\ "
Character x = 'a'
Integer x = 1
Double x = 1.0
Long x = 1L
Double x = 1D
Float x = 1F
true x = true
false x = false
null x = null
Expression expr = `x.y.z == "test"` An Expression literal allows an unbound expression to be passed to other code where it can be bound into a different context.

Namespace prefix expansions, however, will use the Namespace context in which the Expression is parsed, which is the same as the containing Expression.

Operators

Basic

( expr ) Sub-Expression x * (y + z) Change precedence by grouping terms


Numeric

num1 + num2 Add x + y
num1 - num2 Subtract x - y
num1 * num2 Multiply x * y
num1 / num2 Divide x / y
num1 % num2 Modulus x % y


Logical

bool1 && bool2 Logical AND x && y y will not be evaluated when x is false
bool1 || bool2 Logical OR x || y y will not be evaluated when x is true
bool1 ^ bool2 Exclusive OR x ^ y
! bool1 Negate !x
bool1 ? trueResult : falseResult Conditional x ? a : b


Relational

expression1 == expression2 Equals x == y performs Object.equals()
expression1 != expression2 Not Equal x != y performs Object.equals()
expression1 < expression2 Less than x < y
expression1 <= expression2 Less than or equals x <= y
expression1 > expression2 Greater than x > y
expression1 >= expression2 Greater than or equals x >= y
iterableExpression ?= itemExpression Contains setX ?= y


Properties, Methods and Functors

object . propName Dereference property x.y
object . methodName ( param1 , ...) Method call: standard x.y(p1,p2,...)
object . methodName ( param1 , ... , binding1 , ...) Method call: bound params x.y(p1, p2, np1:=a, np2:=b, ...) See extended syntax
functor .( param1 , ... , binding1 , ...) Functor call myFunctor.(p1, p2, np1:=a, np2:=b, ...) See extended syntax


Collections

iterX [ integerX ] Select: element at index x[index]
iterX [ booleanX ] Select: filtered subset x[ .y == z ] See extended syntax
mapX [ keyX ] Select: associative mapping x[ someKey ] See extended syntax
iterX [ integerX .. integerX ] Select: inclusive range x[ start .. end ] See extended syntax
iterX [ integerX .! integerX ] Select: exclusive range myPages[ mark .! mark+5 ] See extended syntax
iterX.@top Top of sequence finished.@top First item in sequence, or null if empty
iterX #{ xlation } Map myFriends#{ .name } One-to-one mapping into an array of expression result
iterX $[ expression ] Reduce: distinct myCars$[ .color ] Reduce to array with distinct values of expression result
iterX $[ accumulatorX ] Reduce: accumulate myOrders$[ .amount.[*fn:Sum] ] Reduce to single result computed from all collection values. See details.


Context

[ ns : typeName ] Resolve Focus (namespace relative) [myns:MyWidget].someProperty Find nearest reference matching specified type URI in the focus chain (see Focus Resolution)
[ : typeURI ] Resolve Focus (absolute) [:class:/mypkg/MyWidget].someProperty Find nearest reference matching specified type URI in the focus chain (see Focus Resolution)
expression { expression , ... } Subcontext complexResult { .resultPropX * .resultPropY } Redefine the subject (leading '.') to an intermediate result for a list of expressions
Return the result of the last expression in the list.

Assignment / Binding

location = expr Assign x = y + z sets x to and returns y+z
location := expr Bind myParam := y + z Declares a binding for a parameter, field or property name. Only valid in specific constructs.


Concatenation

stringExpression + stringExpression Concatenate String "Foo" + "Bar"
collectionExpression + collectionExpression Concatenate a Collection myCollection + anotherCollection


Structs

See Struct

{ structField , ... } Struct Definition (anonymous) { firstName:="John" , lastName:="Doe" }
{ [#uri] structField , ... } Struct Definition (named) { [#myns:Person] firstName:="John" , lastName:="Doe" }
fieldName := expression Struct Field (bound) firstName:="John"
fieldName : constTypeExpression Struct Field (unassigned) firstName:[@j:String]


Meta

[@ ns : typeName ] Resolve type (namespace relative) [@j:Integer].parseInt("1") Resolve a type (a Reflector object) by its URI. See Type Reflection
[@ : typeURI ] Resolve type (absolute) [@:class:/java/lang/Integer].parseInt("1") Resolve a type (a Reflector object) by its URI. See Type Reflection
expression.@type Inferred type myObject.myProp.@type The bound type of an expression (constant for binding)
expression.@subtype Dynamic type heterogenousCollection[index].@subtype The type of data actually returned by the expression
expression.@cast(typeExpression) Cast objectHolder.value.@cast([@myns:MyType]) The bound type of an expression
typeExpression.@nil Nil channel [@myns:MyType].@nil Empty channel for a specified type


Extension

[* ns : typeName { param1 , ... , binding1, ... } ] Constant object literal [*myns:MyComponent { "superWidget5000", coolFactor:=11 } ].makeItHappen("bigStuff") Create in-place singleton
expression.[* ns : typeName { param1 , ... , binding1, ... } ] Constant function literal result.[*myns:MyComputation { precision:=0.0001D } ] * adjustmentFactor

result.[*myns:MyFunctor { precision:=0.0001D } ].(someOtherInput)
Incorporate a function into channel chain (see detail)

Usage

The syntax of spiralcraft.lang is designed to:

  • provide access to binding targets in an application
  • maintain component encapsulation
  • eliminate coupling entanglements between application layers (smart wiring)
  • fluently express iterative computations

These operations require a basic understanding of the Focus chain and the Contextual Singleton pattern.

URI-based type names

ELaine (spiralcraft.lang) uses a URI-based naming mechanism to uniquely identify types. This allows an Expression to reference types from multiple type models, such as the built-in reflection type model and the DALai data type model.

Type name URIs are used for a number of purposes:

  • Focus Resolution: Finding component instances with the containership hierarchy (Focus Resolution)
  • Type Resolution: Obtaining Reflector references to work with metadata
  • Extension: Referring to component Builder assemblies and Java classes that interact with ELaine

To keep redundancy at a minimum, type names can either be explicitly specified or can utilize contextual namespace mappings as declared in developer artifacts such as XML files.

To use the explicit form, place a colon before the full URI (note the colon before the URI):

 :class:/java/lang/String

To use the namespace:name form, define the namespace prefix to a pathed URI using the relevant mechanism. The resulting URI will be the mapped URI with the local name added as a path segment. The following translates to the URI "class:/java/lang/String"

 j:String

Focus Resolution

Resolve an object in the Focus chain that has a type specified by a type name URI. Used to obtain references to various objects published by the container.

For example, Expressions are used to bind controls to "model" objects, where "model" is defined as what is reachable through the Focus chain (ie., anything made available by the application).

When an Expression is bound (which usually happens once, at load time), the Focus against which the expression is bound is queried for the specified URI. If neither subject of the Focus nor any local singletons are a match, the parent Focus is queried until the chain is exhausted. Typically, the URI is converted to a native representation and any subtypes or implementations will satisfy the query.

This resolution mechanism is the basis of the Contextual Singleton pattern.

Usage

Usage may take the form of [namespace:name] , where name is resolved against the URI mapped to namespace, or may take the form [:uri], where the uri is in absolute form.

The URI resolves against all type models in the Focus chain, including the built-in mapping to the Java class namespace.

Examples

 [myns:MyWidget].someProperty
 [:class:/myPackage/MyWidget].someProperty


Context

An Expression is always bound against a Focus. This Focus is referred to as the "context" of the Expression. Un-prefixed names and method calls anywhere in an Expression refer to the context, for example:

 name
 doSomething(10)

The value exposed as the "context" normally remains constant for the duration of expression evaluation.

At all times within an Expression, a "subject" is defined, and can be referred to using the dot (".") prefix. Initially, the "subject" is the same as the "context".

Operators which involve constructs inside of angle brackets ( [ ... ] ) or curly braces ( { ... } ) usually re-define the "subject" for constructs within the scope of the brackets. This is known as "telescoping".

Telescoping allows us to form concise expressions based on iterative actions, for instance. The following example selects all members of mySet where the name of the color is "blue". mySet refers to some attribute of the expression "context", and .color refers to the property of the element of the mySet under examination.

 mySet[.color.name=="blue"]

When nested telescoping constructs come into play, leading dots "." can be chained to refer to subjects of outer containers. For example, telescoping also allows us to "enclose" an intermediate result to avoid re-evaluation. In this example, a costlyArray is computed once, and the last 5 elements (as a range select) are returned.

 costlyArray { .[ ..@size-5 .! ..@size ] }

Another example

 customer.orders[.amount>customer.threshold]

In the preceding example, the .amount identifier refers to order object in the order list (the subject within the telescope), while the customer identifier refers to the outer context in which the expression was evaluated.

In some cases, an application component will bind an expression into an existing telescope. In the following simple example, a telescoped subject is assumed. The example represents the use of an expression as the criteria of a parameterized DALai Query, where the context supplies the parameters, and the subject represents the object being queried.

.name==nameParam && .dateOfBirth==dateOfBirthParam 

In the preceding example, the .name identifier refers to the item of the collection being searched, where the nameParam identifier refers to the query parameter context (eg. a value entered by a user)

Selection

The array subscript operator supports extended operations on arrays and collections.

Basic indexing:

x[i] // i is an integer expression

Basic indexing, error case:

x[outOfBounds] // returns null

Boolean search through telescoped closure- returns same collection type as x but only matching items

x[<condition>] 
x[.y>z]
x[.id==id][0] // unique condition, return null if not found

Associative:

x[<key>] // x is a map-like object

Type Reflection

Resolve the Reflector for the type associated with a type URI.

This is similar to Focus resolution, but the type metadata is accessed by querying registered TypeModels and obtaining an instance of a Reflector, instead of the Contextual Singleton object that implements the specified type.

Examples

 [@:class:/myPackage/MyWidget]
 [@myns:MyWidget]

Static Method Call

Static methods are members of Java classes, not the objects they instantiate, this they must be accessed through a type reference.

The Type Reflection operator provides access to the BeanReflector (the type Reflector associated with Java reflection) for the Java class.

For Java classes, the BeanReflector maps static class members into the "@" prefixed meta-namespace.

Examples

 [@:class:/myPackage/MyWidget].@myStaticMethod(param1,param2)
 
 [@myns:MyWidget].@myStaticMethod(param1,param2)

Cast

x.@cast([@mytypes:MySubtype])

Dynamic type

The actual type of the value returned from the expression. null is returned if the value is null.

Use with @cast and Type Focus to tailor EL aware content for specific subtypes (ie. access subtype properties).

x.@subtype
x.@subtype==[@mytypes:MySubtype]

Static type

The type (ie. tbe spiralraft.lang.Reflector instance) that will be returned by the sub-expression.

x.@type

Struct

Structs provide a convenient way to return complex data from an expression without having to define a globally referenceable type in some type model.

A Struct has one or more fields. Each field may have a declared type and/or may have its value derived from an expression evaluated when the Struct is created.

In a Struct, the subject is telescoped to the Struct itself. Internal fields can be accessed using a single dot (".") prefix.

The following Struct returns some information about a String, including the sum of the Integer values. Note that the first outer curly brace suffixes myString and defines a subcontext which telescopes to that expression (myString) and the inner curly brace defines the struct itself. The second subcontext (curly brace construct ) telescopes the Struct instance we just created, and performs a computation on its members.

 myString
 {
   { 
     length:=..length
     ,chars:=..characters
     ,sumChars:=.chars$[ .[*fn:Sum] ]
   }
 }
 { .sumChars * .length
 }

Channel reference

Returns a reference to the Channel that represents the bound sub-expression.

Useful for reporting metadata about bound expressions, and for debugging purposes.

x.@channel.myMetaData

Focus reference

A reference to the current Focus against which the sub-expression will be bound. Useful for querying metadata about application structure.

x.@focus
x.@focus.focusChain