let rec
    type symbol =                                               -- lexical type
       IdentSy String  +  NumSy Int +
       plusSy + minusSy + timesSy + divSy +
       eqSy + neSy + ltSy + leSy + gtSy + geSy +
       notSy + andSy + orSy +
       becomesSy + openSy + closeSy + semiSy +
       varSy +
       ifSy + thenSy + elseSy + fiSy +
       whileSy + doSy + odSy +
       beginSy + endSy +
       writeSy +
       eofSy   + symbolErr String

and type Exp =
       Ident String + Num Int +                              -- syntactic types
       UExp symbol Exp + BExp symbol Exp Exp +
       ExpErr String

and type Dec =
       VarDec String + Decs Dec Dec +
       DecErr String

and type Cmd =
       Assign   Exp Exp     +
       IfCmd    Exp Cmd Cmd +
       WhileCmd Exp Cmd     +
       Cmds     Cmd Cmd     +
       Block    Dec Cmd     +
       Write    Exp         +
       CmdErr   String

in let parse inp =                                           -- parse a program
  let rec
      lexical "" = eofSy, "" ||                              -- lexical routine

      lexical s  =
         if mem (hd s) [' '; '\n'; '\t'] then lexical (tl s)
         else case s in
              ':' . '=' . R  :  becomesSy,R ||
              '<' . '>' . R  :  neSy,     R ||
              '<' . '=' . R  :  leSy,     R ||
              '>' . '=' . R  :  geSy,     R ||
              s: let w,r = takeword s
                 in if isdigit(hd w) then NumSy(stoi w), r
                    else assoc w
                     (if isalpha(hd w) then [
                       ("if",   ifSy);                              -- keywords
                       ("then", thenSy); ("else", elseSy);
                       ("fi",   fiSy);
                       ("while",whileSy); ("do",   doSy); ("od",   odSy);
                       ("begin",beginSy); ("end",  endSy);
                       ("write",writeSy);
                       ("var",  varSy);
                       ("not", notSy); ("and", andSy); ("or",  orSy);
                       (w, IdentSy w)  ]
                      else [
                       ("(", openSy); (")", closeSy);
                       ("=", eqSy);   (";", semiSy);
                       ("+", plusSy); ("-", minusSy);
                       ("*", timesSy);("/", divSy);
                       ("<", ltSy);   (">", gtSy);
                       (w, symbolErr w) ]
                     ), r
              end


  and Expression =                                       -- parse an Expression

      let rec
          OprSeq NonTerm separators inp =
             let rec T, rest = NonTerm inp         -- at least one Non-Terminal
             and s T1 inp =
                   if mem (hd inp) separators then  -- optional sep NonTerm ...
                      let T2, rest2 = NonTerm (tl inp)
                      in  case T2 in
                          ExpErr _: T2, rest2 ||
                          _       : s (BExp (hd inp) T1 T2) rest2      -- more?
                          end
                   else T1, inp
             in case T in
                ExpErr _: T, rest ||
                _       : s T rest
                end

      and opd (openSy . rest) =
             let T,(s . rest2) = Expression rest
             in  if s=closeSy then T, rest2
                 else ExpErr "missing )", (s.rest2)  ||

          opd (IdentSy x . rest) = Ident x, rest ||

          opd (NumSy n . rest)   = Num n, rest   ||

          opd (s . rest) =
             if mem s [plusSy; minusSy; notSy] then  -- Unary Operators
                let T, rest2 = opd rest
                in case T in
                   ExpErr _: T, rest ||
                   _       : UExp s T, rest2
                   end
             else ExpErr "bad operand", (s.rest)

      and term       = OprSeq opd      [timesSy; divSy]
      and arithExp   = OprSeq term     [plusSy; minusSy]
      and relExp     = OprSeq arithExp [eqSy; neSy; ltSy; leSy; gtSy; geSy]

      in  OprSeq relExp [andSy; orSy]                     -- end of Expression


  and Declarations inp =                                  -- >=1 Declaration ;

      let Declaration (varSy . IdentSy x . rest) = VarDec x, rest ||
          Declaration r            = DecErr "bad Decln", r

      in case Declaration inp in
         DecErr m, r    : DecErr m, r ||
         D1, semiSy.rest: case Declarations rest in
                          DecErr m, r: D1, rest ||
                          D2, rest   : Decs D1 D2, rest
                          end ||
         D1, r          : DecErr "Decs, no ;", r
         end                                             -- end of Declarations


  and Command inp =                                          -- parse a Command

     let rec
         ifCommand inp =
         case Expression inp in
         ExpErr m, r    : CmdErr("if "@m),r ||
         E, thenSy.rest1:                                               -- then
            case Commands rest1 in
            CmdErr m, r        : CmdErr m, r ||
            Ctrue, elseSy.rest2:                                        -- else
               case Commands rest2 in
               CmdErr m, r       : CmdErr m, r ||
               Cfalse, fiSy.rest3: IfCmd E Ctrue Cfalse, rest3 ||         -- fi
               _, r              : CmdErr "no fi", r
               end  ||
            _, r               : CmdErr "no else", r
            end  ||
         _, r           : CmdErr "no then", r
         end

     and whileCommand inp =
         case Expression inp in
         ExpErr m, r : CmdErr("while "@m), r ||
         E, doSy.rest: case Commands rest in                              -- do
                       CmdErr m, r      : CmdErr("do "@m), r ||
                       Body, (odSy.rest): WhileCmd E Body, rest ||        -- od
                       _, r             : CmdErr "no od", r
                       end  ||
         _, r        : CmdErr "no do", r
         end

     and blockCommand inp =
         case Declarations inp in
         DecErr m, r: case Commands inp in                    -- begin Cmds end
                      C, endSy.rest: C, rest ||                          -- end
                      _, r         : CmdErr "no end", r
                      end  ||
         D, r       : case Commands r in                -- begin Decs; Cmds end
                      CmdErr m, r  : CmdErr m, r ||
                      C, endSy.rest: Block D C, rest ||                  -- end
                      _, r         : CmdErr "no end", r
                      end
         end

     and assignment inp =                                         -- Exp := Exp
         case Expression inp in
         ExpErr m, r        : CmdErr(m@":="), r ||
         LHS, becomesSy.rest: case Expression rest in
                              ExpErr m, rest2: CmdErr(":="@m), rest2   ||
                              RHS, rest2     : Assign LHS RHS, rest2
                              end  ||
         _, r               : CmdErr "no :=", r
         end

      and Commands inp =                                          -- cmd1; cmd2
          case Command inp in
          CmdErr m, r      : CmdErr m, r ||
          C1, semiSy . rest: case Commands rest in
                             CmdErr m, r : CmdErr m, r ||
                             C2, rest2   : Cmds C1 C2, rest2     -- right assoc
                             end ||
          C1, r            : C1, r
          end

      in case inp in
         ifSy.   rest: ifCommand    rest ||
         whileSy.rest: whileCommand rest ||
         beginSy.rest: blockCommand rest ||
         writeSy.rest: case Expression rest in
                       ExpErr m, r: CmdErr("write "@m), r ||
                       E, r       : Write E, r
                       end  ||
         _           : assignment inp
         end                                                  -- end of Command

  in  case Command (choplist lexical inp) in
      CmdErr m, _: CmdErr m ||               -- syntax error
      T, [eofSy] : T        ||               -- syntax ok
      _, _       : CmdErr "junk at eof"      -- not all parsed
      end                                                       -- end of parse


--\fB Simple Recursive Descent Parser in LML. \fP
-- L.Allison, Department of Computer Science, Monash University, Australia
