Skip to content

Declarations

SourceFile = NamespaceDecl { Using } { Declaration } .
NamespaceDecl = "namespace" QualifiedName .
QualifiedName = identifier { "." identifier } .
Using = "using" string_lit
| "using" "static" string_lit
| "using" identifier "=" string_lit .
Declaration = [ "pub" ] ( DataDecl | RefDataDecl | ChoiceDecl | RefChoiceDecl
| EnumDecl | InterfaceDecl | DelegateFuncDecl
| StaticFuncDecl | FuncDecl | ConstDecl | DeriveDecl ) .

Every source file begins with exactly one NamespaceDecl. Name resolution and the semantics of Using are specified in Names & resolution. A declaration is internal unless prefixed with pub.

Type = QualifiedName [ TypeArgs ] [ "?" ] // named type, optional generic args, optional nullable
| "*" Type // pointer
| "readonly" "*" Type // read-only by-ref (parameter position)
| "(" TypeList ")" // tuple
| FuncPtrType .
FuncPtrType = "&" "(" [ TypeList "->" ] Type ")" . // &(int, int -> int) , &(-> bool)
TypeArgs = "<" TypeList ">" .
TypeList = Type { "," Type } .

A type name in declaration position shall be PascalCase (ES2160). *T is a pointer; *RefData is ill-formed (ES2003). T? denotes an optional (see Types → nullable).

DataDecl = [ "readonly" ] "data" TypeName [ Generics ]
( "{" [ FieldList ] "}" | "(" [ ParamList ] ")" ) .
FieldList = Field { ( "," | newline ) Field } .
Field = [ "let" | "var" | "pub" ] identifier ":" Type [ "=" Expr ]
| [ "pub" ] [ "*" ] TypeName . // embedded (anonymous) field
Generics = "<" identifier { "," identifier } ">" .

A data introduces a value type: assignment copies it, and two values are equal iff their fields are equal. The representation (CLR struct or class) is implementation-defined and unobservable (Allocation). The following are ill-formed:

  • a field whose type contains the enclosing type by value — ES2002; break the cycle with *T.
  • an init block — ES3012; construct with a composite literal or factory.

readonly data makes every field immutable and emits [IsReadOnly]. The positional form data T(params) adds positional construction. An embedded field promotes the embedded type’s members to the enclosing type. Generic type parameters are reified.

RefDataDecl = [ "open" | "abstract" ] "ref" "data" TypeName [ Generics ]
[ ":" BaseList ] "{" { Member } "}" .
BaseList = TypeName { "," TypeName } . // base class, if any, is first
Member = Field | Init | Method | EventDecl | ConstDecl | ReturnsClause .
Init = "init" "(" [ ParamList ] ")" [ ":" "base" "(" [ ArgList ] ")" ] Block .
Method = [ "virtual" | "abstract" | ":" ] "func" identifier
"(" [ ParamList ] ")" [ ReturnType ] ( Block | "=" Expr | ε ) .
ReturnsClause = "returns" Type . // class-level default return

A ref data is a CLR class (identity, reference equality). It is sealed by default — emitted as a sealed class, with no modifier to write; open makes it inheritable, abstract makes it non-instantiable and inheritable. open is the only shape that is both instantiable and inheritable; E#‘s OO stance is that a class is either abstract or sealed, so open is slated to become an explicit .esproj opt-in, disabled by default. Field defaults run before the init body. Inheritance — the member forms, the : func role inference, : base(...), and the ES2120–2128 diagnostics — is specified in the guide page Inheritance. EventDecl is specified in Delegates & events.

ChoiceDecl = "choice" TypeName [ Generics ] "{" { Case } "}" .
RefChoiceDecl = "ref" "choice" TypeName [ Generics ] "{" { Case } "}" .
Case = identifier [ "(" ParamList ")" ] .

A choice is a tagged union — a value is exactly one case, each with zero or more payload fields. A value choice emits as a tag enum plus a struct (always a struct). A ref choice emits an abstract base with one sealed subclass per case; the per-case CLR type is Outer_case. Generic choice is reified. Cases are constructed by factory (T.case(args)) or dot-case shorthand (.case(args)) and consumed by match.

EnumDecl = "enum" TypeName "{" { EnumCase } "}" .
EnumCase = identifier [ "=" int_lit ] .

Emits as a CLR System.Enum with int32 underlying type. A case without an explicit value takes the previous value + 1 (the first defaults to 0). Cases are constructed with a trailing () (Dir.north()).

InterfaceDecl = "interface" TypeName [ Generics ] "{" { InterfaceMember } "}" .
InterfaceMember = "func" identifier "(" [ ParamList ] ")" [ ReturnType ]
| EventDecl .

Emits as a CLR interface. Conformance is nominal: a type conforms only if it names the interface after :, and the match is exact (name, parameter types, return type). A type that would satisfy an undeclared interface raises ES2153. Where only the pointer method set conforms, the __Ptr_T wrapper implements the interface (see Pointers).

FuncDecl = "func" identifier [ Generics ] "(" [ ParamList ] ")" [ ReturnType ]
( Block | "=" Expr ) .
ReturnType = ( "->" | "returns" ) Type .
ParamList = Param { "," Param } .
Param = [ "out" ] [ "readonly" ] identifier ":" Type
| identifier ":" "*" Type .
StaticFuncDecl = "static" "func" TypeName "{" { StaticMember } "}" .
StaticMember = ConstDecl | "let" identifier [ ":" Type ] "=" Expr
| "var" identifier [ ":" Type ] "=" Expr | FuncDecl | ReturnsClause .
DelegateFuncDecl = "delegate" "func" TypeName "(" [ ParamList ] ")" [ ReturnType ] .

A free function’s name shall be camelCase (ES2161); a method (first parameter a data receiver) and a static func member may be PascalCase. A function whose first parameter is an E#-declared data value is promoted to a method on that type and shall be called as recv.f(...); calling it free is ES2142. Promotion, by-ref/out parameters, function pointers, and the returns default-return clause are specified in Functions and Pointers & by-ref. A static func body shall contain only fields, const, and functions; a bare statement is ES1010. A delegate func mints a nominal delegate type (Delegates & events).

ConstDecl = "const" identifier [ ":" Type ] "=" Expr .
DeriveDecl = "derive" identifier { "," identifier } . // precedes a data declaration

A const initializer shall fold to a compile-time literal; otherwise ES1011 (use let). const is valid at namespace, static func, ref data, and function-body scope. derive generates members at compile time: equality emits Equals / GetHashCode / == / !=; debug emits ToString().