The primitive expressions have no parsing ambiguities that need to be resolved by precedence and associativity. Instead, they form the "atomic" expressions that the rest of the expression grammar builds on.
Although "a + b * c + d" parses as "a + (b * c) + d", you can obtain a different ordering by saying, for example, "(a + b) * (c + d)".
A variable name as an expression yields the value of that variable. Which variable? The variable of the same name whose definition is in scope. The scope rules are explained here. The syntax of an identifier is the same as in Java, except that the "$" character isn't allowed, and names beginning with "_" are reserved for compiler generated temporary variable names.
When the keyword "_" is used as an expression, it is just shorthand for the variable name "ForAllX". It is best thought of as a blank to be filled in, making a region or a twister accepting candidates for that blank. For example, "def r := _ >= 3" produces a region containing all integers greater than or equal to three. Such a region can be used as a one argument function, and acts as if that argument fills in the blank. For example, "r(5)" yields true, as if we were evaluating "5 >= 3".
LiteralIntegers are precision-unlimited integers expressed as a sequence of digits in base 10, 8, or 16, depending on whether it begins with a non-zero digit, "0", or "0x" respectively. The syntax is just like Java's except that it may also contain embedded "_"s which are ignored. This lets you write more readable large integers like 31_536_000 (the number of seconds in a non-leap-year).
LiteralFloat64s are also just like Java's literal double precision numbers, except that E also allows and ignores embedded "_"s.
LiteralCharacters are just like Java's literal unicode characters.
LiteralStrings are just like Java literal unicode strings.
An expression describes both a computation to be executed, and the value yielded by executing that computation. Of these two, the notation of a normal expression is optimized more to make the nature of the computation clear. Since a literal expression involves no computation, its notation is instead optimized to describe the value of the expression. A quasi-literal expression is one that does involve computation to produce a value, and may produce a different value different times, but whose notation is optimized to express the nature of the value produced rather than the computation used to produce it.
To support quasi-literal expressions and quasi-literal patterns (explained elsewhere***), E introduces the notion of the pluggable parser. ***more to be said.
Looks up the named protocol handler (the identifier to the left of the colon) in the collection named "uriGetters" in the current scope, and asks that to dereference the uri-body string (the string to the right of the colon). Note that this string may contain a uri fragment specification -- a "#" followed by more uri-body string. It is up to the protocol handler to either interpret this as a fragment, or, if inappropriate, throw an exception. In no case should the protocol handler interpret a "#" and the following string as part of the main uri-body string. Each protocol handler should document how it handles fragments.
If a single letter, "a" thru "z" or "A" thru "Z" appears as a protocol handler, it is assumed to be a drive letter, so an implicit "file:" is added to the left, making the drive letter and the following colon the beginning of the uri-body string.
See "URI Expressions".
See "The if Expressions".
Unless stated otherwise, all text on this page which is either unattributed or by Mark S. Miller is hereby placed in the public domain.