XMLScanner.Mod 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527
  1. MODULE XMLScanner; (** AUTHOR "swalthert"; PURPOSE "XML scanner"; *)
  2. IMPORT
  3. KernelLog, Streams, Strings, DynamicStrings;
  4. CONST
  5. (* String pooling settings *)
  6. Str_ElementName* = 1;
  7. Str_AttributeName* = 2;
  8. Str_CharRef* = 10;
  9. Str_EntityRef* = 11;
  10. Str_EntityValue* = 12;
  11. Str_AttributeValue* = 13;
  12. Str_Comment* = 20;
  13. Str_ProcessingInstruction* = 21;
  14. Str_CDataSection* = 22;
  15. Str_SystemLiteral* = 23;
  16. Str_PublicLiteral* = 24;
  17. Str_CharData* = 25;
  18. Str_Other* = 30;
  19. (** Scanner: Tokens *)
  20. Invalid* = -1;
  21. TagElemStartOpen* = 0; (** '<' *)
  22. TagElemEndOpen* = 1; (** '</' *)
  23. TagDeclOpen* = 2; (** '<!NAME' *)
  24. TagClose* = 3; (** '>' *)
  25. TagEmptyElemClose* = 4; (** '/>' *)
  26. TagXMLDeclOpen* = 5; (** '<?xml' *)
  27. TagPIOpen* = 6; (** '<?', PITarget := GetStr() *)
  28. TagPIClose* = 7; (** '?>' *)
  29. TagCondSectOpen* = 8; (** '<![' *)
  30. TagCondSectClose* = 9; (** ']]>' *)
  31. BracketOpen* = 10; (** '[' *)
  32. BracketClose* = 11; (** ']' *)
  33. ParenOpen* = 12; (** '(' *)
  34. ParenClose* = 13; (** ')' *)
  35. Comment* = 14; (** '<!--' chars '-->', chars := GetStr() *)
  36. CDataSect* = 15; (** '<![CDATA[' chars ']]>', chars := GetStr() *)
  37. CharRef* = 16; (** '&#' number ';' or '&#x' hexnumber ';', number, hexnumber := GetStr() *)
  38. EntityRef* = 17; (** '&' name ';', name := GetStr() *)
  39. ParamEntityRef* = 18; (** '%' name ';', name := GetStr() *)
  40. CharData* = 19; (** chars := GetStr() *)
  41. Literal* = 20; (** '"'chars'"' or "'"chars"'", chars := GetStr() *)
  42. Name* = 21; (** Name ::= (Letter | '_' | ':') {NameChar}
  43. NameChar ::= Letter | Digit | '.' | '-' | '_' | ':' | CombiningChar | Extender
  44. chars := GetStr() *)
  45. Nmtoken* = 22; (** Nmtoken ::= NameChar {NameChar}, chars := GetStr() *)
  46. PoundName* = 23; (** '#'name, name := GetStr() *)
  47. Question* = 24; (** '?' *)
  48. Asterisk* = 25; (** '*' *)
  49. Plus* = 26; (** '+' *)
  50. Or* = 27; (** '|' *)
  51. Comma* = 28; (** ',' *)
  52. Percent* = 29; (** '%' *)
  53. Equal* = 30; (** '=' *)
  54. Eof* = 31;
  55. LF = 0AX;
  56. CR = 0DX;
  57. TYPE
  58. String = Strings.String;
  59. Scanner* = OBJECT
  60. VAR
  61. sym-: SHORTINT; (** current token *)
  62. line-, col-, oldpos, pos: LONGINT;
  63. reportError*: PROCEDURE {DELEGATE} (pos, line, row: LONGINT; CONST msg: ARRAY OF CHAR);
  64. nextCh: CHAR; (* look-ahead *)
  65. dynstr: DynamicStrings.DynamicString; (* buffer for CharData, Literal, Name, CharRef, EntityRef and ParamEntityRef *)
  66. r : Streams.Reader;
  67. stringPool : DynamicStrings.Pool;
  68. stringPooling : SET;
  69. (** Initialize scanner to read from the given ascii file *)
  70. PROCEDURE & Init*(r: Streams.Reader);
  71. BEGIN
  72. reportError := DefaultReportError;
  73. SELF.r := r;
  74. NEW(dynstr);
  75. line := 1; pos := 0; col := 0;
  76. stringPool := NIL;
  77. stringPooling := {};
  78. NextCh();
  79. END Init;
  80. PROCEDURE SetStringPooling*(stringPooling : SET);
  81. BEGIN
  82. SELF.stringPooling := stringPooling;
  83. IF (stringPooling = {}) THEN
  84. stringPool := NIL;
  85. ELSIF (stringPool = NIL) THEN
  86. NEW(stringPool);
  87. END;
  88. ASSERT((stringPool = NIL) = (stringPooling = {}));
  89. END SetStringPooling;
  90. PROCEDURE Error(CONST msg: ARRAY OF CHAR);
  91. BEGIN
  92. sym := Invalid;
  93. reportError(GetPos(), line, col, msg)
  94. END Error;
  95. PROCEDURE NextCh;
  96. BEGIN
  97. IF (nextCh = CR) OR (nextCh = LF) THEN INC(line); col := 0;
  98. ELSE INC(col)
  99. END;
  100. IF r.res # Streams.Ok THEN
  101. nextCh := 0X; sym := Eof
  102. ELSE
  103. nextCh := r.Get(); INC(pos);
  104. END
  105. END NextCh;
  106. PROCEDURE ReadTillChar(ch: CHAR);
  107. BEGIN
  108. dynstr.Clear;
  109. WHILE (nextCh # ch) & (sym # Eof) DO
  110. dynstr.AppendCharacter(nextCh);
  111. NextCh();
  112. END;
  113. IF sym = Eof THEN sym := Invalid END
  114. END ReadTillChar;
  115. PROCEDURE SkipWhiteSpaces;
  116. BEGIN
  117. WHILE IsWhiteSpace(nextCh) & (sym # Eof) DO
  118. NextCh()
  119. END
  120. END SkipWhiteSpaces;
  121. PROCEDURE ScanPoundName;
  122. BEGIN
  123. dynstr.Clear;
  124. dynstr.AppendCharacter(nextCh);
  125. NextCh();
  126. WHILE (('a' <= nextCh) & (nextCh <= 'z')) OR (('A' <= nextCh) & (nextCh <= 'Z')) OR
  127. (('0' <= nextCh) & (nextCh <= '9')) OR (nextCh = '.') OR (nextCh = '-') OR (nextCh = '_') OR (nextCh = ':') DO
  128. dynstr.AppendCharacter(nextCh);
  129. NextCh();
  130. END;
  131. IF sym # Eof THEN sym := PoundName ELSE sym := Invalid END
  132. END ScanPoundName;
  133. (* Possible results:
  134. Name
  135. Nmtoken
  136. Invalid *)
  137. PROCEDURE ScanNm;
  138. BEGIN
  139. SkipWhiteSpaces();
  140. IF (('0' <= nextCh) & (nextCh <= '9')) OR (nextCh = '.') OR (nextCh = '-') THEN
  141. sym := Nmtoken
  142. ELSIF (('a' <= nextCh) & (nextCh <= 'z')) OR (('A' <= nextCh) & (nextCh <= 'Z')) OR (nextCh = '_') OR (nextCh = ':') THEN
  143. sym := Name
  144. ELSE
  145. sym := Invalid; RETURN
  146. END;
  147. dynstr.Clear;
  148. dynstr.AppendCharacter(nextCh);
  149. NextCh();
  150. WHILE ((('a' <= nextCh) & (nextCh <= 'z')) OR (('A' <= nextCh) & (nextCh <= 'Z')) OR
  151. (('0' <= nextCh) & (nextCh <= '9')) OR (nextCh = '.') OR (nextCh = '-') OR (nextCh = '_')
  152. OR (nextCh = ':')) & (sym # Eof) DO
  153. dynstr.AppendCharacter(nextCh);
  154. NextCh();
  155. END;
  156. IF sym = Eof THEN sym := Invalid END
  157. END ScanNm;
  158. (* Scan Comment after comment open tag '<!--', write characters to dynstr.
  159. Possible results:
  160. Invalid
  161. Comment *)
  162. PROCEDURE ScanComment;
  163. BEGIN
  164. dynstr.Clear;
  165. LOOP
  166. WHILE (nextCh # '-') & (sym # Eof) DO
  167. dynstr.AppendCharacter(nextCh);
  168. NextCh()
  169. END;
  170. IF nextCh = '-' THEN
  171. NextCh();
  172. IF nextCh = '-' THEN
  173. NextCh();
  174. IF nextCh = '>' THEN
  175. NextCh(); sym := Comment; RETURN
  176. ELSE
  177. sym := Invalid; RETURN
  178. END
  179. ELSE
  180. dynstr.AppendCharacter('-');
  181. END
  182. ELSE
  183. sym := Invalid; RETURN
  184. END
  185. END
  186. END ScanComment;
  187. (* Possible results:
  188. CharData
  189. TagCDataSectClose
  190. Invalid *)
  191. PROCEDURE ScanCDataSect;
  192. VAR bc: LONGINT; escape : BOOLEAN;
  193. BEGIN
  194. IF sym = Eof THEN
  195. sym := Invalid;
  196. RETURN
  197. END;
  198. dynstr.Clear;
  199. LOOP
  200. WHILE (nextCh # ']') & (sym # Eof) DO
  201. dynstr.AppendCharacter(nextCh);
  202. NextCh()
  203. END;
  204. IF nextCh = ']' THEN
  205. bc := 1; escape := FALSE; NextCh();
  206. WHILE nextCh = ']' DO
  207. INC(bc); NextCh();
  208. IF nextCh = '>' THEN
  209. NextCh(); escape := TRUE;
  210. END
  211. END;
  212. IF escape THEN
  213. WHILE (bc > 2) DO
  214. DEC(bc);
  215. dynstr.AppendCharacter(']');
  216. END;
  217. sym := CDataSect; RETURN
  218. ELSE
  219. WHILE (bc > 0) DO
  220. DEC(bc); dynstr.AppendCharacter(']');
  221. END;
  222. END;
  223. ELSE
  224. sym := CharData; RETURN
  225. END
  226. END
  227. END ScanCDataSect;
  228. (* possible results:
  229. Invalid
  230. ParamEntityRef *)
  231. PROCEDURE ScanPEReference;
  232. BEGIN
  233. ReadTillChar(';'); NextCh();
  234. IF sym # Invalid THEN sym := ParamEntityRef END
  235. END ScanPEReference;
  236. (* possible results:
  237. Invalid
  238. CharRef
  239. EntityRef *)
  240. PROCEDURE ScanReference;
  241. BEGIN
  242. IF nextCh = '#' THEN
  243. NextCh();
  244. ReadTillChar(';'); NextCh();
  245. IF sym # Invalid THEN sym := CharRef END;
  246. ELSE
  247. ReadTillChar(';'); NextCh();
  248. IF sym # Invalid THEN sym := EntityRef END
  249. END
  250. END ScanReference;
  251. (** possible results:
  252. Invalid
  253. TagPIClose
  254. CharData *)
  255. PROCEDURE ScanPInstruction*;
  256. BEGIN
  257. IF sym = Eof THEN
  258. sym := Invalid;
  259. RETURN
  260. END;
  261. dynstr.Clear;
  262. LOOP
  263. WHILE (nextCh # '?') & (sym # Eof) DO
  264. dynstr.AppendCharacter(nextCh);
  265. NextCh();
  266. END;
  267. IF nextCh = '?' THEN
  268. NextCh();
  269. IF nextCh = '>' THEN
  270. sym := TagPIClose; NextCh(); RETURN
  271. ELSE
  272. dynstr.AppendCharacter('?');
  273. END
  274. ELSIF sym = Eof THEN
  275. sym := Invalid; RETURN
  276. ELSE
  277. sym := CharData; RETURN
  278. END
  279. END
  280. END ScanPInstruction;
  281. (** Possible results:
  282. Invalid
  283. TagPIOpen
  284. TagCondSectOpen
  285. TagDeclOpen
  286. TagXMLDeclOpen
  287. TagClose
  288. TagEmptyElemClose
  289. TagPIClose
  290. TagCondSectClose
  291. Comment
  292. CharRef
  293. EntityRef
  294. ParamEntityRef
  295. Literal
  296. Name
  297. Nmtoken
  298. PoundName
  299. Question
  300. Asterisk
  301. Plus
  302. Or
  303. Comma
  304. Percent
  305. Equal
  306. ParenOpen
  307. ParenClose
  308. BracketOpen
  309. BracketClose *)
  310. PROCEDURE ScanMarkup*;
  311. VAR ch: CHAR;
  312. BEGIN
  313. SkipWhiteSpaces();
  314. oldpos := GetPos();
  315. IF sym = Eof THEN
  316. sym := Eof; RETURN
  317. END;
  318. CASE nextCh OF
  319. | '<': NextCh();
  320. IF nextCh = '!' THEN
  321. NextCh();
  322. IF nextCh = '-' THEN
  323. NextCh();
  324. IF nextCh = '-' THEN
  325. NextCh(); ScanComment()
  326. ELSE
  327. Error("'<!--' expected")
  328. END
  329. ELSIF nextCh = '[' THEN
  330. sym := TagCondSectOpen
  331. ELSE
  332. ScanNm();
  333. IF sym = Name THEN
  334. sym := TagDeclOpen
  335. ELSE
  336. Error("'<!NAME' expected")
  337. END
  338. END
  339. ELSIF nextCh = '?' THEN
  340. NextCh(); ScanNm();
  341. IF sym = Name THEN
  342. sym := TagPIOpen
  343. ELSE
  344. Error("'<?' Name expected")
  345. END
  346. ELSE
  347. Error("'<?' Name or '<!--' expected")
  348. END
  349. | '/': NextCh();
  350. IF nextCh = '>' THEN
  351. NextCh(); sym := TagEmptyElemClose
  352. ELSE
  353. sym := Invalid
  354. END
  355. | '>': NextCh(); sym := TagClose
  356. | '%': NextCh();
  357. IF nextCh = ' ' THEN
  358. sym := Percent
  359. ELSE
  360. ScanPEReference()
  361. END
  362. | '?': NextCh();
  363. IF nextCh = '>' THEN
  364. NextCh();
  365. sym := TagPIClose
  366. ELSE
  367. sym := Question
  368. END
  369. | '*': NextCh(); sym := Asterisk
  370. | '+': NextCh(); sym := Plus
  371. | '|': NextCh(); sym := Or
  372. | ',': NextCh(); sym := Comma
  373. | '(': NextCh(); sym := ParenOpen
  374. | ')': NextCh(); sym := ParenClose
  375. | '[': NextCh(); sym := BracketOpen
  376. | ']': NextCh();
  377. IF nextCh = ']' THEN
  378. NextCh();
  379. IF nextCh = '>' THEN
  380. NextCh(); sym := TagCondSectClose
  381. ELSE
  382. Error("']]>' expected")
  383. END
  384. ELSE
  385. sym := BracketClose
  386. END
  387. | '=': NextCh(); sym := Equal
  388. | '"', "'": ch := nextCh; NextCh(); ReadTillChar(ch); NextCh();
  389. IF sym # Invalid THEN sym := Literal END;
  390. | '#': ScanPoundName()
  391. ELSE ScanNm()
  392. END
  393. END ScanMarkup;
  394. (** possible results:
  395. TagElemEndOpen
  396. TagPIOpen
  397. TagDocTypeOpen
  398. CDataSect
  399. TagElemStartOpen
  400. Comment
  401. CharData
  402. CharRef
  403. EntityRef
  404. Eof *)
  405. PROCEDURE ScanContent*;
  406. VAR op : LONGINT;
  407. BEGIN
  408. op := GetPos();
  409. SkipWhiteSpaces(); oldpos := GetPos();
  410. IF sym = Eof THEN nextCh := 0X END;
  411. CASE nextCh OF
  412. | 0X: sym := Eof
  413. | '<': NextCh();
  414. CASE nextCh OF
  415. | '/': sym := TagElemEndOpen; NextCh()
  416. | '?': NextCh(); ScanNm();
  417. IF (sym = Name) THEN
  418. IF dynstr.EqualsTo("xml", TRUE) THEN
  419. sym := TagXMLDeclOpen
  420. ELSE
  421. sym := TagPIOpen
  422. END
  423. ELSE
  424. Error("'<? Name' expected")
  425. END
  426. | '!': NextCh();
  427. IF nextCh = '-' THEN
  428. NextCh();
  429. IF nextCh = '-' THEN
  430. NextCh(); ScanComment()
  431. ELSE
  432. Error("'<!--' expected")
  433. END
  434. ELSIF nextCh = '[' THEN
  435. NextCh(); ScanNm();
  436. IF (sym = Name) & dynstr.EqualsTo("CDATA", FALSE) & (nextCh = '[') THEN
  437. NextCh(); ScanCDataSect()
  438. ELSE
  439. Error("'<[CDATA[' expected'")
  440. END
  441. ELSE
  442. ScanNm();
  443. IF sym = Name THEN
  444. sym := TagDeclOpen
  445. ELSE
  446. Error("'<!xml' or '<!NAME' expected")
  447. END
  448. END
  449. ELSE
  450. sym:=TagElemStartOpen
  451. END
  452. (* | '?': NextCh();
  453. IF nextCh = '>' THEN
  454. NextCh(); sym := TagPIClose
  455. ELSE
  456. Error("'?>' expected")
  457. END
  458. *) | '&': NextCh(); ScanReference()
  459. ELSE
  460. dynstr.Clear;
  461. REPEAT
  462. dynstr.AppendCharacter(nextCh);
  463. NextCh();
  464. UNTIL (nextCh='<') OR (sym = Eof);
  465. oldpos := op;
  466. sym := CharData
  467. END
  468. END ScanContent;
  469. PROCEDURE GetString*(type : LONGINT): String;
  470. VAR string : String;
  471. BEGIN
  472. IF (type IN stringPooling) THEN
  473. string := stringPool.Get(dynstr);
  474. ELSE
  475. string := dynstr.ToArrOfChar();
  476. END;
  477. RETURN string;
  478. END GetString;
  479. PROCEDURE GetPos*(): LONGINT;
  480. BEGIN
  481. RETURN pos - 1
  482. END GetPos;
  483. PROCEDURE GetOldPos*(): LONGINT;
  484. BEGIN
  485. RETURN oldpos
  486. END GetOldPos;
  487. END Scanner;
  488. PROCEDURE IsWhiteSpace(ch: CHAR): BOOLEAN;
  489. BEGIN
  490. RETURN (ch = 020X) OR (ch = 9X) OR (ch = 0DX) OR (ch = 0AX)
  491. END IsWhiteSpace;
  492. PROCEDURE DefaultReportError(pos, line, col: LONGINT; CONST msg: ARRAY OF CHAR);
  493. BEGIN
  494. KernelLog.Enter; KernelLog.Char(CHR(9H)); KernelLog.Char(CHR(9H)); KernelLog.String("pos "); KernelLog.Int(pos, 6);
  495. KernelLog.String(", line "); KernelLog.Int(line, 0); KernelLog.String(", col "); KernelLog.Int(col, 0);
  496. KernelLog.String(" "); KernelLog.String(msg); KernelLog.Exit;
  497. END DefaultReportError;
  498. END XMLScanner.