-- Copyright (c) 1991-2002, The Numerical Algorithms Group Ltd.
-- All rights reserved.
-- Copyright (C) 2007-2014, Gabriel Dos Reis.
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are
-- met:
--
--     - Redistributions of source code must retain the above copyright
--       notice, this list of conditions and the following disclaimer.
--
--     - Redistributions in binary form must reproduce the above copyright
--       notice, this list of conditions and the following disclaimer in
--       the documentation and/or other materials provided with the
--       distribution.
--
--     - Neither the name of The Numerical ALgorithms Group Ltd. nor the
--       names of its contributors may be used to endorse or promote products
--       derived from this software without specific prior written permission.
--
-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
-- IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
-- TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-- PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
-- OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
-- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
-- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--

--
-- Abstract:
--   This file defines the Boot grammar and parser.  The parser
--   is hand-written based on `parser combinators' technology.
--


import includer
import scanner
import ast
namespace BOOTTRAN
module parser
 
--%
--% Snapshot of the parser state
--%

structure %ParserState ==
  Record(toks: %List %Tokens, trees: %List %Ast, pren: %Short, scp: %Short,
    cur: %Token,tu: %LoadUnit) with
      parserTokens == (.toks)       -- remaining token sequence
      parserTrees == (.trees)       -- list of successful parse trees
      parserNesting == (.pren)      -- parenthesis nesting level
      parserScope == (.scp)         -- scope nesting level
      parserCurrentToken == (.cur)  -- current token
      parserLoadUnit == (.tu)       -- current translation unit

makeParserState toks ==
  mk%ParserState(toks,nil,0,0,nil,makeLoadUnit())

++ Access the value of the current token
macro parserTokenValue ps ==
  tokenValue parserCurrentToken ps

++ Access the class of the current token
macro parserTokenClass ps ==
  tokenClass parserCurrentToken ps

++ Access the position of the current token
macro parserTokenPosition ps ==
  tokenPosition parserCurrentToken ps

macro parserGensymSequenceNumber ps ==
  currentGensymNumber parserLoadUnit ps

--%


bpFirstToken ps ==
  parserCurrentToken(ps) :=
    parserTokens ps = nil => mk%Token("ERROR","NOMORE",parserTokenPosition ps)
    first parserTokens ps
  true
 
bpFirstTok ps ==
  parserCurrentToken(ps) :=
    parserTokens ps = nil => mk%Token("ERROR","NOMORE",parserTokenPosition ps)
    first parserTokens ps
  parserNesting ps > 0 and parserTokenClass ps = "KEY" =>
    parserTokenValue ps is "SETTAB" =>
      parserScope(ps) := parserScope ps + 1
      bpNext ps
    parserTokenValue ps is "BACKTAB" =>
      parserScope(ps) := parserScope ps - 1
      bpNext ps
    parserTokenValue ps is "BACKSET" =>
      bpNext ps
    true
  true
 
bpNext ps ==
  parserTokens(ps) := rest parserTokens ps
  bpFirstTok ps
 
bpNextToken ps ==
  parserTokens(ps) := rest parserTokens ps
  bpFirstToken ps
 
bpRequire(ps,f) ==
  apply(f,[ps]) or bpTrap ps

bpState ps ==
  [parserTokens ps,parserTrees ps,parserNesting ps,parserScope ps]

 
bpRestore(ps,x)==
  parserTokens(ps) := first x
  bpFirstToken ps
  parserTrees(ps) := second x
  parserNesting(ps) := third x
  parserScope(ps) := CADDDR x
  true
 
bpPush(ps,x) ==
  parserTrees(ps) := [x,:parserTrees ps]
 
bpPushId ps ==
  parserTrees(ps) := [bfReName parserTokenValue ps,:parserTrees ps]
 
bpPop1 ps ==
  a := first parserTrees ps
  parserTrees(ps) := rest parserTrees ps
  a
 
bpPop2 ps ==
  a := second parserTrees ps
  parserTrees(ps).rest := CDDR parserTrees ps
  a
 
bpPop3 ps ==
  a := third parserTrees ps
  parserTrees(ps).rest.rest := CDDDR parserTrees ps
  a
 
bpIndentParenthesized(ps,f) ==
  scope := parserScope ps
  try
    parserScope(ps) := 0
    a := parserCurrentToken ps
    bpEqPeek(ps,"OPAREN") =>
      parserNesting(ps) := parserNesting ps + 1
      bpNext ps
      apply(f,[ps]) and bpFirstTok ps and
              (bpEqPeek(ps,"CPAREN") or bpParenTrap(ps,a)) =>
        parserNesting(ps) := parserNesting ps - 1
        bpNextToken ps
        parserScope ps = 0 => true
        parserTokens(ps) := [:bpAddTokens(ps,parserScope ps),:parserTokens ps]
        bpFirstToken ps
        parserNesting ps = 0 =>
          bpCancel ps
          true
        true
      bpEqPeek(ps,"CPAREN") =>
        bpPush(ps,bfTuple [])
        parserNesting(ps) := parserNesting ps - 1
        bpNextToken ps
        true
      bpParenTrap(ps,a)
    false
  finally parserScope(ps) := scope
 
bpParenthesized(ps,f) ==
  a := parserCurrentToken ps
  bpEqKey(ps,"OPAREN") =>
    apply(f,[ps]) and (bpEqKey(ps,"CPAREN") or bpParenTrap(ps,a)) => true
    bpEqKey(ps,"CPAREN") =>
      bpPush(ps,bfTuple [])
      true
    bpParenTrap(ps,a)
  false
 
bpBracket(ps,f) ==
  a := parserCurrentToken ps
  bpEqKey(ps,"OBRACK") =>
    apply(f,[ps]) and (bpEqKey(ps,"CBRACK") or bpBrackTrap(ps,a)) =>
      bpPush(ps,bfBracket bpPop1 ps)
    bpEqKey(ps,"CBRACK") => bpPush(ps,[])
    bpBrackTrap(ps,a)
  false
 
bpPileBracketed(ps,f) ==
  bpEqKey(ps,"SETTAB") => 
    bpEqKey(ps,"BACKTAB") => true
    apply(f,[ps]) and (bpEqKey(ps,"BACKTAB") or bpPileTrap ps) =>
      bpPush(ps,bfPile bpPop1 ps)
    false
  false
 
bpListof(ps,f,str1,g)==
  apply(f,[ps]) =>
    bpEqKey(ps,str1) and bpRequire(ps,f) =>
      a := parserTrees ps
      parserTrees(ps) := nil
      while bpEqKey(ps,str1) and bpRequire(ps,f) repeat nil
      parserTrees(ps) := [reverse! parserTrees ps,:a]
      bpPush(ps,apply(g,[[bpPop3 ps,bpPop2 ps,:bpPop1 ps]]))
    true
  false
 
 
-- to do ,<backset>
bpListofFun(ps,f,h,g)==
  apply(f,[ps]) =>
    apply(h,[ps]) and bpRequire(ps,f) =>
      a := parserTrees ps
      parserTrees(ps) := nil
      while apply(h,[ps]) and bpRequire(ps,f) repeat nil
      parserTrees(ps) := [reverse! parserTrees ps,:a]
      bpPush(ps,apply(g,[[bpPop3 ps,bpPop2 ps,:bpPop1 ps]]))
    true
  false
 
bpList(ps,f,str1)==
  apply(f,[ps]) =>
    bpEqKey(ps,str1) and bpRequire(ps,f) =>
      a := parserTrees ps
      parserTrees(ps) := nil
      while bpEqKey(ps,str1) and bpRequire(ps,f) repeat nil
      parserTrees(ps) := [reverse! parserTrees ps,:a]
      bpPush(ps,[bpPop3 ps,bpPop2 ps,:bpPop1 ps])
    bpPush(ps,[bpPop1 ps])
  bpPush(ps,nil)
 
bpOneOrMore(ps,f) ==
  apply(f,[ps])=>
    a := parserTrees ps
    parserTrees(ps) := nil
    while apply(f,[ps]) repeat nil
    parserTrees(ps) := [reverse! parserTrees ps,:a]
    bpPush(ps,[bpPop2 ps,:bpPop1 ps])
  false
 
 
-- s must transform the head of the stack
bpAnyNo(ps,s) ==
  while apply(s,[ps]) repeat nil
  true
 
 
-- AndOr(k,p,f)= k p
bpAndOr(ps,keyword,p,f)==
  bpEqKey(ps,keyword) and bpRequire(ps,p)
    and bpPush(ps,apply(f,[parserLoadUnit ps,bpPop1 ps]))
 
bpConditional(ps,f) ==
  bpEqKey(ps,"IF") and bpRequire(ps,function bpWhere) and (bpEqKey(ps,"BACKSET") or true) =>
    bpEqKey(ps,"SETTAB") =>
      bpEqKey(ps,"THEN") =>
	bpRequire(ps,f) and bpElse(ps,f) and bpEqKey(ps,"BACKTAB")
      bpMissing(ps,"THEN")
    bpEqKey(ps,"THEN") => bpRequire(ps,f) and bpElse(ps,f)
    bpMissing(ps,"then")
  false
 
bpElse(ps,f)==
  a := bpState ps
  bpBacksetElse ps =>
    bpRequire(ps,f) and
      bpPush(ps,bfIf(bpPop3 ps,bpPop2 ps,bpPop1 ps))
  bpRestore(ps,a)
  bpPush(ps,bfIfThenOnly(bpPop2 ps,bpPop1 ps))
 
bpBacksetElse ps ==
  bpEqKey(ps,"BACKSET") => bpEqKey(ps,"ELSE")
  bpEqKey(ps,"ELSE")
 
bpEqPeek(ps,s) == 
  parserTokenClass ps = "KEY" and symbolEq?(s,parserTokenValue ps)
 
bpEqKey(ps,s) ==
  parserTokenClass ps = "KEY" and symbolEq?(s,parserTokenValue ps) and bpNext ps

bpEqKeyNextTok(ps,s) ==   
  parserTokenClass ps = "KEY" and symbolEq?(s,parserTokenValue ps) and bpNextToken ps
 
bpPileTrap ps   == bpMissing(ps,"BACKTAB")
bpBrackTrap(ps,x) == bpMissingMate(ps,"]",x)
bpParenTrap(ps,x) == bpMissingMate(ps,")",x)
 
bpSpecificErrorHere(ps,key) ==
  bpSpecificErrorAtToken(parserCurrentToken ps, key)

bpSpecificErrorAtToken(tok, key) ==
  a := tokenPosition tok
  SoftShoeError(a,key)
 
bpGeneralErrorHere ps ==
  bpSpecificErrorHere(ps,'"syntax error")
 
bpIgnoredFromTo(pos1, pos2) ==
  shoeConsole strconc('"ignored from line ", toString lineNo pos1)
  shoeConsole lineString pos1
  shoeConsole strconc(shoeSpaces lineCharacter pos1,'"|")
  shoeConsole strconc('"ignored through line ", toString lineNo pos2)
  shoeConsole lineString pos2
  shoeConsole strconc(shoeSpaces lineCharacter pos2,'"|")

bpMissingMate(ps,close,open)==
  bpSpecificErrorAtToken(open, '"possibly missing mate")
  bpMissing(ps,close)
 
bpMissing(ps,s) ==
  bpSpecificErrorHere(ps,strconc(PNAME s,'" possibly missing"))
  throw 'TRAPPED : BootParserException
 
bpCompMissing(ps,s) ==
  bpEqKey(ps,s) or bpMissing(ps,s)
 
bpTrap ps ==
  bpGeneralErrorHere ps
  throw 'TRAPPED : BootParserException
 
bpRecoverTrap ps ==
  bpFirstToken ps
  pos1 := parserTokenPosition ps
  bpMoveTo(ps,0)
  pos2 := parserTokenPosition ps
  bpIgnoredFromTo(pos1, pos2)
  bpPush(ps,[['"pile syntax error"]])
 
bpListAndRecover(ps,f)==
  a := parserTrees ps
  b := nil
  parserTrees(ps) := nil
  done := false
  c := parserTokens ps
  while not done repeat
    found :=
      try apply(f,[ps])
      catch(e: BootParserException) => e
    if found is "TRAPPED"
    then
       parserTokens(ps) := c
       bpRecoverTrap ps
    else if not found
	 then
	   parserTokens(ps) := c
	   bpGeneralErrorHere ps
	   bpRecoverTrap ps
    if bpEqKey(ps,"BACKSET")
    then
       c := parserTokens ps
    else if bpEqPeek(ps,"BACKTAB") or parserTokens ps = nil
	 then
	    done := true
	 else
	   parserTokens(ps) := c
	   bpGeneralErrorHere ps
	   bpRecoverTrap ps
	   if bpEqPeek(ps,"BACKTAB")  or parserTokens ps = nil
	   then done:=true
	   else
	       bpNext ps
	       c := parserTokens ps
    b := [bpPop1 ps,:b]
  parserTrees(ps) := a
  bpPush(ps,reverse! b)
 
bpMoveTo(ps,n) ==
   parserTokens ps = nil  => true
   bpEqPeek(ps,"BACKTAB") =>
     n=0  => true
     bpNextToken ps
     parserScope(ps) := parserScope ps - 1
     bpMoveTo(ps,n-1)
   bpEqPeek(ps,"BACKSET") =>
     n=0  => true
     bpNextToken ps
     bpMoveTo(ps,n)
   bpEqPeek(ps,"SETTAB")  =>
     bpNextToken ps
     bpMoveTo(ps,n+1)
   bpEqPeek(ps,"OPAREN")  =>
     bpNextToken ps
     parserNesting(ps) := parserNesting(ps) + 1
     bpMoveTo(ps,n)
   bpEqPeek(ps,"CPAREN")  =>
     bpNextToken ps
     parserNesting(ps) := parserNesting ps - 1
     bpMoveTo(ps,n)
   bpNextToken ps
   bpMoveTo(ps,n)
 
-- A fully qualified name could be interpreted as a left reduction
-- of an '::' infix operator.  At the moment, we don't use
-- that general interpretation.

-- When this routine is called, a symbol is already pushed on the
-- stack.  When this routine finished execution, we have either
-- reduced a '::' and a name, or nothing.  In either case, a 
-- symbol is present on the stack.
bpQualifiedName ps ==
  bpEqPeek(ps,"COLON-COLON") =>
    bpNext ps
    parserTokenClass ps = "ID" and bpPushId ps and bpNext ps
      and bpPush(ps,bfColonColon(bpPop2 ps, bpPop1 ps))
  false

++ Name:
++   ID
++   Name :: ID
bpName ps ==
  parserTokenClass ps = "ID" =>
    bpPushId ps
    bpNext ps
    bpAnyNo(ps,function bpQualifiedName)
  false

++ Constant:
++   INTEGER
++   FLOAT
++   LISP
++   LISPEXPR
++   LINE
++   QUOTE S-Expression
++   STRING
++   INERT
bpConstTok ps ==
  parserTokenClass ps in '(INTEGER FLOAT) =>
    bpPush(ps,parserTokenValue ps)
    bpNext ps
  parserTokenClass ps = "LISP" =>
    bpPush(ps,%Lisp parserTokenValue ps) and bpNext ps
  parserTokenClass ps = "LISPEXP" =>
    bpPush(ps,parserTokenValue ps) and bpNext ps
  parserTokenClass ps = "LINE" =>
    bpPush(ps,["+LINE",parserTokenValue ps]) and bpNext ps
  bpEqPeek(ps,"QUOTE") =>
    bpNext ps
    bpRequire(ps,function bpSexp) and
      bpPush(ps,bfSymbol bpPop1 ps)
  bpString ps or bpFunction ps or bpInert ps

bpInert ps ==
  parserTokenClass ps = 'INERT =>
    bpPush(ps,bfInert parserTokenValue ps) and bpNext ps
  nil

bpChar ps ==
  parserTokenClass ps = "ID" and parserTokenValue ps is "char" =>
    a := bpState ps
    bpApplication ps =>
      s := bpPop1 ps
      s is ["char",.] => bpPush(ps,s)
      bpRestore(ps,a)
      false
    false
  false

++ Subroutine of bpExportItem.  Parses tails of ExportItem.
bpExportItemTail ps ==
  bpEqKey(ps,"BEC") and bpRequire(ps,function bpAssign) and
    bpPush(ps,%Assignment(bpPop2 ps, bpPop1 ps))
      or bpSimpleDefinitionTail ps

++ ExportItem:
++   Structure
++   TypeAliasDefinition
++   Signature
++   Signature := Where
++   Signature  == Where
bpExportItem ps ==
  bpEqPeek(ps,"STRUCTURE") => bpStruct ps
  a := bpState ps
  bpName ps =>
    bpEqPeek(ps,"COLON") =>
      bpRestore(ps,a)
      bpRequire(ps,function bpSignature)
      bpExportItemTail ps or true
    bpRestore(ps,a)
    bpTypeAliasDefinition ps
  false

++ ExportItemList:
++    Signature
++    ExportItemList Signature
bpExportItemList ps ==
  bpListAndRecover(ps,function bpExportItem)

++ ModuleInterface:
++   WHERE pile-bracketed ExporItemList
bpModuleInterface ps ==
  bpEqKey(ps,"WHERE") =>
    bpPileBracketed(ps,function bpExportItemList)
      or (bpExportItem ps and bpPush(ps,[bpPop1 ps]))
        or bpTrap ps
  bpPush(ps,nil)

++ ModuleExports:
++   OPAREN IdList CPAREN
bpModuleExports ps ==
  bpParenthesized(ps,function bpIdList) => bpPush(ps,bfUntuple bpPop1 ps)
  bpPush(ps,nil)

++ Parse a module definitoin
++   Module:
++     MODULE Name OptionalModuleExports OptionalModuleInterface
bpModule ps ==
  bpEqKey(ps,"MODULE") => 
    bpRequire(ps,function bpName)
    bpModuleExports ps
    bpModuleInterface ps
    bpPush(ps,%Module(bpPop3 ps,bpPop2 ps,bpPop1 ps))
  nil

++ Parse a module import, or a import declaration for a foreign entity.
++ Import:
++    IMPORT Signature FOR Name
++    IMPORT Name
++    IMPORT NAMESPACE LongName
bpImport ps ==
  bpEqKey(ps,"IMPORT") =>
    bpEqKey(ps,"NAMESPACE") =>
      bpLeftAssoc(ps,'(DOT),function bpName) and
        bpPush(ps,%Import bfNamespace bpPop1 ps)
          or bpTrap ps
    a := bpState ps
    bpRequire(ps,function bpName)
    bpEqPeek(ps,"COLON") =>
      bpRestore(ps,a)
      bpRequire(ps,function bpSignature) and 
        (bpEqKey(ps,"FOR") or bpTrap ps) and
           bpRequire(ps,function bpName) and
              bpPush(ps,%ImportSignature(bpPop1 ps, bpPop1 ps))
    bpPush(ps,%Import bpPop1 ps)
  false

++
++ Namespace:
++    NAMESPACE Name
bpNamespace ps ==
  bpEqKey(ps,"NAMESPACE") and (bpName ps or bpDot ps) and
    bpPush(ps,bfNamespace bpPop1 ps)

-- Parse a type alias defnition:
--    TypeAliasDefinition: 
--       TypeName <=> logical-expression
bpTypeAliasDefinition ps ==
  bpTypeName ps and 
    bpEqKey(ps,"TDEF") and bpLogical ps and
      bpPush(ps,%TypeAlias(bpPop2 ps,bpPop1 ps))

bpTypeName ps ==
  bpTerm(ps,function bpIdList) or bpTrap ps

++ Parse a signature declaration
++  Signature:
++    Name COLON Mapping
bpSignature ps ==
  bpName ps and bpSignatureTail ps

bpSignatureTail ps ==
  bpEqKey(ps,"COLON") and bpRequire(ps,function bpTyping) and
    bpPush(ps,bfSignature(bpPop2 ps,bpPop1 ps))


++ SimpleMapping:
++   Application
++   Application -> Application
bpSimpleMapping ps ==
  bpApplication ps =>
    bpEqKey(ps,"ARROW") and bpRequire(ps,function bpApplication) and
      bpPush(ps,%Mapping(bpPop1 ps, [bpPop1 ps]))
    true
  false

++ ArgtypeList:
++   ( ArgtypeSequence )
++ ArgtypeSequence:
++   SimpleMapping
++   SimpleMapping , ArgtypeSequence
bpArgtypeList ps ==
  bpTuple(ps,function bpSimpleMapping)

++ Parse a mapping expression
++   Mapping:
++     ArgtypeList -> Application
bpMapping ps ==
  bpParenthesized(ps,function bpArgtypeList) and 
     bpEqKey(ps,"ARROW") and bpApplication ps and 
       bpPush(ps,%Mapping(bpPop1 ps, bfUntuple bpPop1 ps))

bpCancel ps ==
  a := bpState ps
  bpEqKeyNextTok(ps,"SETTAB") =>
    bpCancel ps =>
      bpEqKeyNextTok(ps,"BACKTAB") => true
      bpRestore(ps,a)
      false
    bpEqKeyNextTok(ps,"BACKTAB") => true
    bpRestore(ps,a)
    false
  false

bpAddTokens(ps,n) ==
  n=0 => nil
  n>0=> [mk%Token("KEY","SETTAB",parserTokenPosition ps),:bpAddTokens(ps,n-1)]
  [mk%Token("KEY","BACKTAB",parserTokenPosition ps),:bpAddTokens(ps,n+1)]
 
bpExceptions ps ==
  bpEqPeek(ps,"DOT") or bpEqPeek(ps,"QUOTE") or
       bpEqPeek(ps,"OPAREN") or bpEqPeek(ps,"CPAREN") or
          bpEqPeek(ps,"SETTAB") or bpEqPeek(ps,"BACKTAB")
             or bpEqPeek(ps,"BACKSET")
 
 
bpSexpKey ps ==
  parserTokenClass ps = "KEY" and not bpExceptions ps =>
    a := parserTokenValue ps has SHOEINF
    a = nil => bpPush(ps,keywordId parserTokenValue ps) and bpNext ps
    bpPush(ps,a) and bpNext ps
  false
 
bpAnyId ps ==
  bpEqKey(ps,"MINUS") and (parserTokenClass ps = "INTEGER" or bpTrap ps) and
    bpPush(ps,-parserTokenValue ps) and bpNext ps
      or bpSexpKey ps
        or parserTokenClass ps in '(ID INTEGER STRING FLOAT) and
             bpPush(ps,parserTokenValue ps) and  bpNext ps
 
bpSexp ps ==
  bpAnyId ps or
      bpEqKey(ps,"QUOTE") and bpRequire(ps,function bpSexp)
         and bpPush(ps,bfSymbol bpPop1 ps) or
             bpIndentParenthesized(ps,function bpSexp1)
 
bpSexp1 ps == bpFirstTok ps and
  bpSexp ps and
   (bpEqKey(ps,"DOT") and bpSexp ps and bpPush(ps,[bpPop2 ps,:bpPop1 ps]) or
         bpSexp1 ps and bpPush(ps,[bpPop2 ps,:bpPop1 ps])) or
             bpPush(ps,nil)
 
bpPrimary1 ps ==
  bpParenthesizedApplication ps or
    bpDot ps or
      bpConstTok ps or 
        bpConstruct ps or
          bpCase ps or
            bpStruct ps or
              bpPDefinition ps or
                bpBPileDefinition ps

bpParenthesizedApplication ps ==
  bpName ps and bpAnyNo(ps,function bpArgumentList)

bpArgumentList ps ==
  bpPDefinition ps and 
    bpPush(ps,bfApplication(bpPop2 ps, bpPop1 ps))
 
bpPrimary ps ==
  bpFirstTok ps and (bpPrimary1 ps or bpPrefixOperator ps )
 
bpDot ps ==
  bpEqKey(ps,"DOT") and bpPush(ps,bfDot())
 
bpPrefixOperator ps ==
   parserTokenClass ps = "KEY" and
     parserTokenValue ps has SHOEPRE and bpPushId ps and  bpNext ps
 
bpInfixOperator ps ==
  parserTokenClass ps = "KEY" and
    parserTokenValue ps has SHOEINF and bpPushId ps and  bpNext ps
 
bpSelector ps ==
  bpEqKey(ps,"DOT") and (bpPrimary ps
     and bpPush(ps,bfElt(bpPop2 ps,bpPop1 ps))
	or bpPush(ps,bfSuffixDot bpPop1 ps))
 
bpApplication ps==
   bpPrimary ps and bpAnyNo(ps,function bpSelector) and
      (bpApplication ps and
            bpPush(ps,bfApplication(bpPop2 ps,bpPop1 ps)) or true)
              or bpNamespace ps
 
++ Typing:
++   SimpleType
++   Mapping
++   FORALL Variable DOT Typing
bpTyping ps ==
  bpEqKey(ps,"FORALL") =>
    bpRequire(ps,function bpVariable)
    (bpDot ps and bpPop1 ps) or bpTrap ps
    bpRequire(ps,function bpTyping)
    bpPush(ps,%Forall(bpPop2 ps, bpPop1 ps))
  bpMapping ps or bpSimpleMapping ps

++ Typed:
++   Application : Typing
++   Application @ Typing
bpTyped ps ==
  bpApplication ps and
     bpSignatureTail ps => true
     bpEqKey(ps,"AT") =>
       bpRequire(ps,function bpTyping) and
         bpPush(ps,bfRestrict(bpPop2 ps, bpPop1 ps))
     true
 
bpExpt ps == bpRightAssoc(ps,'(POWER),function bpTyped)
 
bpInfKey(ps,s) ==
  parserTokenClass ps = "KEY" and
    symbolMember?(parserTokenValue ps,s) and bpPushId ps and bpNext ps
 
bpInfGeneric(ps,s) ==
  bpInfKey(ps,s) and (bpEqKey(ps,"BACKSET") or true)
 
bpRightAssoc(ps,o,p)==
  a := bpState ps
  apply(p,[ps]) =>
    while  bpInfGeneric(ps,o) and (bpRightAssoc(ps,o,p) or bpTrap ps) repeat
      bpPush(ps,bfInfApplication(bpPop2 ps,bpPop2 ps,bpPop1 ps))
    true
  bpRestore(ps,a)
  false
 
bpLeftAssoc(ps,operations,parser)==
  apply(parser,[ps]) =>
    while bpInfGeneric(ps,operations) and bpRequire(ps,parser)
     repeat
       bpPush(ps,bfInfApplication(bpPop2 ps,bpPop2 ps,bpPop1 ps))
    true
  false
 
bpString ps ==
  parserTokenClass ps = "STRING" and
    bpPush(ps,quote makeSymbol parserTokenValue ps) and bpNext ps
 
bpFunction ps ==
  bpEqKey(ps,"FUNCTION") and bpRequire(ps,function bpPrimary1)
    and bpPush(ps,bfFunction bpPop1 ps)

bpThetaName ps ==
  parserTokenClass ps = "ID" and parserTokenValue ps has SHOETHETA =>
    bpPushId ps
    bpNext ps
  false
 
bpReduceOperator ps ==
   bpInfixOperator ps or bpString ps or bpThetaName ps
 
bpReduce ps ==
  a := bpState ps
  bpReduceOperator ps and bpEqKey(ps,"SLASH") =>
    bpEqPeek(ps,"OBRACK") => 
      bpRequire(ps,function bpDConstruct) and
	bpPush(ps,bfReduceCollect(parserLoadUnit ps,bpPop2 ps,bpPop1 ps))
    bpRequire(ps,function bpApplication) and
      bpPush(ps,bfReduce(parserLoadUnit ps,bpPop2 ps,bpPop1 ps))
  bpRestore(ps,a)
  false
 
bpTimes ps ==
    bpReduce ps or bpLeftAssoc(ps,'(TIMES  SLASH),function bpExpt)

bpEuclid ps ==
  bpLeftAssoc(ps,'(QUO REM),function bpTimes)
 
bpMinus ps ==
  bpInfGeneric(ps,'(MINUS)) and bpRequire(ps,function bpEuclid)
    and bpPush(ps,bfApplication(bpPop2 ps,bpPop1 ps))
      or bpEuclid ps
 
bpArith ps ==
  bpLeftAssoc(ps,'(PLUS MINUS),function bpMinus)
 
bpIs ps ==
  bpArith ps and
     bpInfKey(ps,'(IS ISNT)) and bpRequire(ps,function bpPattern) =>
       bpPush(ps,bfISApplication(parserLoadUnit ps,bpPop2 ps,bpPop2 ps,bpPop1 ps))
     bpEqKey(ps,"HAS") and bpRequire(ps,function bpApplication) =>
       bpPush(ps,bfHas(bpPop2 ps, bpPop1 ps))
     true
 
bpBracketConstruct(ps,f)==
  bpBracket(ps,f) and bpPush(ps,bfConstruct bpPop1 ps)
 
bpCompare ps ==
  bpIs ps and (bpInfKey(ps,'(SHOEEQ SHOENE LT LE GT GE IN))
     and bpRequire(ps,function bpIs)
	and bpPush(ps,bfInfApplication(bpPop2 ps,bpPop2 ps,bpPop1 ps))
	    or true)
              or bpLeave ps
                or bpThrow ps
 
bpAnd ps == 
  bpLeftAssoc(ps,'(AND),function bpCompare)

bpThrow ps ==
  bpEqKey(ps,"THROW") and bpApplication ps =>
    -- Allow user-supplied matching type tag
    bpSignatureTail ps
    bpPush(ps,bfThrow bpPop1 ps)
  nil

++  Try:
++    try Assign CatchItems
bpTry ps ==
  bpEqKey(ps,"TRY") =>
    bpAssign ps
    cs := []
    while bpHandler(ps,"CATCH") repeat
      bpCatchItem ps
      cs := [bpPop1 ps,:cs]
    bpHandler(ps,"FINALLY") =>
      bpFinally ps and
        bpPush(ps,bfTry(bpPop2 ps,reverse! [bpPop1 ps,:cs]))
    cs = nil => bpTrap ps -- missing handlers
    bpPush(ps,bfTry(bpPop1 ps,reverse! cs))
  nil            

bpCatchItem ps ==
  bpRequire(ps,function bpExceptionVariable) and
    (bpEqKey(ps,"EXIT") or bpTrap ps) and
      bpRequire(ps,function bpAssign) and
        bpPush(ps,%Catch(bpPop2 ps,bpPop1 ps))

bpExceptionVariable ps ==
  t := parserCurrentToken ps
  bpEqKey(ps,"OPAREN") and 
    bpRequire(ps,function bpSignature) and
      (bpEqKey(ps,"CPAREN") or bpMissing(ps,t))
        or bpTrap ps

bpFinally ps ==
  bpRequire(ps,function bpAssign) and
    bpPush(ps,%Finally bpPop1 ps)

bpHandler(ps,key) ==
  s := bpState ps
  (bpEqKey(ps,"BACKSET") or bpEqKey(ps,"SEMICOLON")) and bpEqKey(ps,key) => true
  bpRestore(ps,s)
  false

++ Leave:
++   LEAVE Logical
bpLeave ps ==
  bpEqKey(ps,"LEAVE") and bpRequire(ps,function bpLogical) and
    bpPush(ps,bfLeave bpPop1 ps)

++ Do:
++  IN Namespace Do
++  DO Assign
bpDo ps ==
  bpEqKey(ps,"IN") =>
    bpRequire(ps,function bpNamespace)
    bpRequire(ps,function bpDo)
    bpPush(ps,bfAtScope(bpPop2 ps,bpPop1 ps))
  bpEqKey(ps,"DO") and bpRequire(ps,function bpAssign) and
    bpPush(ps,bfDo bpPop1 ps)

++ Return:
++   RETURN Assign
++   Leave
++   Throw
++   And
bpReturn ps==
  (bpEqKey(ps,"RETURN") and bpRequire(ps,function bpAssign) and
	 bpPush(ps,bfReturnNoName bpPop1 ps)) 
    or bpLeave ps
      or bpThrow ps
        or bpAnd ps
          or bpDo ps
 
 
bpLogical ps ==
  bpLeftAssoc(ps,'(OR),function bpReturn)
 
bpExpression ps ==
  bpEqKey(ps,"COLON") and (bpLogical ps and
     bpPush(ps,bfApplication ("COLON",bpPop1 ps))
           or bpTrap ps) or bpLogical ps
 
bpStatement ps ==
  bpConditional(ps,function bpWhere) or bpLoop ps
    or bpExpression ps
      or bpTry ps
 
bpLoop ps ==
  bpIterators ps and
   (bpCompMissing(ps,"REPEAT") and
      bpRequire(ps,function bpWhere) and
         bpPush(ps,bfLp(parserLoadUnit ps,bpPop2 ps,bpPop1 ps)))
           or bpEqKey(ps,"REPEAT") and bpRequire(ps,function bpLogical) and
               bpPush(ps,bfLoop1(parserLoadUnit ps,bpPop1 ps))
 
bpSuchThat ps ==
  bpAndOr(ps,"BAR",function bpWhere,function bfSuchthat)
 
bpWhile ps ==
  bpAndOr(ps,"WHILE",function bpLogical,function bfWhile)
 
bpUntil ps ==
  bpAndOr(ps,"UNTIL",function bpLogical,function bfUntil)
 
bpFormal ps ==
  bpVariable ps or bpDot ps

bpForIn ps ==
  bpEqKey(ps,"FOR") and bpRequire(ps,function bpFormal) and (bpCompMissing(ps,"IN"))
      and (bpRequire(ps,function bpSeg) and
       (bpEqKey(ps,"BY") and bpRequire(ps,function bpArith) and
        bpPush(ps,bfForInBy(parserLoadUnit ps,bpPop3 ps,bpPop2 ps,bpPop1 ps))) or
         bpPush(ps,bfForin(parserLoadUnit ps,bpPop2 ps,bpPop1 ps)))
 
bpSeg ps ==
   bpArith ps and
      (bpEqKey(ps,"SEG") and
       (bpArith ps and bpPush(ps,bfSegment2(bpPop2 ps,bpPop1 ps))
         or bpPush(ps,bfSegment1(bpPop1 ps))) or true)
 
bpIterator ps ==
  bpForIn ps or bpSuchThat ps or bpWhile ps or bpUntil ps
 
bpIteratorList ps ==
  bpOneOrMore(ps,function bpIterator)
    and bpPush(ps,bfIterators bpPop1 ps)
 
bpCrossBackSet ps ==
  bpEqKey(ps,"CROSS") and (bpEqKey(ps,"BACKSET") or true)
 
bpIterators ps ==
  bpListofFun(ps,function bpIteratorList,
    function bpCrossBackSet,function bfCross)
 
bpAssign ps ==
  a := bpState ps
  bpStatement ps =>
    bpEqPeek(ps,"BEC") =>
      bpRestore(ps,a)
      bpRequire(ps,function bpAssignment)
    bpEqPeek(ps,"GIVES") =>
      bpRestore(ps,a)
      bpRequire(ps,function bpLambda)
    bpEqPeek(ps,"LARROW") =>
      bpRestore(ps,a)
      bpRequire(ps,function bpKeyArg)
    true
  bpRestore(ps,a)
  false
 
bpAssignment ps ==
  bpAssignVariable ps and
    bpEqKey(ps,"BEC") and
      bpRequire(ps,function bpAssign) and
	 bpPush(ps,bfAssign(parserLoadUnit ps,bpPop2 ps,bpPop1 ps))
 
++ Parse a lambda expression
++   Lambda ::= Variable +-> Assign
bpLambda ps ==
  bpVariable ps and
    bpEqKey(ps,"GIVES") and
      bpRequire(ps,function bpAssign) and
        bpPush(ps,bfLambda(bpPop2 ps,bpPop1 ps))

bpKeyArg ps ==
  bpName ps and bpEqKey(ps,"LARROW") and bpLogical ps and
    bpPush(ps,bfKeyArg(bpPop2 ps,bpPop1 ps))

-- should only be allowed in sequences
bpExit ps ==
  bpAssign ps and (bpEqKey(ps,"EXIT") and
      (bpRequire(ps,function bpWhere) and
	 bpPush(ps,bfExit(bpPop2 ps,bpPop1 ps)))
	   or true)

bpDefinition ps ==
  bpEqKey(ps,"MACRO") =>
    bpName ps and bpStoreName ps and
      bpCompoundDefinitionTail(ps,function %Macro)
        or bpTrap ps
  a := bpState ps
  bpExit ps =>
    bpEqPeek(ps,"DEF") =>
       bpRestore(ps,a)
       bpDef ps
    bpEqPeek(ps,"TDEF") =>
       bpRestore(ps,a)
       bpTypeAliasDefinition ps
    true
  bpRestore(ps,a)
  false
 
bpStoreName ps ==
  enclosingFunction(parserLoadUnit ps) := first parserTrees ps
  sideConditions(parserLoadUnit ps) := nil
  true

bpDef ps ==  
  bpName ps and bpStoreName ps and bpDefTail(ps,function %Definition)
    or bpNamespace ps and bpSimpleDefinitionTail ps
 
bpDDef ps ==
  bpName ps and bpDefTail(ps,function %Definition)

++ Parse the remaining of a simple definition.
bpSimpleDefinitionTail ps ==
  bpEqKey(ps,"DEF") and
    bpRequire(ps,function bpWhere)
      and bpPush(ps,%ConstantDefinition(bpPop2 ps, bpPop1 ps))

++ Parse the remaining of a compound definition.
bpCompoundDefinitionTail(ps,f) ==
  bpVariable ps and 
    bpEqKey(ps,"DEF") and bpRequire(ps,function bpWhere) and 
      bpPush(ps,apply(f,[bpPop3 ps,bpPop2 ps,bpPop1 ps]))


++ Parse the remainding of a definition.  When we reach this point
++ we know we must parse a definition and we have already parsed
++ the name of the main operator in the definition.
bpDefTail(ps,f) ==
  bpSimpleDefinitionTail ps
    or bpCompoundDefinitionTail(ps,f)
 
bpWhere ps ==
  bpDefinition ps and
     (bpEqKey(ps,"WHERE") and bpRequire(ps,function bpDefinitionItem)
         and bpPush(ps,bfWhere(parserLoadUnit ps,bpPop1 ps,bpPop1 ps))
           or true)
 
bpDefinitionItem ps ==
  a := bpState ps
  bpDDef ps => true
  bpRestore(ps,a)
  bpBDefinitionPileItems ps => true
  bpRestore(ps,a)
  bpPDefinitionItems ps => true
  bpRestore(ps,a)
  bpWhere ps
 
bpDefinitionPileItems ps ==
  bpListAndRecover(ps,function bpDefinitionItem)
    and bpPush(ps,%Pile bpPop1 ps)
 
bpBDefinitionPileItems ps ==
  bpPileBracketed(ps,function bpDefinitionPileItems)
 
bpSemiColonDefinition ps ==
  bpSemiListing(ps,function bpDefinitionItem,function %Pile)
 
bpPDefinitionItems ps ==
  bpParenthesized(ps,function bpSemiColonDefinition)
 
bpComma ps == 
  bpModule ps or bpImport ps or bpTuple(ps,function bpWhere)
 
bpTuple(ps,p) ==
  bpListofFun(ps,p,function bpCommaBackSet,function bfTuple)
 
bpCommaBackSet ps == 
  bpEqKey(ps,"COMMA") and (bpEqKey(ps,"BACKSET") or true)
 
bpSemiColon ps == 
  bpSemiListing(ps,function bpComma,function bfSequence)
 
bpSemiListing(ps,p,f) ==
  bpListofFun(ps,p,function bpSemiBackSet,f)
 
bpSemiBackSet ps == 
  bpEqKey(ps,"SEMICOLON") and (bpEqKey(ps,"BACKSET") or true)
 
bpPDefinition ps ==
  bpIndentParenthesized(ps,function bpSemiColon)
 
bpPileItems ps ==
  bpListAndRecover(ps,function bpSemiColon) and bpPush(ps,bfSequence bpPop1 ps)
 
bpBPileDefinition ps ==
  bpPileBracketed(ps,function bpPileItems)
 
bpIteratorTail ps ==
  (bpEqKey(ps,"REPEAT") or true) and bpIterators ps
 
bpConstruct ps ==
  bpBracket(ps,function bpConstruction)
 
bpConstruction ps==
  bpComma ps and
     (bpIteratorTail ps and
       bpPush(ps,bfCollect(parserLoadUnit ps,bpPop2 ps,bpPop1 ps))
         or bpPush(ps,bfTupleConstruct bpPop1 ps))
 
bpDConstruct ps ==
  bpBracket(ps,function bpDConstruction)
 
bpDConstruction ps ==
  bpComma ps and
     (bpIteratorTail ps and
	  bpPush(ps,bfDCollect(bpPop2 ps,bpPop1 ps)) or
	     bpPush(ps,bfDTuple bpPop1 ps))
 
 
 
--PATTERN
 
bpPattern ps ==
  bpBracketConstruct(ps,function bpPatternL)
    or bpChar ps or bpName ps or bpConstTok ps
 
bpEqual ps ==
   bpEqKey(ps,"SHOEEQ") and (bpApplication ps or bpConstTok ps or
                bpTrap ps) and bpPush(ps,bfEqual bpPop1 ps)
 
bpRegularPatternItem ps ==
  bpEqual ps
    or bpConstTok ps or bpDot ps or
      bpName ps and
        ((bpEqKey(ps,"BEC") and bpRequire(ps,function bpPattern)
           and bpPush(ps,bfAssign(parserLoadUnit ps,bpPop2 ps,bpPop1 ps))) or true)
               or bpBracketConstruct(ps,function bpPatternL)
 
bpRegularPatternItemL ps ==
      bpRegularPatternItem ps and bpPush(ps,[bpPop1 ps])
 
bpRegularList ps ==
       bpListof(ps,function bpRegularPatternItemL,"COMMA",function bfAppend)
 
bpPatternColon ps ==
  bpEqKey(ps,"COLON") and bpRequire(ps,function bpRegularPatternItem)
    and bpPush(ps,[bfColon bpPop1 ps])
 
 
-- only one colon
bpPatternL ps ==
  bpPatternList ps and bpPush(ps,bfTuple bpPop1 ps)
 
bpPatternList ps ==
  bpRegularPatternItemL ps =>
    while (bpEqKey(ps,"COMMA") and (bpRegularPatternItemL ps or
	(bpPatternTail ps
	  and bpPush(ps,[:bpPop2 ps,:bpPop1 ps])
	    or bpTrap ps;false) )) repeat
	      bpPush(ps,[:bpPop2 ps,:bpPop1 ps])
    true
  bpPatternTail ps
 
bpPatternTail ps ==
  bpPatternColon ps and
      (bpEqKey(ps,"COMMA") and bpRequire(ps,function bpRegularList)
	   and bpPush(ps,[:bpPop2 ps,:bpPop1 ps]) or true)
 
-- BOUND VARIABLE

++ We are parsing parameters in a function definition.  We have
++ just seen a parameter name; we are attempting to see whether
++ it might be followed by a type annotation, or whether it actually
++ a form with a specific pattern structure, or whether it has
++ a default value.
bpRegularBVItemTail ps ==
  bpSignatureTail ps
    or bpEqKey(ps,"BEC") and bpRequire(ps,function bpPattern) and
       bpPush(ps,bfAssign(parserLoadUnit ps,bpPop2 ps,bpPop1 ps))
         or bpEqKey(ps,"IS") and bpRequire(ps,function bpPattern) and
            bpPush(ps,bfAssign(parserLoadUnit ps,bpPop2 ps,bpPop1 ps))
           or bpEqKey(ps,"DEF") and bpRequire(ps,function bpApplication) and
              bpPush(ps,%DefaultValue(bpPop2 ps, bpPop1 ps))


bpRegularBVItem ps ==
  bpBVString ps 
    or bpConstTok ps 
      or (bpName ps and (bpRegularBVItemTail ps or true))
        or bpBracketConstruct(ps,function bpPatternL)
 
bpBVString ps ==
  parserTokenClass ps = "STRING" and
    bpPush(ps,["BVQUOTE",makeSymbol parserTokenValue ps]) and bpNext ps
 
bpRegularBVItemL ps ==
  bpRegularBVItem ps and bpPush(ps,[bpPop1 ps])
 
bpColonName ps ==
  bpEqKey(ps,"COLON") and (bpName ps or bpBVString ps or bpTrap ps)
 
 
-- at most one colon at end
bpBoundVariablelist ps ==
  bpRegularBVItemL ps =>
    while (bpEqKey(ps,"COMMA") and (bpRegularBVItemL ps or
	(bpColonName ps
	  and bpPush(ps,bfColonAppend(bpPop2 ps,bpPop1 ps))
	    or bpTrap ps;false) )) repeat
	       bpPush(ps,[:bpPop2 ps,:bpPop1 ps])
    true
  bpColonName ps and bpPush(ps,bfColonAppend(nil,bpPop1 ps))
 

bpVariable ps ==
    bpParenthesized(ps,function bpBoundVariablelist) and
       bpPush(ps,bfTupleIf bpPop1 ps)
         or bpBracketConstruct(ps,function bpPatternL)
                or bpName ps or bpConstTok ps
 
bpAssignVariable ps ==
      bpBracketConstruct(ps,function bpPatternL) or bpAssignLHS ps
 
bpAssignLHS ps ==
  not bpName ps => false
  bpSignatureTail ps => true -- variable declaration
  bpArgumentList ps and 
    (bpEqPeek(ps,"DOT")
      or (bpEqPeek(ps,"BEC") and bpPush(ps,bfPlace bpPop1 ps)) 
        or bpTrap ps)
  bpEqKey(ps,"DOT") =>            -- field path
    bpList(ps,function bpPrimary,"DOT") and 
      bpChecknull ps and
        bpPush(ps,bfTuple([bpPop2 ps,:bpPop1 ps]))
  true

bpChecknull ps ==
  a := bpPop1 ps
  a = nil => bpTrap ps
  bpPush(ps,a)

bpStruct ps ==
   bpEqKey(ps,"STRUCTURE") and
      bpRequire(ps,function bpTypeName) and
        (bpEqKey(ps,"DEF") or bpTrap ps) and
           (bpRecord ps or bpTypeList ps) and
             bpPush(ps,%Structure(bpPop2 ps,bpPop1 ps))
 
++ Record:
++   "Record" "(" FieldList ")"
bpRecord ps ==
  s := bpState ps
  bpName ps and bpPop1 ps is "Record" =>
    (bpParenthesized(ps,function bpFieldList) or bpTrap ps) and
      bpGlobalAccessors ps and
        bpPush(ps,%Record(bfUntuple bpPop2 ps,bpPop1 ps))
  bpRestore(ps,s)
  false

++ FieldList:
++    Signature
++    Signature , FieldList
bpFieldList ps ==
  bpTuple(ps,function bpSignature)

bpGlobalAccessors ps ==
  bpEqKey(ps,"WITH") =>
    bpPileBracketed(ps,function bpAccessorDefinitionList) or bpTrap ps
  bpPush(ps,nil)

bpAccessorDefinitionList ps ==
  bpListAndRecover(ps,function bpAccessorDefinition)

++ AccessorDefinition:
++   Name DEF FieldSection
bpAccessorDefinition ps ==
  bpRequire(ps,function bpName) and
    (bpEqKey(ps,"DEF") or bpTrap ps) and
       bpRequire(ps,function bpFieldSection) and
         bpPush(ps,%AccessorDef(bpPop2 ps,bpPop1 ps))

++ FieldSection:
++    "(" DOT Name ")"
bpFieldSection ps ==
  bpParenthesized(ps,function bpSelectField)

bpSelectField ps ==
  bpEqKey(ps,"DOT") and bpName ps

bpTypeList ps == 
  bpPileBracketed(ps,function bpTypeItemList)
    or bpTypeItem ps and bpPush(ps,[bpPop1 ps])

bpTypeItem ps ==
  bpTerm(ps,function bpIdList)
 
bpTypeItemList ps ==  
  bpListAndRecover(ps,function bpTypeItem)
 
bpTerm(ps,idListParser) ==
  bpRequire(ps,function bpName) and
    ((bpParenthesized(ps,idListParser) and
      bpPush(ps,bfNameArgs(bpPop2 ps,bpPop1 ps)))
	or bpName ps and bpPush(ps,bfNameArgs(bpPop2 ps,bpPop1 ps)))
	 or bpPush(ps,bfNameOnly bpPop1 ps)
 
bpIdList ps == 
  bpTuple(ps,function bpName)
 
bpCase ps ==
  bpEqKey(ps,"CASE") and
    bpRequire(ps,function bpWhere) and
       (bpEqKey(ps,"OF") or bpMissing(ps,"OF")) and
	     bpPiledCaseItems ps
 
bpPiledCaseItems ps ==
   bpPileBracketed(ps,function bpCaseItemList) and
       bpPush(ps,bfCase(parserLoadUnit ps,bpPop2 ps,bpPop1 ps))

bpCaseItemList ps ==
   bpListAndRecover(ps,function bpCaseItem)

bpCasePatternVar ps ==
  bpName ps or bpDot ps

bpCasePatternVarList ps ==
  bpTuple(ps,function bpCasePatternVar)
 
bpCaseItem ps ==
    (bpTerm(ps,function bpCasePatternVarList) or bpTrap ps) and
       (bpEqKey(ps,"EXIT") or bpTrap ps) and
         bpRequire(ps,function bpWhere) and
            bpPush(ps,bfCaseItem(bpPop2 ps,bpPop1 ps))


++ Main entry point into the parser module.
bpOutItem ps ==
  op := enclosingFunction parserLoadUnit ps
  varno := parserGensymSequenceNumber ps
  try
    enclosingFunction(parserLoadUnit ps) := nil
    parserGensymSequenceNumber(ps) := 0
    bpRequire(ps,function bpComma)
  catch(e: BootSpecificError) =>
    bpSpecificErrorHere(ps,e)
    bpTrap ps
  finally
    parserGensymSequenceNumber(ps) := varno
    enclosingFunction(parserLoadUnit ps) := op
  b := bpPop1 ps
  t :=
    b is ["+LINE",:.] => [ b ]
    b is ["L%T",l,r] and symbol? l => 
      $InteractiveMode => [["SETQ",l,r]]
      [["DEFPARAMETER",l,r]]
    translateToplevel(ps,b,false)
  bpPush(ps,t)