blob: d253dd9ecdd022eb9677928ce1abfca4d663c4a6 [file] [log] [blame]
(* CLexBuf.mod provides a lexical buffer for clex.
Copyright (C) 2003-2025 Free Software Foundation, Inc.
Contributed by Gaius Mulley <gaius.mulley@southwales.ac.uk>.
This file is part of GNU Modula-2.
GNU Modula-2 is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GNU Modula-2 is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Modula-2; see the file COPYING3. If not see
<http://www.gnu.org/licenses/>. *)
IMPLEMENTATION MODULE CLexBuf ;
IMPORT cflex ;
FROM SYSTEM IMPORT ADDRESS ;
FROM Storage IMPORT ALLOCATE, DEALLOCATE ;
FROM DynamicStrings IMPORT string, InitString, InitStringCharStar, Equal, Mark, KillString ;
FROM FormatStrings IMPORT Sprintf1 ;
FROM NameKey IMPORT Name, NulName, makekey, KeyToCharStar ;
FROM M2Printf IMPORT printf0, printf1, printf2, printf3 ;
FROM Assertion IMPORT Assert ;
FROM SymbolKey IMPORT NulKey, SymbolTree, InitTree, DelSymKey, PutSymKey, GetSymKey ;
FROM Indexing IMPORT Index, InitIndex, IsIndiceInIndex, GetIndice, PutIndice ;
CONST
MaxBucketSize = 100 ;
Debugging = FALSE ;
TYPE
SourceList = POINTER TO sourcelist ;
sourcelist = RECORD
left,
right: SourceList ;
name : String ;
line : CARDINAL ;
END ;
TokenDesc = RECORD
token: toktype ;
str : Name ;
int : INTEGER ;
line : CARDINAL ;
file : SourceList ;
END ;
TokenBucket = POINTER TO tokenbucket ;
tokenbucket = RECORD
buf : ARRAY [0..MaxBucketSize] OF TokenDesc ;
len : CARDINAL ;
next: TokenBucket ;
END ;
ListDesc = RECORD
head,
tail : TokenBucket ;
LastBucketOffset: CARDINAL ;
END ;
MacroArgs = POINTER TO macroargs ;
macroargs = RECORD
next: MacroArgs ;
str : Name ;
END ;
Macro = POINTER TO macro ;
macro = RECORD
str : Name ;
tokno : CARDINAL ;
noArgs: CARDINAL ;
args : MacroArgs ;
END ;
VAR
CurrentSource : SourceList ;
UseBufferedTokens,
CurrentUsed : BOOLEAN ;
ListOfTokens : ListDesc ;
CurrentTokNo : CARDINAL ;
MacroDefinitions : SymbolTree ;
MacroIndex : Index ;
DefineNo : CARDINAL ;
EnabledMacros : BOOLEAN ;
(* M A C R O *)
(*
EnableMacroSubstitutions -
*)
PROCEDURE EnableMacroSubstitutions (b: BOOLEAN) ;
BEGIN
EnabledMacros := b
END EnableMacroSubstitutions ;
(*
IsMacroDefined - returns TRUE if macro, n, was defined.
*)
PROCEDURE IsMacroDefined (n: Name) : BOOLEAN ;
VAR
i: CARDINAL ;
m: Macro ;
BEGIN
i := GetSymKey(MacroDefinitions, n) ;
IF i=0
THEN
RETURN( FALSE )
ELSE
m := GetIndice(MacroIndex, i) ;
IF m=NIL
THEN
RETURN( FALSE )
ELSE
RETURN( TRUE )
END
END
END IsMacroDefined ;
(*
NoArgs - returns the number of arguments for macro, n.
-1 if the macro does not exist
*)
PROCEDURE NoArgs (n: Name) : INTEGER ;
VAR
m: Macro ;
i: CARDINAL ;
BEGIN
IF IsMacroDefined(n)
THEN
i := GetSymKey(MacroDefinitions, n) ;
m := GetIndice(MacroIndex, i) ;
RETURN( m^.noArgs )
ELSE
RETURN( -1 )
END
END NoArgs ;
(*
DefineMacro - defines macro, n, as defined to start at token, t.
*)
PROCEDURE DefineMacro (n: Name; t: CARDINAL) ;
VAR
m: Macro ;
i: CARDINAL ;
BEGIN
NEW(m) ;
WITH m^ DO
str := n ;
tokno := t ;
noArgs := 0 ;
args := NIL
END ;
UnDefineMacro(n) ;
i := GetSymKey(MacroDefinitions, n) ;
IF i=NulKey
THEN
PutSymKey(MacroDefinitions, n, DefineNo) ;
i := DefineNo ;
INC(DefineNo)
END ;
PutIndice(MacroIndex, i, m)
END DefineMacro ;
(*
UnDefineMacro -
*)
PROCEDURE UnDefineMacro (n: Name) ;
VAR
m: Macro ;
i: CARDINAL ;
BEGIN
IF IsMacroDefined(n)
THEN
i := GetSymKey(MacroDefinitions, n) ;
m := GetIndice(MacroIndex, i) ;
PutIndice(MacroIndex, i, NIL) ;
DISPOSE(m)
END
END UnDefineMacro ;
(*
PushMacroDefinition - pushes the macro definition, n, onto the token stream.
It returns TRUE if the macro was found and pushed.
*)
PROCEDURE PushMacroDefinition (n: Name) : BOOLEAN ;
VAR
m: Macro ;
t: CARDINAL ;
b: TokenBucket ;
i: CARDINAL ;
BEGIN
IF EnabledMacros AND IsMacroDefined(n)
THEN
i := GetSymKey(MacroDefinitions, n) ;
m := GetIndice(MacroIndex, i) ;
WITH m^ DO
IF tokno>0
THEN
t := tokno ;
LOOP
b := FindTokenBucket(t) ;
WITH b^.buf[t] DO
IF token=endhashtok
THEN
RETURN( TRUE )
ELSE
IF IsMacroDefined(str) AND (str#n)
THEN
IF PushMacroDefinition(str)
THEN
END
ELSE
AddTokToList(token, str, int, line, file)
END
END
END ;
INC(t)
END
END
END ;
RETURN( TRUE )
ELSE
RETURN( FALSE )
END
END PushMacroDefinition ;
(* e n d o f M A C R O r o u t i n e s *)
PROCEDURE stop ; BEGIN END stop ;
(*
Init - initializes the token list and source list.
*)
PROCEDURE Init ;
BEGIN
currenttoken := eoftok ;
CurrentTokNo := 0 ;
CurrentSource := NIL ;
ListOfTokens.head := NIL ;
ListOfTokens.tail := NIL ;
UseBufferedTokens := FALSE ;
InitTree(MacroDefinitions) ;
EnabledMacros := TRUE ;
DefineNo := 1 ;
MacroIndex := InitIndex(1)
END Init ;
(*
AddTo - adds a new element to the end of SourceList, CurrentSource.
*)
PROCEDURE AddTo (l: SourceList) ;
BEGIN
l^.right := CurrentSource ;
l^.left := CurrentSource^.left ;
CurrentSource^.left^.right := l ;
CurrentSource^.left := l ;
l^.left^.line := cflex.GetLineNo()
END AddTo ;
(*
SubFrom - subtracts, l, from the source list.
*)
PROCEDURE SubFrom (l: SourceList) ;
BEGIN
l^.left^.right := l^.right ;
l^.right^.left := l^.left
END SubFrom ;
(*
NewElement - returns a new SourceList
*)
PROCEDURE NewElement (s: ADDRESS) : SourceList ;
VAR
l: SourceList ;
BEGIN
NEW(l) ;
IF l=NIL
THEN
HALT
ELSE
WITH l^ DO
name := InitStringCharStar(s) ;
left := NIL ;
right := NIL
END
END ;
RETURN( l )
END NewElement ;
(*
NewList - initializes an empty list with the classic dummy header element.
*)
PROCEDURE NewList () : SourceList ;
VAR
l: SourceList ;
BEGIN
NEW(l) ;
WITH l^ DO
left := l ;
right := l ;
name := NIL
END ;
RETURN( l )
END NewList ;
(*
CheckIfNeedToDuplicate - checks to see whether the CurrentSource has
been used, if it has then duplicate the list.
*)
PROCEDURE CheckIfNeedToDuplicate ;
VAR
l, h: SourceList ;
BEGIN
IF CurrentUsed
THEN
l := CurrentSource^.right ;
h := CurrentSource ;
CurrentSource := NewList() ;
WHILE l#h DO
AddTo(NewElement(l^.name)) ;
l := l^.right
END
END
END CheckIfNeedToDuplicate ;
(*
PushFile - indicates that, filename, has just been included.
*)
PROCEDURE PushFile (filename: ADDRESS) ;
VAR
l: SourceList ;
BEGIN
CheckIfNeedToDuplicate ;
AddTo(NewElement(filename)) ;
IF Debugging
THEN
IF CurrentSource^.right#CurrentSource
THEN
l := CurrentSource ;
REPEAT
printf2('name = %s, line = %d\n', l^.name, l^.line) ;
l := l^.right
UNTIL l=CurrentSource
END
END
END PushFile ;
(*
PopFile - indicates that we are returning to, filename, having finished
an include.
*)
PROCEDURE PopFile (filename: ADDRESS) ;
VAR
l: SourceList ;
BEGIN
CheckIfNeedToDuplicate ;
IF (CurrentSource#NIL) AND (CurrentSource^.left#CurrentSource)
THEN
l := CurrentSource^.left ; (* last element *)
SubFrom(l) ;
DISPOSE(l) ;
IF (CurrentSource^.left#CurrentSource) AND
(NOT Equal(CurrentSource^.name, Mark(InitStringCharStar(filename))))
THEN
(* mismatch in source file names after preprocessing files *)
END
ELSE
(* source file list is empty, cannot pop an include.. *)
END
END PopFile ;
(*
KillList - kills the SourceList providing that it has not been used.
*)
PROCEDURE KillList ;
VAR
l, k: SourceList ;
BEGIN
IF (NOT CurrentUsed) AND (CurrentSource#NIL)
THEN
l := CurrentSource ;
REPEAT
k := l ;
l := l^.right ;
DISPOSE(k)
UNTIL l=CurrentSource
END
END KillList ;
(*
ReInitialize - re-initialize the all the data structures.
*)
PROCEDURE ReInitialize ;
VAR
s, t: TokenBucket ;
BEGIN
IF ListOfTokens.head#NIL
THEN
t := ListOfTokens.head ;
REPEAT
s := t ;
t := t^.next ;
DISPOSE(s) ;
UNTIL t=NIL ;
CurrentUsed := FALSE ;
KillList
END ;
Init
END ReInitialize ;
(*
SetFile - sets the current filename to, filename.
*)
PROCEDURE SetFile (filename: ADDRESS) ;
BEGIN
KillList ;
CurrentUsed := FALSE ;
CurrentSource := NewList() ;
AddTo(NewElement(filename))
END SetFile ;
(*
OpenSource - Attempts to open the source file, s.
The success of the operation is returned.
*)
PROCEDURE OpenSource (s: String) : BOOLEAN ;
BEGIN
IF UseBufferedTokens
THEN
GetToken ;
RETURN( TRUE )
ELSE
IF cflex.OpenSource(string(s))
THEN
SetFile(string(s)) ;
SyncOpenWithBuffer ;
GetToken ;
RETURN( TRUE )
ELSE
RETURN( FALSE )
END
END
END OpenSource ;
(*
CloseSource - closes the current open file.
*)
PROCEDURE CloseSource ;
BEGIN
IF UseBufferedTokens
THEN
WHILE currenttoken#eoftok DO
GetToken
END
ELSE
(* a subsequent call to cflex.OpenSource will really close the file *)
END
END CloseSource ;
(*
ResetForNewPass - reset the buffer pointers to the beginning ready for
a new pass
*)
PROCEDURE ResetForNewPass ;
BEGIN
CurrentTokNo := 0 ;
UseBufferedTokens := TRUE
END ResetForNewPass ;
(*
DisplayToken -
*)
PROCEDURE DisplayToken ;
VAR
n: Name ;
BEGIN
cflex.CError(string(InitString('current token'))) ;
IF currenttoken=identtok
THEN
n := makekey(currentstring) ;
printf1('currenttoken = %a\n', n)
ELSE
CASE currenttoken OF
eoftok : printf0('eoftok\n') |
startok : printf0('*\n') |
arrowtok : printf0('->\n') |
structtok : printf0('struct\n') |
lsbratok : printf0('[\n') |
rsbratok : printf0(']\n') |
lcbratok : printf0('{\n') |
rcbratok : printf0('}\n') |
lparatok : printf0('(\n') |
rparatok : printf0(')\n') |
semicolontok : printf0(';\n') |
longtok : printf0('long\n') |
inttok : printf0('int\n') |
chartok : printf0('char\n') |
enumtok : printf0('enum\n') |
typedeftok : printf0('typedef\n') |
floattok : printf0('float\n') |
doubletok : printf0('double\n') |
unsignedtok : printf0('unsigned\n') |
consttok : printf0('const\n') |
periodperiodperiodtok: printf0('...\n') |
integertok : printf0('integer number\n') |
hexintegertok : printf0('hexadecimal number\n') |
octintegertok : printf0('octal number\n') |
identtok : printf0('identifier\n') |
realtok : printf0('real number\n') |
conststringtok : printf0('constant string\n') |
constchartok : printf0('constant char\n') |
codetok : printf0('some C code\n') |
starthashtok : printf0('start#\n') |
endhashtok : printf0('end#\n') |
definetok : printf0('define\n') |
definedtok : stop ; printf0('defined\n') |
undeftok : printf0('undef\n') |
iftok : printf0('if\n') |
elsetok : printf0('else\n') |
endiftok : printf0('endif\n') |
ifdeftok : printf0('ifdef\n') |
ifndeftok : printf0('ifndef\n') |
nottok : printf0('not\n') |
includetok : printf0('include\n') |
commatok : printf0('comma\n') |
periodtok : printf0('period\n') |
gretok : printf0('gre\n') |
lesstok : printf0('less\n') |
ortok : printf0('or\n') |
andtok : printf0('and\n') |
bartok : printf0('bar\n') |
ambersandtok : printf0('ambersand\n') |
shiftlefttok : printf0('shiftleft\n') |
shiftrighttok : printf0('shiftright\n') |
divtok : printf0('div\n') |
modtok : printf0('mod\n') |
sizeoftok : printf0('sizeof\n') |
hattok : printf0('hat\n') |
equaltok : printf0('equal\n') |
notequaltok : printf0('notequal\n') |
greequaltok : printf0('greequal\n') |
lessequaltok : printf0('lessequal\n') |
plustok : printf0('plus\n') |
minustok : printf0('minus\n') |
tildetok : printf0('tilde\n') |
externtok : printf0('extern\n') |
statictok : printf0('static\n') |
autotok : printf0('auto\n') |
registertok : printf0('register\n') |
voidtok : printf0('void\n') |
shorttok : printf0('short\n') |
signedtok : printf0('signed\n') |
uniontok : printf0('union\n') |
colontok : printf0('colon\n') |
becomestok : printf0('becomes\n') |
volatiletok : printf0('volatile\n') |
typetok : printf0('type\n')
ELSE
cflex.CError(string(InitString('unrecognised token')))
END
END
END DisplayToken ;
(*
GetToken - gets the next token into currenttoken.
*)
PROCEDURE GetToken ;
VAR
t: CARDINAL ;
b: TokenBucket ;
l: CARDINAL ;
BEGIN
IF UseBufferedTokens
THEN
t := CurrentTokNo ;
b := FindTokenBucket(t) ;
WITH b^.buf[t] DO
currenttoken := token ;
currentstring := KeyToCharStar(str) ;
currentinteger := int ;
IF Debugging
THEN
l := line
END
END ;
IF Debugging
THEN
printf3('line %d (# %d %d) ', l, t, CurrentTokNo) ;
DisplayToken
END ;
INC(CurrentTokNo)
ELSE
IF ListOfTokens.tail=NIL
THEN
cflex.AdvanceToken ;
IF ListOfTokens.tail=NIL
THEN
HALT
END
END ;
IF ListOfTokens.LastBucketOffset>CurrentTokNo
THEN
t := CurrentTokNo ;
b := FindTokenBucket(t) ;
WITH b^.buf[t] DO
currenttoken := token ;
currentstring := KeyToCharStar(str) ;
currentinteger := int ;
IF Debugging
THEN
l := line
END
END ;
INC(CurrentTokNo)
ELSE
WITH ListOfTokens.tail^ DO
IF CurrentTokNo-ListOfTokens.LastBucketOffset<len
THEN
WITH buf[CurrentTokNo-ListOfTokens.LastBucketOffset] DO
currenttoken := token ;
currentstring := KeyToCharStar(str) ;
currentinteger := int
END ;
IF Debugging
THEN
(* printf1('# %d ', CurrentTokNo) ; *)
DisplayToken
END ;
INC(CurrentTokNo)
ELSE
cflex.AdvanceToken ;
GetToken ;
(* printf0('\n'); cflex.CError(string(InitString('current token'))) ; *)
END
END
END
END
END GetToken ;
(*
FlushTokens - removes the last token.
*)
PROCEDURE FlushTokens ;
BEGIN
INC(CurrentTokNo)
END FlushTokens ;
(*
SyncOpenWithBuffer - synchronise the buffer with the start of a file.
Skips all the tokens to do with the previous file.
*)
PROCEDURE SyncOpenWithBuffer ;
BEGIN
IF ListOfTokens.tail#NIL
THEN
WITH ListOfTokens.tail^ DO
CurrentTokNo := ListOfTokens.LastBucketOffset+len
END
END
END SyncOpenWithBuffer ;
(*
InsertToken - inserts a symbol, token, infront of the current token
ready for the next pass.
*)
PROCEDURE InsertToken (token: toktype) ;
BEGIN
IF ListOfTokens.tail#NIL
THEN
WITH ListOfTokens.tail^ DO
IF len>0
THEN
buf[len-1].token := token
END
END ;
AddTokToList(currenttoken, NulName, 0, GetLineNo(), CurrentSource) ;
GetToken
END
END InsertToken ;
(*
InsertTokenAndRewind - inserts a symbol, token, infront of the current token
and then moves the token stream back onto the inserted token.
*)
PROCEDURE InsertTokenAndRewind (token: toktype) ;
BEGIN
IF ListOfTokens.tail#NIL
THEN
WITH ListOfTokens.tail^ DO
IF len>0
THEN
buf[len-1].token := token
END
END ;
AddTokToList(currenttoken, NulName, 0, GetLineNo(), CurrentSource) ;
currenttoken := token
END
END InsertTokenAndRewind ;
(*
GetLineNo - returns the current line number where the symbol occurs in
the source file.
*)
PROCEDURE GetLineNo () : CARDINAL ;
BEGIN
IF CurrentTokNo=0
THEN
RETURN( 0 )
ELSE
RETURN( TokenToLineNo(GetTokenNo(), 0) )
END
END GetLineNo ;
(*
GetTokenNo - returns the current token number.
*)
PROCEDURE GetTokenNo () : CARDINAL ;
BEGIN
IF CurrentTokNo=0
THEN
RETURN( 0 )
ELSE
RETURN( CurrentTokNo-1 )
END
END GetTokenNo ;
(*
FindTokenBucket - returns the TokenBucket corresponding to the TokenNo.
*)
PROCEDURE FindTokenBucket (VAR TokenNo: CARDINAL) : TokenBucket ;
VAR
b: TokenBucket ;
BEGIN
b := ListOfTokens.head ;
WHILE b#NIL DO
WITH b^ DO
IF TokenNo<len
THEN
RETURN( b )
ELSE
DEC(TokenNo, len)
END
END ;
b := b^.next
END ;
RETURN( NIL )
END FindTokenBucket ;
(*
TokenToLineNo - returns the line number of the current file for the
TokenNo. The depth refers to the include depth.
A depth of 0 is the current file, depth of 1 is the file
which included the current file. Zero is returned if the
depth exceeds the file nesting level.
*)
PROCEDURE TokenToLineNo (TokenNo: CARDINAL; depth: CARDINAL) : CARDINAL ;
VAR
b: TokenBucket ;
l: SourceList ;
BEGIN
b := FindTokenBucket(TokenNo) ;
IF b=NIL
THEN
RETURN( 0 )
ELSE
IF depth=0
THEN
RETURN( b^.buf[TokenNo].line )
ELSE
l := b^.buf[TokenNo].file^.left ;
WHILE depth>0 DO
l := l^.left ;
IF l=b^.buf[TokenNo].file^.left
THEN
RETURN( 0 )
END ;
DEC(depth)
END ;
RETURN( l^.line )
END
END
END TokenToLineNo ;
(*
FindFileNameFromToken - returns the complete FileName for the appropriate
source file yields the token number, TokenNo.
The, Depth, indicates the include level: 0..n
Level 0 is the current. NIL is returned if n+1
is requested.
*)
PROCEDURE FindFileNameFromToken (TokenNo: CARDINAL; depth: CARDINAL) : String ;
VAR
b: TokenBucket ;
l: SourceList ;
BEGIN
b := FindTokenBucket(TokenNo) ;
IF b=NIL
THEN
RETURN( NIL )
ELSE
l := b^.buf[TokenNo].file^.left ;
WHILE depth>0 DO
l := l^.left ;
IF l=b^.buf[TokenNo].file^.left
THEN
RETURN( NIL )
END ;
DEC(depth)
END ;
RETURN( l^.name )
END
END FindFileNameFromToken ;
(*
GetFileName - returns a String defining the current file.
*)
PROCEDURE GetFileName () : String ;
BEGIN
RETURN( FindFileNameFromToken(GetTokenNo(), 0) )
END GetFileName ;
(*
AddTokToList - adds a token to a dynamic list.
*)
PROCEDURE AddTokToList (t: toktype; n: Name;
i: INTEGER; l: CARDINAL; f: SourceList) ;
BEGIN
IF ListOfTokens.head=NIL
THEN
NEW(ListOfTokens.head) ;
IF ListOfTokens.head=NIL
THEN
(* list error *)
END ;
ListOfTokens.tail := ListOfTokens.head ;
ListOfTokens.tail^.len := 0
ELSIF ListOfTokens.tail^.len=MaxBucketSize
THEN
Assert(ListOfTokens.tail^.next=NIL) ;
NEW(ListOfTokens.tail^.next) ;
IF ListOfTokens.tail^.next=NIL
THEN
(* list error *)
ELSE
ListOfTokens.tail := ListOfTokens.tail^.next ;
ListOfTokens.tail^.len := 0
END ;
INC(ListOfTokens.LastBucketOffset, MaxBucketSize)
END ;
WITH ListOfTokens.tail^ DO
next := NIL ;
WITH buf[len] DO
token := t ;
str := n ;
int := i ;
line := l ;
file := f
END ;
INC(len)
END
END AddTokToList ;
(*
IsLastTokenEof - returns TRUE if the last token was an eoftok
*)
PROCEDURE IsLastTokenEof () : BOOLEAN ;
VAR
b: TokenBucket ;
BEGIN
IF ListOfTokens.tail#NIL
THEN
IF ListOfTokens.tail^.len=0
THEN
b := ListOfTokens.head ;
IF b=ListOfTokens.tail
THEN
RETURN( FALSE )
END ;
WHILE b^.next#ListOfTokens.tail DO
b := b^.next
END ;
ELSE
b := ListOfTokens.tail
END ;
WITH b^ DO
RETURN( buf[len-1].token=eoftok )
END
END ;
RETURN( FALSE )
END IsLastTokenEof ;
(* ***********************************************************************
*
* These functions allow c.flex to deliver tokens into the buffer
*
************************************************************************* *)
(*
AddTok - adds a token to the buffer.
*)
PROCEDURE AddTok (t: toktype) ;
BEGIN
IF NOT ((t=eoftok) AND IsLastTokenEof())
THEN
AddTokToList(t, NulName, 0, cflex.GetLineNo(), CurrentSource) ;
CurrentUsed := TRUE
END
END AddTok ;
(*
AddTokCharStar - adds a token to the buffer and an additional string, s.
A copy of string, s, is made.
*)
PROCEDURE AddTokCharStar (t: toktype; s: ADDRESS) ;
BEGIN
IF (t=identtok) AND PushMacroDefinition(makekey(s))
THEN
(* do nothing *)
ELSE
AddTokToList(t, makekey(s), 0, cflex.GetLineNo(), CurrentSource) ;
CurrentUsed := TRUE
END
END AddTokCharStar ;
(*
AddTokInteger - adds a token and an integer to the buffer.
*)
PROCEDURE AddTokInteger (t: toktype; i: INTEGER) ;
VAR
s: String ;
lineno: CARDINAL ;
BEGIN
lineno := cflex.GetLineNo() ;
s := Sprintf1(Mark(InitString('%d')), lineno) ;
AddTokToList(t, makekey(string(s)), i, lineno, CurrentSource) ;
s := KillString(s) ;
CurrentUsed := TRUE
END AddTokInteger ;
BEGIN
Init
END CLexBuf.