Skip to content

Syntax Reference

This document helps understand how Temper code is parsed. Readers can use temper repl to get feedback on how the language interprets a piece of code. Especially useful is the describe REPL command which lets you view a snapshot of the compilation state at various processing stages.

Temper's syntax should be familiar to users of “C-like” languages: languages that use {...} around blocks and semicolons (;) separate computational steps. It is most similar to TypeScript; types follow names (name: Type) with a colon in between. But its syntax is distinct from JS/TS in details.

Some diffences include:

  • Temper uses let for named function declarations so that there is no confusion about when a named function's name is visible in the surrounding scope. Temper is a macro language so this is important when macros can operate on declarations.
  • Temper allows for interpolation into any string, so "chars${expr}". For backwards compatibility, JavaScript could only allow interpolation into back-tick strings (`chars${expr}`).
  • Temper has substantially different syntax for function expressions (fn (x: Int): Int { x + 1 } instead of (x: Int): Int => x + 1) and function types (fn <T> (T): T instead of <T>(T) => T).
  • Temper's import and export syntax, which allows connecting modules together is different.

The grammar below explains the main syntactic categories. It's meant to be advisory, to help learners discover features by following grammatical threads.

It is not an exact grammar. Temper has a three-stage parse: lexical analysis, operator precedence grouping, tree building. This grammar is derived from the tree builder which operates on a stream of tokens after an operator precedence parser has inserted synthetic parentheses into the token stream and after some other token level rewriting operations.

Since Temper is a macro language, some language features that would have separate syntactic paths in a non-macro language are instead implemented as macros; they parse as regular function calls, but those functions are macros that apply at a leter compilation-stage. For example, if is a macro so there is no dedicated syntax for if statements below.

Structure of a file

Syntax for Root

Root := TopLevels end-of-file TopLevels end-of-file

The root of a Temper module is a sequence of top-levels followed by an end of file marker.

Syntax for TopLevels

TopLevels := (TopLevelsInSemi | TopLevel)* TopLevelsInSemi TopLevel

Top-levels are separated by semicolons in a module body or block.

Syntax for TopLevel

TopLevel := TopLevelNoGarbage | Garbage TopLevelNoGarbage Garbage

A top-level is roughly a whole declaration, or expression. Temper is an expression language, so most statement-like constructs can also nest.

Syntax for Garbage

Garbage := Consumes problem tokens and emits an error syntax-tree node Consumes problem tokens and emits an error syntax-tree node

Syntax for TopLevelNoGarbage

TopLevelNoGarbage := EmbeddedComment? TopLevelNoGarbageNoComment EmbeddedComment TopLevelNoGarbageNoComment

Syntax for TrailingSemi

TrailingSemi := ";"? Nop* ; Nop

Semicolons (;) are allowed at the end of a block or module body. An expression followed by a semicolon is not implicitly the result of the containing block or module.

Trailing semicolons are never inserted.

Statements

Syntax for Stmt

Stmt := LabeledStmt | MatchBranch | StmtBlock | CommaExpr MatchBranch LabeledStmt StmtBlock CommaExpr

Statements are not a large syntactic category, but include labeled statements (like someName: /* loop or block */), jumps (break, continue, return, etc.) which go to the end or beginning of a containing statement and which may refer to a label.

Besides those, any expression may appear in statement position.

Syntax for Nop

Nop := ";" ;

A semicolon used to separate statements. Since our parser is built around an operator precedence parser, and semicolon is a low precedence operator, this grammar consumes them, but does not require them.

Not all semicolons need to appear explicitly in program text.

Automatic Semicolon Insertion

