E is an expression language, but, unfortunately, C++ is not. To deal
with this mismatch, we compile E using one of two functions:
- FOR_VALUE(EExpr) => resultVarName; outputs C++ statements
- FOR_EFFECT(EExpr); outputs C++ statements
DefineExpr
Special Case: Pattern defines only a single variable |
e`def @var :@slotMakerExpr := @expr`
|
C++
|
FOR_EFFECT
|
def resultName := FOR_VALUE(expr)
MUST_MATCH(patt, resultName)
|
FOR_VALUE
|
//same |
Example |
def a := c + d
|
Ref result := c.call(&DoAdd, d);
Ref a = result;
|
General Case |
e`def @patt := @expr`
|
C++
|
FOR_EFFECT
|
def resultName := FOR_VALUE(expr)
DECLARE_TEMPS(patt)
MUST_MATCH(patt, resultName)
ACTUALS_FROM_TEMPS(patt)
|
FOR_VALUE
|
//same |
Example |
def [a, b] := c + d
|
Ref result := c.call(&DoAdd, d);
Ref aTemp;
Ref bTemp;
ListMustMatch(&aTemp, &bTemp, result);
Ref a = aTemp;
Ref b = bTemp;
|
"def" patt ":=" rValue.
Match patt against the value of rValue. Neither patt nor rValue
may
use any variables defined by the other. This allows their scopes
to
be order independent.
//If patt is a FinalPattern, then it works according to the "def"
//rules above. Otherwise match against temporary variables.
If match
//succeeds, initialize the actual local variables to the temporaries.
//Anywhere it fails, throw the (XXX to-be-specified) suitable
//exception. Since the actual variable would then never come into
//scope, they need not be defined and initialized.
CallExpr
General Case |
e`@recip.@verb(@arg0,...)`
|
C++
|
FOR_EFFECT
|
def recipName := FOR_VALUE(recip)
def arg0Name := FOR_VALUE(arg0)
...
cpp`$recipName.call(&$DoVerb, $arg0Name, ...);`
|
FOR_VALUE
|
def recipName := FOR_VALUE(recip)
def arg0Name := FOR_VALUE(arg0)
...
def resultName := newTemp()
cpp`Ref $resultName := $recipName.call(&$DoVerb, $arg0Name, ...);`
|
Example |
a + b
|
Ref result = a.call(&DoAdd, b);
|
Eval left to right, then synchronously call the verb method of the value
of recip with the values of the args.
SendExpr
General Case |
e`@recip <- @verb(@arg0,...)`
|
C++
|
FOR_EFFECT
|
def recipName := FOR_VALUE(recip)
def arg0Name := FOR_VALUE(arg0)
...
cpp`$recipName.sendOnly(&$DoVerb, $arg0Name, ...);`
|
FOR_VALUE
|
def recipName := FOR_VALUE(recip)
def arg0Name := FOR_VALUE(arg0)
...
def resultName := newTemp()
cpp`Ref $resultName := $recipName.send(&$DoVerb, $arg0Name, ...);`
|
Example |
a <- add(b)
|
Ref result = a.send(&DoAdd, b);
|
Like call, but asks that recip do the operation <i>eventually</i>,
in its own turn.
EscapeExpr
General Case |
e`escape @hatch { @body }`
|
C++
|
FOR_EFFECT
|
|
FOR_VALUE
|
|
Example |
def result := escape break {
loop {
if (foo()) {
break(x)
}
}
}
|
Ref result;
Ejector * ejector = new Ejector();
Ref break = ejector.ref();
try {
while(true) {
if (test(foo.call(&DoRun))) {
break.call(&DoRun, x);
}
}
} catch (Ejection *ej) {
result = ejector->result(ej);
} finally {
ejector->disable();
}
|
Bind hatch to an escape hatch. If the escape hatch's run/1 is called
during the execution of body, the escape expression will be exited early,
and the run argument will be the value. If run/0 is called, it's
as if run(null) were called.
HideExpr
e`{ @ body }`
Evaluate body, but hide all variable names it defines from the surrounding
scope.
Only has an effect on the compiler's scope calculation. Generates
no code.
IfExpr
General Case |
e`if (@query) { @then } else {@els }`
|
C++
|
FOR_EFFECT
|
def queryName := FOR_VALUE(query)
cpp`if (test($queryName)) {
${FOR_EFFECT(then)}
} else {
${FOR_EFFECT(els)}
}
|
FOR_VALUE
|
def queryName := FOR_VALUE(query)
def resultName := newTemp()
cpp`Ref $resultName;
if (test($queryName)) {
${def name0 := FOR_VALUE(then)}
cpp`$resultName = $name0;
} else {
${def name1 := FOR_VALUE(els)}
cpp`$resultName = $name1;
}
|
Example |
|
|
Evaluate test to a boolean. If true, the value is the evaluation
of then. Else the value is the evaluation of els.
Not yet dealt with: then evaluates in the scope
of query, els does not.
LiteralExpr
This value is the value of the expression.
|
E |
C++ |
Unicode Character Literal |
'c'
|
Ref('c')
|
String |
"foo"
|
Ref("foo")
|
boolean |
true
|
Ref(true)
|
false
|
Ref(false)
|
-2**30 <= Integer < 2 ** 30 |
1234
|
Ref(1234)
|
BigInteger |
1000000000000
|
BigInteger.call(&DoFromString,
Ref("1000000000000"))
|
float64 |
3.14159
|
Ref(3.14159)
|
LoopExpr
General Case |
e`loop { @body }`
|
C++
|
FOR_EFFECT
|
cpp`while (true) {
${FOR_EFFECT(body)}
}
|
FOR_VALUE
|
//same
|
Example |
|
|
Execute body repeatedly forever (or until stopped by other means).
Not yet formatted, edited, or made readable
But does contain useful content. Proceed past this
point at your own risk.
MatchBindExpr
specimen "=~" patt
Match patt against the value of specimen. Say whether it matches.
Ref specimen = TRANSLATE(specimen);
boolean result = true;
Ref local1;
...
do {
//match specimen against patt, binding to temporary
variables as
//we go. Anywhere we fail, we do:
result = false;
break;
//After all matching steps are completed, we fall through
to
//binding the actual variables:
local1 = temp1;
...
} while(false);
if (!result) {
local1 = local2 = ... = //appropriate broken ref
}
//result is value of MatchBindExpr
ScopeExpr
"meta" "scope"
Reifies the current runtime scope as a runtime scope object.
Object visitScopeExpr();
SeqExpr
first "\n" second
Do first, then evaluate to the result of second.
TRANSLATE(first);
TRANSLATE(second);
MetaExpr
"meta" "(" varName ")"
varName must be the paramName of an enclosing object-expression or
plumbing-expression. This returns the EMeta giving meta-level
access to that object.
Object visitMetaExpr(NounExpr noun);
CatchExpr
"try" "{" attempt "}" "catch" patt "{" catcher "}"
Evaluate attempt. If it completes successfully, then its value
is the
value of the catch-expression. If it throws an exception, match
it
against patt. If it succeeds, evaluate to the evaluation of catcher.
Otherwise rethrow the exception.
Ref result;
try {
result = TRANSLATE(attempt);
} catch (Ref &problem) {
if (TRANSLATE(`$problem =~ $patt`)) {
result = TRANSLATE(catcher);
} else {
throw problem;
}
}
//result is value of Catch expression
FinallyExpr
"try" "{" attempt "}" "finally" "{" unwinder "}"
Evaluate attempt. On the way out, whether by successful completion
or
abrupt termination (throw or escape), in all cases evaluate the
unwinder before leaving. If attempt succeeds, then the value of
the
finally-expression is the value of attempt.
Ref result;
try {
result = TRANSLATE(attempt);
} finally {
TRANSLATE(unwinder);
}
//result is value of Finally expression
|