FoxScanner.Mod 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599
  1. MODULE FoxScanner; (** AUTHOR "fof & fn"; PURPOSE "Oberon Compiler: Scanner"; **)
  2. (* (c) fof ETH Zürich, 2009 *)
  3. IMPORT Streams, Strings, Diagnostics, Basic := FoxBasic, D := Debugging, Commands, StringPool;
  4. CONST
  5. Trace = FALSE; (* debugging output *)
  6. (* overal scanner limitation *)
  7. MaxIdentifierLength* = 128;
  8. (* parametrization of numeric scanner: *)
  9. MaxHexDigits* = 8; (* maximal hexadecimal longint length *)
  10. MaxHugeHexDigits* = 16; (* maximal hexadecimal hugeint length *)
  11. MaxRealExponent* = 38; (* maximal real exponent *)
  12. MaxLongrealExponent* = 308; (* maximal longreal exponent *)
  13. (* scanner constants *)
  14. EOT* = 0X; LF* = 0AX; CR* = 0DX; TAB* = 09X; ESC* = 1BX;
  15. TYPE
  16. StringType* = Strings.String;
  17. IdentifierType *= StringPool.Index;
  18. IdentifierString*= ARRAY MaxIdentifierLength+1 OF CHAR;
  19. CONST
  20. (** tokens *)
  21. (*
  22. note: order of tokens is important for the parser, do not modify without looking it up
  23. FoxProgTools.Enum --export --linefeed=6
  24. None
  25. (* RelationOps: Equal ... Is *)
  26. Equal DotEqual Unequal DotUnequal
  27. Less DotLess LessEqual DotLessEqual Greater DotGreater GreaterEqual DotGreaterEqual
  28. LessLessQ GreaterGreaterQ Questionmarks ExclamationMarks
  29. In Is
  30. (* MulOps: Times ... And *)
  31. Times TimesTimes DotTimes PlusTimes Slash Backslash DotSlash Div Mod And
  32. (* AddOps: Or ... Minus *)
  33. Or Plus Minus
  34. (* Prefix Unary Operators Plus ... Not *)
  35. Not
  36. (* expressions may start with Plus ... Identifier *)
  37. LeftParenthesis LeftBracket LeftBrace Number Character String Nil Imag True False Self Result New Identifier
  38. (* statementy may start with Self ... Begin *)
  39. If Case While Repeat For Loop With Exit Await Return Begin
  40. (* symbols, expressions and statements cannot start with *)
  41. Semicolon Transpose RightBrace RightBracket RightParenthesis
  42. Questionmark ExclamationMark
  43. LessLess GreaterGreater
  44. Upto Arrow Period Comma Colon Of Then Do To By Becomes Bar End Else Elsif Until Finally
  45. (* declaration elements *)
  46. Code Const Type Var Out Procedure Operator Import Definition Module Cell CellNet Extern
  47. (* composite type symbols *)
  48. Array Object Record Pointer Enum Port Address Size Alias
  49. (* assembler constants *)
  50. Ln PC PCOffset
  51. (* number types *)
  52. Shortint Integer Longint Hugeint Real Longreal
  53. Comment EndOfText Escape
  54. ~
  55. *)
  56. None*= 0;
  57. (* RelationOps: Equal ... Is *)
  58. Equal*= 1; DotEqual*= 2; Unequal*= 3; DotUnequal*= 4; Less*= 5; DotLess*= 6;
  59. LessEqual*= 7; DotLessEqual*= 8; Greater*= 9; DotGreater*= 10; GreaterEqual*= 11; DotGreaterEqual*= 12;
  60. LessLessQ*= 13; GreaterGreaterQ*= 14; Questionmarks*= 15; ExclamationMarks*= 16; In*= 17; Is*= 18;
  61. (* MulOps: Times ... And *)
  62. Times*= 19; TimesTimes*= 20; DotTimes*= 21; PlusTimes*= 22; Slash*= 23; Backslash*= 24;
  63. DotSlash*= 25; Div*= 26; Mod*= 27; And*= 28;
  64. (* AddOps: Or ... Minus *)
  65. Or*= 29; Plus*= 30; Minus*= 31;
  66. (* Prefix Unary Operators Plus ... Not *)
  67. Not*= 32;
  68. (* expressions may start with Plus ... Identifier *)
  69. LeftParenthesis*= 33; LeftBracket*= 34; LeftBrace*= 35; Number*= 36; Character*= 37; String*= 38;
  70. Nil*= 39; Imag*= 40; True*= 41; False*= 42; Self*= 43; Result*= 44;
  71. New*= 45; Identifier*= 46;
  72. (* statementy may start with Self ... Begin *)
  73. If*= 47; Case*= 48; While*= 49; Repeat*= 50; For*= 51; Loop*= 52;
  74. With*= 53; Exit*= 54; Await*= 55; Return*= 56; Begin*= 57;
  75. (* symbols, expressions and statements cannot start with *)
  76. Semicolon*= 58; Transpose*= 59; RightBrace*= 60; RightBracket*= 61; RightParenthesis*= 62; Questionmark*= 63;
  77. ExclamationMark*= 64; LessLess*= 65; GreaterGreater*= 66; Upto*= 67; Arrow*= 68; Period*= 69;
  78. Comma*= 70; Colon*= 71; Of*= 72; Then*= 73; Do*= 74; To*= 75;
  79. By*= 76; Becomes*= 77; Bar*= 78; End*= 79; Else*= 80; Elsif*= 81;
  80. Until*= 82; Finally*= 83;
  81. (* declaration elements *)
  82. Code*= 84; Const*= 85; Type*= 86; Var*= 87; Out*= 88; Procedure*= 89;
  83. Operator*= 90; Import*= 91; Definition*= 92; Module*= 93; Cell*= 94; CellNet*= 95;
  84. Extern*= 96;
  85. (* composite type symbols *)
  86. Array*= 97; Object*= 98; Record*= 99; Pointer*= 100; Enum*= 101; Port*= 102;
  87. Address*= 103; Size*= 104; Alias*= 105;
  88. (* assembler constants *)
  89. Ln*= 106; PC*= 107; PCOffset*= 108;
  90. (* number types *)
  91. Shortint*= 109; Integer*= 110; Longint*= 111; Hugeint*= 112; Real*= 113; Longreal*= 114;
  92. Comment*= 115; EndOfText*= 116; Escape*= 117;
  93. SingleQuote = 27X; DoubleQuote* = 22X;
  94. Ellipsis = 7FX; (* used in Scanner.GetNumber to return with ".." when reading an interval like 3..5 *)
  95. Uppercase*=0;
  96. Lowercase*=1;
  97. Unknown*=2;
  98. TYPE
  99. (* keywords book keeping *)
  100. Keyword* = ARRAY 32 OF CHAR;
  101. KeywordTable* = OBJECT(Basic.HashTableInt); (* string -> index *)
  102. VAR table: POINTER TO ARRAY OF LONGINT;
  103. PROCEDURE &InitTable*(size: LONGINT);
  104. VAR i: LONGINT;
  105. BEGIN
  106. Init(size); NEW(table,size); FOR i := 0 TO size-1 DO table[i] := -1; END;
  107. END InitTable;
  108. PROCEDURE IndexByIdentifier*(identifier: IdentifierType): LONGINT;
  109. VAR stringPoolIndex: LONGINT;
  110. BEGIN
  111. IF Has(identifier) THEN
  112. RETURN GetInt(identifier)
  113. ELSE (* do not modify index *)
  114. RETURN -1
  115. END;
  116. END IndexByIdentifier;
  117. PROCEDURE IndexByString*(CONST name: ARRAY OF CHAR): LONGINT;
  118. VAR stringPoolIndex: LONGINT;
  119. BEGIN
  120. StringPool.GetIndex(name,stringPoolIndex);
  121. IF Has(stringPoolIndex) THEN
  122. RETURN GetInt(stringPoolIndex)
  123. ELSE (* do not modify index *)
  124. RETURN -1
  125. END;
  126. END IndexByString;
  127. PROCEDURE IdentifierByIndex*(index: LONGINT; VAR identifier: IdentifierType);
  128. BEGIN
  129. identifier := table[index]
  130. END IdentifierByIndex;
  131. PROCEDURE StringByIndex*(index: LONGINT; VAR name: ARRAY OF CHAR);
  132. VAR stringPoolIndex: LONGINT;
  133. BEGIN
  134. stringPoolIndex := table[index];
  135. IF stringPoolIndex < 0 THEN
  136. name := ""
  137. ELSE
  138. StringPool.GetString(stringPoolIndex,name);
  139. END;
  140. END StringByIndex;
  141. PROCEDURE PutString*(CONST name: ARRAY OF CHAR; index: LONGINT);
  142. VAR stringPoolIndex: LONGINT;
  143. BEGIN
  144. StringPool.GetIndex(name,stringPoolIndex);
  145. table[index] := stringPoolIndex;
  146. PutInt(stringPoolIndex,index);
  147. END PutString;
  148. END KeywordTable;
  149. TYPE
  150. Token*=LONGINT;
  151. (**
  152. symbol: data structure for the data transfer of the last read input from the scanner to the parser
  153. **)
  154. Symbol*= RECORD
  155. start*,end*,line-: LONGINT; (* start and end position of symbol *)
  156. token*: Token; (* token of symbol *)
  157. identifier*: IdentifierType; (* identifier *)
  158. identifierString*: IdentifierString; (* cache of identifier's string *)
  159. string*: StringType; (* string or identifier *)
  160. stringLength*: LONGINT; (* length of string, if stringLength = 2 then this may be interpreted as character and integer = ORD(ch) *)
  161. numberType*: LONGINT; (* Integer, HugeInteger, Real or Longreal *)
  162. integer*: LONGINT;
  163. hugeint*: HUGEINT; (*! unify longint and hugeint *)
  164. character*: CHAR;
  165. real*: LONGREAL;
  166. END;
  167. StringMaker* = OBJECT (* taken from TF's scanner *)
  168. VAR length : LONGINT;
  169. data : StringType;
  170. PROCEDURE &Init*(initialSize : LONGINT);
  171. BEGIN
  172. IF initialSize < 256 THEN initialSize := 256 END;
  173. NEW(data, initialSize); length := 0;
  174. END Init;
  175. PROCEDURE Add*(CONST buf: ARRAY OF CHAR; ofs, len: LONGINT; propagate: BOOLEAN; VAR res: LONGINT);
  176. VAR i : LONGINT; n: StringType;
  177. BEGIN
  178. IF length + len + 1 >= LEN(data) THEN
  179. NEW(n, LEN(data) + len + 1); FOR i := 0 TO length - 1 DO n[i] := data[i] END;
  180. data := n
  181. END;
  182. WHILE len > 0 DO
  183. data[length] := buf[ofs];
  184. INC(ofs); INC(length); DEC(len)
  185. END;
  186. data[length] := 0X;
  187. END Add;
  188. (* remove last n characters *)
  189. PROCEDURE Shorten*(n : LONGINT);
  190. BEGIN
  191. DEC(length, n);
  192. IF length < 0 THEN length := 0 END;
  193. IF length > 0 THEN data[length - 1] := 0X ELSE data[length] := 0X END
  194. END Shorten;
  195. PROCEDURE Clear*;
  196. BEGIN
  197. data[0] := 0X;
  198. length := 0
  199. END Clear;
  200. PROCEDURE GetWriter*() : Streams.Writer;
  201. VAR w : Streams.Writer;
  202. BEGIN
  203. NEW(w, SELF.Add, 256);
  204. RETURN w
  205. END GetWriter;
  206. PROCEDURE GetReader*(): Streams.Reader;
  207. VAR r: Streams.StringReader;
  208. BEGIN
  209. NEW(r, 256);
  210. r.Set(data^);
  211. RETURN r
  212. END GetReader;
  213. PROCEDURE GetString*(VAR len: LONGINT) : StringType;
  214. BEGIN
  215. len := length;
  216. RETURN data
  217. END GetString;
  218. PROCEDURE GetStringCopy*(VAR len: LONGINT): StringType;
  219. VAR new: StringType;
  220. BEGIN
  221. len := length;
  222. NEW(new,len+1);
  223. COPY(data^,new^);
  224. RETURN new
  225. END GetStringCopy;
  226. END StringMaker;
  227. (** scanner reflects the following EBNF
  228. Symbol = String | Token | Number | Keyword | Identifier.
  229. Token = | '#' | '&' | '(' ['*' any '*' ')'] | ')' | '*'['*'] | '+'['*'] | ',' | '-' | '.' [ '.' | '*' | '/' | '=' | '#' | '>'['='] | '<' ['=']
  230. | '/' | ':' ['='] | ';' | '<' ['=' | '<' ['?'] ] | '=' | '>' [ '=' | '>' ['?']]
  231. | '[' | ']' | '^' | '{' | '|' | '}' | '~' | '\' | '`' | '?' ['?'] | '!' ['!']
  232. Identifier = Letter {Letter | Digit | '_'}.
  233. Letter = 'A' | 'B' | .. | 'Z' | 'a' | 'b' | .. | 'z'.
  234. Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' .
  235. String = '"' {Character} '"' | "'" {Character} "'".
  236. Character = Digit [HexDigit] 'X'.
  237. Number = Integer | Real.
  238. Integer = Digit {Digit} | Digit {HexDigit} 'H' | '0x' {HexDigit}.
  239. Real = Digit {Digit} '.' {Digit} [ScaleFactor].
  240. ScaleFactor = ('E' | 'D') ['+' | '-'] digit {digit}.
  241. HexDigit = Digit | 'A' | 'B' | 'C' | 'D' | 'E' | 'F'.
  242. **)
  243. Scanner* = OBJECT
  244. VAR
  245. (* helper state information *)
  246. source-: StringType;
  247. reader-: Streams.Reader; (* source *)
  248. diagnostics: Diagnostics.Diagnostics; (* error logging *)
  249. ch-: CHAR; (* look-ahead character *)
  250. position-: LONGINT; (* current position *)
  251. line-: LONGINT;
  252. error-: BOOLEAN; (* if error occured during scanning *)
  253. firstIdentifier: BOOLEAN; (* support of lower vs. upper case keywords *)
  254. case-: LONGINT;
  255. stringWriter: Streams.Writer;
  256. stringMaker: StringMaker;
  257. useLineNumbers*: BOOLEAN;
  258. (*
  259. source: name of the source code for reference in error outputs
  260. reader: input stream
  261. position: reference position (offset) of the input stream , for error output
  262. diagnostics: error output object
  263. *)
  264. PROCEDURE & InitializeScanner*( CONST source: ARRAY OF CHAR; reader: Streams.Reader; position: LONGINT; diagnostics: Diagnostics.Diagnostics );
  265. BEGIN
  266. NEW(stringMaker,1024);
  267. stringWriter := stringMaker.GetWriter();
  268. error := FALSE;
  269. NEW(SELF.source, Strings.Length(source)+1);
  270. COPY (source, SELF.source^);
  271. SELF.reader := reader;
  272. SELF.diagnostics := diagnostics;
  273. ch := " ";
  274. case := Unknown;
  275. firstIdentifier := TRUE;
  276. IF reader = NIL THEN ch := EOT ELSE GetNextCharacter END;
  277. IF Trace THEN D.Str( "New scanner " ); D.Ln; END;
  278. SELF.position := position;
  279. SELF.line := position;
  280. useLineNumbers := FALSE;
  281. END InitializeScanner;
  282. PROCEDURE ResetCase*; (*! needs a better naming ! *)
  283. BEGIN
  284. firstIdentifier := TRUE; case := Unknown;
  285. END ResetCase;
  286. PROCEDURE SetCase*(c: LONGINT);
  287. BEGIN
  288. case := c;
  289. END SetCase;
  290. (** report an error occured during scanning **)
  291. PROCEDURE ErrorS(CONST msg: ARRAY OF CHAR);
  292. BEGIN
  293. IF diagnostics # NIL THEN
  294. IF useLineNumbers THEN
  295. diagnostics.Error(source^, line+1, Diagnostics.Invalid, msg)
  296. ELSE
  297. diagnostics.Error(source^, position, Diagnostics.Invalid, msg)
  298. END;
  299. END;
  300. error := TRUE;
  301. END ErrorS;
  302. (** report an error occured during scanning **)
  303. PROCEDURE Error( code: INTEGER );
  304. VAR errorMessage: ARRAY 256 OF CHAR;
  305. BEGIN
  306. IF diagnostics # NIL THEN
  307. Basic.GetErrorMessage(code,"",errorMessage);
  308. IF useLineNumbers THEN
  309. diagnostics.Error(source^, line+1, code, errorMessage)
  310. ELSE
  311. diagnostics.Error(source^, position, code, errorMessage)
  312. END;
  313. END;
  314. error := TRUE;
  315. END Error;
  316. (** get next character, end of text results in ch = EOT **)
  317. PROCEDURE GetNextCharacter*;
  318. BEGIN
  319. reader.Char(ch); INC(position);
  320. IF ch = LF THEN INC(line) END;
  321. (*
  322. (* not necessary, as Streams returns 0X if reading failed, but in case Streams.Reader.Char is modified ... *)
  323. IF reader.res # Streams.Ok THEN ch := EOT END;
  324. *)
  325. END GetNextCharacter;
  326. (*
  327. The following is an implementation of the KMP algorithm used in order to traverse strings until some pattern occurs.
  328. It is not necessary for our implementation of string escape sequences, because the first character of the pattern does not occur in the pattern elsewhere
  329. I found the code useful and keep it here for the time being....
  330. (* generate a table to be able to quickly search for string containing overlaps - KMP algorithm *)
  331. PROCEDURE MakeOverlapTable*(CONST pattern: ARRAY OF CHAR; VAR table: ARRAY OF LONGINT);
  332. VAR i, cnd: LONGINT;
  333. BEGIN
  334. ASSERT(pattern[0] # 0X);
  335. (* if first character did not match: reset search *)
  336. table[0] := -1;
  337. (* if second character did not match: compare to first *)
  338. IF pattern[1] # 0X THEN
  339. table[1] := 0;
  340. END;
  341. (* for all other characters: switch back to previous overlay in pattern *)
  342. i := 2; cnd := 0;
  343. WHILE(pattern[i] # 0X) DO
  344. (* do patterns [i-cnd, i-1] match with pattern[0.. cnd] ? *)
  345. IF pattern[i-1] = pattern[cnd] THEN
  346. INC(cnd); table[i] := cnd; INC(i);
  347. (* no, switch back to last overlap, if possible *)
  348. ELSIF cnd > 0 THEN cnd := table[cnd]
  349. (* not possible: restart at beginning *)
  350. ELSE table[i] := 0; INC(i)
  351. END;
  352. END;
  353. END MakeOverlapTable;
  354. (* using KMP substring search algorithm consume and reproduce all characters of a string until endString *)
  355. PROCEDURE GetString(CONST endString: ARRAY OF CHAR);
  356. VAR escapePos: LONGINT; ech: CHAR; i: LONGINT; table: ARRAY 16 OF LONGINT;
  357. next: LONGINT;
  358. PROCEDURE Append(ch :CHAR);
  359. BEGIN
  360. IF ch = 0X THEN
  361. ErrorS("Unexpected end of text in string"); error := TRUE
  362. ELSE
  363. stringWriter.Char(ch)
  364. END;
  365. END Append;
  366. BEGIN
  367. MakeOverlapTable(endString, table);
  368. (* traverse *)
  369. escapePos := 0; ech := endString[0];
  370. GetNextCharacter;
  371. REPEAT
  372. IF ch = ech THEN
  373. INC(escapePos); ech := endString[escapePos];
  374. GetNextCharacter;
  375. ELSIF escapePos = 0 THEN (* frequent case *)
  376. Append(ch); GetNextCharacter;
  377. ELSE
  378. (* overlaps ? *)
  379. next := table[escapePos];
  380. IF next < 0 THEN next := 0 END;
  381. (* account for "forgotten" characters *)
  382. FOR i := 0 TO escapePos-1-next DO
  383. Append(endString[i]);
  384. END;
  385. (* to next overlapping ? *)
  386. escapePos := table[escapePos];
  387. (* no overlapping *)
  388. IF escapePos < 0 THEN
  389. Append(ch);
  390. escapePos := 0;
  391. GetNextCharacter;
  392. END;
  393. ech := endString[escapePos];
  394. END;
  395. UNTIL (ch = EOT) OR (ech = 0X);
  396. END GetString;
  397. *)
  398. (* simple case can be utilized when endString does not contain first character, which is the case for our string convention *)
  399. PROCEDURE ConsumeStringUntil(CONST endString: ARRAY OF CHAR; useControl: BOOLEAN);
  400. VAR escapePos: LONGINT; ech: CHAR; i: LONGINT; startPosition: LONGINT;
  401. CONST
  402. Control = '\';
  403. Delimiter = '"';
  404. PROCEDURE Append(ch :CHAR);
  405. BEGIN
  406. IF ch = 0X THEN
  407. ErrorS("Unexpected end of text in string"); error := TRUE;
  408. ELSE
  409. stringWriter.Char(ch)
  410. END;
  411. END Append;
  412. BEGIN
  413. (* traverse *)
  414. escapePos := 0; ech := endString[0]; startPosition := position;
  415. GetNextCharacter;
  416. REPEAT
  417. IF ch = ech THEN
  418. INC(escapePos); ech := endString[escapePos];
  419. GetNextCharacter;
  420. ELSIF useControl & (ch = Control) THEN
  421. GetNextCharacter;
  422. IF (ch = Control) OR (ch = Delimiter) THEN
  423. Append(ch)
  424. ELSIF ch = 'n' THEN
  425. Append(CR); Append(LF);
  426. ELSIF ch = 't' THEN
  427. Append(TAB)
  428. ELSE
  429. ErrorS("Unknown control sequence")
  430. END;
  431. GetNextCharacter
  432. ELSIF escapePos = 0 THEN (* frequent case *)
  433. Append(ch); GetNextCharacter;
  434. ELSE
  435. (* account for "forgotten" characters *)
  436. FOR i := 0 TO escapePos-1 DO
  437. Append(endString[i]);
  438. END;
  439. (* restart *)
  440. ech := endString[0]; escapePos := 0;
  441. END;
  442. UNTIL (ch = EOT) OR (ech = 0X) OR error;
  443. IF ch = EOT THEN position := startPosition; ErrorS("Unexpected end of text in string") END;
  444. END ConsumeStringUntil;
  445. PROCEDURE GetEscapedString(VAR symbol: Symbol);
  446. VAR endString: ARRAY 4 OF CHAR; escape: CHAR;
  447. BEGIN
  448. (* backslash already consumed *)
  449. stringMaker.Clear;
  450. IF ch = '"' THEN
  451. escape := 0X;
  452. ELSE
  453. escape := ch; GetNextCharacter;
  454. END;
  455. ASSERT((ch = '"') OR (ch = "'"));
  456. REPEAT
  457. IF escape # 0X THEN
  458. endString[0] := ch;
  459. endString[1] := escape;
  460. endString[2] := '\';
  461. endString[3] := 0X;
  462. ELSE
  463. endString[0] := ch;
  464. endString[1] := '\';
  465. endString[2] := 0X;
  466. END;
  467. ConsumeStringUntil(endString, escape = 0X);
  468. UNTIL TRUE;
  469. stringWriter.Char(0X);
  470. stringWriter.Update;
  471. symbol.string := stringMaker.GetStringCopy(symbol.stringLength);
  472. END GetEscapedString;
  473. (** get a string starting at current position
  474. string = {'"' {Character} '"'} | {"'" {Character} "'"}.
  475. **)
  476. (* multiline indicates that a string may occupy more than one lines, either concatenated or via multi-strings " " " "
  477. *)
  478. PROCEDURE GetString(VAR symbol: Symbol; multiLine, multiString, useControl: BOOLEAN);
  479. VAR och: CHAR; error: BOOLEAN; done: BOOLEAN;
  480. CONST control = '\';
  481. PROCEDURE Append(ch :CHAR);
  482. BEGIN
  483. IF ch = 0X THEN
  484. ErrorS("Unexpected end of text in string"); error := TRUE
  485. ELSE
  486. stringWriter.Char(ch)
  487. END;
  488. END Append;
  489. BEGIN
  490. stringMaker.Clear;
  491. och := ch; error := FALSE;
  492. REPEAT
  493. LOOP
  494. IF error THEN EXIT END;
  495. GetNextCharacter;
  496. IF (ch = och) OR (ch = EOT) THEN EXIT END;
  497. IF useControl & (ch = control) THEN
  498. GetNextCharacter;
  499. IF (ch = control) OR (ch = och) THEN
  500. Append(ch)
  501. ELSIF ch = 'n' THEN
  502. Append(CR); Append(LF);
  503. ELSIF ch = 't' THEN
  504. Append(TAB)
  505. ELSE
  506. ErrorS("Unknown control sequence")
  507. END;
  508. ELSE
  509. IF ~multiLine & (ch < " ") THEN Error( Basic.StringIllegalCharacter ); EXIT END;
  510. Append(ch)
  511. END;
  512. END;
  513. IF ch = EOT THEN
  514. ErrorS("Unexpected end of text in string")
  515. ELSE
  516. GetNextCharacter;
  517. IF multiString THEN SkipBlanks END;
  518. END;
  519. UNTIL ~multiString OR (ch # och);
  520. stringWriter.Char(0X);
  521. stringWriter.Update;
  522. symbol.string := stringMaker.GetStringCopy(symbol.stringLength);
  523. END GetString;
  524. (**
  525. Identifier = Letter {Letter | Digit | '_'} .
  526. Letter = 'A' | 'B' | .. | 'Z' | 'a' | 'b' | .. | 'z' .
  527. Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'.
  528. '_' is the underscore character
  529. **)
  530. PROCEDURE GetIdentifier( VAR symbol: Symbol );
  531. VAR i: LONGINT;
  532. BEGIN
  533. i := 0;
  534. REPEAT symbol.identifierString[i] := ch; INC( i ); GetNextCharacter UNTIL reservedCharacter[ORD( ch )] OR (i = MaxIdentifierLength);
  535. IF i = MaxIdentifierLength THEN Error( Basic.IdentifierTooLong ); DEC( i ) END;
  536. symbol.identifierString[i] := 0X;
  537. StringPool.GetIndex(symbol.identifierString, symbol.identifier);
  538. END GetIdentifier;
  539. (**
  540. Number = Integer | Real.
  541. Integer = Digit {Digit} | Digit {HexDigit} 'H' | '0x' {HexDigit}.
  542. Real = Digit {Digit} '.' {Digit} [ScaleFactor].
  543. ScaleFactor = ('E' | 'D') ['+' | '-'] digit {digit}.
  544. HexDigit = Digit | 'A' | 'B' | 'C' | 'D' | 'E' | 'F'.
  545. Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' .
  546. **)
  547. PROCEDURE GetNumber(VAR symbol: Symbol): Token;
  548. VAR i, nextInt, m, n, d, e, si: LONGINT;
  549. dig: ARRAY 24 OF CHAR;
  550. f: LONGREAL; expCh: CHAR; neg, long: BOOLEAN;
  551. result: Token;
  552. hugeint, tenh, number: HUGEINT;
  553. digits: LONGINT;
  554. (** 10^e **)
  555. PROCEDURE Ten( e: LONGINT ): LONGREAL;
  556. VAR x, p: LONGREAL;
  557. BEGIN
  558. x := 1; p := 10;
  559. WHILE e > 0 DO
  560. IF ODD( e ) THEN x := x * p END;
  561. e := e DIV 2;
  562. IF e > 0 THEN p := p * p END (* prevent overflow *)
  563. END;
  564. RETURN x
  565. END Ten;
  566. (** return decimal number associated to character ch , error if none **)
  567. PROCEDURE Decimal( ch: CHAR ): LONGINT;
  568. BEGIN (* ("0" <= ch) & (ch <= "9") OR ("A" <= ch) & (ch <= "F") *)
  569. IF ch <= "9" THEN RETURN ORD( ch ) - ORD( "0" ) ELSE Error( Basic.NumberIllegalCharacter ); RETURN 0 END
  570. END Decimal;
  571. (** return hexadecimal number associated to character ch, error if none **)
  572. PROCEDURE Hexadecimal( ch: CHAR ): LONGINT;
  573. BEGIN
  574. IF ch <= "9" THEN RETURN ORD( ch ) - ORD( "0" )
  575. ELSIF ch <= "F" THEN RETURN ORD( ch ) - ORD( "A" ) + 10
  576. ELSIF ch <= "f" THEN RETURN ORD( ch ) - ORD( "a" ) + 10
  577. ELSE Error( Basic.NumberIllegalCharacter ); RETURN 0
  578. END
  579. END Hexadecimal;
  580. BEGIN (* ("0" <= ch) & (ch <= "9") *)
  581. result := Number;
  582. i := 0; m := 0; n := 0; d := 0; si := 0; long := FALSE;
  583. IF (ch = "0") & (reader.Peek() = "x") THEN (* hex number *)
  584. digits := 0;
  585. GetNextCharacter; GetNextCharacter;
  586. WHILE (ch >= "0") & (ch <= "9") OR (ch >= "a") & (ch <="f") OR (ch >= "A") & (ch <= "F") DO
  587. number := number * 10H + Hexadecimal(ch);
  588. INC(digits);
  589. GetNextCharacter;
  590. END;
  591. symbol.hugeint := number;
  592. symbol.integer := SHORT(number);
  593. IF digits > MaxHexDigits THEN
  594. symbol.numberType := Hugeint
  595. ELSE
  596. symbol.numberType := Integer
  597. END;
  598. RETURN result;
  599. END;
  600. LOOP (* read mantissa *)
  601. IF ("0" <= ch) & (ch <= "9") OR (d = 0) & ("A" <= ch) & (ch <= "F") THEN
  602. IF (m > 0) OR (ch # "0") THEN (* ignore leading zeros *)
  603. IF n < LEN( dig ) THEN dig[n] := ch; INC( n ) END;
  604. INC( m )
  605. END;
  606. symbol.identifierString[si] := ch; INC( si ); GetNextCharacter; INC( i )
  607. ELSIF ch = "." THEN
  608. symbol.identifierString[si] := ch; INC( si ); GetNextCharacter;
  609. IF ch = "." THEN ch := Ellipsis; EXIT
  610. ELSIF d = 0 THEN (* i > 0 *) d := i
  611. ELSE Error( Basic.NumberIllegalCharacter )
  612. END
  613. ELSE EXIT
  614. END
  615. END; (* 0 <= n <= m <= i, 0 <= d <= i *)
  616. IF d = 0 THEN (* integer *)
  617. IF n = m THEN
  618. symbol.integer := 0; i := 0; symbol.hugeint := 0;
  619. IF ch = "X" THEN (* character *)
  620. symbol.identifierString[si] := ch; INC( si ); GetNextCharacter; result := Character;
  621. IF (n <= 2) THEN
  622. WHILE i < n DO symbol.integer := symbol.integer * 10H + Hexadecimal( dig[i] ); INC( i ) END;
  623. symbol.character := CHR(symbol.integer);
  624. ELSE Error( Basic.NumberTooLarge )
  625. END
  626. ELSIF ch = "H" THEN (* hexadecimal *)
  627. symbol.identifierString[si] := ch; INC( si ); GetNextCharacter;
  628. IF (n < MaxHexDigits) OR (n=MaxHexDigits) & (dig[0] <= "7") THEN (* otherwise the positive (!) number is not in the range of longints *)
  629. symbol.numberType := Integer;
  630. (* IF (n = MaxHexDigits) & (dig[0] > "7") THEN (* prevent overflow *) symbol.integer := -1 END; *)
  631. WHILE i < n DO symbol.integer := symbol.integer * 10H + Hexadecimal( dig[i] ); INC( i ) END;
  632. symbol.hugeint := symbol.integer;
  633. ELSIF n <= MaxHugeHexDigits THEN
  634. symbol.numberType := Hugeint;
  635. IF (n = MaxHugeHexDigits) & (dig[0] > "7") THEN (* prevent overflow *) symbol.hugeint := -1 END;
  636. WHILE i < n DO symbol.hugeint := Hexadecimal( dig[i] ) + symbol.hugeint * 10H; INC( i ) END;
  637. symbol.integer :=SHORT(symbol.hugeint);
  638. ELSE
  639. symbol.numberType := Hugeint; (* to make parser able to go on *)
  640. Error( Basic.NumberTooLarge )
  641. END
  642. ELSE (* decimal *)
  643. symbol.numberType := Integer;
  644. WHILE (i < n) & ~long DO
  645. d := Decimal( dig[i] ); INC( i );
  646. IF symbol.integer >= MAX(LONGINT) DIV 10 THEN (* multiplication overflow *)long := TRUE END;
  647. nextInt := symbol.integer*10+d;
  648. IF nextInt >=0 THEN symbol.integer := nextInt ELSE (* overflow *) long := TRUE END;
  649. END;
  650. IF long THEN
  651. i := 0; (* restart computation , artificial limit because of compiler problems with hugeint *)
  652. hugeint := 0;
  653. tenh := 10; (* compiler does not like constants here ! *)
  654. symbol.numberType := Hugeint;
  655. WHILE i < n DO
  656. d := Decimal( dig[i] ); INC( i );
  657. IF hugeint >= MAX(HUGEINT) DIV 10 THEN Error( Basic.NumberTooLarge) END;
  658. hugeint := hugeint * tenh + d;
  659. IF hugeint < 0 THEN Error( Basic.NumberTooLarge ) END
  660. END;
  661. symbol.hugeint := hugeint;
  662. symbol.integer := SHORT(symbol.hugeint);
  663. ELSE
  664. symbol.hugeint := symbol.integer;
  665. END
  666. END
  667. ELSE
  668. symbol.numberType := Hugeint;
  669. Error( Basic.NumberTooLarge )
  670. END
  671. ELSE (* fraction *)
  672. f := 0; e := 0; expCh := "E";
  673. WHILE n > 0 DO (* 0 <= f < 1 *) DEC( n ); f := (Decimal( dig[n] ) + f) / 10 END;
  674. IF (ch = "E") OR (ch = "D") THEN
  675. expCh := ch; symbol.identifierString[si] := ch; INC( si ); GetNextCharacter; neg := FALSE;
  676. IF ch = "-" THEN neg := TRUE; symbol.identifierString[si] := ch; INC( si ); GetNextCharacter
  677. ELSIF ch = "+" THEN symbol.identifierString[si] := ch; INC( si ); GetNextCharacter
  678. END;
  679. IF ("0" <= ch) & (ch <= "9") THEN
  680. REPEAT
  681. n := Decimal( ch ); symbol.identifierString[si] := ch; INC( si ); GetNextCharacter;
  682. IF e <= (MAX( INTEGER ) - n) DIV 10 THEN e := e * 10 + n ELSE Error( Basic.NumberTooLarge ) END
  683. UNTIL (ch < "0") OR ("9" < ch);
  684. IF neg THEN e := -e END
  685. ELSE Error( Basic.NumberIllegalCharacter )
  686. END
  687. END;
  688. DEC( e, i - d - m ); (* decimal point shift *)
  689. IF expCh = "E" THEN
  690. symbol.numberType := Real;
  691. IF (1 - MaxRealExponent < e) & (e <= MaxRealExponent) THEN
  692. IF e < 0 THEN symbol.real := f / Ten( -e ) ELSE symbol.real := f * Ten( e ) END
  693. ELSE Error( Basic.NumberTooLarge )
  694. END
  695. ELSE
  696. symbol.numberType := Longreal;
  697. IF (1 - MaxLongrealExponent < e) & (e <= MaxLongrealExponent) THEN
  698. IF e < 0 THEN symbol.real := f / Ten( -e ) ELSE symbol.real := f * Ten( e ) END
  699. ELSE Error( Basic.NumberTooLarge )
  700. END
  701. END
  702. END;
  703. symbol.identifierString[si] := 0X;
  704. RETURN result;
  705. END GetNumber;
  706. (** read / skip a comment **)
  707. PROCEDURE ReadComment(VAR symbol: Symbol);
  708. VAR level: LONGINT;
  709. BEGIN
  710. stringMaker.Clear;
  711. level := 1;
  712. WHILE (level > 0) & (ch # EOT) DO
  713. IF ch = "(" THEN
  714. stringWriter.Char(ch);
  715. GetNextCharacter;
  716. IF ch = "*" THEN INC(level); stringWriter.Char(ch); GetNextCharacter; END;
  717. ELSIF ch = "*" THEN
  718. stringWriter.Char(ch);
  719. GetNextCharacter;
  720. IF ch =")" THEN DEC(level); stringWriter.Char(ch); GetNextCharacter; END;
  721. ELSE
  722. stringWriter.Char(ch);
  723. GetNextCharacter;
  724. END;
  725. END;
  726. IF level > 0 THEN
  727. Error(Basic.CommentNotClosed)
  728. END;
  729. stringWriter.Char(0X);
  730. stringWriter.Update;
  731. stringMaker.Shorten(2); (* remove comment closing *)
  732. symbol.token := Comment;
  733. symbol.string := stringMaker.GetString(symbol.stringLength);
  734. END ReadComment;
  735. PROCEDURE SkipToEndOfCode*(VAR startPos,endPos: LONGINT; VAR symbol: Symbol): Token;
  736. VAR s: LONGINT;
  737. BEGIN
  738. ASSERT(case # Unknown);
  739. stringMaker.Clear;
  740. startPos := symbol.end;
  741. IF useLineNumbers THEN startPos := line END;
  742. s := symbol.token;
  743. WHILE (s # EndOfText) & (s # End) & (s # With) DO
  744. symbol.start := position;
  745. endPos := position;
  746. CASE ch OF
  747. 'A' .. 'Z','a'..'z': s := Identifier;
  748. GetIdentifier(symbol);
  749. IF (case=Uppercase) & (symbol.identifierString = "END") OR (case=Lowercase) & (symbol.identifierString = "end") THEN
  750. s := End
  751. ELSIF (case = Uppercase) & (symbol.identifierString = "WITH") OR (case = Lowercase) & (symbol.identifierString = "with") THEN
  752. s := With
  753. ELSE
  754. stringWriter.String(symbol.identifierString);
  755. END;
  756. ELSE
  757. stringWriter.Char(ch);
  758. GetNextCharacter;
  759. END;
  760. symbol.end := position;
  761. END;
  762. stringWriter.Update;
  763. symbol.string := stringMaker.GetStringCopy(symbol.stringLength);
  764. symbol.token := s;
  765. IF Trace THEN
  766. D.String("skip to end: "); D.Int(startPos,1); D.String(","); D.Int(endPos,1); D.Ln;
  767. OutSymbol(D.Log,symbol); D.Ln;
  768. END;
  769. RETURN s
  770. END SkipToEndOfCode;
  771. PROCEDURE SkipBlanks;
  772. BEGIN
  773. WHILE (ch <= " ") & (ch # ESC) DO (*ignore control characters*)
  774. IF ch = EOT THEN
  775. IF Trace THEN D.String("EOT"); D.Ln; END;
  776. RETURN
  777. ELSE GetNextCharacter
  778. END
  779. END;
  780. END SkipBlanks;
  781. (** get next symbol **)
  782. PROCEDURE GetNextSymbol*(VAR symbol: Symbol ): BOOLEAN;
  783. VAR s,token: LONGINT;
  784. BEGIN
  785. SkipBlanks;
  786. IF useLineNumbers THEN
  787. symbol.start := line+1;
  788. ELSE
  789. symbol.start := position
  790. END;
  791. symbol.line := line;
  792. stringMaker.Clear;
  793. CASE ch OF (* ch > " " *)
  794. EOT: s := EndOfText
  795. |ESC: s := Escape;; GetNextCharacter
  796. | DoubleQuote:
  797. s := String; GetString(symbol,TRUE, TRUE, FALSE);
  798. | SingleQuote:
  799. s := String; GetString(symbol,FALSE, FALSE,FALSE);
  800. (* to be replaced by:
  801. s := Character; GetString(symbol);
  802. IF symbol.stringLength #2 THEN (* stringlength = 1 for empty string '' *)
  803. Error(Basic.IllegalCharacterValue)
  804. END;
  805. *)
  806. | '#': s := Unequal; GetNextCharacter
  807. | '&': s := And; GetNextCharacter
  808. | '(': GetNextCharacter;
  809. IF ch = '*' THEN GetNextCharacter; ReadComment(symbol); s := Comment; ELSE s := LeftParenthesis END
  810. | ')': s := RightParenthesis; GetNextCharacter
  811. | '*': GetNextCharacter; IF ch = '*' THEN GetNextCharacter; s := TimesTimes ELSE s := Times END
  812. | '+': GetNextCharacter; IF ch = '*' THEN GetNextCharacter; s := PlusTimes ELSE s := Plus END
  813. | ',': s := Comma; GetNextCharacter
  814. | '-': s := Minus; GetNextCharacter
  815. | '.': GetNextCharacter;
  816. IF ch = '.' THEN GetNextCharacter; s := Upto;
  817. ELSIF ch = '*' THEN GetNextCharacter; s := DotTimes;
  818. ELSIF ch = '/' THEN GetNextCharacter; s := DotSlash;
  819. ELSIF ch='=' THEN GetNextCharacter; s := DotEqual;
  820. ELSIF ch='#' THEN GetNextCharacter; s := DotUnequal;
  821. ELSIF ch='>' THEN GetNextCharacter;
  822. IF ch='=' THEN s := DotGreaterEqual; GetNextCharacter
  823. ELSE s := DotGreater;
  824. END
  825. ELSIF ch='<' THEN GetNextCharacter;
  826. IF ch='=' THEN s := DotLessEqual; GetNextCharacter
  827. ELSE s := DotLess;
  828. END
  829. ELSE s := Period END
  830. | '/': s := Slash; GetNextCharacter
  831. | '0'..'9': s := GetNumber(symbol);
  832. | ':': GetNextCharacter;
  833. IF ch = '=' THEN GetNextCharacter; s := Becomes ELSE s := Colon END
  834. | ';': s := Semicolon; GetNextCharacter
  835. | '<': GetNextCharacter;
  836. IF ch = '=' THEN GetNextCharacter; s := LessEqual
  837. ELSIF ch ='<' THEN GetNextCharacter;
  838. IF ch ='?' THEN GetNextCharacter; s := LessLessQ
  839. ELSE s := LessLess
  840. END;
  841. ELSE s := Less;
  842. END
  843. | '=': s := Equal; GetNextCharacter
  844. | '>': GetNextCharacter;
  845. IF ch = '=' THEN GetNextCharacter; s := GreaterEqual
  846. ELSIF ch ='>' THEN GetNextCharacter;
  847. IF ch ='?' THEN GetNextCharacter; s := GreaterGreaterQ
  848. ELSE s := GreaterGreater
  849. END;
  850. ELSE s := Greater; END
  851. | '[': s := LeftBracket; GetNextCharacter
  852. | ']': s := RightBracket; GetNextCharacter
  853. | '^': s := Arrow; GetNextCharacter
  854. | '{': s := LeftBrace; GetNextCharacter
  855. | '|': s := Bar; GetNextCharacter
  856. | '}': s := RightBrace; GetNextCharacter
  857. | '~': s := Not; GetNextCharacter
  858. | '\': s := Backslash; GetNextCharacter;
  859. IF ch = DoubleQuote THEN
  860. s := String;
  861. GetEscapedString(symbol);
  862. (*
  863. GetString(symbol, TRUE, TRUE, TRUE)
  864. *)
  865. ELSIF (ch > " ") & (reader.Peek() = DoubleQuote) THEN
  866. s := String;
  867. GetEscapedString(symbol);
  868. END;
  869. | '`': s := Transpose; GetNextCharacter
  870. | '?': s := Questionmark; GetNextCharacter; IF ch = '?' THEN s := Questionmarks; GetNextCharacter END;
  871. | '!': s := ExclamationMark; GetNextCharacter; IF ch = '!' THEN s := ExclamationMarks; GetNextCharacter END;
  872. | Ellipsis:
  873. s := Upto; GetNextCharacter
  874. | 'A'..'Z': s := Identifier; GetIdentifier( symbol );
  875. IF (case=Uppercase) OR (case=Unknown) THEN
  876. token := keywordsUpper.IndexByIdentifier(symbol.identifier);
  877. IF (token >= 0) THEN s := token END;
  878. IF (s = Module) OR (s=CellNet) THEN case := Uppercase END;
  879. END;
  880. | 'a'..'z': s := Identifier; GetIdentifier( symbol);
  881. IF (case = Lowercase) OR (case=Unknown) THEN
  882. token := keywordsLower.IndexByIdentifier(symbol.identifier);
  883. IF (token >= 0) THEN s := token END;
  884. IF (s = Module) OR (s=CellNet) THEN case := Lowercase END;
  885. END;
  886. IF firstIdentifier & (s # Module) & (s # CellNet) & (case = Unknown) THEN case := Uppercase; s := Identifier END;
  887. ELSE s := Identifier; GetIdentifier( symbol );
  888. END;
  889. firstIdentifier := FALSE;
  890. symbol.token := s;
  891. symbol.end := position;
  892. IF Trace THEN OutSymbol(D.Log,symbol); D.Ln; END;
  893. RETURN ~error
  894. END GetNextSymbol;
  895. PROCEDURE ResetError*();
  896. BEGIN error := FALSE
  897. END ResetError;
  898. (** set the diagnostics mode of the scanner (diagnostics = NIL ==> no report) and reset the error state
  899. intended for silent symbol peeeking after the end of a module *)
  900. PROCEDURE ResetErrorDiagnostics*(VAR diagnostics: Diagnostics.Diagnostics);
  901. VAR b: BOOLEAN; d: Diagnostics.Diagnostics;
  902. BEGIN
  903. error := FALSE;
  904. d := SELF.diagnostics; SELF.diagnostics := diagnostics; diagnostics := d;
  905. END ResetErrorDiagnostics;
  906. END Scanner;
  907. Context*=RECORD
  908. position, readerPosition, line : LONGINT;
  909. ch: CHAR;
  910. END;
  911. (** assembler scanner reflects the following EBNF
  912. Symbol = String | Token | Number | Identifier.
  913. Token = '\' | '#' | '(' ['*' any '*' ')'] | ')' | CR [LF] | LF | '*' | '+' | ',' | '-' | '~' | '.' | '/' | '%' | ':' | ';' | '=' | '[' | ']' | '{' | '}' | '!' | '^' | '$'['$'].
  914. String = '"' {Character} '"' | "'" {Character} "'".
  915. Identifier = '@' | Letter {'@' | '.' | Letter | Digit | '_'} .
  916. Letter = 'A' | 'B' | .. | 'Z' | 'a' | 'b' | .. | 'z' .
  917. Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'.
  918. Number = Integer | Real.
  919. Character = Digit [HexDigit] 'X'.
  920. Integer = Digit {Digit} | Digit {HexDigit} 'H' | '0x' {HexDigit}.
  921. Real = Digit {Digit} '.' {Digit} [ScaleFactor].
  922. ScaleFactor = ('E' | 'D') ['+' | '-'] digit {digit}.
  923. HexDigit = Digit | 'A' | 'B' | 'C' | 'D' | 'E' | 'F'.
  924. **)
  925. AssemblerScanner* = OBJECT (Scanner) (*! move to different module? unify with compiler scanner? *)
  926. VAR
  927. startContext-: Context;
  928. PROCEDURE &InitAssemblerScanner*( CONST source: ARRAY OF CHAR; reader: Streams.Reader; position: LONGINT; diagnostics: Diagnostics.Diagnostics );
  929. BEGIN
  930. InitializeScanner(source,reader,position,diagnostics);
  931. GetContext(startContext);
  932. END InitAssemblerScanner;
  933. PROCEDURE GetContext*(VAR context: Context);
  934. BEGIN
  935. context.ch := ch;
  936. context.position := position;
  937. context.line := line;
  938. context.readerPosition := reader.Pos();
  939. END GetContext;
  940. PROCEDURE SetContext*(CONST context: Context);
  941. BEGIN
  942. reader.SetPos(context.readerPosition);
  943. ch := context.ch;
  944. line := context.line;
  945. position := context.position;
  946. END SetContext;
  947. PROCEDURE SkipToEndOfLine*;
  948. BEGIN
  949. WHILE (ch # EOT) & (ch # CR) & (ch # LF) DO
  950. GetNextCharacter
  951. END;
  952. END SkipToEndOfLine;
  953. (**
  954. note: in contrast to a regular identifier, an assembler scanner identifier may also contain periods and the '@'-symbol
  955. Identifier = '@' | Letter {'@' | '.' | Letter | Digit | '_'} .
  956. Letter = 'A' | 'B' | .. | 'Z' | 'a' | 'b' | .. | 'z' .
  957. Digit = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9'.
  958. '_' is the underscore character
  959. **)
  960. PROCEDURE GetIdentifier( VAR symbol: Symbol );
  961. VAR
  962. i: LONGINT;
  963. PROCEDURE CharacterIsAllowed(character: CHAR): BOOLEAN;
  964. BEGIN
  965. CASE character OF
  966. | 'a' .. 'z', 'A' .. 'Z', '0' .. '9', '@', '.', '_': RETURN TRUE
  967. ELSE RETURN FALSE
  968. END;
  969. END CharacterIsAllowed;
  970. BEGIN
  971. i := 0;
  972. REPEAT
  973. symbol.identifierString[i] := ch; INC( i ); GetNextCharacter
  974. UNTIL ~CharacterIsAllowed(ch) OR (i = MaxIdentifierLength);
  975. IF i = MaxIdentifierLength THEN Error( Basic.IdentifierTooLong ); DEC( i ) END;
  976. symbol.identifierString[i] := 0X;
  977. END GetIdentifier;
  978. PROCEDURE GetNumber(VAR symbol: Symbol): Token;
  979. VAR number: HUGEINT; result: Token; digits: LONGINT;
  980. (** return hexadecimal number associated to character ch, error if none **)
  981. PROCEDURE Hexadecimal( ch: CHAR ): LONGINT;
  982. BEGIN
  983. IF (ch >= "0") & (ch <= "9") THEN RETURN ORD( ch ) - ORD( "0" )
  984. ELSIF (ch >= "a") & (ch <= "f") THEN RETURN ORD( ch ) - ORD( "a" ) + 10
  985. ELSE Error( Basic.NumberIllegalCharacter ); RETURN 0
  986. END
  987. END Hexadecimal;
  988. BEGIN
  989. result := Number;
  990. IF (ch = "0") THEN
  991. IF reader.Peek() = "x" THEN (* hex number *)
  992. digits := 0;
  993. GetNextCharacter; GetNextCharacter;
  994. WHILE (ch >= "0") & (ch <= "9") OR (ch >= "a") & (ch <="f") DO
  995. number := number * 10H + Hexadecimal(ch);
  996. INC(digits);
  997. GetNextCharacter;
  998. END;
  999. symbol.hugeint := number;
  1000. symbol.integer := SHORT(number);
  1001. IF digits > MaxHexDigits THEN
  1002. symbol.numberType := Hugeint
  1003. ELSE
  1004. symbol.numberType := Integer
  1005. END;
  1006. ELSIF reader.Peek() = "b" THEN (* binary number *)
  1007. digits := 0;
  1008. GetNextCharacter; GetNextCharacter;
  1009. WHILE (ch >= "0") & (ch <= "1") DO
  1010. number := number * 2;
  1011. INC(digits);
  1012. IF ch = "1" THEN INC(number) END;
  1013. GetNextCharacter;
  1014. END;
  1015. symbol.hugeint := number;
  1016. symbol.integer := SHORT(number);
  1017. IF digits > 32 THEN
  1018. symbol.numberType := Hugeint
  1019. ELSE
  1020. symbol.numberType := Integer
  1021. END;
  1022. ELSE RETURN GetNumber^(symbol)
  1023. END;
  1024. ELSE RETURN GetNumber^(symbol)
  1025. END;
  1026. RETURN result
  1027. END GetNumber;
  1028. (** get next symbol **)
  1029. PROCEDURE GetNextSymbol*(VAR symbol: Symbol ): BOOLEAN;
  1030. VAR s: LONGINT;
  1031. PROCEDURE SkipBlanks;
  1032. BEGIN
  1033. WHILE (ch <= ' ') & (ch # CR) & (ch # LF) & (ch # EOT) DO (* ignore control characters except line feeds *)
  1034. GetNextCharacter
  1035. END;
  1036. END SkipBlanks;
  1037. BEGIN
  1038. REPEAT
  1039. SkipBlanks;
  1040. IF useLineNumbers THEN
  1041. symbol.start := line+1;
  1042. ELSE
  1043. symbol.start := position
  1044. END;
  1045. symbol.line := line;
  1046. CASE ch OF (* ch > ' ' *)
  1047. | EOT: s := EndOfText;
  1048. | DoubleQuote:
  1049. s := String; GetString(symbol, TRUE, FALSE, TRUE);
  1050. | SingleQuote:
  1051. s := Character; GetString(symbol, FALSE, FALSE, FALSE); symbol.character := symbol.string[0];
  1052. IF symbol.stringLength #2 THEN (* stringlength = 1 for empty string '' *)
  1053. Error(Basic.IllegalCharacterValue)
  1054. END;
  1055. | '\': s := Backslash; GetNextCharacter;
  1056. IF ch = DoubleQuote THEN s := String; GetString(symbol, FALSE, FALSE, TRUE) END;
  1057. | '#': s := Unequal; GetNextCharacter; (* for the ARM assembler *)
  1058. | '(': GetNextCharacter;
  1059. IF ch = '*' THEN GetNextCharacter; ReadComment(symbol); s := Comment; ELSE s := LeftParenthesis END
  1060. | ')': s := RightParenthesis; GetNextCharacter
  1061. | CR: GetNextCharacter; s := Ln;IF ch = LF THEN GetNextCharacter END;
  1062. | LF: GetNextCharacter; s := Ln; IF ch = CR THEN GetNextCharacter END;
  1063. | '*': s := Times; GetNextCharacter;
  1064. | '+': s := Plus ; GetNextCharacter;
  1065. | ',': s := Comma; GetNextCharacter
  1066. | '-': s := Minus; GetNextCharacter
  1067. | '~': s := Not; GetNextCharacter
  1068. | '.': s:= Period; GetNextCharacter
  1069. | '/': s := Div; GetNextCharacter
  1070. | '%': s := Mod; GetNextCharacter
  1071. | '0'..'9': s := GetNumber(symbol);
  1072. | ':': s := Colon; GetNextCharacter;
  1073. | ';': s := Comment; SkipToEndOfLine;
  1074. | '=': s := Equal; GetNextCharacter
  1075. | '[': s := LeftBracket; GetNextCharacter
  1076. | ']': s := RightBracket; GetNextCharacter
  1077. | '{': s := LeftBrace; GetNextCharacter
  1078. | '}': s := RightBrace; GetNextCharacter
  1079. | '!': s := ExclamationMark; GetNextCharacter;
  1080. | '^': s := Arrow; GetNextCharacter;
  1081. | 'A'..'Z': s := Identifier; GetIdentifier( symbol );
  1082. | 'a'..'z': s := Identifier; GetIdentifier( symbol);
  1083. | '@': s := Identifier; GetIdentifier( symbol); (* the '@'-symbol initiates an assembly scanner identifier *)
  1084. | '$': GetNextCharacter;
  1085. IF ch = '$' THEN s := PCOffset; GetNextCharacter ELSE s := PC; END
  1086. ELSE s := None; GetNextCharacter;
  1087. END;
  1088. symbol.end := position;
  1089. UNTIL s # Comment;
  1090. symbol.token := s;
  1091. IF Trace THEN D.Ln; D.Str( "Scan at " ); D.Int( symbol.start,1 ); D.Str( ": " ); OutSymbol(D.Log,symbol); D.Update; END;
  1092. RETURN ~error
  1093. END GetNextSymbol;
  1094. END AssemblerScanner;
  1095. VAR
  1096. reservedCharacter: ARRAY 256 OF BOOLEAN;
  1097. tokens-: ARRAY EndOfText+1 OF Keyword;
  1098. keywordsLower, keywordsUpper: KeywordTable;
  1099. (** return a new scanner on a stream, error output via diagnostics **)
  1100. PROCEDURE NewScanner*( CONST source: ARRAY OF CHAR; reader: Streams.Reader; position: LONGINT; diagnostics: Diagnostics.Diagnostics ): Scanner;
  1101. VAR s: Scanner;
  1102. BEGIN
  1103. NEW( s, source, reader, position, diagnostics ); RETURN s;
  1104. END NewScanner;
  1105. PROCEDURE NewAssemblerScanner*( CONST source: ARRAY OF CHAR; reader: Streams.Reader; position: LONGINT; diagnostics: Diagnostics.Diagnostics ): AssemblerScanner;
  1106. VAR s: AssemblerScanner;
  1107. BEGIN
  1108. NEW( s, source, reader, position, diagnostics ); RETURN s;
  1109. END NewAssemblerScanner;
  1110. PROCEDURE SymbolToString*(CONST symbol: Symbol; case: LONGINT; VAR str: ARRAY OF CHAR);
  1111. VAR id: StringPool.Index;
  1112. BEGIN
  1113. CASE symbol.token OF
  1114. Identifier, Number: COPY(symbol.identifierString, str)
  1115. | String, Comment: ASSERT(LEN(str) >= LEN(symbol.string^)); COPY(symbol.string^, str);
  1116. ELSE
  1117. GetKeyword(case, symbol.token, id);
  1118. IF id < 0 THEN str := "" ELSE StringPool.GetString(id, str) END;
  1119. END;
  1120. END SymbolToString;
  1121. (** debugging output **)
  1122. PROCEDURE OutSymbol*(w: Streams.Writer; CONST symbol: Symbol);
  1123. VAR str: ARRAY 256 OF CHAR;
  1124. BEGIN
  1125. w.Int(symbol.start,1); w.String("-");w.Int(symbol.end,1); w.String(":");
  1126. w.String(tokens[symbol.token]);
  1127. IF symbol.token= Number THEN
  1128. CASE symbol.numberType OF
  1129. Integer: w.String("(integer)")
  1130. |Hugeint: w.String("(hugeint)")
  1131. |Real: w.String("(real)")
  1132. |Longreal: w.String("(longreal)")
  1133. END;
  1134. END;
  1135. IF symbol.token = String THEN
  1136. w.String(":"); w.Char('"'); w.String(symbol.string^); w.Char('"');
  1137. ELSIF symbol.token = Comment THEN
  1138. w.String("(*"); w.String(symbol.string^); w.String("*)");
  1139. ELSE
  1140. SymbolToString(symbol, Uppercase, str); w.String(": "); w.String(str);
  1141. END
  1142. END OutSymbol;
  1143. (** reserved characters are the characters that may not occur within an identifier **)
  1144. PROCEDURE InitReservedCharacters;
  1145. VAR i: LONGINT;
  1146. BEGIN
  1147. FOR i := 0 TO LEN( reservedCharacter ) - 1 DO
  1148. CASE CHR(i) OF
  1149. | 'a' .. 'z', 'A' .. 'Z': reservedCharacter[i] := FALSE;
  1150. | '0'..'9': reservedCharacter[i] := FALSE;
  1151. | '_': reservedCharacter[i] := FALSE
  1152. ELSE
  1153. reservedCharacter[i] := TRUE
  1154. END;
  1155. END;
  1156. END InitReservedCharacters;
  1157. (* get keyword by token *)
  1158. PROCEDURE GetKeyword*(case:LONGINT; token: LONGINT; VAR identifier: IdentifierType);
  1159. BEGIN
  1160. IF case = Uppercase THEN
  1161. keywordsUpper.IdentifierByIndex(token,identifier);
  1162. ELSE ASSERT(case=Lowercase);
  1163. keywordsLower.IdentifierByIndex(token,identifier);
  1164. END;
  1165. END GetKeyword;
  1166. PROCEDURE InitTokens;
  1167. VAR i: LONGINT;
  1168. BEGIN
  1169. tokens[None] := "None";
  1170. tokens[Equal] := "Equal";
  1171. tokens[DotEqual] := "DotEqual";
  1172. tokens[Unequal] := "Unequal";
  1173. tokens[DotUnequal] := "DotUnequal";
  1174. tokens[Less] := "Less";
  1175. tokens[DotLess] := "DotLess";
  1176. tokens[LessEqual] := "LessEqual";
  1177. tokens[DotLessEqual] := "DotLessEqual";
  1178. tokens[Greater] := "Greater";
  1179. tokens[DotGreater] := "DotGreater";
  1180. tokens[GreaterEqual] := "GreaterEqual";
  1181. tokens[DotGreaterEqual] := "DotGreaterEqual";
  1182. tokens[LessLessQ] := "LessLessQ";
  1183. tokens[GreaterGreaterQ] := "GreaterGreaterQ";
  1184. tokens[In] := "In";
  1185. tokens[Is] := "Is";
  1186. tokens[Times] := "Times";
  1187. tokens[TimesTimes] := "TimesTimes";
  1188. tokens[DotTimes] := "DotTimes";
  1189. tokens[PlusTimes] := "PlusTimes";
  1190. tokens[Slash] := "Slash";
  1191. tokens[Backslash] := "Backslash";
  1192. tokens[DotSlash] := "DotSlash";
  1193. tokens[Div] := "Div";
  1194. tokens[Mod] := "Mod";
  1195. tokens[And] := "And";
  1196. tokens[Or] := "Or";
  1197. tokens[Plus] := "Plus";
  1198. tokens[Minus] := "Minus";
  1199. tokens[Not] := "Not";
  1200. tokens[LeftParenthesis] := "LeftParenthesis";
  1201. tokens[LeftBracket] := "LeftBracket";
  1202. tokens[LeftBrace] := "LeftBrace";
  1203. tokens[Number] := "Number";
  1204. tokens[Character] := "Character";
  1205. tokens[String] := "String";
  1206. tokens[Nil] := "Nil";
  1207. tokens[Imag] := "Imag";
  1208. tokens[True] := "True";
  1209. tokens[False] := "False";
  1210. tokens[Self] := "Self";
  1211. tokens[New] := "New";
  1212. tokens[Result] := "Result";
  1213. tokens[Identifier] := "Identifier";
  1214. tokens[If] := "If";
  1215. tokens[Case] := "Case";
  1216. tokens[While] := "While";
  1217. tokens[Repeat] := "Repeat";
  1218. tokens[For] := "For";
  1219. tokens[Loop] := "Loop";
  1220. tokens[With] := "With";
  1221. tokens[Exit] := "Exit";
  1222. tokens[Await] := "Await";
  1223. tokens[Return] := "Return";
  1224. tokens[Begin] := "Begin";
  1225. tokens[Semicolon] := "Semicolon";
  1226. tokens[Transpose] := "Transpose";
  1227. tokens[RightBrace] := "RightBrace";
  1228. tokens[RightBracket] := "RightBracket";
  1229. tokens[RightParenthesis] := "RightParenthesis";
  1230. tokens[Questionmark] := "Questionmark";
  1231. tokens[ExclamationMark] := "ExclamationMark";
  1232. tokens[Questionmarks] := "Questionmarks";
  1233. tokens[ExclamationMarks] := "ExclamationMarks";
  1234. tokens[LessLess] := "LessLess";
  1235. tokens[GreaterGreater] := "GreaterGreater";
  1236. tokens[Upto] := "Upto";
  1237. tokens[Arrow] := "Arrow";
  1238. tokens[Period] := "Period";
  1239. tokens[Comma] := "Comma";
  1240. tokens[Colon] := "Colon";
  1241. tokens[Of] := "Of";
  1242. tokens[Then] := "Then";
  1243. tokens[Do] := "Do";
  1244. tokens[To] := "To";
  1245. tokens[By] := "By";
  1246. tokens[Becomes] := "Becomes";
  1247. tokens[Bar] := "Bar";
  1248. tokens[End] := "End";
  1249. tokens[Else] := "Else";
  1250. tokens[Elsif] := "Elsif";
  1251. tokens[Extern] := "Extern";
  1252. tokens[Until] := "Until";
  1253. tokens[Finally] := "Finally";
  1254. tokens[Code] := "Code";
  1255. tokens[Const] := "Const";
  1256. tokens[Type] := "Type";
  1257. tokens[Var] := "Var";
  1258. tokens[Out] := "Out";
  1259. tokens[Procedure] := "Procedure";
  1260. tokens[Operator] := "Operator";
  1261. tokens[Import] := "Import";
  1262. tokens[Definition] := "Definition";
  1263. tokens[Module] := "Module";
  1264. tokens[Cell] := "Cell";
  1265. tokens[CellNet] := "CellNet";
  1266. tokens[Array] := "Array";
  1267. tokens[Object] := "Object";
  1268. tokens[Record] := "Record";
  1269. tokens[Pointer] := "Pointer";
  1270. tokens[Enum] := "Enum";
  1271. tokens[Port] := "Port";
  1272. tokens[Address] := "Address";
  1273. tokens[Alias] := "Alias";
  1274. tokens[Size] := "Size";
  1275. tokens[Ln] := "Ln";
  1276. tokens[PC] := "PC";
  1277. tokens[PCOffset] := "PCOffset";
  1278. tokens[Shortint] := "Shortint";
  1279. tokens[Integer] := "Integer";
  1280. tokens[Longint] := "Longint";
  1281. tokens[Hugeint] := "Hugeint";
  1282. tokens[Real] := "Real";
  1283. tokens[Longreal] := "Longreal";
  1284. tokens[Comment] := "Comment";
  1285. tokens[EndOfText] := "EndOfText";
  1286. FOR i := 0 TO EndOfText DO ASSERT(tokens[i] # "") END;
  1287. END InitTokens;
  1288. (** enter keywords in the list of keywords (both upper- and lowercase) **)
  1289. PROCEDURE InitKeywords;
  1290. PROCEDURE Upper(CONST source: ARRAY OF CHAR; VAR dest: ARRAY OF CHAR);
  1291. VAR c: CHAR; i: LONGINT;
  1292. BEGIN
  1293. i := 0;
  1294. REPEAT
  1295. c := source[i];
  1296. IF (c >= 'a') & (c<= 'z') THEN c := CHR(ORD(c)-ORD('a')+ORD('A')) END;
  1297. dest[i] := c; INC(i);
  1298. UNTIL c = 0X;
  1299. END Upper;
  1300. PROCEDURE Enter1(CONST name: ARRAY OF CHAR; token: LONGINT; case: SET);
  1301. BEGIN
  1302. IF Lowercase IN case THEN keywordsLower.PutString(name,token) END;
  1303. IF Uppercase IN case THEN keywordsUpper.PutString(name,token) END;
  1304. Basic.SetErrorExpected(token,name);
  1305. END Enter1;
  1306. PROCEDURE Enter(CONST name: ARRAY OF CHAR; token: LONGINT);
  1307. VAR upper: Keyword;
  1308. BEGIN
  1309. Enter1(name,token,{Lowercase});
  1310. Upper(name,upper);
  1311. Enter1(upper,token,{Uppercase});
  1312. END Enter;
  1313. PROCEDURE EnterSymbol(CONST name: ARRAY OF CHAR; token: LONGINT);
  1314. BEGIN
  1315. Enter1(name,token,{Lowercase,Uppercase});
  1316. END EnterSymbol;
  1317. BEGIN
  1318. NEW(keywordsUpper,EndOfText+1);
  1319. NEW(keywordsLower,EndOfText+1);
  1320. (* constructs and statements *)
  1321. Enter( "cell", Cell );
  1322. Enter( "cellnet", CellNet);
  1323. Enter( "await" , Await);
  1324. Enter( "begin" , Begin);
  1325. Enter( "by" , By);
  1326. Enter( "const" , Const);
  1327. Enter( "case" , Case);
  1328. Enter( "code" , Code);
  1329. Enter( "definition", Definition);
  1330. Enter( "do" , Do);
  1331. Enter( "div" , Div);
  1332. Enter( "end" , End);
  1333. Enter( "enum", Enum);
  1334. Enter( "else" , Else);
  1335. Enter( "elsif" , Elsif);
  1336. Enter( "exit" , Exit);
  1337. Enter( "extern" , Extern);
  1338. Enter( "false" , False);
  1339. Enter( "for" , For);
  1340. Enter( "finally" , Finally);
  1341. Enter( "if" , If);
  1342. Enter( "imag" , Imag);
  1343. Enter( "in" , In);
  1344. Enter( "is" , Is);
  1345. Enter( "import" , Import);
  1346. Enter( "loop" , Loop);
  1347. Enter( "module", Module);
  1348. Enter( "mod" , Mod);
  1349. Enter( "nil" , Nil );
  1350. Enter( "of" , Of);
  1351. Enter( "or" , Or);
  1352. Enter( "out", Out);
  1353. Enter( "operator" , Operator);
  1354. Enter( "procedure" , Procedure);
  1355. Enter( "port", Port);
  1356. Enter( "repeat" , Repeat);
  1357. Enter( "return" , Return);
  1358. Enter( "self", Self);
  1359. Enter( "new", New);
  1360. Enter( "result", Result);
  1361. Enter( "then" , Then);
  1362. Enter( "true" , True);
  1363. Enter( "to" , To);
  1364. Enter( "type" , Type);
  1365. Enter( "until" , Until );
  1366. Enter( "var" , Var );
  1367. Enter( "while" , While);
  1368. Enter( "with" , With);
  1369. (* types *)
  1370. Enter( "array" , Array );
  1371. Enter( "object" , Object);
  1372. Enter( "pointer" , Pointer);
  1373. Enter( "record" , Record);
  1374. Enter( "address" , Address);
  1375. Enter( "size" , Size);
  1376. Enter( "alias" , Alias);
  1377. (* symbols *)
  1378. EnterSymbol( "#", Unequal);
  1379. EnterSymbol( "&", And);
  1380. EnterSymbol( "(", LeftParenthesis);
  1381. EnterSymbol( ")", RightParenthesis);
  1382. EnterSymbol( "*", Times);
  1383. EnterSymbol( "**",TimesTimes);
  1384. EnterSymbol( "+", Plus);
  1385. EnterSymbol( "+*", PlusTimes);
  1386. EnterSymbol( ",", Comma);
  1387. EnterSymbol( "-", Minus);
  1388. EnterSymbol(".",Period );
  1389. EnterSymbol("..",Upto );
  1390. EnterSymbol(".*",DotTimes );
  1391. EnterSymbol("./",DotSlash );
  1392. EnterSymbol(".=",DotEqual );
  1393. EnterSymbol(".#",DotUnequal );
  1394. EnterSymbol(".>",DotGreater );
  1395. EnterSymbol(".>=",DotGreaterEqual );
  1396. EnterSymbol(".<", DotLess);
  1397. EnterSymbol(".<=",DotLessEqual );
  1398. EnterSymbol( "/", Slash);
  1399. EnterSymbol( ":", Colon);
  1400. EnterSymbol( ":=",Becomes);
  1401. EnterSymbol( ";", Semicolon);
  1402. EnterSymbol( "<", Less);
  1403. EnterSymbol( "<=", LessEqual);
  1404. EnterSymbol( "=", Equal);
  1405. EnterSymbol( ">", Greater);
  1406. EnterSymbol( ">=", GreaterEqual);
  1407. EnterSymbol( "[", LeftBracket);
  1408. EnterSymbol( "]", RightBracket);
  1409. EnterSymbol( "^", Arrow);
  1410. EnterSymbol( "{", LeftBrace);
  1411. EnterSymbol( "|",Bar);
  1412. EnterSymbol( "}", RightBrace);
  1413. EnterSymbol( "~", Not);
  1414. EnterSymbol( "\", Backslash);
  1415. EnterSymbol( "`", Transpose);
  1416. EnterSymbol( "?",Questionmark);
  1417. EnterSymbol( "??",Questionmarks);
  1418. EnterSymbol( "!",ExclamationMark);
  1419. EnterSymbol( "!!",ExclamationMarks);
  1420. EnterSymbol( "<<",LessLess);
  1421. EnterSymbol( "<<?",LessLessQ);
  1422. EnterSymbol( ">>",GreaterGreater);
  1423. EnterSymbol( ">>?",GreaterGreaterQ);
  1424. Basic.SetErrorMessage(Number,"missing number");
  1425. Basic.SetErrorMessage(String,"missing string");
  1426. Basic.SetErrorMessage(Character,"missing character");
  1427. Basic.SetErrorMessage(Identifier,"missing identifier");
  1428. Basic.SetErrorMessage(EndOfText,"unexpected symbol before end");
  1429. END InitKeywords;
  1430. (** debugging / reporting **)
  1431. PROCEDURE ReportKeywords*(context: Commands.Context);
  1432. VAR i: LONGINT; name: Keyword;
  1433. BEGIN
  1434. FOR i := 0 TO EndOfText DO
  1435. context.out.Int(i,1); context.out.String(": ");
  1436. context.out.Char('"');
  1437. keywordsLower.StringByIndex(i,name);
  1438. context.out.String(name);
  1439. context.out.Char('"');
  1440. context.out.String(", ");
  1441. context.out.Char('"');
  1442. keywordsUpper.StringByIndex(i,name);
  1443. context.out.String(name);
  1444. context.out.Char('"');
  1445. context.out.Ln;
  1446. END;
  1447. END ReportKeywords;
  1448. (*
  1449. PROCEDURE TestScanner*(context: Commands.Context);
  1450. VAR filename: ARRAY 256 OF CHAR; reader: Streams.Reader; scanner: Scanner;sym: Symbol;
  1451. BEGIN
  1452. context.arg.SkipWhitespace; context.arg.String(filename);
  1453. reader := TextUtilities.GetTextReader(filename);
  1454. scanner := NewScanner(filename,reader,0,NIL);
  1455. REPEAT
  1456. IF scanner.GetNextSymbol(sym) THEN
  1457. OutSymbol(context.out,sym);context.out.Ln;
  1458. END;
  1459. UNTIL scanner.error OR (sym.token=EndOfText)
  1460. END TestScanner;
  1461. *)
  1462. BEGIN
  1463. InitReservedCharacters; InitTokens; InitKeywords
  1464. END FoxScanner.
  1465. FoxScanner.ReportKeywords
  1466. FoxScanner.TestScanner Test.Mod ~