Semicolons are inserted in the following places:

  • After } that end a line except before a close bracket or an operator token that is not prefix.
  • Before { that starts a line except after an open bracket or an operator token that is not postfix.

This is more conservative than semicolon insertion in JavaScript, but still simplifies several things.

All adjacent statements are separated by semicolons

There's no need to have a set of special statements like if (...) stmt0 else stmt1 that do not need to be followed by a semicolon. Productions for a series of statements and declarations can simply assume that semicolons appear before them.

No limited set of statement continuers

We don't need a special set of statement continuers like else so that we know that the token sequence } else { is part of one statement. This lets us use common cues to allow new continuers like

foo(x) {
  // Ruby-style block
} bar(y) {
  // ruby-style block
}
// ⏸️

which de-sugars to a single statement

foo(x, fn { ... }, bar = fn (f) { f(y, fn { ... }) });
// ⏸️

vs something without a continuer

foo(x) {
  // Ruby-style block
}                         // <-- Semicolon inserted here
bar(y) {
  // Ruby-style-block
}
// ⏸️

which de-sugars to two statements

foo(x, fn { ... });
bar(y, fn { ... });
// ⏸️
Motivation

Developers of C-like languages are used to not following }s that end a statement with a semicolon.

The exception is class definitions in C++ which, unlike Java and more recent C-like languages do need to be followed by semicolons.

That that trips me up everytime I go back to C++ seems evidence that requiring semicolons after statements that end with something block-like would be a burden to developers.

Syntax for LabeledStmt

LabeledStmt := (LeftLabel | QuasiHole) ":" (Call | StmtBlock) LeftLabel QuasiHole : Call StmtBlock

Declares a label and associates it as markers for the beginning and end of a statement, so that breaks and continues within that statement may refer to it explicitly.

Unlike TypeScript, we do not allow labeling any statement. This allows conveying property declarations like the p: T in

interface I {
  p: T;
}
// ⏸️

to the disAmbiguate stage with that property declaration treated as a (Call (Name ":") (Name "p") (Name "T"))

Otherwise, we would have to treat class bodies as a special syntactic category to avoid ambiguity with

do {
  p: T;
}
// ⏸️

or the disambiguation would need to convert T's from statement context to expression context.

Syntax for LeftLabel

LeftLabel := UnreservedWord UnreservedWord

A label that that can be jumped to as by break and continue. This is left in the left-hand-side sense: it is a declaration, not a use.

Syntax for Jump

Jump := ("break" | "continue") LabelOrHole? break continue LabelOrHole

A jump to a location within the same function body that does not skip over any necessary variable initializations.

Syntax for LabelOrHole

LabelOrHole := Label | QuasiHole Label QuasiHole

Syntax for Label

Label := UnreservedWord UnreservedWord

A label that can be jumped to as by break and continue.

Syntax for AwaitReturnThrowYield

AwaitReturnThrowYield := ("await" | "return" | "throw" | "yield") (Expr | Garbage | ()) return await throw yield Expr Garbage

await, return, throw, and yield are operators which affect control flow and operate on expressions.

return(42); for example, looks like a function call but the parentheses are not required:

  • return; is an application of an operator even though there are no parentheses.
  • return 42; is an application of the operator to the arguments (42) even though there are no explicit parentheses.

Syntax for StmtBlock

StmtBlock := RawBlock RawBlock

A { ... } delimited block of statements.

Expressions

Syntax for Expr

