# m2compiler-V3 grammar (bare EBNF) 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. ```ebnf 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 . ```