O7S.Mod 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  1. MODULE O7S; (* NW 19.9.93 / 20.3.2017 Scanner in Oberon-07*)
  2. IMPORT SYSTEM, Texts, Oberon;
  3. (* Oberon Scanner does lexical analysis. Input is Oberon-Text, output is
  4. sequence of symbols, i.e identifiers, numbers, strings, and special symbols.
  5. Recognises all Oberon keywords and skips comments. The keywords are
  6. recorded in a table.
  7. Get(sym) delivers next symbol from input text with Reader R.
  8. Mark(msg) records error and delivers error message with Writer W.
  9. If Get delivers ident, then the identifier (a string) is in variable id, if int or char
  10. in ival, if real in rval, and if string in str (and slen) *)
  11. TYPE
  12. LONGINT = INTEGER;
  13. BYTE = CHAR;
  14. CONST IdLen* = 32;
  15. NKW = 34; (*nof keywords*)
  16. maxExp = 38; stringBufSize = 256;
  17. (*lexical symbols*)
  18. null = 0; times* = 1; rdiv* = 2; div* = 3; mod* = 4;
  19. and* = 5; plus* = 6; minus* = 7; or* = 8; eql* = 9;
  20. neq* = 10; lss* = 11; leq* = 12; gtr* = 13; geq* = 14;
  21. in* = 15; is* = 16; arrow* = 17; period* = 18;
  22. char* = 20; int* = 21; real* = 22; false* = 23; true* = 24;
  23. nil* = 25; string* = 26; not* = 27; lparen* = 28; lbrak* = 29;
  24. lbrace* = 30; ident* = 31;
  25. if* = 32; while* = 34; repeat* = 35; case* = 36; for* = 37;
  26. comma* = 40; colon* = 41; becomes* = 42;
  27. upto* = 43; rparen* = 44;
  28. rbrak* = 45; rbrace* = 46; then* = 47; of* = 48; do* = 49;
  29. to* = 50; by* = 51; semicolon* = 52; end* = 53; bar* = 54;
  30. else* = 55; elsif* = 56; until* = 57; return* = 58;
  31. array* = 60; record* = 61; pointer* = 62; const* = 63; type* = 64;
  32. var* = 65; procedure* = 66; begin* = 67; import* = 68;
  33. module* = 69; eot = 70;
  34. TYPE Ident* = ARRAY IdLen OF CHAR;
  35. VAR ival-, slen-: LONGINT; (*results of Get*)
  36. rval-: REAL;
  37. id-: Ident; (*for identifiers*)
  38. str-: ARRAY stringBufSize OF CHAR;
  39. errcnt-: INTEGER;
  40. ch: CHAR; (*last character read*)
  41. errpos: LONGINT;
  42. R: Texts.Reader;
  43. W: Texts.Writer;
  44. k: INTEGER;
  45. KWX: ARRAY 10 OF INTEGER;
  46. keyTab: ARRAY NKW OF
  47. RECORD sym: INTEGER; id: ARRAY 12 OF CHAR END;
  48. PROCEDURE FLT (x: INTEGER): REAL;
  49. BEGIN RETURN x
  50. END FLT;
  51. PROCEDURE CopyId* (VAR ident: Ident);
  52. BEGIN ident := id
  53. END CopyId;
  54. PROCEDURE Pos* (): LONGINT;
  55. BEGIN RETURN SHORT(Texts.Pos(R)) - 1
  56. END Pos;
  57. PROCEDURE Mark* ((*IN*) msg: ARRAY OF CHAR);
  58. VAR p: LONGINT;
  59. BEGIN p := Pos();
  60. IF (p > errpos) & (errcnt < 25) THEN
  61. Texts.WriteLn(W); Texts.WriteString(W, " pos "); Texts.WriteInt(W, p, 1); Texts.Write(W, " ");
  62. Texts.WriteString(W, msg); Texts.Append(Oberon.Log, W.buf)
  63. END ;
  64. INC(errcnt); errpos := p + 4
  65. END Mark;
  66. PROCEDURE Identifier (VAR sym: INTEGER);
  67. VAR i, k: INTEGER;
  68. err: BOOLEAN;
  69. BEGIN i := 0;
  70. err := FALSE;
  71. REPEAT
  72. IF i < IdLen-1 THEN id[i] := ch; INC(i)
  73. ELSIF ~err THEN err := TRUE; Mark("identifier too long")
  74. END;
  75. Texts.Read(R, ch)
  76. UNTIL (ch < "0") OR (ch > "9") & (ch < "A") OR (ch > "Z") & (ch < "a") OR (ch > "z");
  77. id[i] := 0X;
  78. IF i < 10 THEN k := KWX[i-1]; (*search for keyword*)
  79. WHILE (id # keyTab[k].id) & (k < KWX[i]) DO INC(k) END ;
  80. IF k < KWX[i] THEN sym := keyTab[k].sym ELSE sym := ident END
  81. ELSE sym := ident
  82. END
  83. END Identifier;
  84. PROCEDURE String;
  85. VAR i: INTEGER;
  86. BEGIN i := 0; Texts.Read(R, ch);
  87. WHILE ~R.eot & (ch # 22X) DO
  88. IF ch >= " " THEN
  89. IF i < stringBufSize-1 THEN str[i] := ch; INC(i) ELSE Mark("string too long") END ;
  90. END ;
  91. Texts.Read(R, ch)
  92. END ;
  93. str[i] := 0X; INC(i); Texts.Read(R, ch); slen := i
  94. END String;
  95. PROCEDURE HexString;
  96. VAR i, m, n: INTEGER;
  97. BEGIN i := 0; Texts.Read(R, ch);
  98. WHILE ~R.eot & (ch # "$") DO
  99. WHILE ~R.eot & (ch <= " ") DO Texts.Read(R, ch) END ; (*skip*)
  100. IF ("0" <= ch) & (ch <= "9") THEN m := ORD(ch) - 30H
  101. ELSIF ("A" <= ch) & (ch <= "F") THEN m := ORD(ch) - 37H
  102. ELSE m := 0; Mark("hexdig expected")
  103. END;
  104. Texts.Read(R, ch);
  105. IF ("0" <= ch) & (ch <= "9") THEN n := ORD(ch) - 30H
  106. ELSIF ("A" <= ch) & (ch <= "F") THEN n := ORD(ch) - 37H
  107. ELSE n := 0; Mark("hexdig expected")
  108. END;
  109. IF i < stringBufSize THEN str[i] := CHR(m*10H + n); INC(i) ELSE Mark("string too long") END;
  110. Texts.Read(R, ch)
  111. END;
  112. Texts.Read(R, ch); slen := i (*no 0X appended!*)
  113. END HexString;
  114. PROCEDURE Ten (e: LONGINT): REAL;
  115. VAR x, t: REAL;
  116. BEGIN x := 1.0; t := 10.0;
  117. WHILE e > 0 DO
  118. IF ODD(e) THEN x := t * x END ;
  119. t := t * t; e := e DIV 2
  120. END ;
  121. RETURN x
  122. END Ten;
  123. PROCEDURE Number (VAR sym: INTEGER);
  124. CONST max = 2147483647 (*2^31 - 1*);
  125. VAR i, k, e, n, s, h: LONGINT; x: REAL;
  126. d: ARRAY 16 OF INTEGER;
  127. negE: BOOLEAN;
  128. BEGIN ival := 0; i := 0; n := 0; k := 0;
  129. REPEAT
  130. IF n < 16 THEN d[n] := ORD(ch)-30H; INC(n) ELSE Mark("too many digits"); n := 0 END ;
  131. Texts.Read(R, ch)
  132. UNTIL (ch < "0") OR (ch > "9") & (ch < "A") OR (ch > "F");
  133. IF (ch = "H") OR (ch = "R") OR (ch = "X") THEN (*hex*)
  134. REPEAT h := d[i];
  135. IF h >= 10 THEN h := h-7 END ;
  136. k := k*10H + h; INC(i) (*no overflow check*)
  137. UNTIL i = n;
  138. IF ch = "X" THEN sym := char;
  139. IF k < 100H THEN ival := k ELSE Mark("illegal value"); ival := 0 END
  140. ELSIF ch = "R" THEN sym := real; rval := SYSTEM.VAL(REAL, k)
  141. ELSE sym := int; ival := k
  142. END ;
  143. Texts.Read(R, ch)
  144. ELSIF ch = "." THEN
  145. Texts.Read(R, ch);
  146. IF ch = "." THEN (*double dot*) ch := 7FX; (*decimal integer*)
  147. REPEAT
  148. IF d[i] < 10 THEN
  149. IF k <= (max-d[i]) DIV 10 THEN k := k *10 + d[i] ELSE Mark("too large"); k := 0 END
  150. ELSE Mark("bad integer")
  151. END ;
  152. INC(i)
  153. UNTIL i = n;
  154. sym := int; ival := k
  155. ELSE (*real number*) x := 0.0; e := 0;
  156. REPEAT (*integer part*) x := x * 10.0 + FLT(d[i]); INC(i) UNTIL i = n;
  157. WHILE (ch >= "0") & (ch <= "9") DO (*fraction*)
  158. x := x * 10.0 + FLT(ORD(ch) - 30H); DEC(e); Texts.Read(R, ch)
  159. END ;
  160. IF (ch = "E") OR (ch = "D") THEN (*scale factor*)
  161. Texts.Read(R, ch); s := 0;
  162. IF ch = "-" THEN negE := TRUE; Texts.Read(R, ch)
  163. ELSE negE := FALSE;
  164. IF ch = "+" THEN Texts.Read(R, ch) END
  165. END ;
  166. IF (ch >= "0") & (ch <= "9") THEN
  167. REPEAT s := s*10 + ORD(ch)-30H; Texts.Read(R, ch)
  168. UNTIL (ch < "0") OR (ch >"9");
  169. IF negE THEN e := e-s ELSE e := e+s END
  170. ELSE Mark("digit?")
  171. END
  172. END ;
  173. IF e < 0 THEN
  174. IF e >= -maxExp THEN x := x / Ten(-e) ELSE x := 0.0 END
  175. ELSIF e > 0 THEN
  176. IF e <= maxExp THEN x := Ten(e) * x ELSE x := 0.0; Mark("too large") END
  177. END ;
  178. sym := real; rval := x
  179. END
  180. ELSE (*decimal integer*)
  181. REPEAT
  182. IF d[i] < 10 THEN
  183. IF k <= (max-d[i]) DIV 10 THEN k := k*10 + d[i] ELSE Mark("too large"); k := 0 END
  184. ELSE Mark("bad integer")
  185. END ;
  186. INC(i)
  187. UNTIL i = n;
  188. sym := int; ival := k
  189. END
  190. END Number;
  191. PROCEDURE comment;
  192. BEGIN Texts.Read(R, ch);
  193. REPEAT
  194. WHILE ~R.eot & (ch # "*") DO
  195. IF ch = "(" THEN Texts.Read(R, ch);
  196. IF ch = "*" THEN comment END
  197. ELSE Texts.Read(R, ch)
  198. END
  199. END ;
  200. WHILE ch = "*" DO Texts.Read(R, ch) END
  201. UNTIL (ch = ")") OR R.eot;
  202. IF ~R.eot THEN Texts.Read(R, ch) ELSE Mark("unterminated comment") END
  203. END comment;
  204. PROCEDURE Get* (VAR sym: INTEGER);
  205. BEGIN
  206. REPEAT
  207. WHILE ~R.eot & (ch <= " ") DO Texts.Read(R, ch) END;
  208. IF R.eot THEN sym := eot
  209. ELSIF ch < "A" THEN
  210. IF ch < "0" THEN
  211. IF ch = 22X THEN String; sym := string
  212. ELSIF ch = "#" THEN Texts.Read(R, ch); sym := neq
  213. ELSIF ch = "$" THEN HexString; sym := string
  214. ELSIF ch = "&" THEN Texts.Read(R, ch); sym := and
  215. ELSIF ch = "(" THEN Texts.Read(R, ch);
  216. IF ch = "*" THEN sym := null; comment ELSE sym := lparen END
  217. ELSIF ch = ")" THEN Texts.Read(R, ch); sym := rparen
  218. ELSIF ch = "*" THEN Texts.Read(R, ch); sym := times
  219. ELSIF ch = "+" THEN Texts.Read(R, ch); sym := plus
  220. ELSIF ch = "," THEN Texts.Read(R, ch); sym := comma
  221. ELSIF ch = "-" THEN Texts.Read(R, ch); sym := minus
  222. ELSIF ch = "." THEN Texts.Read(R, ch);
  223. IF ch = "." THEN Texts.Read(R, ch); sym := upto ELSE sym := period END
  224. ELSIF ch = "/" THEN Texts.Read(R, ch); sym := rdiv
  225. ELSE Texts.Read(R, ch); (* ! % ' *) sym := null
  226. END
  227. ELSIF ch < ":" THEN Number(sym)
  228. ELSIF ch = ":" THEN Texts.Read(R, ch);
  229. IF ch = "=" THEN Texts.Read(R, ch); sym := becomes ELSE sym := colon END
  230. ELSIF ch = ";" THEN Texts.Read(R, ch); sym := semicolon
  231. ELSIF ch = "<" THEN Texts.Read(R, ch);
  232. IF ch = "=" THEN Texts.Read(R, ch); sym := leq ELSE sym := lss END
  233. ELSIF ch = "=" THEN Texts.Read(R, ch); sym := eql
  234. ELSIF ch = ">" THEN Texts.Read(R, ch);
  235. IF ch = "=" THEN Texts.Read(R, ch); sym := geq ELSE sym := gtr END
  236. ELSE (* ? @ *) Texts.Read(R, ch); sym := null
  237. END
  238. ELSIF ch < "[" THEN Identifier(sym)
  239. ELSIF ch < "a" THEN
  240. IF ch = "[" THEN sym := lbrak
  241. ELSIF ch = "]" THEN sym := rbrak
  242. ELSIF ch = "^" THEN sym := arrow
  243. ELSE (* _ ` *) sym := null
  244. END ;
  245. Texts.Read(R, ch)
  246. ELSIF ch < "{" THEN Identifier(sym) ELSE
  247. IF ch = "{" THEN sym := lbrace
  248. ELSIF ch = "}" THEN sym := rbrace
  249. ELSIF ch = "|" THEN sym := bar
  250. ELSIF ch = "~" THEN sym := not
  251. ELSIF ch = 7FX THEN sym := upto
  252. ELSE sym := null
  253. END ;
  254. Texts.Read(R, ch)
  255. END
  256. UNTIL sym # null
  257. END Get;
  258. PROCEDURE Init* (T: Texts.Text; pos: LONGINT);
  259. BEGIN errpos := pos; errcnt := 0; Texts.OpenReader(R, T, pos); Texts.Read(R, ch)
  260. END Init;
  261. PROCEDURE EnterKW (sym: INTEGER; (*IN*) name: ARRAY OF CHAR);
  262. BEGIN
  263. (* keyTab[k].id := name; *) COPY(name, keyTab[k].id);
  264. keyTab[k].sym := sym; INC(k)
  265. END EnterKW;
  266. BEGIN Texts.OpenWriter(W); k := 0; KWX[0] := 0; KWX[1] := 0;
  267. EnterKW(if, "IF");
  268. EnterKW(do, "DO");
  269. EnterKW(of, "OF");
  270. EnterKW(or, "OR");
  271. EnterKW(to, "TO");
  272. EnterKW(in, "IN");
  273. EnterKW(is, "IS");
  274. EnterKW(by, "BY");
  275. KWX[2] := k;
  276. EnterKW(end, "END");
  277. EnterKW(nil, "NIL");
  278. EnterKW(var, "VAR");
  279. EnterKW(div, "DIV");
  280. EnterKW(mod, "MOD");
  281. EnterKW(for, "FOR");
  282. KWX[3] := k;
  283. EnterKW(else, "ELSE");
  284. EnterKW(then, "THEN");
  285. EnterKW(true, "TRUE");
  286. EnterKW(type, "TYPE");
  287. EnterKW(case, "CASE");
  288. KWX[4] := k;
  289. EnterKW(elsif, "ELSIF");
  290. EnterKW(false, "FALSE");
  291. EnterKW(array, "ARRAY");
  292. EnterKW(begin, "BEGIN");
  293. EnterKW(const, "CONST");
  294. EnterKW(until, "UNTIL");
  295. EnterKW(while, "WHILE");
  296. KWX[5] := k;
  297. EnterKW(record, "RECORD");
  298. EnterKW(repeat, "REPEAT");
  299. EnterKW(return, "RETURN");
  300. EnterKW(import, "IMPORT");
  301. EnterKW(module, "MODULE");
  302. KWX[6] := k;
  303. EnterKW(pointer, "POINTER");
  304. KWX[7] := k; KWX[8] := k;
  305. EnterKW(procedure, "PROCEDURE");
  306. KWX[9] := k
  307. END O7S.