-- PLParseImpl.Mesa -- used to be parse.mesa DIRECTORY Disp: TYPE USING [Print], IO: TYPE USING [char, CR, int, Put, PutF, rope, ROPE, TAB], PL: TYPE USING [Eval, GetSpecialNodes, Insert, Lookup, LSTNode, LSTNodeRecord, Node, NodeRecord, NodeType, OS, Preorder, rEQUAL, RErr, rFCN, rID, rPFUNC1, rSTR, rUNARY, SErr, SN, Symbol, Z], PString: TYPE USING [CopyStream, EmptyS, Item, MakeNUM, NewStream, Stream], Rope: TYPE USING [Concat, Control, FromRefText, ROPE]; PLParseImpl: CEDAR PROGRAM IMPORTS Disp, S: PString, P:PL, Rope, IO EXPORTS PL = { OPEN PL, Rope, IO; N: ZONE = P.Z; TokType: TYPE = {ZERO,LP,RP,LB,RB,COMMA,EOF,SEP,LC,RC, COLON,HOLE,DIV,STR,ID,GTR,ASS,PROG,FCN,SEQOF,OPT, DELETE,SEQOFC,MAPPLY, PLUS,MINUS, GOBBLE,TILDE,PALT,COMB, SEQ, CATL, WILD, FAIL,ITER,SCREEN,EQUAL}; NodeType: TYPE = PL.NodeType; LSTNode: TYPE = PL.LSTNode; LSTNodeRecord: TYPE = PL.LSTNodeRecord; Node: TYPE = PL.Node; NodeRecord: TYPE = PL.NodeRecord; Symbol: TYPE = PL.Symbol; String: TYPE = ROPE; -- progstr: PString.Stream; currn: Node ← NIL; tokenType: TokType ← ZERO; tokenString: String ← NIL; tokenID: Symbol ← NIL; nchar: CHARACTER; stcnt, linecnt, charcnt: INT; Fail,MTSt: Node; Nail: PL.LSTNode; savinput: PString.Stream; checking: BOOLEAN ← FALSE; -- set by CheckRoutine, used by BinaryExp and FunctionBody -- dist: prog EOF -- prog: stmt { SEP prog } | stmt SEP -- stmt: var ASS stmt | exp -- ChoiceExp: ThenExp {PALT ThenExp} -- ThenExp: BinaryExp {GTR BinaryExp} -- BinaryExp: BinaryExp {(DIV | MAPPLY| GOBBLE| ITER | PLUS | MINUS | CATL | SEQ | EQUAL ) PrefixExp} | BinaryExp PrefixExp -- PrefixExp: UNARY PrefixExp | PFUNC1 PrefixExp | MINUS PrefixExp | TILDE PrefixExp | SimpleExp {SEQOF | SEQOFC | OPT | DELETE} -- SimpleExp: STR | ID | PFUNC | SCREEN | WILD | FAIL | HOLE | LB RB | LB prog RB | LC stmt RC | ID COLON {= PrefixExp ;}prog | LB prog RB COLON {= PrefixExp ;} prog -- | LP stmt RP -- -- Convention: peektok is the first token for each of the routines, e.g. -- peektok = STR for the Base CheckRoutine: PROC[prog: Node] RETURNS [ans: Node] = { Ch: PROC[n:Node] RETURNS[BOOLEAN] = { WITH n SELECT FROM f: rFCN => WITH f.parms SELECT FROM p: rEQUAL => { [] ← P.Eval[n, NIL]; }; ENDCASE; ENDCASE; RETURN[TRUE]; }; Ch1: PROC[n:Node] RETURNS[BOOLEAN] = { IF n.Type=EQUAL THEN { P.OS.Put[rope["Missed Equality Check"], char[CR]]; Disp.Print[n]; }; RETURN[TRUE]; }; WITH prog SELECT FROM s: rSTR => {checking ← TRUE; {p: Node ← Dist[s.str ! UNWIND => checking ← FALSE]; checking ← FALSE; [] ← P.Eval[p, NIL]; P.Preorder[p, Ch]; P.Preorder[p, Ch1]; ans ← Nail}}; ENDCASE => P.RErr["input to check not string"]; }; FillRoutine: PROC[prog: Node] RETURNS [ans: Node] = {WITH prog SELECT FROM s: rSTR => {ans ← Dist[s.str]; [] ← P.Eval[ans, NIL]}; ENDCASE => P.RErr["input to check not string"]; }; Dist: PUBLIC PROC [p: ROPE] RETURNS[Node] = { -- this is the kickoff routine - call only once -- p is the node to which has the string to be compiled n: Node ← NIL; progstr ← S.NewStream[p]; savinput ← S.CopyStream[progstr]; charcnt ← stcnt ← linecnt ← 1; nchar ← ' ; GetTok; -- set up peek vals n ← Prog[]; IF tokenType ~= EOF THEN ErrorMsg["Parser expected EOF"]; P.Preorder[n,CheckPattern]; RETURN[n]; }; CheckPattern: PROC[n: Node] RETURNS[BOOLEAN] = { IF n.Type = PATTERN THEN RETURN[FALSE]; IF n.Type = SEQOF OR n.Type = SEQOFC OR n.Type = OPT OR n.Type = DELETE OR n.Type = HOLE THEN ErrorMsg["Pattern operator not surrounded by { }"]; RETURN[TRUE]; }; Prog: PROC RETURNS [res: Node] = { res ← NIL; res ← Stmt[]; IF tokenType = SEP THEN { GetTok; IF tokenType ~= EOF THEN res ← N.NEW[NodeRecord←[,PROG[res,Prog[]]]]; }; }; Stmt: PROC RETURNS[i: Node] = { i ← ChoiceExp[]; IF tokenType = ASS THEN { WITH i SELECT FROM x: rID =>{t: Symbol = x.name; GetTok; i ← N.NEW[NodeRecord←[,ASS[t,Stmt[]]]]; } ENDCASE => ErrorMsg["Missing ; or assignment to non-variable"]; }; RETURN[i]; }; -- this parses left-assoc instead of right assoc. ChoiceExp: PROC RETURNS[Node] = { i,j: Node ← NIL; IF tokenType = PALT THEN i ← currn ELSE i ← ThenExp[]; WHILE tokenType = PALT DO GetTok; j ← ThenExp[]; i ← N.NEW[NodeRecord←[,PALT[i,j]]] ENDLOOP; RETURN[i]; }; ThenExp: PROC RETURNS[Node] = { i,j: Node ← NIL; IF tokenType = GTR THEN i ← currn ELSE i ← BinaryExp[]; WHILE tokenType = GTR DO GetTok; j ← BinaryExp[]; i ← N.NEW[NodeRecord←[,GTR[i,j]]] ENDLOOP; RETURN[i]; }; BinaryExp: PROC RETURNS[i: Node] = { loop: BOOLEAN ← TRUE; p: TokType ← tokenType; i ← IF p = DIV OR p = MAPPLY OR p = GOBBLE OR p = ITER OR p = PLUS OR p = CATL OR p = SEQ OR p = PLUS OR p = CATL OR p = SEQ THEN currn ELSE PrefixExp[]; WHILE loop DO loop ← TRUE; SELECT tokenType FROM DIV => { GetTok; i ← N.NEW[NodeRecord←[,PAPPLY[i,PrefixExp[]]]]; }; MAPPLY => { GetTok; i ← N.NEW[NodeRecord←[,MAPPLY[i,PrefixExp[]]]]; }; GOBBLE => { GetTok; i ← N.NEW[NodeRecord←[,GOBBLE[i,PrefixExp[]]]]; }; ITER => { GetTok; i ← N.NEW[NodeRecord←[,ITER[i,PrefixExp[]]]]; }; PLUS => { GetTok; i ← N.NEW[NodeRecord←[,PLUS[i,PrefixExp[]]]]; }; MINUS => { GetTok; i ← N.NEW[NodeRecord←[,MINUS[i,PrefixExp[]]]]; }; CATL => { GetTok; i ← N.NEW[NodeRecord←[,CATL[i,PrefixExp[]]]]; }; SEQ => { GetTok; i ← N.NEW[NodeRecord←[,SEQ[i,PrefixExp[]]]]; }; EQUAL => { GetTok; IF checking THEN i ← N.NEW[NodeRecord←[,EQUAL[i,PrefixExp[]]]] ELSE [] ← PrefixExp[]; }; ENDCASE => { -- check to see if this is a cat -- the list below must be kept up to date. -- It is those things in First[PreFixExp] p ← tokenType; IF p=ID OR p=STR OR p = COMB OR p = LB OR p = LC OR p = LP OR p = TILDE OR p = MINUS OR p = SCREEN OR p = HOLE OR p=WILD OR p = FAIL THEN i ← N.NEW[NodeRecord←[,CAT[i,PrefixExp[]]]] ELSE loop ← FALSE; }; ENDLOOP; RETURN[i]; }; PrefixExp: PROC RETURNS[i:Node] = { SELECT tokenType FROM ID => WITH tokenID SELECT FROM s: rUNARY => {t: Symbol ← tokenID; GetTok; RETURN[N.NEW[NodeRecord ←[,COMB[t,PrefixExp[]]]]]; }; s: rPFUNC1 => {t: Symbol ← tokenID; GetTok; RETURN[N.NEW[NodeRecord ←[,COMB[t,PrefixExp[]]]]]; }; ENDCASE; TILDE => { GetTok; RETURN[N.NEW[NodeRecord ←[,TILDE[PrefixExp[]]]]]; }; MINUS => { GetTok; i ← N.NEW[NodeRecord ←[,MINUS[P.SN[S.MakeNUM[0]],PrefixExp[]]]]; RETURN; }; ENDCASE; i ← SimpleExp[]; WHILE tokenType = SEQOF OR tokenType = SEQOFC OR tokenType = OPT OR tokenType = DELETE DO IF tokenType = SEQOF THEN { GetTok; i ← N.NEW[NodeRecord ←[,SEQOF[i]]]; } ELSE IF tokenType = SEQOFC THEN { GetTok; i ← N.NEW[NodeRecord ←[,SEQOFC[i]]]; } ELSE IF tokenType = OPT THEN { GetTok; i ← N.NEW[NodeRecord ←[,OPT[i]]]; } ELSE IF tokenType = DELETE THEN { GetTok; i ← N.NEW[NodeRecord ←[,DELETE[i]]]; }; ENDLOOP; }; SimpleExp: PROC RETURNS[t: Node] = { SELECT tokenType FROM STR => {x:String=tokenString; GetTok; RETURN[P.SN[x]]; }; ID => { t ← N.NEW[NodeRecord ← [,ID[tokenID]]]; GetTok; IF tokenType=COLON THEN t ← FunctionBody[t]; RETURN; }; SCREEN => { GetTok; RETURN[currn] }; HOLE => { GetTok; RETURN[N.NEW[NodeRecord ← [,HOLE[]]]]; }; WILD => { GetTok; RETURN[N.NEW[NodeRecord ← [,WILD[]]]]; }; FAIL => { GetTok; RETURN[Fail]; }; LB => { GetTok; IF tokenType = RB THEN { GetTok;RETURN[Nail]}; {pans: LSTNode ← N.NEW[LSTNodeRecord ← [,LST[Prog[],Nail]]]; t ← pans; WHILE tokenType = COMMA DO GetTok; pans.listtail ← N.NEW[LSTNodeRecord ← [,LST[Prog[],Nail]]]; pans ← pans.listtail; ENDLOOP; IF tokenType ~= RB THEN ErrorMsg["Missing ']'"]; GetTok; IF tokenType=COLON THEN t ← FunctionBody[t]; RETURN}; }; LP => { -- used solely for parenthesization GetTok; t ← Prog[]; IF tokenType ~= RP THEN ErrorMsg["Parser expected ')'"]; GetTok; RETURN[t]; }; LC => { GetTok; t ← Prog[]; IF tokenType ~= RC THEN ErrorMsg["Parser expected '}'"]; GetTok; RETURN[N.NEW[NodeRecord←[,PATTERN[t]]]]; }; ENDCASE; ErrorMsg["Parser did not recognize Simple Expression"]; }; FunctionBody: PROC[bv: Node] RETURNS [b: Node] = { GetTok; IF tokenType=EQUAL THEN { testVal: Node ← NIL; b ←NIL; GetTok; testVal ← PrefixExp[]; IF tokenType#SEP THEN ErrorMsg["Missing ; after :="]; GetTok; IF checking THEN bv ← N.NEW[NodeRecord ← [,EQUAL[bv,testVal]]]; }; b ← N.NEW[NodeRecord ← [,FCN[bv, Prog[]]]]; }; wk: REF TEXT ← NEW[TEXT[100]]; GetTok: PROC= { i: CARDINAL; sym: Symbol; got: BOOLEAN ← FALSE; c: CHARACTER; loop: BOOLEAN ← TRUE; uc: BOOLEAN; WHILE loop DO loop ← FALSE; WHILE nchar = ' OR nchar = TAB OR nchar = CR DO [] ← GetNChar[] ENDLOOP; SELECT nchar FROM 0C => tokenType ← EOF; '" => { st: Node ← NIL; t: String ← ""; i ← 0; DO IF i = wk.maxLength THEN { wk.length ← i; t ← Rope.Concat[t, Rope.FromRefText[wk]]; i ← 0; }; wk[i] ← GetNChar[]; IF wk[i] = 0C THEN ErrorMsg["String ran off end, probably omitted quote"]; IF wk[i] = '" THEN { wk.length ← i; t ← Rope.Concat[t, Rope.FromRefText[wk]]; EXIT; }; IF wk[i] = '↑ THEN wk[i] ← Usual[GetNChar[]]; i ← i + 1; ENDLOOP; tokenType ← STR; tokenString←t}; IN ['0..'9] => { wk[0] ← nchar; i ← 1; WHILE i < wk.maxLength DO wk[i] ← GetNChar[]; IF wk[i] = 0C THEN EXIT; IF wk[i] NOT IN ['0..'9] THEN EXIT; i ← i + 1; ENDLOOP; IF i >= wk.maxLength THEN ErrorMsg["Number too long for parser"]; wk.length ← i; tokenType ← STR; tokenString ← Rope.FromRefText[wk]; got ← TRUE; }; '' => { -- single quote, just like " except terminated diff. i ← 0; WHILE i < wk.maxLength AND (GetNChar[] IN ['A..'Z] OR nchar IN ['a..'z] OR nchar IN ['0..'9] OR nchar = '. OR nchar = '↑) DO wk[i] ← IF nchar = '↑ THEN Usual[GetNChar[]] ELSE nchar; i ← i + 1; ENDLOOP; IF i >= wk.maxLength THEN ErrorMsg["String too long for parser"]; wk.length ← i; tokenType ← STR; tokenString ← Rope.FromRefText[wk]; got ← TRUE; }; '( => tokenType ←LP; ') => tokenType ←RP; '[ => tokenType ←LB; '] => tokenType ←RB; '{ => tokenType ←LC; '} => tokenType ←RC; ': => tokenType ←COLON; '~ => tokenType ←TILDE; '% => tokenType ←ITER; '@ => tokenType ←SCREEN; '/ => { [] ← GetNChar[]; IF nchar ~= '/ THEN { tokenType ←DIV; got ← TRUE; } ELSE { [] ← GetNChar[]; IF nchar ~= '/ THEN { tokenType ←MAPPLY; got ← TRUE; } ELSE tokenType ←GOBBLE; }; }; ', => { [] ← GetNChar[]; IF nchar = ', THEN tokenType ←CATL ELSE IF nchar = '! THEN { tokenType ←SEQOFC; } ELSE IF nchar ~= ', THEN { tokenType ←COMMA; got ← TRUE; }; }; '| => tokenType ←PALT; '. => { [] ← GetNChar[]; IF nchar ~= '. THEN ErrorMsg["Unknown character '.'"] ELSE { [] ← GetNChar[]; IF nchar ~= '. THEN ErrorMsg["Unknown character '.'"] ELSE tokenType ←HOLE; }; }; '+ => tokenType ←PLUS; '* => tokenType ←DELETE; '> => tokenType ←GTR; '# => tokenType ←WILD; '; => { stcnt ← stcnt + 1; tokenType ←SEP; }; '← => tokenType ←ASS; '= => tokenType ← EQUAL; '- => { [] ← GetNChar[] ; IF nchar = '- THEN { [] ← GetNChar[]; IF nchar ~= '- THEN { tokenType ←SEQ; got ← TRUE; } ELSE { DO c ← GetNChar[]; IF c = 0C OR c = CR THEN EXIT; IF c ~= '- THEN LOOP; c ← GetNChar[]; IF c = 0C OR c = CR THEN EXIT; IF c ~= '- THEN LOOP; c ← GetNChar[]; IF c = '- OR c = 0C OR c = CR THEN EXIT; ENDLOOP; loop ← TRUE; got ← TRUE; } } ELSE { tokenType ←MINUS; got ← TRUE; }; }; '? => tokenType ←OPT; '! => tokenType ←SEQOF; IN ['a..'z], IN ['A..'Z] => { i ← 0; uc ← FALSE; WHILE nchar IN ['a..'z] OR nchar IN ['A..'Z] OR nchar IN ['0..'9] DO wk[i] ← nchar; uc ← uc OR nchar IN['A..'Z]; i ← i + 1; [] ← GetNChar[]; ENDLOOP; wk.length ← i; sym ← P.Lookup[Rope.FromRefText[wk]]; IF sym = NIL THEN { IF ~uc AND i > 1 THEN ErrorMsg["Unknown primitive function name"]; tokenType ← ID; tokenID ← P.Insert[Rope.FromRefText[wk],[,,VAL[NIL]]]; } ELSE {tokenType ← ID; tokenID ← sym}; got ← TRUE; }; ENDCASE => ErrorMsg["Unknown character"]; IF ~got THEN [] ← GetNChar[]; ENDLOOP; }; SetCurrentNode: PUBLIC PROC[n:Node] = { currn ← n; }; GetNChar: PROC RETURNS [CHARACTER] = { IF nchar = CR THEN savinput ← progstr; [nchar, progstr] ← S.Item[progstr]; IF nchar = Control['Z] THEN WHILE nchar ~= CR DO [nchar,progstr] ← S.Item[progstr] ENDLOOP; charcnt ← IF nchar = CR THEN 1 ELSE charcnt + 1; IF nchar = CR THEN linecnt ← linecnt + 1; RETURN[nchar]; }; ErrorMsg: PROC[str: ROPE] = { c: CHARACTER; P.OS.PutF["Syntax error, Line: %g, Stmt: %g, Char: %g %g*nIn command ", int[linecnt], int[stcnt], int[charcnt], rope[str]]; WHILE ~S.EmptyS[savinput] DO [c, savinput] ← S.Item[savinput]; IF c = CR THEN EXIT; P.OS.Put[char[c]]; ENDLOOP; P.OS.Put[char[CR]]; P.SErr[""] }; Usual: PROC[c: CHARACTER] RETURNS[CHARACTER] = { j: CARDINAL; SELECT c FROM ' => RETURN[' ]; '" => RETURN['"]; '↑ => RETURN['↑]; '' => RETURN['']; IN ['0..'9] =>{ j ← (c - '0)*64; j ← j + (GetNChar[] - '0) * 8; j ← j + (GetNChar[] - '0); RETURN[LOOPHOLE[j]]; }; ENDCASE => RETURN[IF c<100C THEN c ELSE c - 100B]; }; ParseSetup: PUBLIC PROC = { SetCurrentNode[Nail]; [] ← P.Insert["check",[,,ZARY[CheckRoutine]]]; [] ← P.Insert["fill",[,,ZARY[FillRoutine]]]; [] ← P.Insert["fail",[,,VAL[Fail]]]}; [Fail,MTSt,Nail] ← P.GetSpecialNodes[]; }.