123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459 |
- MODULE JSonParser;
- (**
- project = "JSon"
- organization = ""
- contributors = ""
- version = "System/Rsrc/About"
- copyright = "Kushnir Piotr Michailovich"
- license = "Docu/BB-License"
- purpose = "Парсер"
- changes = "
- - 20130103, pk, автогенерация заголовка
- - 20130103, pk, реализовал парсинг структур пока без вложенности и обработки ошибок
- - 20130109, pk, переделал сканер с учётом экранирования символов и вайтспейсов
- -20130112, pk, поправил баг с неочищаемым буфером
- - 20120117, pk, добавил дополнительный разбор на целые и вещественные числа в StdTarget, добавил пошаговый парсинг для источников, пополняющихся постепенно
- - 20130718, pk, убрал зависимость от ypk
- - 20140109, pk, пофиксил парсинг спецсимволов
- - 20140117, pk, исправил обработку пустых строковых полей
- - 20140301, pk, модифицировал типы переменных в Target, ведь Holder теперь скрыт
- - 20150209, pk, портирование для fw
- "
- issues = ""
- **)
-
- IMPORT
- Out, Str;
-
- CONST
- eot* = 1;
- err* = 2;
- continue* = 0;
-
- none = 0;
- objBegin = 1;
- objEnd = 2;
- arrBegin = 3;
- arrEnd = 4;
- valueSep = 5;
- nameSep = 6;
-
- obj* = 1;
- arr* = 2;
-
- TYPE
- Reader* = POINTER TO ABSTRACT RECORD END;
-
- Parser* = POINTER TO ABSTRACT RECORD END;
-
- Target* = POINTER TO ABSTRACT RECORD END;
-
- Directory* = POINTER TO ABSTRACT RECORD END;
-
- StdDir = POINTER TO RECORD (Directory) END;
-
- StdParser = POINTER TO RECORD (Parser)
- sc: Scanner;
- root: StackItem;
- t: Target;
- END;
-
- (* StdTarget = POINTER TO RECORD (Target)
- res, root, this: YSonModels.Value;
- name: ARRAY 256 OF CHAR;
- END; *)
-
- StackItem = POINTER TO RECORD
- pos, type: INTEGER;
- next: StackItem;
- END;
-
- Scanner = RECORD
- rd: Reader;
- res: INTEGER;
- this: RECORD
- inQuotes: BOOLEAN;
- buf: Buffer;
- sym: INTEGER;
- END;
- END;
-
- Buffer = RECORD
- empty: BOOLEAN;
- quoted: BOOLEAN;
- x: Str.Dyn;
- END;
-
- Char = RECORD
- x: CHAR;
- esc: BOOLEAN;
- END;
-
- VAR
- dir-, prev-, stdDir-: Directory;
- PROCEDURE (r: Reader) Read- (OUT ch: CHAR): BOOLEAN, NEW, ABSTRACT;
- PROCEDURE (r: Reader) SetPos- (x: INTEGER), NEW, ABSTRACT;
- PROCEDURE (r: Reader) Pos* (): INTEGER, NEW, ABSTRACT;
- PROCEDURE (r: Reader) ConnectTo* (source: ANYPTR), NEW, ABSTRACT;
- PROCEDURE (r: Reader) Base* (): ANYPTR, NEW, ABSTRACT;
- PROCEDURE (r: Reader) Eot*(): BOOLEAN, NEW, ABSTRACT;
-
- PROCEDURE (d: Directory) New* (rd: Reader): Parser, NEW, ABSTRACT;
-
- PROCEDURE (p: Parser) Parse*(OUT res: INTEGER): ANYPTR, NEW, ABSTRACT;
- PROCEDURE (p: Parser) SetTarget* (t: Target), NEW, EMPTY;
- PROCEDURE (p: Parser) Result*(): ANYPTR, NEW, ABSTRACT;
- PROCEDURE (p: Parser) Begin*, NEW, ABSTRACT;
- PROCEDURE (p: Parser) Step* (OUT res: INTEGER), NEW, ABSTRACT;
-
- PROCEDURE (t: Target) LevelDown* (id, type: INTEGER), NEW, ABSTRACT;
- PROCEDURE (t: Target) NextName* (IN s: ARRAY OF CHAR), NEW, ABSTRACT;
- PROCEDURE (t: Target) LevelUp*, NEW, ABSTRACT;
- PROCEDURE (t: Target) ThisValue* (IN x: ARRAY OF CHAR; quoted: BOOLEAN), NEW, ABSTRACT;
- PROCEDURE (t: Target) Result- (): ANYPTR, NEW, ABSTRACT;
-
- (* PROCEDURE (t: StdTarget) Result(): ANYPTR;
- BEGIN
- RETURN t.res
- END Result;
-
- PROCEDURE (t: StdTarget) LevelDown (id, type: INTEGER);
- VAR new, root: YSonModels.Value;
- BEGIN
- CASE type OF
- obj: new:=YSonModels.NewObject();
- |arr: new:=YSonModels.NewArray(0);
- ELSE HALT(100) END;
- ASSERT(new#NIL, 40);
- IF t.res=NIL THEN
- t.res:=new;
- t.root:=NIL;
- t.this:=new;
- ELSE
- root:=t.this;
- WITH root: YSonModels.Array DO
- root.SetLength(root.NofVal()+1);
- root.Set(root.NofVal()-1, new);
- |root: YSonModels.Object DO
- ASSERT(t.name$#'', 41);
- root.Add(t.name$, new);
- ELSE HALT(100) END;
- t.root:=t.this;
- t.this:=new;
- END;
- END LevelDown;
-
- PROCEDURE (t: StdTarget) NextName (IN s: ARRAY OF CHAR);
- BEGIN
- t.name:=s$;
- END NextName;
-
- PROCEDURE (t: StdTarget) LevelUp;
- BEGIN
- t.this:=t.root;
- IF t.this#NIL THEN
- t.root:=t.this.Owner();
- END;
- END LevelUp;
-
- PROCEDURE (t: StdTarget) ThisValue (IN x: ARRAY OF CHAR; quoted: BOOLEAN);
- CONST decimal = '.';
- VAR root: YSonModels.Value; v: YSonModels.Value; r: REAL; res: INTEGER;
- BEGIN
- root:=t.this;
- ASSERT(root#NIL, 20);
- IF quoted THEN
- v:=YSonModels.NewString(x$)
- ELSE
- IF (x$='true') THEN v:=YSonModels.NewLiteral(YSonModels.true)
- ELSIF (x$='false') THEN v:=YSonModels.NewLiteral(YSonModels.false)
- ELSIF (x$='null') THEN v:=YSonModels.NewLiteral(YSonModels.null)
- ELSIF (x$#'') THEN
- Str.StringToReal(x$, r, res);
- IF res=0 THEN
- Str.Find(x$, decimal, 0, res);
- IF res>-1 THEN
- v:=YSonModels.NewNumber(YSonModels.real, r)
- ELSE
- v:=YSonModels.NewNumber(YSonModels.int, r)
- END;
- ELSE HALT(100) END;
- ELSE HALT(100) END
- END;
- WITH root: YSonModels.Array DO
- root.SetLength(root.NofVal()+1);
- root.Set(root.NofVal()-1, v);
- |root: YSonModels.Object DO
- ASSERT(t.name$#'', 40);
- root.Add(t.name$, v);
- ELSE HALT(100) END;
- END ThisValue;
- *)
- PROCEDURE NewStackItem(pos: INTEGER): StackItem;
- VAR s: StackItem;
- BEGIN
- NEW(s);
- s.pos:=pos;
- RETURN s;
- END NewStackItem;
-
- PROCEDURE Push(root: StackItem; pos, type: INTEGER);
- VAR new: StackItem;
- BEGIN
- new:=NewStackItem(pos); new.type:=type;
- new.next:=root.next;
- root.next:=new;
- END Push;
-
- PROCEDURE Pop(root: StackItem): StackItem;
- VAR old: StackItem;
- BEGIN
- IF root.next#NIL THEN
- old:=root.next;
- root.next:=old.next;
- old.next:=NIL;
- END;
- RETURN old;
- END Pop;
-
- PROCEDURE (VAR sc: Scanner) ConnectTo (rd: Reader), NEW;
- BEGIN
- ASSERT(rd#NIL, 20); ASSERT(rd.Base()#NIL, 21); ASSERT(~rd.Eot(), 22);
- sc.rd:=rd;
- END ConnectTo;
-
- PROCEDURE (VAR sc: Scanner) Init, NEW;
- BEGIN
- sc.res:=continue;
- sc.this.inQuotes:=FALSE;
- END Init;
-
- PROCEDURE (VAR b: Buffer) Empty, NEW;
- BEGIN
- b.empty:=TRUE;
- END Empty;
-
- PROCEDURE (VAR b: Buffer) AddChar(x: CHAR), NEW;
- BEGIN
- b.empty:=FALSE;
- IF b.x=NIL THEN b.x:=Str.NewFrom('') END;
- b.x.Add(x)
- END AddChar;
-
- PROCEDURE (VAR b: Buffer) Clear, NEW;
- BEGIN
- b.empty:=FALSE;
- b.quoted:=FALSE;
- b.x:=NIL;
- END Clear;
-
- PROCEDURE (VAR b: Buffer) Len(): INTEGER, NEW;
- VAR res: INTEGER;
- BEGIN
- res:=0;
- IF b.x#NIL THEN res:=b.x.Len() END;
- RETURN res
- END Len;
-
- PROCEDURE SkipWhite (rd: Reader; OUT res: INTEGER);
- VAR x: CHAR; stop: BOOLEAN; pos: INTEGER;
- BEGIN
- stop:=FALSE;
- pos:=rd.Pos();
- WHILE~stop & rd.Read(x) DO
- CASE x OF
- 09X, 0AX, 0DX, ' ': stop:=FALSE;
- ELSE
- stop:=TRUE;
- rd.SetPos(pos);
- res:=continue
- END;
- pos:=rd.Pos();
- END;
- IF ~stop THEN res:=eot END;
- END SkipWhite;
-
- PROCEDURE ReadChar (rd: Reader; OUT ch: Char; OUT res: INTEGER);
- VAR x: CHAR; stop: BOOLEAN; u: ARRAY 6 OF CHAR; uc: INTEGER;
- BEGIN
- res:=continue; stop:=FALSE; ch.esc:=FALSE; uc:=-1;
- WHILE ~stop & rd.Read(x) DO
- IF (x='\') & ~ch.esc THEN
- ch.esc:=TRUE;
- ELSIF ch.esc & (uc<0) THEN
- CASE x OF
- '\', '"', '/' (*, 08X, 09X, 0DX, 0AX, 0CX *): ch.x:=x; stop:=TRUE;
- |'u': uc:=0;
- |'b': ch.x:=08X; stop:=TRUE;
- |'f': ch.x:=0CX; stop:=TRUE;
- |'n': ch.x:=0AX; stop:=TRUE;
- |'r': ch.x:=0DX; stop:=TRUE;
- |'t': ch.x:=09X; stop:=TRUE;
- ELSE
- res:=err; stop:=TRUE;
- END
- ELSIF ch.esc & (uc>=0) THEN
- IF uc < 4 THEN
- u[uc]:=x; INC(uc);
- ELSE
- u[4]:='H'; u[5]:=0X;
- Str.StringToInt(u$, uc, res);
- IF res=0 THEN
- ch.x:=CHR(uc);
- ELSE
- res:=err;
- END;
- stop:=TRUE;
- uc:=-1;
- END;
- ELSIF (x#'\') & ~ch.esc THEN
- ch.x:=x;
- stop:=TRUE;
- END;
- END;
- IF ~stop THEN res:=eot END;
- END ReadChar;
-
- PROCEDURE (VAR sc: Scanner) ClearBuffer, NEW;
- BEGIN
- sc.this.buf.Clear;
- END ClearBuffer;
-
- PROCEDURE (VAR sc: Scanner) Read, NEW;
- VAR c: Char;
- BEGIN
- ASSERT(sc.rd#NIL, 20);
- sc.res:=continue;
- IF ~sc.this.inQuotes THEN SkipWhite(sc.rd, sc.res) END;
- IF sc.res=continue THEN
- sc.this.sym:=none;
- ReadChar(sc.rd, c, sc.res);
- IF ~c.esc THEN
- CASE c.x OF
- '"': sc.this.inQuotes:=~sc.this.inQuotes;
- IF sc.this.inQuotes THEN sc.this.buf.Empty; sc.this.buf.quoted:=TRUE END
- ELSE
- IF ~sc.this.inQuotes THEN
- CASE c.x OF
- |'{': sc.this.sym:=objBegin;
- |'[': sc.this.sym:=arrBegin;
- |':': sc.this.sym:=nameSep;
- |',': sc.this.sym:=valueSep;
- |'}': sc.this.sym:=objEnd;
- |']': sc.this.sym:=arrEnd;
- ELSE sc.this.buf.AddChar(c.x) END;
- ELSE sc.this.buf.AddChar(c.x) END;
- END;
- ELSE
- sc.this.buf.AddChar(c.x)
- END;
- END;
- END Read;
-
- PROCEDURE (p: StdParser) Begin;
- (* VAR t: StdTarget; *)
- BEGIN
- p.root:=NewStackItem(-1);
- (* IF p.t=NIL THEN
- NEW(t); p.t:=t;
- END; *)
- ASSERT(p.t#NIL, 60);
- p.sc.Init;
- END Begin;
-
- PROCEDURE (p: StdParser) Step(OUT res: INTEGER);
- VAR type: INTEGER; i: StackItem; x: POINTER TO ARRAY OF CHAR;
-
- PROCEDURE Value;
- BEGIN
- IF p.sc.this.buf.empty THEN NEW(x, 1); x[0]:=0X;
- ELSIF p.sc.this.buf.Len()>0 THEN x:=p.sc.this.buf.x.CopyOf() END;
- IF x#NIL THEN p.t.ThisValue(x$, p.sc.this.buf.quoted) END;
- END Value;
-
- BEGIN
- ASSERT(p.sc.res=continue, 20);
- p.sc.Read;
- CASE p.sc.this.sym OF
- |objBegin:
- Push(p.root, p.sc.rd.Pos(), obj);
- p.t.LevelDown(p.sc.rd.Pos(), obj);
- |arrBegin:
- Push(p.root, p.sc.rd.Pos(), arr);
- p.t.LevelDown(p.sc.rd.Pos(), arr);
- |objEnd:
- Value;
- p.t.LevelUp;
- p.sc.ClearBuffer;
- |arrEnd:
- Value;
- p.t.LevelUp;
- p.sc.ClearBuffer;
- |nameSep:
- IF p.sc.this.buf.Len()>0 THEN
- x:=p.sc.this.buf.x.CopyOf();
- p.t.NextName(x$)
- END;
- p.sc.ClearBuffer;
- |valueSep:
- Value;
- p.sc.ClearBuffer;
- ELSE END;
- res:=p.sc.res;
- END Step;
-
- PROCEDURE (p: StdParser) Parse(OUT res: INTEGER): ANYPTR;
- VAR x: ANYPTR;
- BEGIN
- p.Begin;
- res:=p.sc.res;
- WHILE ~(res IN {err, eot}) DO
- p.Step(res);
- END;
- IF res=eot THEN
- x:=p.Result();
- END;
- RETURN x;
- END Parse;
-
- PROCEDURE (p: StdParser) Result(): ANYPTR;
- VAR x: ANYPTR;
- BEGIN
- IF p.t#NIL THEN x:=p.t.Result() END;
- RETURN x
- END Result;
-
- PROCEDURE (p: StdParser) SetTarget(t: Target);
- BEGIN
- ASSERT(t#NIL, 20);
- p.t:=t;
- END SetTarget;
-
- PROCEDURE (d: StdDir) New (rd: Reader): Parser;
- VAR p: StdParser;
- BEGIN
- ASSERT(rd#NIL, 20); ASSERT(rd.Base()#NIL, 21);
- NEW(p);
- p.sc.ConnectTo(rd);
- RETURN p;
- END New;
-
- PROCEDURE Install* (d: Directory);
- BEGIN
- ASSERT(d#NIL, 20);
- prev:=dir;
- dir:=d;
- END Install;
-
- PROCEDURE Init;
- VAR d: StdDir;
- BEGIN
- NEW(d); Install(d);
- stdDir:=d;
- END Init;
-
- BEGIN
- Init;
- END JSonParser.
|