grammar.md 7.5 KB

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.

  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                              .