Parser.cp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459
  1. MODULE JSonParser;
  2. (**
  3. project = "JSon"
  4. organization = ""
  5. contributors = ""
  6. version = "System/Rsrc/About"
  7. copyright = "Kushnir Piotr Michailovich"
  8. license = "Docu/BB-License"
  9. purpose = "Парсер"
  10. changes = "
  11. - 20130103, pk, автогенерация заголовка
  12. - 20130103, pk, реализовал парсинг структур пока без вложенности и обработки ошибок
  13. - 20130109, pk, переделал сканер с учётом экранирования символов и вайтспейсов
  14. -20130112, pk, поправил баг с неочищаемым буфером
  15. - 20120117, pk, добавил дополнительный разбор на целые и вещественные числа в StdTarget, добавил пошаговый парсинг для источников, пополняющихся постепенно
  16. - 20130718, pk, убрал зависимость от ypk
  17. - 20140109, pk, пофиксил парсинг спецсимволов
  18. - 20140117, pk, исправил обработку пустых строковых полей
  19. - 20140301, pk, модифицировал типы переменных в Target, ведь Holder теперь скрыт
  20. - 20150209, pk, портирование для fw
  21. "
  22. issues = ""
  23. **)
  24. IMPORT
  25. Out, Str;
  26. CONST
  27. eot* = 1;
  28. err* = 2;
  29. continue* = 0;
  30. none = 0;
  31. objBegin = 1;
  32. objEnd = 2;
  33. arrBegin = 3;
  34. arrEnd = 4;
  35. valueSep = 5;
  36. nameSep = 6;
  37. obj* = 1;
  38. arr* = 2;
  39. TYPE
  40. Reader* = POINTER TO ABSTRACT RECORD END;
  41. Parser* = POINTER TO ABSTRACT RECORD END;
  42. Target* = POINTER TO ABSTRACT RECORD END;
  43. Directory* = POINTER TO ABSTRACT RECORD END;
  44. StdDir = POINTER TO RECORD (Directory) END;
  45. StdParser = POINTER TO RECORD (Parser)
  46. sc: Scanner;
  47. root: StackItem;
  48. t: Target;
  49. END;
  50. (* StdTarget = POINTER TO RECORD (Target)
  51. res, root, this: YSonModels.Value;
  52. name: ARRAY 256 OF CHAR;
  53. END; *)
  54. StackItem = POINTER TO RECORD
  55. pos, type: INTEGER;
  56. next: StackItem;
  57. END;
  58. Scanner = RECORD
  59. rd: Reader;
  60. res: INTEGER;
  61. this: RECORD
  62. inQuotes: BOOLEAN;
  63. buf: Buffer;
  64. sym: INTEGER;
  65. END;
  66. END;
  67. Buffer = RECORD
  68. empty: BOOLEAN;
  69. quoted: BOOLEAN;
  70. x: Str.Dyn;
  71. END;
  72. Char = RECORD
  73. x: CHAR;
  74. esc: BOOLEAN;
  75. END;
  76. VAR
  77. dir-, prev-, stdDir-: Directory;
  78. PROCEDURE (r: Reader) Read- (OUT ch: CHAR): BOOLEAN, NEW, ABSTRACT;
  79. PROCEDURE (r: Reader) SetPos- (x: INTEGER), NEW, ABSTRACT;
  80. PROCEDURE (r: Reader) Pos* (): INTEGER, NEW, ABSTRACT;
  81. PROCEDURE (r: Reader) ConnectTo* (source: ANYPTR), NEW, ABSTRACT;
  82. PROCEDURE (r: Reader) Base* (): ANYPTR, NEW, ABSTRACT;
  83. PROCEDURE (r: Reader) Eot*(): BOOLEAN, NEW, ABSTRACT;
  84. PROCEDURE (d: Directory) New* (rd: Reader): Parser, NEW, ABSTRACT;
  85. PROCEDURE (p: Parser) Parse*(OUT res: INTEGER): ANYPTR, NEW, ABSTRACT;
  86. PROCEDURE (p: Parser) SetTarget* (t: Target), NEW, EMPTY;
  87. PROCEDURE (p: Parser) Result*(): ANYPTR, NEW, ABSTRACT;
  88. PROCEDURE (p: Parser) Begin*, NEW, ABSTRACT;
  89. PROCEDURE (p: Parser) Step* (OUT res: INTEGER), NEW, ABSTRACT;
  90. PROCEDURE (t: Target) LevelDown* (id, type: INTEGER), NEW, ABSTRACT;
  91. PROCEDURE (t: Target) NextName* (IN s: ARRAY OF CHAR), NEW, ABSTRACT;
  92. PROCEDURE (t: Target) LevelUp*, NEW, ABSTRACT;
  93. PROCEDURE (t: Target) ThisValue* (IN x: ARRAY OF CHAR; quoted: BOOLEAN), NEW, ABSTRACT;
  94. PROCEDURE (t: Target) Result- (): ANYPTR, NEW, ABSTRACT;
  95. (* PROCEDURE (t: StdTarget) Result(): ANYPTR;
  96. BEGIN
  97. RETURN t.res
  98. END Result;
  99. PROCEDURE (t: StdTarget) LevelDown (id, type: INTEGER);
  100. VAR new, root: YSonModels.Value;
  101. BEGIN
  102. CASE type OF
  103. obj: new:=YSonModels.NewObject();
  104. |arr: new:=YSonModels.NewArray(0);
  105. ELSE HALT(100) END;
  106. ASSERT(new#NIL, 40);
  107. IF t.res=NIL THEN
  108. t.res:=new;
  109. t.root:=NIL;
  110. t.this:=new;
  111. ELSE
  112. root:=t.this;
  113. WITH root: YSonModels.Array DO
  114. root.SetLength(root.NofVal()+1);
  115. root.Set(root.NofVal()-1, new);
  116. |root: YSonModels.Object DO
  117. ASSERT(t.name$#'', 41);
  118. root.Add(t.name$, new);
  119. ELSE HALT(100) END;
  120. t.root:=t.this;
  121. t.this:=new;
  122. END;
  123. END LevelDown;
  124. PROCEDURE (t: StdTarget) NextName (IN s: ARRAY OF CHAR);
  125. BEGIN
  126. t.name:=s$;
  127. END NextName;
  128. PROCEDURE (t: StdTarget) LevelUp;
  129. BEGIN
  130. t.this:=t.root;
  131. IF t.this#NIL THEN
  132. t.root:=t.this.Owner();
  133. END;
  134. END LevelUp;
  135. PROCEDURE (t: StdTarget) ThisValue (IN x: ARRAY OF CHAR; quoted: BOOLEAN);
  136. CONST decimal = '.';
  137. VAR root: YSonModels.Value; v: YSonModels.Value; r: REAL; res: INTEGER;
  138. BEGIN
  139. root:=t.this;
  140. ASSERT(root#NIL, 20);
  141. IF quoted THEN
  142. v:=YSonModels.NewString(x$)
  143. ELSE
  144. IF (x$='true') THEN v:=YSonModels.NewLiteral(YSonModels.true)
  145. ELSIF (x$='false') THEN v:=YSonModels.NewLiteral(YSonModels.false)
  146. ELSIF (x$='null') THEN v:=YSonModels.NewLiteral(YSonModels.null)
  147. ELSIF (x$#'') THEN
  148. Str.StringToReal(x$, r, res);
  149. IF res=0 THEN
  150. Str.Find(x$, decimal, 0, res);
  151. IF res>-1 THEN
  152. v:=YSonModels.NewNumber(YSonModels.real, r)
  153. ELSE
  154. v:=YSonModels.NewNumber(YSonModels.int, r)
  155. END;
  156. ELSE HALT(100) END;
  157. ELSE HALT(100) END
  158. END;
  159. WITH root: YSonModels.Array DO
  160. root.SetLength(root.NofVal()+1);
  161. root.Set(root.NofVal()-1, v);
  162. |root: YSonModels.Object DO
  163. ASSERT(t.name$#'', 40);
  164. root.Add(t.name$, v);
  165. ELSE HALT(100) END;
  166. END ThisValue;
  167. *)
  168. PROCEDURE NewStackItem(pos: INTEGER): StackItem;
  169. VAR s: StackItem;
  170. BEGIN
  171. NEW(s);
  172. s.pos:=pos;
  173. RETURN s;
  174. END NewStackItem;
  175. PROCEDURE Push(root: StackItem; pos, type: INTEGER);
  176. VAR new: StackItem;
  177. BEGIN
  178. new:=NewStackItem(pos); new.type:=type;
  179. new.next:=root.next;
  180. root.next:=new;
  181. END Push;
  182. PROCEDURE Pop(root: StackItem): StackItem;
  183. VAR old: StackItem;
  184. BEGIN
  185. IF root.next#NIL THEN
  186. old:=root.next;
  187. root.next:=old.next;
  188. old.next:=NIL;
  189. END;
  190. RETURN old;
  191. END Pop;
  192. PROCEDURE (VAR sc: Scanner) ConnectTo (rd: Reader), NEW;
  193. BEGIN
  194. ASSERT(rd#NIL, 20); ASSERT(rd.Base()#NIL, 21); ASSERT(~rd.Eot(), 22);
  195. sc.rd:=rd;
  196. END ConnectTo;
  197. PROCEDURE (VAR sc: Scanner) Init, NEW;
  198. BEGIN
  199. sc.res:=continue;
  200. sc.this.inQuotes:=FALSE;
  201. END Init;
  202. PROCEDURE (VAR b: Buffer) Empty, NEW;
  203. BEGIN
  204. b.empty:=TRUE;
  205. END Empty;
  206. PROCEDURE (VAR b: Buffer) AddChar(x: CHAR), NEW;
  207. BEGIN
  208. b.empty:=FALSE;
  209. IF b.x=NIL THEN b.x:=Str.NewFrom('') END;
  210. b.x.Add(x)
  211. END AddChar;
  212. PROCEDURE (VAR b: Buffer) Clear, NEW;
  213. BEGIN
  214. b.empty:=FALSE;
  215. b.quoted:=FALSE;
  216. b.x:=NIL;
  217. END Clear;
  218. PROCEDURE (VAR b: Buffer) Len(): INTEGER, NEW;
  219. VAR res: INTEGER;
  220. BEGIN
  221. res:=0;
  222. IF b.x#NIL THEN res:=b.x.Len() END;
  223. RETURN res
  224. END Len;
  225. PROCEDURE SkipWhite (rd: Reader; OUT res: INTEGER);
  226. VAR x: CHAR; stop: BOOLEAN; pos: INTEGER;
  227. BEGIN
  228. stop:=FALSE;
  229. pos:=rd.Pos();
  230. WHILE~stop & rd.Read(x) DO
  231. CASE x OF
  232. 09X, 0AX, 0DX, ' ': stop:=FALSE;
  233. ELSE
  234. stop:=TRUE;
  235. rd.SetPos(pos);
  236. res:=continue
  237. END;
  238. pos:=rd.Pos();
  239. END;
  240. IF ~stop THEN res:=eot END;
  241. END SkipWhite;
  242. PROCEDURE ReadChar (rd: Reader; OUT ch: Char; OUT res: INTEGER);
  243. VAR x: CHAR; stop: BOOLEAN; u: ARRAY 6 OF CHAR; uc: INTEGER;
  244. BEGIN
  245. res:=continue; stop:=FALSE; ch.esc:=FALSE; uc:=-1;
  246. WHILE ~stop & rd.Read(x) DO
  247. IF (x='\') & ~ch.esc THEN
  248. ch.esc:=TRUE;
  249. ELSIF ch.esc & (uc<0) THEN
  250. CASE x OF
  251. '\', '"', '/' (*, 08X, 09X, 0DX, 0AX, 0CX *): ch.x:=x; stop:=TRUE;
  252. |'u': uc:=0;
  253. |'b': ch.x:=08X; stop:=TRUE;
  254. |'f': ch.x:=0CX; stop:=TRUE;
  255. |'n': ch.x:=0AX; stop:=TRUE;
  256. |'r': ch.x:=0DX; stop:=TRUE;
  257. |'t': ch.x:=09X; stop:=TRUE;
  258. ELSE
  259. res:=err; stop:=TRUE;
  260. END
  261. ELSIF ch.esc & (uc>=0) THEN
  262. IF uc < 4 THEN
  263. u[uc]:=x; INC(uc);
  264. ELSE
  265. u[4]:='H'; u[5]:=0X;
  266. Str.StringToInt(u$, uc, res);
  267. IF res=0 THEN
  268. ch.x:=CHR(uc);
  269. ELSE
  270. res:=err;
  271. END;
  272. stop:=TRUE;
  273. uc:=-1;
  274. END;
  275. ELSIF (x#'\') & ~ch.esc THEN
  276. ch.x:=x;
  277. stop:=TRUE;
  278. END;
  279. END;
  280. IF ~stop THEN res:=eot END;
  281. END ReadChar;
  282. PROCEDURE (VAR sc: Scanner) ClearBuffer, NEW;
  283. BEGIN
  284. sc.this.buf.Clear;
  285. END ClearBuffer;
  286. PROCEDURE (VAR sc: Scanner) Read, NEW;
  287. VAR c: Char;
  288. BEGIN
  289. ASSERT(sc.rd#NIL, 20);
  290. sc.res:=continue;
  291. IF ~sc.this.inQuotes THEN SkipWhite(sc.rd, sc.res) END;
  292. IF sc.res=continue THEN
  293. sc.this.sym:=none;
  294. ReadChar(sc.rd, c, sc.res);
  295. IF ~c.esc THEN
  296. CASE c.x OF
  297. '"': sc.this.inQuotes:=~sc.this.inQuotes;
  298. IF sc.this.inQuotes THEN sc.this.buf.Empty; sc.this.buf.quoted:=TRUE END
  299. ELSE
  300. IF ~sc.this.inQuotes THEN
  301. CASE c.x OF
  302. |'{': sc.this.sym:=objBegin;
  303. |'[': sc.this.sym:=arrBegin;
  304. |':': sc.this.sym:=nameSep;
  305. |',': sc.this.sym:=valueSep;
  306. |'}': sc.this.sym:=objEnd;
  307. |']': sc.this.sym:=arrEnd;
  308. ELSE sc.this.buf.AddChar(c.x) END;
  309. ELSE sc.this.buf.AddChar(c.x) END;
  310. END;
  311. ELSE
  312. sc.this.buf.AddChar(c.x)
  313. END;
  314. END;
  315. END Read;
  316. PROCEDURE (p: StdParser) Begin;
  317. (* VAR t: StdTarget; *)
  318. BEGIN
  319. p.root:=NewStackItem(-1);
  320. (* IF p.t=NIL THEN
  321. NEW(t); p.t:=t;
  322. END; *)
  323. ASSERT(p.t#NIL, 60);
  324. p.sc.Init;
  325. END Begin;
  326. PROCEDURE (p: StdParser) Step(OUT res: INTEGER);
  327. VAR type: INTEGER; i: StackItem; x: POINTER TO ARRAY OF CHAR;
  328. PROCEDURE Value;
  329. BEGIN
  330. IF p.sc.this.buf.empty THEN NEW(x, 1); x[0]:=0X;
  331. ELSIF p.sc.this.buf.Len()>0 THEN x:=p.sc.this.buf.x.CopyOf() END;
  332. IF x#NIL THEN p.t.ThisValue(x$, p.sc.this.buf.quoted) END;
  333. END Value;
  334. BEGIN
  335. ASSERT(p.sc.res=continue, 20);
  336. p.sc.Read;
  337. CASE p.sc.this.sym OF
  338. |objBegin:
  339. Push(p.root, p.sc.rd.Pos(), obj);
  340. p.t.LevelDown(p.sc.rd.Pos(), obj);
  341. |arrBegin:
  342. Push(p.root, p.sc.rd.Pos(), arr);
  343. p.t.LevelDown(p.sc.rd.Pos(), arr);
  344. |objEnd:
  345. Value;
  346. p.t.LevelUp;
  347. p.sc.ClearBuffer;
  348. |arrEnd:
  349. Value;
  350. p.t.LevelUp;
  351. p.sc.ClearBuffer;
  352. |nameSep:
  353. IF p.sc.this.buf.Len()>0 THEN
  354. x:=p.sc.this.buf.x.CopyOf();
  355. p.t.NextName(x$)
  356. END;
  357. p.sc.ClearBuffer;
  358. |valueSep:
  359. Value;
  360. p.sc.ClearBuffer;
  361. ELSE END;
  362. res:=p.sc.res;
  363. END Step;
  364. PROCEDURE (p: StdParser) Parse(OUT res: INTEGER): ANYPTR;
  365. VAR x: ANYPTR;
  366. BEGIN
  367. p.Begin;
  368. res:=p.sc.res;
  369. WHILE ~(res IN {err, eot}) DO
  370. p.Step(res);
  371. END;
  372. IF res=eot THEN
  373. x:=p.Result();
  374. END;
  375. RETURN x;
  376. END Parse;
  377. PROCEDURE (p: StdParser) Result(): ANYPTR;
  378. VAR x: ANYPTR;
  379. BEGIN
  380. IF p.t#NIL THEN x:=p.t.Result() END;
  381. RETURN x
  382. END Result;
  383. PROCEDURE (p: StdParser) SetTarget(t: Target);
  384. BEGIN
  385. ASSERT(t#NIL, 20);
  386. p.t:=t;
  387. END SetTarget;
  388. PROCEDURE (d: StdDir) New (rd: Reader): Parser;
  389. VAR p: StdParser;
  390. BEGIN
  391. ASSERT(rd#NIL, 20); ASSERT(rd.Base()#NIL, 21);
  392. NEW(p);
  393. p.sc.ConnectTo(rd);
  394. RETURN p;
  395. END New;
  396. PROCEDURE Install* (d: Directory);
  397. BEGIN
  398. ASSERT(d#NIL, 20);
  399. prev:=dir;
  400. dir:=d;
  401. END Install;
  402. PROCEDURE Init;
  403. VAR d: StdDir;
  404. BEGIN
  405. NEW(d); Install(d);
  406. stdDir:=d;
  407. END Init;
  408. BEGIN
  409. Init;
  410. END JSonParser.