Declaration ::= TypeDecl | StructDecl | ProcDecl | VarDecl | MacroDecl
| LabDecl .
TypeDecl ::= "T" ID "=" Type .
ID ::= "#" number |
[ "{" ID "}" ] identifier [ "." ID ].
StructDecl ::= StructID ID "{" { MemberDecl
";" } "}" .
StructID ::= "E" | "S"
| "U" | "I" .
MemberDecl ::= ("m"|"c"|"d"|"p")
ID [ ":" Type ] [ "{" Expr "}" ]
ProcDecl ::= ("F"|"P"|"M") identifier ProcType
[ "{" [StmtList] "}" ] .
ProcType ::= [Scope]
["P"] "(" Params ")" [Call] [Type] .
MacroDecl ::= ("#"|"X"|"M") identifier [ "("
Params ")" ] "{" [StmtList] "}" .
VarDecl ::= ("V"|"C") [Scope] identifier [ ":"
Type ] [ "{" [ExprList] "}" ] .
LabDecl ::= "L" identifier .
DeclID = "T" | "E" | "S" | "U" | "F" | "P" | "V" | "C" | "L" .
Item ::= [ Literal ] [ "(" { Item "," } ")" ] [ ":" Item ] [ "="
Expr ] .
Literal ::= number | identifier | '"' string '"' | "'" character
"'" .
VarConst ::= identifier [ ":" Type ] [ "{" ExprList "}" ] .
Type ::= identifier "=" { TypeMod } ( '"' identifier '"' | BaseType
) .
TypeMod ::= "*" | "[" { ExprList } "]" | "(" ParamList ")" |
Identifiers can be alphanumeric or operator-shortcuts. They cannot begin
with any of the other Literal item delimiters.
Parentheses always must be paired, unless they are embedded into a
Literal. For optimized scanning the following set of paired delimiters
should be used:
Commas "," inside item lists are definite item separators (terminators). Other list separators may be used to structure individual items. The suggested convention of item terminators instead of item separators simplifies the input and output procedures for list/tree structures.
Newline and whitespace characters are unspecific separators, with no special meaning, but they can occur only in places where other separators are allowed. Inside Literals all whitespace characters are part of the literal, but newline characters are not allowed.
The semantical interpretation of all Items is implied by the Item name. The part after the closing Item parentheses can have a special structure, as outlined above, but the exact interpretation depends on the actual context.
The C operators (!, ~, -, *, &) can be used, where ambiguous unary/binary
operators can be distinguished by the argument count.
Pre/postfix increment and decrement deserve different operators.
Subexpressions may have an empty operator, i.e. only the expression
is stored inside parentheses.
Member selection (".", "->") can be expressed in the same syntax, with
appropriate grouping.
Array selection can be expressed by an array operator [](array,index).
Explicit type casts and conversions should be specified by distinct
cast and convert operators. Type conversions can be implied by specific
operators, and possibly can be represented by an optional ":"<type>
postfix attribute. By convention a double-quoted typename can be used as
an identifier for type conversions; this comes close to procedural type
casts (C++, Delphi), but may lead to ambiguities. Explicit type casts must
be retained in explicit Items, possibly with explicit source as well as
target types.
Assignment operators can be splitted into two operations: assign(lhs,op(lhs,rhs))
The same syntax can be used for conditional statements (if, switch), as long as statments can be distinguished from expressions.
This operator can be used to represent the C comma operator.
For numeric values more attributes can be inserted, to e.g. distinguish integral and floating point values, the original base of integeral values (decimal, octal...) etc.
These statements can use the according expression syntax of assignment, function-call and general expressions.
A Switch can have discontiguous case values, i.e. none or one statement list can pertain to one or more values, so that a special syntax is required. A Switch also can have an default branch, and different fall-through behaviour.
switch(cond {,case(values,statement)} [,default(statement)]) /* C fall
through convention */
select(<same syntax>) /* Pascal else-if like convention */
The <values> argument should be a list of explicit (constant) values, for single values the parentheses around the list may be omitted.
Distinct loop types may be defined, to reflect e.g.:
The C setjmp/longjmp constructs should be represented by some try-except syntax, to reflect the irregular control flow for these constructs.
Procedure declarations should follow the Declaration syntax: (params)result-type.
The parentheses should not be confused with list-delimiters, i.e. the declaration
should be a positional argument of the procedure definition:
proc(name:params-and-type,compound-stmt)
In all cases a distinction should be made between internal (local) and exported (public) declarations and definitions. This can be done in Delphi-like unit(...) blocks, with interface(...) and implementation(...) sections, as well as optional uses(...) and initialization(...) and finalization(...) sections, with type(...), const(...), var(...), proc(...) and inline(...) subsections. A distinction between Const and Var is not necessary, but IMO procedure declarations and definitions should be grouped into according blocks. Namespaces should be compact, i.e. a namespace (or scope) cannot be extended in other places or units. The Inline subsection is intended for C #defines, which can be interpreted as various higher level declarations (constants, inline procedures...).
Procedure definitions can be split into an procedure type, i.e. roughly the procedure declaration, and the specific implementation. Then the declaration part can be stored in the appropriate (public/local) scope, and the definition part only refers to that declaration, but does not repeat it entirely.