std/expression
Expression and ExpressionNode, the typed tree a quoted parameter hands over, plus assert and nameOf.
Diese Seite gibt es noch nicht auf Deutsch, deshalb steht sie hier auf Englisch. Zur englischen Seite
Die generierte Referenz von std/expression zeigt jede öffentliche Deklaration mit Signatur, Doc-Kommentar und Beispielen.
std/expression is quoted expressions: if a parameter or a binding has the type Expression<Value>, the compiler
type checks the argument as an ordinary Value and then passes it together with its expression tree. Expression
values can only be created by the compiler; ExpressionNode trees are plain data and can be built, matched and
transformed by anybody. Every name below is already in scope through the prelude.
Import#
use Expression, ExpressionNode, nameOf, assert from "std/expression"fn double(value: Expression<Int>): Int {
print nameOf(value)
value.value() * 2
}
const width = 3
print double(width)Declarations#
Expression#
native type Expression<Value> {
tree: ExpressionNode
source: String
location: SourceLocation
native fn value(): Value
native fn captures(): List<EncodedValue>
}tree is the static data built at compile time, source is the source text of the quoted expression
("_.age >= minAge"), and value() is the ordinary value - for a function type, the closure itself - evaluated at
most once. captures() answers the values of the captured variables, in the order of their Captured.index, so a
provider can render "limit = 5" next to the tree without walking the caller's scope.
SourceLocation and TypeReference#
type SourceLocation with Show {
file: String
line: Int
column: Int
fn show(): String
}
type TypeReference with Show {
name: String
arguments: List<Self> = []
fn show(): String
}SourceLocation shows as "{file}:{line}:{column}". TypeReference is a description of a type as data, not
reflection - there is no way back from a TypeReference to the type it names.
ExpressionNode#
type ExpressionNode {
case Literal(value: EncodedValue, of: TypeReference)
case Parameter(index: Int, name: String, of: TypeReference)
case Captured(index: Int, name: String, of: TypeReference)
case Field(target: Self, name: String, of: TypeReference)
case Call(target: Self?, owner: TypeReference, method: String, arguments: List<Self>, of: TypeReference)
case Construct(arguments: List<Self>, of: TypeReference)
case Unary(operator: UnaryOperator, operand: Self, of: TypeReference)
case Binary(operator: BinaryOperator, left: Self, right: Self, of: TypeReference)
case Conditional(condition: Self, then: Self, else: Self, of: TypeReference)
case Lambda(parameters: List<String>, body: Self, of: TypeReference)
case Items(items: List<Self>, of: TypeReference)
case Interpolation(parts: List<Self>)
fn capturedNodes(): List<Self>
}An ordinary algebraic data type: names are already resolved and everything is typed, so an implicit _, a named
closure parameter, a receiver and an implicit self all show up as explicit Parameter, Field and Call nodes.
UnaryOperator (Negate, Not) and BinaryOperator (Add, Equal, Less, And, ...) are the two small enums the
Unary and Binary cases carry. A provider matches on the cases it understands and ends with _ => Fail(...),
because new node kinds arrive with new versions of the language. capturedNodes() collects every Captured node below
one node, which is what assert uses to render each captured value next to its name.
nameOf#
fn nameOf<Value>(expression: Expression<Value>): StringThe name of what was written, never its value: nameOf(user.email) is "email", nameOf(limit) is "limit". For a
type there is the compile-time function typeName<User>() instead.
assert#
fn assert(condition: Expression<Bool>)Panics if condition is false, with the source text and every captured value in the message:
Assertion failed: adults.length() > limit (adults = [...], limit = 5) at main.trb:12:1There are no matchers: assert is the one function std/test needs, because the expression tree already carries
enough to explain a failure.
Related#
- Quoted expressions - what an
Expression<Value>parameter hands a function, and how it is created. - std/test -
assertin atest/groupbody. - The standard library - the other packages.