Extracted from compiler/src/M2.atg (Coco/R PRODUCTIONS
with semantic actions and attributes stripped). Terminals in
double quotes are literals; lowercase names are tokens.
M2
= Unit "." .
(* Units: program modules compile fully; DEFINITION and
IMPLEMENTATION modules parse + check now but lower in step 4
(each ends with one 230); same for nested local modules. *)
Unit
= DefUnit
| ImplUnit
| ProgModule .
DefUnit
= "DEFINITION" "MODULE"
GetIdent
";"
{ Import }
{ ConstBlock | TypeBlock | VarBlock
| ProcHeading ";" }
"END"
GetIdent .
ImplUnit
= "IMPLEMENTATION" "MODULE"
GetIdent
";"
{ Import }
DeclSeq
[ "BEGIN"
[ StatSeq ] ]
"END"
GetIdent .
ProgModule
= "MODULE"
GetIdent
[ Priority ]
";"
{ Import }
DeclSeq
[ "BEGIN"
[ StatSeq ] ]
"END"
GetIdent .
DeclSeq
= { ConstBlock | TypeBlock | VarBlock | ProcDecl ";"
| NestedModule ";" | ClassItem ";" } .
(* Local module, Wirth form. Parses + checks fully (own scope);
lowering waits for step 4, marked by one 230 at END. *)
NestedModule
= "MODULE"
GetIdent
[ Priority ]
";"
{ Import }
[ Export ]
DeclSeq
[ "BEGIN"
[ StatSeq ] ]
"END"
GetIdent .
Priority
= "[" integer "]" .
Export
= "EXPORT" (* names wire up in step 4 *)
[ "QUALIFIED" ]
GetIdent
{ "," GetIdent }
";" .
Import
= "FROM"
GetIdent
"IMPORT"
ImpList ";"
| "IMPORT"
ImpList ";" .
ImpList
= GetIdent
{ ","
GetIdent } .
(* Opaque TYPE declarations (definition modules). The targetless
alias resolves to InvalidType until step 4 completes it. *)
(* Scalar-phase TYPEs: named types, integer subranges, enumerations.
Opaque "TYPE T;" needs isDef (definition units); elsewhere 231.
Composite forms (ARRAY/RECORD/SET/POINTER) arrive with step 3. *)
TypeBlock
= "TYPE" { TypeItem ";" | ClassItem ";" } .
TypeItem
= GetIdent
( "=" Type
| ) .
Type
= TypeIdent
| Subrange
| Enum .
TypeIdent
= GetIdent .
Subrange
= "[" Expr ".." Expr
"]" .
Enum
= "("
GetIdent
{ "," GetIdent }
")" .
(* Clarion-form classes (docs/OOP.txt): declaration + single
inheritance + IMPLEMENTATION blocks. Scopes and member checks
now; lowering (vtable, dispatch, THIS) later — one 230 per
class/impl block. Methods end with ";" per the Table example
(not "," as in the sketch). No underscores in identifiers. *)
(* Single CLASS item in both loops: separating declaration from
IMPLEMENTATION at the loop level needs 2-token lookahead
(CLASS ident vs CLASS IMPLEMENTATION), which LL(1) cannot do.
The second token decides after CLASS is consumed. A misplaced
CLASS IMPLEMENTATION inside TYPE still parses (harmless: the
whole unit ends 230 until lowering). *)
ClassItem
= "CLASS" ( "IMPLEMENTATION" ClassImplRest | ClassRest ) .
ClassRest
= GetIdent
[ Parents ]
";"
{ ClassField ";" }
{ MethodHeading ";" }
"END"
GetIdent .
Parents
= "(" Parent1
{ "," GetIdent }
")" .
Parent1
= GetIdent .
ClassField
= GetIdent
( "=" GetIdent
|
{ "," GetIdent }
":" TypeIdent ) .
MethodHeading
=
[ "VIRTUAL" ]
ProcHeading .
ClassImplRest
= GetIdent
";" { MethodImpl ";" }
[ "BEGIN"
[ StatSeq ] ]
"END"
GetIdent .
MethodImpl
= MethodHeading ";"
( "FORWARD"
| Block ) .
ConstBlock
= "CONST" { ConstDecl ";" } .
ConstDecl
= GetIdent
"="
Expr .
VarBlock
= "VAR" { VarDecl ";" } .
VarDecl
= VarIdents ":"
TypeIdent .
VarIdents
= GetIdent
{ ","
GetIdent } .
ParIdents
= GetIdent
{ "," GetIdent } .
(* Procedure headings enter now (scopes, params, result); bodies
parse + check with one 230 at END (lowering = step 4).
FORWARD marks; the body heading re-enters (signature compare
tightens in step 4). *)
ProcHeading
= "PROCEDURE"
GetIdent
[ FormalParams ]
[ ":" TypeIdent ] .
FormalParams
= "(" [ ParamSection { ";" ParamSection } ] ")" .
ParamSection
=
[ "VAR" ]
ParIdents ":" TypeIdent .
ProcDecl
= ProcHeading ";"
( "FORWARD"
| Block ) .
Block
= DeclSeq
[ "BEGIN"
[ StatSeq ] ]
"END"
GetIdent .
StatSeq
= Statement { ";" Statement } .
Statement
= Assign
| IfStat
| WhileStat
| RepeatStat
| LoopStat
| ForStat
| CaseStat
| ReturnStat
| "EXIT" .
Assign
= Design ":="
Expr .
IfStat
= "IF"
Expr
"THEN" StatSeq
{ "ELSIF"
Expr
"THEN" StatSeq }
[ "ELSE"
StatSeq ]
"END" .
WhileStat
= "WHILE"
Expr
"DO" StatSeq
"END" .
RepeatStat
= "REPEAT"
StatSeq
"UNTIL" Expr .
LoopStat
= "LOOP"
StatSeq
"END" .
(* FOR with static-sign BY (literal, non-zero; 220 otherwise).
Runtime direction would need a compare-select; the literal
sign picks cslew/csegew at "DO" time. *)
ForStat
= "FOR"
GetIdent
":=" Expr
"TO" Expr
[ "BY" Expr ]
"DO"
StatSeq
"END" .
CaseStat
= "CASE" Expr
"OF" CaseAlt
{ "|" CaseAlt }
[ "ELSE" StatSeq ]
"END" .
(* Compare-chain lowering: each alternative ends its match-tests
with "jmp lAfter", so the no-match fallthrough skips the body:
"cmp; jnz(lBody,lF); lF: ... ; jmp lAfter; lBody: S; jmp lEnd;
lAfter:". Falls into the next alternative, ELSE, or END. *)
CaseAlt
=
CaseLabel
{ "," CaseLabel }
":"
StatSeq .
CaseLabel
= Expr
[ ".." Expr ] .
ReturnStat
= "RETURN"
[ Expr ] .
Design
= GetIdent .
Expr
= SimExpr
[ Rel SimExpr
] .
Rel
= "="
| "#"
| "<"
| "<="
| ">"
| ">=" .
SimExpr
=
[ "+" | "-" ]
Term
{ AddOp Term
} .
AddOp
= "+"
| "-"
| "OR" .
Term
= Fact { MulOp Fact
} .
MulOp
= "*"
| "/"
| "DIV"
| "MOD"
| "AND" .
Fact
= integer
| real
| string
| Design
| "(" Expr ")"
| "NOT" Fact .
GetIdent
= ident .