MODULEM3CLex EXPORTSM3CLex ,M3CLexF ; IMPORT Text, Fmt, Thread; IMPORT Rd, ASCII, RdExtras; IMPORT M3CToken, M3CHash, M3CReservedWord, M3CSrcPos, M3CLexF; CONST MaxLookahead = 3;
Need three characters lookahead for lexing, for example 3.2E+A which is not going to parse but lexes as a real literal followed by an identifier followed by plus followed by another identifier. Actual lookahead buffer allows one more character for use when disabling the lexer
REVEAL Buffer = BRANDED REF RECORD chars: ARRAY [0..2047] OF CHAR; overflow: Buffer := NIL; END; REVEAL T = M3CLexF.FriendPart BRANDED OBJECT currentTok := M3CToken.Void; isDisabled := FALSE; lookahead: CARDINAL := 0; lookaheadBuffer: ARRAY [0..MaxLookahead] OF CHAR; OVERRIDES init := Init; current := Current; next := Next; position := Position; literal := Literal; identifier := Identifier; disable := Disable; disabled := Disabled; reset := Reset; currentTokenToText := CurrentTokenToText; get := Get; unget := Unget; readId := ReadId; readNumericLiteral := ReadNumericLiteral; readCharLiteral := ReadCharLiteral; readTextLiteral := ReadTextLiteral; readCommentOrPragma := ReadCommentOrPragma; END; PROCEDUREManaging the token buffer; putting characters into the buffer and building arrays or texts from the completed bufferInit ( t: T; rd: Rd.T; identifiers: M3CReservedWord.Table; literals: M3CHash.Table; callBack: CallBack): T= BEGIN t.rd := rd; t. identifiers := identifiers; t.literals := literals; t.callBack := callBack; t.line := 1; t.hashValue := NEW(M3CHash.Value).init(); t.tokenBuffer := NEW(Buffer); RETURN t; END Init; <*INLINE*> PROCEDUREGet (t: T): CHAR RAISES {Rd.Failure, Rd.EndOfFile}= <*FATAL Thread.Alerted*> BEGIN IF t.lookahead > 0 THEN DEC(t.lookahead); RETURN t.lookaheadBuffer[t.lookahead]; ELSE RETURN Rd.GetChar(t.rd); END; END Get; <*INLINE*> PROCEDUREUnget (t: T; ch: CHAR)= BEGIN t.lookaheadBuffer[t.lookahead] := ch; INC(t.lookahead); END Unget;
PROCEDUREReading an identifier or reserved wordBufferToChars ( buffer: Buffer; length: CARDINAL) : REF ARRAY OF CHAR = VAR chars := NEW(REF ARRAY OF CHAR, length); BEGIN VAR mod: CARDINAL := length MOD NUMBER(buffer.chars); pos: CARDINAL; BEGIN IF mod = 0 THEN mod := NUMBER(buffer.chars) END; pos := length - mod; IF mod # 0 THEN SUBARRAY(chars^, pos, mod) := SUBARRAY(buffer.chars, 0, mod); END; WHILE pos # 0 DO buffer := buffer.overflow; DEC(pos, NUMBER(buffer.chars)); SUBARRAY(chars^, pos, NUMBER(buffer.chars)) := buffer.chars; END; END; RETURN chars; END BufferToChars; PROCEDUREBufferToText (buffer: Buffer; length: CARDINAL): TEXT= BEGIN IF length <= NUMBER(buffer.chars) THEN RETURN Text.FromChars(SUBARRAY(buffer.chars, 0, length)); ELSE RETURN Text.FromChars(BufferToChars(buffer, length)^); END; END BufferToText; PROCEDUREAddOverflow (buffer: Buffer): Buffer= BEGIN RETURN NEW(Buffer, overflow := buffer); END AddOverflow; <*INLINE*> PROCEDUREBufferPut ( VAR buffer: Buffer; pos: CARDINAL; ch: CHAR) = VAR mod: CARDINAL := pos MOD NUMBER(buffer.chars); BEGIN IF mod = 0 AND pos # 0 THEN buffer := AddOverflow(buffer) END; buffer.chars[mod] := ch; END BufferPut; <*INLINE*> PROCEDUREHashAndBufferPut ( ch: CHAR; hashValue: M3CHash.Value; VAR buffer: Buffer; VAR pos: CARDINAL) = BEGIN BufferPut(buffer, pos, ch); hashValue.addCharToValue(ch); INC(pos); END HashAndBufferPut;
<*INLINE*> PROCEDUREReading numeric literalsIdOrReservedWord ( t: T; hashValue: M3CHash.Value; READONLY chars: ARRAY OF CHAR) : M3CToken.T = VAR id := t.identifiers.enterCharsWithValue(hashValue, chars); BEGIN TYPECASE id OF <*NOWARN*> | M3CReservedWord.Id(r) => RETURN M3CReservedWord.Token(r); | Symbol_rep(s) => t.cur_identifier := s; RETURN M3CToken.Identifier; END; END IdOrReservedWord; PROCEDUREReadId (t: T; firstCh: CHAR): M3CToken.T RAISES {Rd.Failure}= CONST IdentChars = ASCII.AlphaNumerics + ASCII.Set{'_'}; VAR ch := firstCh; hashValue := t.hashValue; buffer := t.tokenBuffer; pos: CARDINAL := 0; BEGIN hashValue.reset(); TRY REPEAT HashAndBufferPut(ch, hashValue, buffer, pos); ch := Get(t); UNTIL NOT ch IN IdentChars; Unget(t, ch); EXCEPT | Rd.EndOfFile => END; INC(t.offset, pos - 1); IF pos <= NUMBER(buffer.chars) THEN WITH chars = SUBARRAY(buffer.chars, 0, pos) DO RETURN IdOrReservedWord(t, hashValue, chars); END; ELSE RETURN IdOrReservedWord(t, hashValue, BufferToChars(buffer, pos)^); END; END ReadId;
CONST BadLiteralTail = " (bad literal)"; (* Appended to all bad literals *) PROCEDUREReading character and text literalsEnterLiteral ( t: T; ok: BOOLEAN; hashValue: M3CHash.Value; buffer: Buffer; length: CARDINAL) = BEGIN IF NOT ok THEN FOR i := 0 TO Text.Length(BadLiteralTail) - 1 DO HashAndBufferPut( Text.GetChar(BadLiteralTail, i), hashValue, buffer, length); END; END; IF length <= NUMBER(buffer.chars) THEN WITH chars = SUBARRAY(buffer.chars, 0, length) DO t.cur_literal := t.literals.enterCharsWithValue(hashValue, chars); END; ELSE t.cur_literal := t.literals.enterCharsWithValue( hashValue, BufferToChars(buffer, length)^); END; END EnterLiteral; PROCEDURECheckedGet (t: T; VAR eof: BOOLEAN): CHAR RAISES {Rd.Failure}= BEGIN TRY eof := FALSE; RETURN Get(t); EXCEPT | Rd.EndOfFile => eof := TRUE; RETURN '\000'; END; END CheckedGet; PROCEDURECalculateBase (buffer: Buffer; pos: CARDINAL): CARDINAL= VAR mod := pos MOD NUMBER(buffer.chars); val := ORD(buffer.chars[mod]) - ORD('0'); BEGIN IF pos > 0 THEN IF mod = 0 THEN buffer := buffer.overflow END; INC(val, CalculateBase(buffer, pos - 1) * 10); END; RETURN val; END CalculateBase; PROCEDUREHexValue (ch: CHAR; VAR val: CARDINAL): BOOLEAN= BEGIN IF 'a' <= ch AND ch <= 'f' THEN val := ORD(ch) - ORD('a') + 10; RETURN TRUE; ELSIF 'A' <= ch AND ch <= 'F' THEN val := ORD(ch) - ORD('A') + 10; RETURN TRUE; ELSIF '0' <= ch AND ch <= '9' THEN val := ORD(ch) - ORD('0'); RETURN TRUE; ELSE RETURN FALSE; END; (* if *) END HexValue; PROCEDUREReadHexDigits ( t: T; hashValue: M3CHash.Value; VAR buffer: Buffer; VAR pos: CARDINAL; VAR ok: BOOLEAN) : M3CToken.T RAISES {Rd.Failure}= (* PRE: d* seen and in buffer. '_' fetched. *) VAR start := pos; eof: BOOLEAN; ch := CheckedGet(t, eof); (* Char after '_' *) val, base: CARDINAL; result : M3CToken.T := M3CToken.IntegerLiteral; BEGIN ok := TRUE; IF HexValue(ch, val) THEN IF pos > 2 THEN base := 16; ok := FALSE ELSE base := CalculateBase(buffer, pos - 1); END; IF 2 > base OR base > 16 THEN base := 16; ok := FALSE END; HashAndBufferPut('_', hashValue, buffer, pos); REPEAT (* INVARIANT: ch is a hex digit and val is its integer value. *) IF val >= base THEN ok := FALSE END; HashAndBufferPut(ch, hashValue, buffer, pos); ch := CheckedGet(t, eof); UNTIL NOT HexValue(ch, val); (* d*_h+ seen and in buffer. ch is character after. *) IF ch = 'L' OR ch = 'l' THEN HashAndBufferPut(ch, hashValue, buffer, pos); ch := CheckedGet(t, eof); result := M3CToken.LongintLiteral; END; END; IF NOT eof THEN Unget(t, ch) END; (* Char after based literal. *) IF pos = start THEN (* No hex digits. *) ok := FALSE END; RETURN result; END ReadHexDigits; PROCEDUREReadExponent ( t: T; exponent: CHAR; hashValue: M3CHash.Value; VAR buffer: Buffer; VAR pos: CARDINAL) : M3CToken.T RAISES {Rd.Failure}= VAR sign := '\000'; eof: BOOLEAN; ch := CheckedGet(t, eof); BEGIN IF ch = '-' OR ch = '+' THEN sign := ch; ch := CheckedGet(t, eof); END; IF ch IN ASCII.Digits THEN HashAndBufferPut(exponent, hashValue, buffer, pos); IF sign # '\000' THEN HashAndBufferPut(sign, hashValue, buffer, pos); END; REPEAT HashAndBufferPut(ch, hashValue, buffer, pos); ch := CheckedGet(t, eof); UNTIL NOT ch IN ASCII.Digits; IF NOT eof THEN Unget(t, ch) END; IF ASCII.Upper[exponent] = 'D' THEN RETURN M3CToken.LongRealLiteral; ELSIF ASCII.Upper[exponent] = 'X' THEN RETURN M3CToken.ExtendedLiteral; ELSE RETURN M3CToken.RealLiteral; END; ELSE IF NOT eof THEN Unget(t, ch) END; IF sign # '\000' THEN Unget(t, sign) END; Unget(t, exponent); RETURN M3CToken.RealLiteral; END; END ReadExponent; PROCEDUREReadRealOrRange ( t: T; hashValue: M3CHash.Value; VAR buffer: Buffer; VAR pos: CARDINAL) : M3CToken.T RAISES {Rd.Failure}= VAR eof: BOOLEAN; ch := CheckedGet(t, eof); BEGIN IF ch IN ASCII.Digits THEN HashAndBufferPut('.', hashValue, buffer, pos); REPEAT HashAndBufferPut(ch, hashValue, buffer, pos); ch := CheckedGet(t, eof); UNTIL NOT ch IN ASCII.Digits; IF ch IN ASCII.Set{'D', 'd', 'E', 'e', 'X', 'x'} THEN RETURN ReadExponent(t, ch, hashValue, buffer, pos); ELSE IF NOT eof THEN Unget(t, ch) END; RETURN M3CToken.RealLiteral; END; ELSE Unget(t, ch); Unget(t, '.'); RETURN M3CToken.IntegerLiteral; END; END ReadRealOrRange; PROCEDUREReadNumericLiteral ( t: T; firstCh: CHAR) : M3CToken.T RAISES {Rd.Failure}= VAR ch := firstCh; hashValue := t.hashValue; buffer := t.tokenBuffer; pos: CARDINAL := 0; result := M3CToken.IntegerLiteral; ok := TRUE; BEGIN hashValue.reset(); TRY REPEAT HashAndBufferPut(ch, hashValue, buffer, pos); ch := Get(t); UNTIL NOT ch IN ASCII.Digits; IF ch = '_' THEN result := ReadHexDigits(t, hashValue, buffer, pos, ok); ELSIF ch = '.' THEN result := ReadRealOrRange(t, hashValue, buffer, pos); ELSIF ch = 'l' OR ch = 'L' THEN HashAndBufferPut(ch, hashValue, buffer, pos); result := M3CToken.LongintLiteral; ELSE Unget(t, ch); END; EXCEPT | Rd.EndOfFile => END; INC(t.offset, pos - 1); EnterLiteral(t, ok, hashValue, buffer, pos); RETURN result; END ReadNumericLiteral;
PROCEDUREComments and pragmasReadEscape ( t: T; hashValue: M3CHash.Value; VAR buffer: Buffer; VAR pos: CARDINAL; wide: BOOLEAN) : BOOLEAN RAISES {Rd.Failure, Rd.EndOfFile}= CONST SimpleEscapes = ASCII.Set{ 'n', 't', 'r', 'f', '\\', '\'', '\"'}; OctalDigits = ASCII.Set{'0'..'7'}; HexDigits = ASCII.Set{'0'..'9', 'a'..'f', 'A'..'F' }; OctalCnt = ARRAY BOOLEAN(*wide*) OF [0..7] { 2, 5 }; HexCnt = ARRAY BOOLEAN(*wide*) OF [0..7] { 1, 3 }; VAR ch: CHAR; BEGIN ch := Get(t); IF ch IN SimpleEscapes THEN HashAndBufferPut(ch, hashValue, buffer, pos); RETURN TRUE; ELSIF ch IN OctalDigits THEN HashAndBufferPut(ch, hashValue, buffer, pos); FOR i := 1 TO OctalCnt[wide] DO ch := Get(t); IF ch IN OctalDigits THEN HashAndBufferPut(ch, hashValue, buffer, pos); ELSE Unget(t, ch); RETURN FALSE; END; END; RETURN TRUE; ELSIF (ch = 'x') THEN (* hex literal *) HashAndBufferPut(ch, hashValue, buffer, pos); FOR i := 0 TO HexCnt[wide] DO ch := Get(t); IF ch IN HexDigits THEN HashAndBufferPut(ch, hashValue, buffer, pos); ELSE Unget(t, ch); RETURN FALSE; END; END; RETURN TRUE; ELSE Unget(t, ch); RETURN FALSE; END; END ReadEscape; PROCEDUREReadCharLiteral (t: T; wide: BOOLEAN): M3CToken.T RAISES {Rd.Failure}= VAR ch: CHAR; hashValue := t.hashValue; buffer := t.tokenBuffer; pos: CARDINAL := 0; ok := TRUE; BEGIN hashValue.reset(); IF wide THEN HashAndBufferPut('W', hashValue, buffer, pos); END; HashAndBufferPut('\'', hashValue, buffer, pos); TRY ch := Get(t); IF ch IN ASCII.Graphics - ASCII.Set{'\''} THEN HashAndBufferPut(ch, hashValue, buffer, pos); IF ch = '\\' THEN ok := ReadEscape(t, hashValue, buffer, pos, wide); END; ch := Get(t); IF ch = '\'' THEN HashAndBufferPut(ch, hashValue, buffer, pos); ELSE Unget(t, ch); ok := FALSE; END; ELSE Unget(t, ch); ok := FALSE; END; EXCEPT | Rd.EndOfFile => ok := FALSE; END; INC(t.offset, pos - 1); EnterLiteral(t, ok, hashValue, buffer, pos); IF wide THEN RETURN M3CToken.WideCharLiteral; ELSE RETURN M3CToken.CharLiteral; END; END ReadCharLiteral; PROCEDUREReadTextLiteral (t: T; wide: BOOLEAN): M3CToken.T RAISES {Rd.Failure}= VAR ch: CHAR; hashValue := t.hashValue; buffer := t.tokenBuffer; pos: CARDINAL := 0; ok := TRUE; BEGIN hashValue.reset(); IF wide THEN HashAndBufferPut('W', hashValue, buffer, pos); END; HashAndBufferPut('\"', hashValue, buffer, pos); TRY LOOP ch := Get(t); IF ch IN ASCII.Graphics THEN HashAndBufferPut(ch, hashValue, buffer, pos); IF ch = '\\' THEN IF NOT ReadEscape(t, hashValue, buffer, pos, wide) THEN ok := FALSE END; ELSIF ch = '\"' THEN EXIT; ELSE (* loop *) END; ELSE Unget(t, ch); ok := FALSE; EXIT; END; END; EXCEPT | Rd.EndOfFile => ok := FALSE; END; INC(t.offset, pos - 1); EnterLiteral(t, ok, hashValue, buffer, pos); IF wide THEN RETURN M3CToken.WideTextLiteral; ELSE RETURN M3CToken.TextLiteral; END; END ReadTextLiteral;
PROCEDUREGet next tokenReadCommentOrPragmaSection ( t: T; READONLY endOfSection: ASCII.Set; VAR buffer: Buffer; VAR pos: CARDINAL) : CHAR RAISES {Rd.Failure, Rd.EndOfFile}= <*FATAL Thread.Alerted *> BEGIN WITH n = NUMBER(buffer.chars) DO LOOP VAR mod: CARDINAL := pos MOD n; wanted: CARDINAL := n - mod; got: CARDINAL; BEGIN IF mod = 0 AND pos # 0 THEN buffer := AddOverflow(buffer) END; got := RdExtras.GetUntil(t.rd, SUBARRAY(buffer.chars, mod, wanted), endOfSection); INC(pos, got); IF got > wanted THEN (* buffer overflow - loop to get new overflow section added *) DEC(pos); ELSE VAR save: Buffer := NIL; BEGIN IF mod = 0 AND got = 0 AND pos # 0 THEN (* new overflow section is empty and may not be needed if we hit end of stream *) save := buffer; buffer := save.overflow; END; WITH ch = Rd.GetChar(t.rd) DO IF save # NIL THEN save.chars[0] := ch; buffer := save; ELSE BufferPut(buffer, pos, ch); END; INC(pos); RETURN ch; END; END; END; END; END; END; END ReadCommentOrPragmaSection; PROCEDUREReadCommentOrPragma (t: T; isComment: BOOLEAN) RAISES {Rd.Failure}= <*FATAL Thread.Alerted*> VAR nesting := 0; pos: CARDINAL := 2; startOfLine := pos - t.offset; startChar, endChar, ch: CHAR; endOfSection: ASCII.Set; buffer := t.tokenBuffer; CONST EndOfLine = ASCII.Set{'\n', '\r', '\f'}; BEGIN IF isComment THEN startChar := '('; endChar := ')'; endOfSection := EndOfLine + ASCII.Set{'(', '*'}; ELSE startChar := '<'; endChar := '>'; endOfSection := EndOfLine + ASCII.Set{'<', '*'}; END; BufferPut(buffer, 0, startChar); BufferPut(buffer, 1, '*'); (* we know the lookahead buffer is empty at this point so we can safely use 'ReadCommentSection' which ignores the lookahead buffer *) TRY REPEAT ch := ReadCommentOrPragmaSection(t, endOfSection, buffer, pos); REPEAT VAR next: CHAR; BEGIN IF ch # startChar AND ch # '*' THEN (* must be newline *) INC(t.linesInToken); startOfLine := pos; END; next := Rd.GetChar(t.rd); BufferPut(buffer, pos, next); INC(pos); IF ch = startChar THEN IF next = '*' THEN INC(nesting); EXIT END; ELSIF ch = '*' THEN IF next = endChar THEN DEC(nesting); EXIT END; END; ch := next; END; UNTIL NOT ch IN endOfSection; UNTIL nesting < 0; EXCEPT | Rd.EndOfFile => END; WITH text = BufferToText(buffer, pos) DO INC(t.line, t.linesInToken); t.offset := pos - startOfLine; IF startChar = '(' THEN t.callBack.comment(text); ELSE t.callBack.pragma(text); END; END; t.linesInToken := 0; END ReadCommentOrPragma;
PROCEDUREGetNext (t: T) RAISES {Rd.Failure}= VAR ch: CHAR; BEGIN TRY t.linesInToken := 0; t.currentTok := M3CToken.Void; LOOP t.startOfToken := t.offset; ch := Get(t); INC(t.offset); CASE ch OF | '\t', ' ', '\013', '\f' => | '\n', '\r' => INC(t.line); t.offset := 0; | 'A'..'Z', 'a'..'z' => IF (ch = 'W') THEN (* check for a wide-char or wide-text literal *) ch := Get(t); INC(t.offset); IF (ch = '\'') THEN t.currentTok := ReadCharLiteral(t, wide := TRUE); EXIT; ELSIF (ch = '\"') THEN t.currentTok := ReadTextLiteral(t, wide := TRUE); EXIT; ELSE Unget(t, ch); DEC(t.offset); ch := 'W'; END; END; t.currentTok := ReadId(t, ch); EXIT; | '0'..'9'=> t.currentTok := ReadNumericLiteral(t, ch); EXIT; | '\'' => t.currentTok := ReadCharLiteral(t, wide := FALSE); EXIT; | '\"' => t.currentTok := ReadTextLiteral(t, wide := FALSE); EXIT; | '+' => t.currentTok := M3CToken.Plus; EXIT; | '-' => t.currentTok := M3CToken.Minus; EXIT; | '*' => t.currentTok := M3CToken.Times; EXIT; | '/' => t.currentTok := M3CToken.Divide; EXIT; | '<' => t.currentTok := M3CToken.LessThan; ch := Get(t); INC(t.offset); IF ch = '=' THEN t.currentTok := M3CToken.LessThanOrEqual; EXIT; ELSIF ch = ':' THEN t.currentTok := M3CToken.Subtype; EXIT; ELSIF ch = '*' THEN ReadCommentOrPragma(t, IsPragma); ELSE Unget(t, ch); DEC(t.offset); EXIT; END; (* if *) | '>' => ch := Get(t); INC(t.offset); IF ch = '=' THEN t.currentTok := M3CToken.GreaterThanOrEqual; ELSE Unget(t, ch); DEC(t.offset); t.currentTok := M3CToken.GreaterThan; END; (* if *) EXIT; | '#' => t.currentTok := M3CToken.NotEqual; EXIT; | '=' => t.currentTok := M3CToken.Equal; ch := Get(t); INC(t.offset); IF ch = '>' THEN t.currentTok := M3CToken.Implies; ELSE Unget(t, ch); DEC(t.offset); END; (* if *) EXIT; | '{' => t.currentTok := M3CToken.CurlyBra; EXIT; | '}' => t.currentTok := M3CToken.CurlyKet; EXIT; | '[' => t.currentTok := M3CToken.SquareBra; EXIT; | ']' => t.currentTok := M3CToken.SquareKet; EXIT; | '(' => t.currentTok := M3CToken.Bra; ch := Get(t); INC(t.offset); IF ch = '*' THEN ReadCommentOrPragma(t, IsComment); ELSE Unget(t, ch); DEC(t.offset); EXIT; END; (* if *) | ')' => t.currentTok := M3CToken.Ket; EXIT; | ';' => t.currentTok := M3CToken.Semicolon; EXIT; | '|' => t.currentTok := M3CToken.Bar; EXIT; | '^' => t.currentTok := M3CToken.Dereference; EXIT; | '.' => t.currentTok := M3CToken.Dot; ch := Get(t); INC(t.offset); IF ch = '.' THEN t.currentTok := M3CToken.Range; ELSE Unget(t, ch); DEC(t.offset); END; (* if *) EXIT; | ':' => t.currentTok := M3CToken.Colon; ch := Get(t); INC(t.offset); IF ch = '=' THEN t.currentTok := M3CToken.Becomes; ELSE Unget(t, ch); DEC(t.offset); END; (* if *) EXIT; | ',' => t.currentTok := M3CToken.Comma; EXIT; | '&' => t.currentTok := M3CToken.Ampersand; EXIT; ELSE IF t.isDisabled THEN Unget(t, '\000'); DEC(t.offset); EXIT; ELSE t.callBack.badChar(ch); END; END; (* case *) END; EXCEPT | Rd.EndOfFile => END; END GetNext; <*INLINE*> PROCEDURECurrent (t: T): M3CToken.T= BEGIN RETURN t.currentTok; END Current; <*INLINE*> PROCEDURENext (t: T): M3CToken.T RAISES {Rd.Failure}= BEGIN GetNext(t); RETURN t.currentTok END Next; <*INLINE*> PROCEDUREPosition (t: T): M3CSrcPos.T= BEGIN RETURN M3CSrcPos.Pack(t.line - t.linesInToken, t.startOfToken); END Position; <*INLINE*> PROCEDURELiteral (t: T): Literal_rep= BEGIN RETURN t.cur_literal; END Literal; <*INLINE*> PROCEDUREIdentifier (t: T): Symbol_rep= BEGIN RETURN t.cur_identifier; END Identifier; PROCEDUREReset (t: T; pos := M3CSrcPos.Null; s: Rd.T := NIL)= BEGIN IF s # NIL THEN t.rd := s; IF pos = M3CSrcPos.Null THEN t.line := 1; t.offset := 0; END; END; IF pos # M3CSrcPos.Null THEN t.line := M3CSrcPos.Unpack(pos, t.offset); END; t.currentTok := M3CToken.Void; t.cur_identifier := NIL; t.cur_literal := NIL; t.isDisabled := FALSE; t.linesInToken := 0; t.startOfToken := 0; t.lookahead := 0; END Reset; PROCEDUREDisable (t: T)= BEGIN Unget(t, '\000'); t.isDisabled := TRUE; END Disable; PROCEDUREDisabled (t: T): BOOLEAN= BEGIN RETURN t.isDisabled; END Disabled; PROCEDURETokenToText (token: M3CToken.T): TEXT= BEGIN CASE token OF <*NOWARN*> | ORD(FIRST(M3CToken.ReservedWord))..ORD(LAST(M3CToken.ReservedWord)) => RETURN M3CToken.Texts[VAL(token, M3CToken.E)]; | M3CToken.Identifier => RETURN "identifier" | M3CToken.CharLiteral => RETURN "char literal"; | M3CToken.TextLiteral => RETURN "text literal"; | M3CToken.IntegerLiteral => RETURN "integer literal"; | M3CToken.RealLiteral => RETURN "real literal"; | M3CToken.LongRealLiteral => RETURN "longreal literal"; | M3CToken.ExtendedLiteral => RETURN "extended literal"; | M3CToken.Plus => RETURN "\'+\'"; | M3CToken.Minus => RETURN "\'-\'"; | M3CToken.Times => RETURN "\'*\'"; | M3CToken.Divide => RETURN "\'/\'"; | M3CToken.Equal => RETURN "\'=\'"; | M3CToken.NotEqual => RETURN "\'#\'"; | M3CToken.LessThan => RETURN "\'<\'"; | M3CToken.GreaterThan => RETURN "\'>\'"; | M3CToken.LessThanOrEqual => RETURN "\'<=\'"; | M3CToken.GreaterThanOrEqual => RETURN "\'>=\'"; | M3CToken.Ampersand => RETURN "\'&\'"; | M3CToken.Dereference => RETURN "\'^\'"; | M3CToken.Dot => RETURN "\'.\'"; | M3CToken.Bra => RETURN "\'(\'"; | M3CToken.Ket => RETURN "\')\'"; | M3CToken.CurlyBra => RETURN "\'{\'"; | M3CToken.CurlyKet => RETURN "\'}\'"; | M3CToken.SquareBra => RETURN "\'[\'"; | M3CToken.SquareKet => RETURN "\']\'"; | M3CToken.Becomes => RETURN "\':=\'"; | M3CToken.Semicolon => RETURN "\';\'"; | M3CToken.Comma => RETURN "\',\'"; | M3CToken.Colon => RETURN "\':\'"; | M3CToken.Bar => RETURN "\'|\'"; | M3CToken.Range => RETURN "\'..\'"; | M3CToken.Subtype => RETURN "\'<:\'"; | M3CToken.Implies => RETURN "\'=>\'"; | M3CToken.Void => RETURN "end of stream"; END; (* case *) END TokenToText; PROCEDURECurrentTokenToText (t: T): TEXT= VAR text := TokenToText(t.currentTok); BEGIN CASE t.currentTok OF | M3CToken.Identifier => text := Fmt.F("identifier \'%s\'", t.cur_identifier.toText()); | ORD(FIRST(M3CToken.Literal))..ORD(LAST(M3CToken.Literal)) => text := Fmt.F("%s %s", text, t.cur_literal.toText()); ELSE END; RETURN text; END CurrentTokenToText; BEGIN END M3CLex.