-- File: ArpaTokenImpl.mesa - last edit: -- JAV 17-Jun-87 14:48:34 -- Copyright (C) 1987 by Xerox Corporation. All rights reserved. DIRECTORY Ascii USING [CR, NUL, SP, TAB], Heap: TYPE USING [systemZone], Inline USING [BITAND], String USING [ AppendChar, CopyToNewString, InvalidNumber, Length, StringBoundsFault, StringToLongNumber, StringToNumber], ArpaToken; ArpaTokenImpl: MONITOR IMPORTS Heap, Inline, String EXPORTS ArpaToken = BEGIN OPEN Ascii; UnterminatedQuote: PUBLIC SIGNAL = CODE; FilterState: TYPE = ArpaToken.FilterState; StandardFilterState: TYPE = ArpaToken.StandardFilterState; FilterProcType: TYPE = ArpaToken.FilterProcType; GetCharProcType: TYPE = ArpaToken.GetCharProcType; QuoteProcType: TYPE = ArpaToken.QuoteProcType; SkipMode: TYPE = ArpaToken.SkipMode; nonQuote: CHARACTER = ArpaToken.nonQuote; initialLength: CARDINAL = 100; --initial length for ArpaToken strings lengthIncrement: CARDINAL = 100; --amount of additional storage to allocate --on a bounds fault savedString: LONG STRING ← NIL; -- keep one string of length initialLength GetString: ENTRY PROC [length: CARDINAL] RETURNS [LONG STRING] = { ENABLE UNWIND => NULL; IF length = initialLength AND savedString # NIL THEN { ls: LONG STRING ← savedString; savedString ← NIL; ls.length ← 0; RETURN[ls]}; RETURN[Heap.systemZone.NEW[StringBody[length]]]}; FreeString: ENTRY PROC [ls: LONG STRING] = { ENABLE UNWIND => NULL; IF ls # NIL AND ls.maxlength = initialLength AND savedString = NIL THEN { savedString ← ls; RETURN}; Heap.systemZone.FREE[@ls]}; Boolean: PUBLIC PROCEDURE [h: ArpaToken.Handle, signalOnError: BOOLEAN ← TRUE] RETURNS [true: BOOLEAN ← FALSE] = BEGIN temp: STRING ← [10]; BEGIN InvalidChar: PROCEDURE [expected: CHARACTER] RETURNS [invalid: BOOLEAN] = BEGIN String.AppendChar[temp, h.break ← h.getChar[h]]; invalid ← Inline.BITAND[h.break, 337B] # expected; END; Skip[h, NIL, WhiteSpace, TRUE]; String.AppendChar[temp, h.break]; SELECT Inline.BITAND[h.break, 337B] FROM 'T => BEGIN IF InvalidChar['R] OR InvalidChar['U] THEN GO TO invalid; IF InvalidChar['E] THEN GO TO invalid; -- allow compiler to cross-jump RETURN[TRUE]; END; 'F => BEGIN IF InvalidChar['A] OR InvalidChar['L] OR InvalidChar['S] THEN GO TO invalid; IF InvalidChar['E] THEN GO TO invalid; -- allow compiler to cross-jump RETURN[FALSE]; END; ENDCASE => GO TO invalid; EXITS invalid => IF signalOnError THEN SIGNAL SyntaxError[temp]; END; END; -- MAS: Can we get rid of this and just use Inline.LowHalf[LongNumber] -- if HighHalf[] = 0 or -1? Two issues: speed and equivalence of results. Number: PUBLIC PROC [ h: ArpaToken.Handle, radix: CARDINAL, signalOnError: BOOLEAN ← TRUE] RETURNS [u: UNSPECIFIED] = BEGIN s: LONG STRING ← Filtered[h, NIL, NumberFilter]; IF s = NIL THEN {IF signalOnError THEN SIGNAL SyntaxError[s]; RETURN[0]}; u ← String.StringToNumber[ s, radix ! String.InvalidNumber => { IF signalOnError THEN SIGNAL SyntaxError[s]; u ← 0; CONTINUE}; UNWIND => FreeString[s]]; FreeString[s]; END; Decimal: PUBLIC PROC [h: ArpaToken.Handle, signalOnError: BOOLEAN ← TRUE] RETURNS [i: INTEGER] = {i ← Number[h, 10, signalOnError]}; Octal: PUBLIC PROC [h: ArpaToken.Handle, signalOnError: BOOLEAN ← TRUE] RETURNS [c: CARDINAL] = {c ← Number[h, 8, signalOnError]}; LongNumber: PUBLIC PROC [ h: ArpaToken.Handle, radix: CARDINAL, signalOnError: BOOLEAN ← TRUE] RETURNS [u: LONG UNSPECIFIED] = BEGIN s: LONG STRING ← Filtered[h, NIL, NumberFilter]; IF s = NIL THEN {IF signalOnError THEN SIGNAL SyntaxError[s]; RETURN[0]}; u ← String.StringToLongNumber[ s, radix ! String.InvalidNumber => { IF signalOnError THEN SIGNAL SyntaxError[s]; u ← 0; CONTINUE}; UNWIND => FreeString[s]]; FreeString[s]; END; LongDecimal: PUBLIC PROC [h: ArpaToken.Handle, signalOnError: BOOLEAN ← TRUE] RETURNS [i: LONG INTEGER] = {i ← LongNumber[h, 10, signalOnError]}; LongOctal: PUBLIC PROC [h: ArpaToken.Handle, signalOnError: BOOLEAN ← TRUE] RETURNS [c: LONG CARDINAL] = {c ← LongNumber[h, 8, signalOnError]}; Item: PUBLIC PROCEDURE [h: ArpaToken.Handle, temporary: BOOLEAN ← TRUE] RETURNS [value: LONG STRING] = BEGIN value ← GetString[initialLength]; BEGIN ENABLE { UNWIND => FreeString[value]; String.StringBoundsFault => BEGIN ns ← String.CopyToNewString[ s, Heap.systemZone, s.maxlength + lengthIncrement - s.length]; FreeString[s]; RESUME[value ← ns]; END}; Skip[h, NIL, WhiteSpace, TRUE]; DO IF WhiteSpaceInline[h.break] OR h.break = NUL THEN EXIT; String.AppendChar[value, h.break]; h.break ← h.getChar[h]; ENDLOOP; IF value.length = 0 THEN {FreeString[value]; RETURN[NIL]}; IF ~temporary THEN { old: LONG STRING ← value; value ← String.CopyToNewString[old, Heap.systemZone]; FreeString[old]}; END; END; -- No default skip! ZeroData: PROCEDURE [data: FilterState] = INLINE BEGIN pu: LONG POINTER TO LONG UNSPECIFIED = LOOPHOLE[data]; IF pu # NIL THEN pu↑ ← 0; END; Skip: PUBLIC PROCEDURE [ h: ArpaToken.Handle, data: FilterState, filter: FilterProcType, skipInClass: BOOLEAN ← TRUE] = BEGIN ZeroData[data]; WHILE (h.break ← h.getChar[h]) # NUL AND filter[h.break, data] = skipInClass DO ENDLOOP; END; Filtered: PUBLIC PROCEDURE [ h: ArpaToken.Handle, data: FilterState, filter: FilterProcType, skip: SkipMode ← whiteSpace, temporary: BOOLEAN ← TRUE] RETURNS [value: LONG STRING] = BEGIN value ← NIL; ZeroData[data]; -- First handle (possible) skip DO IF (h.break ← h.getChar[h]) = NUL THEN RETURN; IF skip = whiteSpace AND WhiteSpaceInline[h.break] THEN LOOP; IF filter[h.break, data] THEN EXIT; IF skip = none OR skip = whiteSpace THEN RETURN; ENDLOOP; -- Then accumulate all inClass characters value ← GetString[initialLength]; DO ENABLE { UNWIND => FreeString[value]; String.StringBoundsFault => BEGIN ns ← String.CopyToNewString[ s, Heap.systemZone, s.maxlength + lengthIncrement - s.length]; FreeString[s]; RESUME[value ← ns]; END}; String.AppendChar[value, h.break]; IF (h.break ← h.getChar[h]) = NUL OR ~filter[h.break, data] THEN EXIT; ENDLOOP; IF ~temporary THEN { old: LONG STRING ← value; value ← String.CopyToNewString[old, Heap.systemZone]; FreeString[old]}; END; MaybeQuoted: PUBLIC PROCEDURE [ h: ArpaToken.Handle, data: FilterState, filter: FilterProcType ← NonWhiteSpace, isQuote: QuoteProcType ← Quote, skip: SkipMode ← whiteSpace, temporary: BOOLEAN ← TRUE] RETURNS [value: LONG STRING] = BEGIN closeQuote: CHARACTER; QuoteFilter: FilterProcType = { IF c = NUL THEN {SIGNAL UnterminatedQuote; RETURN [FALSE]} ELSE RETURN[c # closeQuote]}; ApplyFilter: FilterProcType ← filter; ZeroData[data]; value ← NIL; -- First handle (possible) skip DO IF (h.break ← h.getChar[h]) = NUL THEN RETURN; IF skip = whiteSpace AND WhiteSpaceInline[h.break] THEN LOOP; IF (closeQuote ← isQuote[h.break]) # nonQuote THEN IF (h.break ← h.getChar[h]) = closeQuote THEN { h.break ← h.getChar[h]; IF h.break = closeQuote THEN EXIT; -- doubling close quote is literal quote character RETURN} ELSE { IF h.break = NUL THEN {SIGNAL UnterminatedQuote; RETURN}; ApplyFilter ← QuoteFilter; EXIT}; IF filter[h.break, data] THEN EXIT; IF skip = none OR skip = whiteSpace THEN RETURN; ENDLOOP; -- Then accumulate all inClass characters value ← GetString[initialLength]; BEGIN ENABLE { UNWIND => FreeString[value]; String.StringBoundsFault => BEGIN ns ← String.CopyToNewString[ s, Heap.systemZone, s.maxlength + lengthIncrement - s.length]; FreeString[s]; RESUME[value ← ns]; END}; WHILE h.break # NUL DO String.AppendChar[value, h.break]; IF ~ApplyFilter[h.break ← h.getChar[h], data] THEN BEGIN IF ApplyFilter = QuoteFilter THEN { h.break ← h.getChar[h]; -- get next character IF h.break = closeQuote THEN LOOP}; -- of closeQuote, include character in token EXIT; END; ENDLOOP; IF ~temporary THEN { old: LONG STRING ← value; value ← String.CopyToNewString[old, Heap.systemZone]; FreeString[old]}; END; END; -- Standard filters Alphabetic: PUBLIC FilterProcType = BEGIN RETURN[SELECT c FROM IN ['a..'z], IN ['A..'Z] => TRUE, ENDCASE => FALSE]; END; AlphaNumeric: PUBLIC FilterProcType = BEGIN RETURN[ SELECT c FROM IN ['a..'z], IN ['A..'Z], IN ['0..'9] => TRUE, ENDCASE => FALSE]; END; Delimited: PUBLIC FilterProcType = -- first non-blank char IS the delimiter. Must be used with skip = nonArpaToken BEGIN delimiter: LONG POINTER TO CHARACTER = LOOPHOLE[data]; IF data = NIL THEN ERROR NilData; IF data[0] # 0 THEN RETURN[c # delimiter↑]; IF ~WhiteSpaceInline[c] AND c # NUL THEN delimiter↑ ← c; RETURN[FALSE]; END; uninitialized: CARDINAL = 0; inHost: CARDINAL = 1; startName: CARDINAL = 2; inName: CARDINAL = 3; FileName: PUBLIC FilterProcType = BEGIN IF data = NIL THEN ERROR NilData; SELECT c FROM IN ['a..'z], IN ['A..'Z], IN ['0..'9], '>, '*, '!, ';, '#, '-, '., '$, '+ => IF data[0] = uninitialized OR data[0] = startName THEN data[0] ← inName; '< => IF data[0] = uninitialized OR data[0] = startName THEN data[0] ← inName ELSE RETURN[FALSE]; '[ => IF data[0] = uninitialized THEN data[0] ← inHost ELSE RETURN[FALSE]; '] => IF data[0] = inHost THEN data[0] ← startName ELSE RETURN[FALSE]; ENDCASE => IF data[0] # inHost THEN RETURN[FALSE]; RETURN[TRUE]; END; Line: PUBLIC FilterProcType = BEGIN RETURN[SELECT c FROM CR, NUL => FALSE, ENDCASE => TRUE]; END; Numeric: PUBLIC FilterProcType = BEGIN RETURN[SELECT c FROM IN ['0..'9] => TRUE, ENDCASE => FALSE]; END; Switches: PUBLIC FilterProcType = -- '~, '-, AlphaNumeric BEGIN RETURN[ SELECT c FROM IN ['a..'z], IN ['A..'Z], IN ['0..'9], '~, '- => TRUE, ENDCASE => FALSE]; END; NumberFilter: FilterProcType = BEGIN RETURN[ SELECT c FROM IN ['0..'9], '+, '-, 'B, 'b, 'D, 'd => TRUE, ENDCASE => FALSE]; END; NonWhiteSpace: PUBLIC FilterProcType = BEGIN RETURN[c # NUL AND ~WhiteSpaceInline[c]]; END; WhiteSpace: PUBLIC FilterProcType = BEGIN RETURN[WhiteSpaceInline[c]]; END; WhiteSpaceInline: PROCEDURE [c: CHARACTER] RETURNS [ --isWhiteSpace:-- BOOLEAN] = INLINE BEGIN RETURN[SELECT c FROM SP, TAB, CR => TRUE, ENDCASE => FALSE]; END; Brackets: PUBLIC QuoteProcType = -- () [] {} <> BEGIN RETURN[ SELECT c FROM '( => '), '[ => '], '{ => '}, '< => '>, ENDCASE => nonQuote]; END; Quote: PUBLIC QuoteProcType = -- '" BEGIN RETURN[SELECT c FROM '', '" => c, ENDCASE => nonQuote]; END; -- Type conversion LSHandle: TYPE = LONG POINTER TO LSObject; LSObject: TYPE = MACHINE DEPENDENT RECORD [ object: ArpaToken.Object, s: LONG STRING, i: CARDINAL]; FreeStringHandle: PUBLIC PROCEDURE [h: ArpaToken.Handle] RETURNS [nil: ArpaToken.Handle ← NIL] = {Heap.systemZone.FREE[@h]}; StringToHandle: PUBLIC PROCEDURE [s: LONG STRING, offset: CARDINAL ← 0] RETURNS [h: ArpaToken.Handle] = BEGIN lsh: LSHandle ← Heap.systemZone.NEW[ LSObject ← [[getChar: StringGetChar, break: Ascii.NUL], s, offset]]; h ← @lsh.object; END; FreeArpaTokenString: PUBLIC PROCEDURE [s: LONG STRING] RETURNS [nil: LONG STRING ← NIL] = {FreeString[s]}; StringGetChar: ArpaToken.GetCharProcType = BEGIN lsh: LSHandle ← LOOPHOLE[h]; IF lsh.i < String.Length[lsh.s] THEN { c ← lsh.s[lsh.i]; lsh.i ← lsh.i + 1} ELSE c ← Ascii.NUL; END; -- SIGNALs NilData: PUBLIC SIGNAL = CODE; SyntaxError: PUBLIC SIGNAL [s: LONG STRING] = CODE; END...