Expr := QuasiTree | QuasiAst | QuasiHole | Jump | AwaitReturnThrowYield | Id | TypeArgumentName | SymbolLiteral | List | New | Prefix | SpecialDot | RegularDot | Call | StringLiteral | "(" ((Disallowed: ()) TopLevelNoGarbageNoComment | Garbage) ")" | Arrow | OfExpr | Infix | Postfix | Member | Specialize | Literal | RegExp | Obj | RawBlock | "(" Garbage RegularDot SpecialDot Prefix New List SymbolLiteral TypeArgumentName Id AwaitReturnThrowYield Jump QuasiHole QuasiAst QuasiTree Call StringLiteral ( Disallowed TopLevelNoGarbageNoComment Garbage ) Arrow OfExpr Infix Postfix Member Specialize Literal RegExp Obj RawBlock ( Garbage

An expression is evaluated to produce a result and/or a side effect.

Syntax for BooleanLiteral

BooleanLiteral := "false" | "true" false true

Syntax for Float64Literal

Float64Literal := ((whole number: [0-9]+*("_")) "." (fraction: [0-9]++("_")) (exponent: ("E" | "e") (() | "+" | "-") [0-9]++("_"))? | (whole number: [0-9]+*("_")) (exponent: ("E" | "e") (() | "+" | "-") [0-9]++("_"))) (suffix: ("D" | "d")+)? | (whole number: [0-9]+*("_")) (suffix: ("D" | "d")+) [0-9] _ whole number . [0-9] _ fraction E e + - [0-9] _ exponent [0-9] _ whole number E e + - [0-9] _ exponent D d suffix [0-9] _ whole number D d suffix

Syntax for Call

Call := CallHead "callJoin:" CallTail | CallHead CallHead callJoin: CallTail CallHead

The call expression includes simple function calls (f(args)) as well as calls with Ruby-style block functions (f { ... }) and flow-control like if (condition) { t } else { e } because [if is a macro][builtin/if].

Syntax for New

New := "new" Type "(" Args? ")" new Type ( Args )

Syntax for StringLiteral

StringLiteral := "\u0022" (SourceCharacter - (\\n\, \\r\, \\\, \"\) | EscapeSequence | (UTF-16 Code Unit: "\\u" Hex Hex Hex Hex) | (Unicode Scalar Values: "\\u{" HexDigits+(",") "}") | (Interpolation: "\u0024{" Expr "}"))* "\u0022" | (Multi-quoted string may start with 3 or more "\s;: "\u0022\u0022\u0022⁺" "LineBreak" (SourceCharacter - (\\\) | EscapeSequence | (UTF-16 Code Unit: "\\u" Hex Hex Hex Hex) | (Unicode Scalar Values: "\\u{" HexDigits+(",") "}") | (Interpolation: "\u0024{" Expr "}"))* "LineBreak" indentation "\u0022\u0022\u0022⁺") " SourceCharacter - ('\n', '\r', '\', '"') EscapeSequence \u Hex Hex Hex Hex UTF-16 Code Unit \u{ HexDigits , } Unicode Scalar Values ${ Expr } Interpolation " """⁺ LineBreak SourceCharacter - ('\') EscapeSequence \u Hex Hex Hex Hex UTF-16 Code Unit \u{ HexDigits , } Unicode Scalar Values ${ Expr } Interpolation LineBreak indentation """⁺ Multi-quoted string may start with 3 or more "'s;

Block Lambdas

Syntax for BlockLambda

BlockLambda := (Lookbehind: !BagPreceder) ("{" BlockLambdaSignatureAndSupers ";;" BlockLambdaBody "}" | RawBlock) !BagPreceder Lookbehind { BlockLambdaSignatureAndSupers ;; BlockLambdaBody } RawBlock

A block lambda is a {...} block that specifies a function value and which cna appear as part of a function call as below:

someFunction { ... }

Optionally, a signature is needed to specify argument names, and may specify the function type wholly or partially.

It may be followed by an extends clause that specifies marker interfaces that are super-types for the produced function value.

The signature is followed by the double-semicolon token (;;) which is distinct from two, space separated semicolons (; ;).

someFunction /* <- callee */ {
  (x): ReturnType               // <- Optional signature
  extends SomeInterfaceType     // <- super types
  ;;                            // <- double semicolon separator

  body
}
// ⏸️

Syntax for BlockLambdaSignatureAndSupers

BlockLambdaSignatureAndSupers := BlockLambdaSignatureAndSupers BlockLambdaSupers | BlockLambdaSignature | Garbage BlockLambdaSignatureAndSupers BlockLambdaSupers BlockLambdaSignature Garbage

The signature of a block lambda explains the names of arguments visible within the body, optionally their types and return type.

The signature also includes other interfaces that the lambda must implement. For example, a function that might pause execution could use a signature line as below:

(x: Int): Int extends GeneratorFn;;

That describes a function that takes an integer x and which also is a sub-type of GeneratorFn.

The extends clause may be left off entirely if no super-types are desired, or multiple super-types may be specified: extends First & Second.

Unlike in a function type, when a name is specified for a block lambda argument, it is the name of the argument, not its type.

let f: fn (Int): Void;
//         ⇧⇧⇧ word is a type

let g(myLambda: fn (Int): Void): Void { myLambda(1); }

g { (x): Void;;
  // ⇧ word is an argument name.
  // In this case, the type is inferred from g's signature.
  doSomethingWith(x + 1);
}
// ⏸️

Syntax for BlockLambdaSignature

BlockLambdaSignature := "(" Formals? ")" ":" Type | "(" ("..." | Formals?) ")" ( Formals ) : Type ( ... Formals )

A block lambda signature line like (x: Int): ReturnType or just (x) to take advantage of the more aggressive type inference for block lambdas than for declared functions.

This is often followed by ;; as it is part of BlockLambdaSignatureAndSupers Syntax.

These syntactic constructs are interpreted as if preceded by fn but the meaning is subtly different.

  • (x: Int) is equivalent to fn (x: Int) where the return type must later be inferrable from the calling context and the body.
  • (x) is equivalent to fn (x) where the argument and return type must later be inferrable.
  • (x): ReturnTypeis equivalent tofn (x): RT` where only argument types must later be inferrable.

Syntax for BlockLambdaSupers

BlockLambdaSupers := ("extends" | "implements") Type ("," Type)* ","? extends implements Type , Type ,

Syntax for BlockLambdaBody

BlockLambdaBody := TopLevelsInSemi | TopLevel? TopLevelsInSemi TopLevel

Uncategorized

Syntax for Arg

Arg := "@" (Expr "(" (Disallowed: ()) Arg ")" | Expr Arg) | (() | (Disallowed: (Disallowed: "let"))) (ArgNoInit "=" Expr | ArgNoInit | "..." | LetArg) | Garbage @ Expr ( Disallowed Arg ) Expr Arg let Disallowed Disallowed ArgNoInit ArgNoInit = Expr ... LetArg Garbage

Syntax for ArgNoInit

ArgNoInit := Pattern ":" Type | (Pattern ":" Type | Pattern) "supports" Type | Pattern Pattern : Type Pattern : Type Pattern supports Type Pattern

Syntax for Args

Args := (Arg ("," Arg)*)? ","? | Arg | Garbage Arg , Arg , Arg Garbage

Syntax for Arrow

Arrow := ArrowHead "=\u003e" (BlockLambda | (Disallowed: ()) Expr) ArrowHead => BlockLambda Disallowed Expr

Syntax for ArrowHead

ArrowHead := "(" Args ")" | ArrowHead ":" Type ( Args ) ArrowHead : Type

Syntax for CallArgs

CallArgs := StringGroupTagged | "(" (ForArgs | Args?) ")" StringGroupTagged ( ForArgs Args )

Arguments to a function call.

A function call's arguments may be one of:

  • a parenthesized, comma separated list of arguments like (a, b, c). See Args
  • a parenthesized, semicolon separated list of 2 or three arguments with a specific purpose. As in (let x = 1; x < 2; ++x) which is what the for loop macro expects.
  • a string group as in a tagged string template like callee"foo ${ bar }".

Syntax for CallHead

CallHead := CalleeAndArgs BlockLambda | CalleeAndRequiredArgs CalleeAndArgs BlockLambda CalleeAndRequiredArgs

The function called, its arguments, and any block lambda

Syntax for CallJoiningWords

CallJoiningWords := (merged to symbol: UnreservedWord+) UnreservedWord merged to symbol

When the call continues with something like } else if (c) {...} we need to include \else_if = fn {...} as a final named parameter to the call that receives the block just closed, so that the called function can delegate its result to later segments. This joins words like else if into the \else_if symbol which follows the call join symbol. A late parse stage pass finds those and groups everything following the joining words into a trailing block function so that the contents of the parentheses and brackets can match their own signature elements based on the joining words.

Syntax for CallTail

CallTail := (CallJoiningWords CallArgs | CallJoiningWords) BlockLambda "callJoin:" CallTail | (CallJoiningWords CallArgs | CallJoiningWords) BlockLambda | CallJoiningWords CallArgs CallJoiningWords CallArgs CallJoiningWords BlockLambda callJoin: CallTail CallJoiningWords CallArgs CallJoiningWords BlockLambda CallJoiningWords CallArgs

Syntax for Callee

Callee := Id UnreservedWord | Callee TypeArguments | Expr Id UnreservedWord Callee TypeArguments Expr

The callee in a function application is a tad complicated.

Our OPP grammar covers many constructs that are bespoke constructs in many languages, so class C extends A, B { ... } is parsed as an application of a block lambda (later turned into a member block) like class(\word, C, \super, A, \super B, fn { .... }).

This production desugars various parts into a combination of the callee class, and symbol/argument pairs.

The \word argument is also used in constructs like function declaration let f<T>(arg: Type) { ... } where the let macro is what is invoked to build a named function declaration.

This production allows a callee to have:

  • an expression specifying the called macro or function,
  • an accompanying word,
  • type parameters like <T, U> (whether the type parameters are actual parameters or formal parameters is determined by the Disambiguate stage),

Syntax for CalleeAndArgs

CalleeAndArgs := CalleeAndArgs ":" Type | CalleeAndArgs ("extends" | "implements") Type ("," Type)* | CalleeAndArgs "forbids" Type ("," Type)* | CalleeAndArgs "supports" Type ("," Type)* | Callee CallArgs | Callee CalleeAndArgs forbids Type , Type CalleeAndArgs extends implements Type , Type CalleeAndArgs : Type CalleeAndArgs supports Type , Type Callee CallArgs Callee

Captures low precedence operators that may follow a parenthesized argument list.

  • : ReturnType desugars to \outType, ReturnType.
  • extends SuperType and implements SuperType* desugars to\super,SuperType`.

Syntax for CalleeAndRequiredArgs

CalleeAndRequiredArgs := CalleeAndRequiredArgs ":" Type | Callee CallArgs CalleeAndRequiredArgs : Type Callee CallArgs

This is like CalleeAndArgs but is used in contexts where we're not sure yet whether this is a call. A call requires at least one of

  • Parenthesized arguments as in callee()
  • Semi-ed arguments as in loopMacro (initialization; condition; increment)
  • A template string as in callee"foo ${bar}"
  • A trailing block as in callee {}

This production succeeds is entered where we may not have a trailing block so must have one of the others.

Syntax for CommaEl

CommaEl := LetBody | Expr | Garbage LetBody Expr Garbage

Syntax for CommaExpr

CommaExpr := CommaOp | Expr CommaOp Expr

Syntax for CommaOp

CommaOp := CommaEl "," (CommaEl ("," CommaEl)* ","?)? CommaEl , CommaEl , CommaEl ,

Syntax for DeclDefault

DeclDefault := "=" Expr = Expr

Syntax for DeclInit

DeclInit := "=" Expr = Expr

Syntax for DeclMulti

DeclMulti := "[" (LetNested "," (LetNested ("," LetNested)* ","?)? | LetNested) "]" [ LetNested , LetNested , LetNested , LetNested ]

Syntax for DeclMultiNamed

DeclMultiNamed := "{" (LetNested ("," LetNested)* ","? | LetNested) "}" { LetNested , LetNested , LetNested }

Syntax for DeclMultiNested

DeclMultiNested := ":" DeclMultiNamed : DeclMultiNamed

Syntax for DeclName

DeclName := UnreservedWord UnreservedWord

Syntax for DeclType

DeclType := ":" Type : Type

Syntax for DeclTypeNested

DeclTypeNested := "is" Type is Type

Syntax for DecoratedLet

DecoratedLet := DecoratedLetBody ("," LetRest)+ | DecoratedLetBody DecoratedLetBody , LetRest DecoratedLetBody

Syntax for DecoratedLetBody

DecoratedLetBody := "@" (Expr DecoratedLetBody | Expr "(" (Disallowed: ()) DecoratedLetBody ")") | LetBody @ Expr DecoratedLetBody Expr ( Disallowed DecoratedLetBody ) LetBody

Syntax for DecoratedTopLevel

DecoratedTopLevel := DecoratedLet | "@" (Expr TopLevelNoGarbageNoComment | Expr "(" TopLevelNoGarbageNoComment ")") DecoratedLet @ Expr TopLevelNoGarbageNoComment Expr ( TopLevelNoGarbageNoComment )

Decorations transform declarations and function and type definitions at compile time.

@SomeName followed by an optional argument list

When a let declaration declares multiple names, any decoration before the let applies to all the names, but declarations immediately before a declared name affect only that name.

Syntax for EmbeddedComment

EmbeddedComment := ()

Comments are not semantically significant but nor are they filtered out entirely.

Temper tries to preserve them when translating documentation, and they are available to backends; for example, the Python backend turns autodoc comments before declarations into Python doc strings.

Syntax for EscapeSequence

EscapeSequence := "\\0" | "\\\\" | "\\/" | "\\\u0022" | "\\\" | "\\`" | "\\{" | "\\}" | "\\\u0024" | "\\b" | "\\t" | "\\n" | "\\f" | "\\r" | "\\" \0 Encodes NUL \\ Encodes a single backslash \/ Encodes a forward-slash \" Encodes a double-quote \' Encodes a single-quote \` Encodes a back-quote \{ Encodes a left curly-bracket \} Encodes a right curly-bracket \$ Encodes a dollar sign \b Encodes a backspace \t Encodes a tab \n Encodes a line-feed a.k.a. new-line \f Encodes a form-feed \r Encodes a carriage-return \ Broken escape sequence encodes nothing

Syntax for ForArgs

ForArgs := ForInit? ";" ForCond? ";" ForIncr? | ForInit? ";;" ForIncr? ForInit ; ForCond ; ForIncr ForInit ;; ForIncr

Syntax for ForCond

ForCond := CommaExpr CommaExpr

Syntax for ForIncr

ForIncr := CommaExpr CommaExpr

Syntax for ForInit

ForInit := ForInitLet | CommaExpr ForInitLet CommaExpr

Syntax for Formal

Formal := "@" (Expr "(" (Disallowed: ()) Arg ")" | Expr Arg) | (() | (Disallowed: (Disallowed: "let"))) (ArgNoInit "=" Expr | FormalNoInit | "..." | LetArg) | Garbage @ Expr ( Disallowed Arg ) Expr Arg let Disallowed Disallowed FormalNoInit ArgNoInit = Expr ... LetArg Garbage

Syntax for FormalNoInit

FormalNoInit := Pattern ":" Type | (Pattern ":" Type | Pattern) "supports" Type | Pattern Pattern : Type Pattern : Type Pattern supports Type Pattern

Syntax for Formals

Formals := (Formal ("," Formal)*)? ","? | Formal | Garbage Formal , Formal , Formal Garbage

Syntax for Id

Id := UnreservedWord UnreservedWord

Syntax for Infix

Infix := Expr "?" Expr ":" Expr | Expr InfixOp Type | Expr InfixOp Expr Expr ? Expr : Expr Expr InfixOp Type Expr InfixOp Expr

Syntax for InfixOp

InfixOp := (but not callJoin:: InfixOperator) InfixOperator but not callJoin:

Syntax for Json

Json := JsonValue | Garbage JsonValue Garbage

Syntax for JsonArray

JsonArray := "[" (JsonValue ("," JsonValue)* ","? | JsonValue?) "]" [ JsonValue , JsonValue , JsonValue ]

Syntax for JsonBoolean

JsonBoolean := "false" | "true" false true

Truth values are represented using the keywords false and true.

Syntax for JsonNull

JsonNull := "null" null

Syntax for JsonNumber

JsonNumber := "-" Number | Number - Number Number

Syntax for JsonObject

JsonObject := "{" (JsonProperty ("," JsonProperty)* ","? | JsonProperty?) "}" { JsonProperty , JsonProperty , JsonProperty }

Syntax for JsonProperty

JsonProperty := JsonString ":" JsonValue JsonString : JsonValue

Syntax for JsonString

JsonString := "(" "\u0022" ContentChars? "\u0022" ")" ( " ContentChars " )

Syntax for JsonValue

JsonValue := JsonObject | JsonArray | JsonString | JsonNumber | JsonBoolean | JsonNull JsonString JsonArray JsonObject JsonNumber JsonBoolean JsonNull

Syntax for Let

Let := LetBody ("," LetRest)+ | LetBody LetBody , LetRest LetBody

Syntax for LetArg

LetArg := "let" DeclName | LetArg DeclDefault | LetArg DeclType | LetArg DeclReifies LetArg DeclDefault let DeclName LetArg DeclType LetArg DeclReifies

Syntax for LetBody

LetBody := "let" DeclName | "let" DeclMulti | "let" DeclMultiNamed | LetBody DeclInit | LetBody DeclType let DeclMulti let DeclName let DeclMultiNamed LetBody DeclInit LetBody DeclType

Syntax for LetNested

LetNested := DeclName | "..." | DeclMulti | DeclMultiNamed | LetNested DeclInit | LetNested DeclMultiNested | LetNested "as" DeclName | LetNested DeclTypeNested DeclMultiNamed DeclMulti ... DeclName LetNested DeclInit LetNested DeclMultiNested LetNested as DeclName LetNested DeclTypeNested

Syntax for LetRest

LetRest := DeclName | DeclMulti | DeclMultiNamed | LetRest DeclInit | LetRest DeclType DeclMulti DeclName DeclMultiNamed LetRest DeclInit LetRest DeclType

Syntax for List

List := "[" (Type ";" ListContent | ListContent) "]" [ Type ; ListContent ListContent ]

Syntax for ListContent

ListContent := ListElements | ListElement | Garbage? ListElements ListElement Garbage

Syntax for ListElement

ListElement := Spread | Expr | Garbage Spread Expr Garbage

Syntax for ListElements

ListElements := (ListHole | ListElement ","?)* ","? ListHole ListElement , ,

Syntax for ListHole

ListHole := "," ,

Syntax for Literal

Literal := Number Number

Syntax for MatchBranch

MatchBranch := "else" "-\u003e" Expr | MatchCase "-\u003e" Expr | MatchCase ("," MatchCase)* ","? "-\u003e" Expr else -> Expr MatchCase -> Expr MatchCase , MatchCase , -> Expr

Relates a match case, e.g. a pattern, to a consequence of matching that pattern.

Syntax for MatchCase

MatchCase := "is" Expr | "case" BalancedTokenList | Expr is Expr case BalancedTokenList Expr

There are two kinds of match cases: run-time type checks that use keyword is, and a value to match.

Syntax for Member

Member := Expr "[" RawCommaOp "]" Expr [ RawCommaOp ]

Syntax for NoPropClass

NoPropClass := ()

Syntax for Obj

Obj := (Lookbehind: BagPreceder) "{" (Props | NoPropClass) "}" BagPreceder Lookbehind { Props NoPropClass }

Syntax for Pattern

Pattern := "..." Expr | Expr ... Expr Expr

Syntax for Postfix

Postfix := Expr PostfixOp Expr PostfixOp

Syntax for PostfixOp

PostfixOp := PostfixOperator PostfixOperator

Syntax for Prefix

Prefix := PrefixOp Expr PrefixOp Expr

Syntax for PrefixOp

PrefixOp := (but not new, case: PrefixOperator) PrefixOperator but not new, case

Syntax for Prop

Prop := PropName ":" Expr | PropName PropName : Expr PropName

Syntax for PropClass

PropClass := "class" ":" Type class : Type

Syntax for PropName

PropName := (In {...} object syntax, any `class` property must appear first: "class") | UnreservedWord class In {...} object syntax, any `class` property must appear first UnreservedWord

Syntax for Props

Props := (PropClass | NoPropClass Prop) ("," Prop)* ","? | PropClass | NoPropClass Prop PropClass NoPropClass Prop , Prop , PropClass NoPropClass Prop

Syntax for QuasiAst

QuasiAst := "\\(" Expr ")" \( Expr )

Syntax for QuasiHole

QuasiHole := "\u0024{" CommaExpr "}" ${ CommaExpr }

Syntax for QuasiInner

QuasiInner := Quasis Quasis

Syntax for QuasiLeaf

QuasiLeaf := any token any token

Syntax for QuasiTree

QuasiTree := "\\{" (QuasiHole | QuasiInner)* "}" \{ QuasiHole QuasiInner }

Syntax for Quasis

Quasis := (QuasiHole | QuasiInner | QuasiLeaf)* QuasiHole QuasiInner QuasiLeaf

Syntax for RawBlock

RawBlock := (Lookbehind: !BagPreceder) "{" TopLevels "}" !BagPreceder Lookbehind { TopLevels }

Syntax for RawCommaOp

RawCommaOp := CommaEl ("," CommaEl)* ","? | CommaEl CommaEl , CommaEl , CommaEl

Syntax for RegExp

RegExp := RegExp RegExp

Syntax for RegularDot

RegularDot := Expr ("." | "?.") SymbolValue? Expr . ?. SymbolValue

Syntax for ReservedWord

ReservedWord := "nym" | "builtins" | "this" | "super" nym builtins this super

Syntax for SpecialDot

SpecialDot := "builtins" "." BuiltinName | "this" | (Lookahead: "super") Garbage builtins . BuiltinName this super Lookahead Garbage

Syntax for Specialize

Specialize := Expr "\u003c" RawCommaOp "\u003e" Expr < RawCommaOp >

Syntax for Spread

Spread := "..." Expr ... Expr

Syntax for StringGroup

StringGroup := "(" /^"+|\$/ StringPart* /^"+|\$/ ")" ( /^"+|'$/ StringPart /^"+|'$/ )

Syntax for StringGroupTagged

StringGroupTagged := "(" /^"+|[\`]$/ StringPartRaw* /^"+|[\`]$/ ")" ( /^"+|['`]$/ StringPartRaw /^"+|['`]$/ )

Syntax for StringHole

StringHole := "\u0024{" (CommaExpr | Garbage) "}" ${ CommaExpr Garbage }

Syntax for StringPart

StringPart := ContentChars+ | StringHole | UnicodeRun ContentChars StringHole UnicodeRun

String interpolation

Strings may contain embedded expressions. When a string contains a ${ followed by an expression, followed by a }, the resulting string value is the concatenation of the content before, content from the expression, and the content after.

"foo ${"bar"} baz"
== "foo bar baz"
// ✅

Interpolated values that aren't String instances have .toString() called on them automatically, which is convenient for types that define toString, such as Int.

let two = 2;
"one ${two} three"
== "one 2 three"
// ✅

An empty interpolation contributes no characters, which means it may be used to embed meta-characters.

"$${}{}" == "\$\{\}"
// ✅

(This mostly comes in handy with tagged strings to give fine-grained control over what the tag receives.)

Empty interpolations can also be used to wrap a long string across multiple lines.

"A very long string ${
  // Breaking this string across multiple lines.
}that runs on and on"
== "A very long string that runs on and on"
// ✅

Empty interpolations also let you include spaces at the end of a line in a multi-quoted string.

"""
Line 1
Line 2 ${}
"""
== "Line 1\nLine 2 "
// ✅

Syntax for StringPartRaw

StringPartRaw := "raw string"+ | StringHoleRaw | UnicodeRunRaw raw string StringHoleRaw UnicodeRunRaw

Parallels [ProductionNames.StringPart] but emits a [ValuePart] instead of routing a string token to [lang.temper.lexer.unpackQuotedString] so that the tag expression gets string content without escape sequences decoded.

Syntax for SymbolLiteral

SymbolLiteral := "\\" SymbolValue \ SymbolValue

Syntax for SymbolValue

SymbolValue := Word Word

Syntax for TopLevelNoGarbageNoComment

TopLevelNoGarbageNoComment := DecoratedTopLevel | Let | Stmt | QuasiHole Let DecoratedTopLevel Stmt QuasiHole

Syntax for TopLevelsInSemi

TopLevelsInSemi := (Nop* TopLevel)* TrailingSemi Nop TopLevel TrailingSemi

Top levels in the context of a larger semicolon separated run.

Syntax for Type

Type := Expr Expr

Syntax for TypeArgument

TypeArgument := Expr Expr

Syntax for TypeArgumentName

TypeArgumentName := ("in" | "out") Id in out Id

Syntax for TypeArguments

TypeArguments := "\u003c" (TypeArgument ("," TypeArgument)* | TypeArgument) "\u003e" < TypeArgument , TypeArgument TypeArgument >