tools added

This commit is contained in:
red-prig 2021-12-12 22:38:04 +03:00
parent d587f2dc78
commit e41f788e2f
9 changed files with 19706 additions and 3 deletions

6
.gitignore vendored
View File

@ -14,6 +14,6 @@
link.res
lib/
backup/
shader_dump/
spirv/
savedata/
shader_dump/*
spirv/*
savedata/*

File diff suppressed because it is too large Load Diff

1805
tools/spirv/spirv.json Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8"?>
<CONFIG>
<ProjectOptions>
<Version Value="11"/>
<PathDelim Value="\"/>
<General>
<Flags>
<SaveClosedFiles Value="False"/>
<MainUnitHasCreateFormStatements Value="False"/>
<MainUnitHasTitleStatement Value="False"/>
<MainUnitHasScaledStatement Value="False"/>
<SaveJumpHistory Value="False"/>
</Flags>
<SessionStorage Value="InProjectDir"/>
<MainUnit Value="0"/>
<Title Value="spirv_helper"/>
<UseAppBundle Value="False"/>
<ResourceType Value="res"/>
</General>
<BuildModes Count="1">
<Item1 Name="Default" Default="True"/>
</BuildModes>
<PublishOptions>
<Version Value="2"/>
<UseFileFilters Value="True"/>
</PublishOptions>
<RunParams>
<FormatVersion Value="2"/>
<Modes Count="0"/>
</RunParams>
<Units Count="1">
<Unit0>
<Filename Value="spirv_helper.lpr"/>
<IsPartOfProject Value="True"/>
</Unit0>
</Units>
</ProjectOptions>
<CompilerOptions>
<Version Value="11"/>
<PathDelim Value="\"/>
<Target>
<Filename Value="spirv_helper"/>
</Target>
<SearchPaths>
<IncludeFiles Value="$(ProjOutDir)"/>
<OtherUnitFiles Value="xpath"/>
<UnitOutputDirectory Value="lib\$(TargetCPU)-$(TargetOS)"/>
</SearchPaths>
</CompilerOptions>
<Debugging>
<Exceptions Count="3">
<Item1>
<Name Value="EAbort"/>
</Item1>
<Item2>
<Name Value="ECodetoolError"/>
</Item2>
<Item3>
<Name Value="EFOpenError"/>
</Item3>
</Exceptions>
</Debugging>
</CONFIG>

View File

@ -0,0 +1,461 @@
{$mode objfpc}{$H+}
Uses
classes,
Sysutils,
gmap,
//spirv,
UJson;
type
TRawStrCompare=class
class function c(var a,b:RawByteString):boolean; static;
end;
TMapStr=specialize TMap<RawByteString,RawByteString,TRawStrCompare>;
TMapGroup=class(TMapStr)
_type:RawByteString;
end;
TOpInfo=packed record
op_min:Word;
op_max:Word;
result:Boolean;
rstype:Boolean;
align:Word;
end;
TOpInfoSet=specialize TMap<RawByteString,TOpInfo,TRawStrCompare>;
function ReCompareText(const S1,S2:RawByteString):sizeint;
var
i,count1,count2: sizeint;
Chr1, Chr2: byte;
P1, P2: PChar;
begin
Count1 := Length(S1);
Count2 := Length(S2);
if (Count1<>Count2) then Exit(Count1-Count2);
if (Count1>0) then
begin
i := 0;
P1 := @S1[1];
P2 := @S2[1];
while (i<Count1) do
begin
Chr1 := byte(p1[i]);
Chr2 := byte(p2[i]);
if (Chr1<>Chr2) then
begin
Exit(Chr1-Chr2);
end;
Inc(I);
end;
end;
end;
class function TRawStrCompare.c(var a,b:RawByteString):boolean;
begin
Result:=ReCompareText(a,b)<0;
end;
var
Comment:RawByteString;
LConstMeta:TStringList;
LEnums:TStringList;
OpInfoSet:TOpInfoSet;
function _getComment(sComment:Tjson):RawByteString;
var
i1,s1,i2,s2:Integer;
r:RawByteString;
tmp:Tjson;
begin
Result:='';
s1:=sComment.Count;
if (s1<>0) then
For i1:=0 to s1-1 do
begin
tmp:=sComment.Item[i1];
s2:=tmp.Count;
if (s2<>0) then
For i2:=0 to s2-1 do
begin
r:=tmp.Item[i2].AsStr;
r:=Trim(r);
if (r<>'') then r:=' '+r;
Result:=Result+r+#13#10;
end;
if (i1<>s1-1) then
Result:=Result+#13#10;
end;
if (Result<>'') then Result:='{'#13#10+Result+'}'#13#10;
end;
Function GetPasLabel(_name:RawByteString):RawByteString;
begin
Case _name of
'Function',
'Generic',
'Private',
'Repeat',
'Const',
'Inline',
'Export':Result:=_name+'_';
else
Result:=_name;
end;
end;
function _getGroup(sValues:Tjson;const _type:RawByteString):TMapGroup;
Var
i,s:Integer;
val:QWORD;
_name,_value,tmp:RawByteString;
begin
Result:=TMapGroup.Create;
s:=sValues.Count;
if (s<>0) then
For i:=0 to s-1 do
begin
_name:=sValues.Name[i];
_value:=sValues.Item[i].AsStr;
if (_type='Bit') then
if TryStrToQWord(_value,val) then
begin
_value:=IntToStr(1 shl val);
end;
if Result.TryGetValue(_value,tmp) then
begin
if (Length(_name)<Length(tmp)) then
if (copy(tmp,Length(tmp)-2,3)<>'KHR') then
begin
Result.Delete(_value);
Result.Insert(_value,_name);
end;
end else
begin
Result.Insert(_value,_name);
end;
end;
if (_type='Bit') then
Result.Insert('0','None');
end;
procedure LoadOp(LGroup:TMapGroup);
var
IG:TMapGroup.TIterator;
begin
OpInfoSet:=TOpInfoSet.Create;
IG:=LGroup.Min;
if Assigned(IG) then
repeat
OpInfoSet.Insert(IG.Value,Default(TOpInfo));
until (not IG.Next);
FreeAndNil(IG);
end;
procedure loadSpirvJson(Const fname:RawByteString);
Var
J,meta,enum,tmp:Tjson;
i,s:Integer;
_name,_type:RawByteString;
LGroup:TMapGroup;
begin
J:=Tjson.NewFromFile(fname);
Comment:=_getComment(J.Path['spv.meta.Comment']);
LConstMeta:=TStringList.Create;
meta:=J.Path['spv.meta'];
s:=meta.Count;
if (s<>0) then
For i:=0 to s-1 do
if (meta.Name[i]<>'Comment') then
begin
LConstMeta.Add(meta.Name[i]+' = '+meta.Item[i].AsStr);
end;
LEnums:=TStringList.Create;
enum:=J.Path['spv.enum'];
s:=enum.Count;
if (s<>0) then
For i:=0 to s-1 do
begin
tmp:=enum.Item[i];
_name:=tmp.Path['Name'].AsStr;
_type:=tmp.Path['Type'].AsStr;
LGroup:=_getGroup(tmp.Path['Values'],_type);
LGroup._type:=_type;
LEnums.AddObject(_name,LGroup);
if (_name='Op') then
LoadOp(LGroup);
end;
J.Free;
end;
function _get_OpInfo(oper:Tjson):TOpInfo;
Var
q,k:RawByteString;
tmp:Tjson;
i,s:Integer;
begin
Result:=Default(TOpInfo);
s:=oper.Count;
if (s<>0) then
For i:=0 to s-1 do
begin
tmp:=oper.Item[i];
k:=tmp.Path['kind'].AsStr;
case k of
'IdResultType':Result.rstype:=True;
'IdResult' :Result.result:=True;
end;
q:=tmp.Path['quantifier'].AsStr;
case q of
'*':Result.op_max:=$FFFF;
'?':if (Result.op_max<>$FFFF) then
begin
Result.op_max:=Result.op_max+1;
end;
else
begin
Result.op_min:=Result.op_min+1;
if (Result.op_max<>$FFFF) then
begin
Result.op_max:=Result.op_max+1;
end;
end;
end;
end;
end;
procedure loadSpirvGrammarJson(Const fname:RawByteString);
Var
J,inst,tmp:Tjson;
i,s:Integer;
opname:RawByteString;
OpInfo:TOpInfo;
IT:TOpInfoSet.TIterator;
begin
J:=Tjson.NewFromFile(fname);
inst:=J.Path['instructions'];
s:=inst.Count;
if (s<>0) then
For i:=0 to s-1 do
begin
tmp:=inst.Item[i];
opname:=tmp.Path['opname'].AsStr;
OpInfo:=_get_OpInfo(tmp.Path['operands']);
IT:=OpInfoSet.Find(opname);
if Assigned(IT) then
begin
IT.Value:=OpInfo;
FreeAndNil(IT);
end;
end;
J.Free;
end;
Function IsJson(Const FName:RawByteString):Boolean;
begin
Result:=False;
Case UpperCase(ExtractFileExt(FName)) of
'.JSON':Result:=True;
end;
end;
Const
prologf='unit spirv;'#$0D#$0A#$0D#$0A+
'{$mode objfpc}{$H+}'#$0D#$0A#$0D#$0A+
'{$WARNINGS OFF}'#$0D#$0A#$0D#$0A+
'interface'#$0D#$0A#$0D#$0A;
ep_impl='implementation'#$0D#$0A#$0D#$0A;
ep_func='end.'#$0D#$0A;
NL=#$0D#$0A;
NLNL=#$0D#$0A#$0D#$0A;
LConst='Const'#$0D#$0A;
LType ='Type'#$0D#$0A;
LEnd =' end;';
LGetStr_i=' function GetStr(w:Word):RawByteString; static;'#$0D#$0A;
LGetInfo_i=' type'#$0D#$0A+
' TOpInfo=packed record'#$0D#$0A+
' op_min:Word;'#$0D#$0A+
' op_max:Word;'#$0D#$0A+
' result:Boolean;'#$0D#$0A+
' rstype:Boolean;'#$0D#$0A+
' align:Word;'#$0D#$0A+
' end;'#$0D#$0A+
' function GetInfo(w:Word):TOpInfo; static;'#$0D#$0A;
LGetInfo_p='function Op.GetInfo(w:Word):TOpInfo; static;'#$0D#$0A+
'begin'#$0D#$0A+
' Result:=Default(TOpInfo);'#$0D#$0A+
' Case w of'#$0D#$0A;
LFunc='function ';
LGetStr_p='.GetStr(w:Word):RawByteString;'#$0D#$0A+
'begin'#$0D#$0A+
' Result:=''???'';'#$0D#$0A+
' Case w of'#$0D#$0A;
LGetStr_e=' end;'#$0D#$0A+
'end;'#$0D#$0A#$0D#$0A;
Procedure SaveToPas(Const FName:RawByteString);
Var
F:Thandle;
i,s:Integer;
_name:RawByteString;
LGroup:TMapGroup;
IG:TMapGroup.TIterator;
IT:TOpInfoSet.TIterator;
begin
F:=FileCreate(FName);
FileWrite(F,PChar(Comment)^,Length(Comment));
FileWrite(F,PChar(prologf)^,Length(prologf));
FileWrite(F,PChar(LConst)^,Length(LConst));
s:=LConstMeta.Count;
if (s<>0) then
begin
For i:=0 to s-1 do
begin
_name:=' '+LConstMeta.Strings[i]+';'+NL;
FileWrite(F,PChar(_name)^,Length(_name));
end;
FileWrite(F,PChar(NL)^,Length(NL));
end;
FileWrite(F,PChar(LType)^,Length(LType));
s:=LEnums.Count;
if (s<>0) then
begin
For i:=0 to s-1 do
begin
LGroup:=TMapGroup(LEnums.Objects[i]);
_name:=LEnums.Strings[i];
_name:=' '+_name+'=object'+' //'+LGroup._type+NL+' '+LConst;
FileWrite(F,PChar(_name)^,Length(_name));
IG:=LGroup.Min;
if Assigned(IG) then
repeat
_name:=' '+GetPasLabel(IG.Value)+' = '+IG.Key+';'+NL;
FileWrite(F,PChar(_name)^,Length(_name));
until (not IG.Next);
FreeAndNil(IG);
if (LGroup._type='Value') then
FileWrite(F,PChar(LGetStr_i)^,Length(LGetStr_i));
if (LEnums.Strings[i]='Op') then
FileWrite(F,PChar(LGetInfo_i)^,Length(LGetInfo_i));
FileWrite(F,PChar(LEnd)^,Length(LEnd));
FileWrite(F,PChar(NLNL)^,Length(NLNL));
end;
end;
FileWrite(F,PChar(ep_impl)^,Length(ep_impl));
s:=LEnums.Count;
if (s<>0) then
begin
For i:=0 to s-1 do
begin
LGroup:=TMapGroup(LEnums.Objects[i]);
if (LGroup._type<>'Value') then Continue;
_name:=LEnums.Strings[i];
_name:=LFunc+_name+LGetStr_p;
FileWrite(F,PChar(_name)^,Length(_name));
IG:=LGroup.Min;
if Assigned(IG) then
repeat
_name:=' '+GetPasLabel(IG.Value)+':Result:='''+IG.Value+''';'+NL;
FileWrite(F,PChar(_name)^,Length(_name));
until (not IG.Next);
FreeAndNil(IG);
FileWrite(F,PChar(LGetStr_e)^,Length(LGetStr_e));
if (LEnums.Strings[i]='Op') then
begin
FileWrite(F,PChar(LGetInfo_p)^,Length(LGetInfo_p));
IG:=LGroup.Min;
if Assigned(IG) then
repeat
IT:=OpInfoSet.Find(IG.Value);
if Assigned(IT) then
begin
_name:=' '+GetPasLabel(IG.Value)+':QWORD(Result):=$'+HexStr(QWORD(IT.Value),16)+';'+NL;
FileWrite(F,PChar(_name)^,Length(_name));
FreeAndNil(IT);
end;
until (not IG.Next);
FreeAndNil(IG);
FileWrite(F,PChar(LGetStr_e)^,Length(LGetStr_e));
end;
end;
end;
FileWrite(F,PChar(ep_func)^,Length(ep_func));
FileClose(F);
end;
begin
DefaultSystemCodePage:=CP_UTF8;
DefaultUnicodeCodePage:=CP_UTF8;
DefaultFileSystemCodePage:=CP_UTF8;
DefaultRTLFileSystemCodePage:=CP_UTF8;
UTF8CompareLocale:=CP_UTF8;
loadSpirvJson('spirv.json');
loadSpirvGrammarJson('spirv.core.grammar.json');
Writeln('Load is Fin');
SaveToPas('spirv.pas');
Writeln('Save is Fin');
readln;
end.

2407
tools/spirv/xpath/UJson.pas Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,368 @@
{
This file is part of the Free Component Library
JSON Data structures
Copyright (c) 2007 by Michael Van Canneyt michael@freepascal.org
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
//modifed by Red_prig
{$mode objfpc}
{$h+}
unit Ufpjson;
interface
uses
{$ifdef fpc}
variants,
{$endif}
{$ifdef pas2js}
JS, RTLConsts, Types,
{$endif}
SysUtils,
classes;
type
TJSONtype = (jtUnknown, jtNumber, jtString, jtBoolean, jtNull, jtArray, jtObject);
TJSONInstanceType = (
jitUnknown,
jitNumberInteger,
{$ifdef fpc}
jitNumberInt64,
jitNumberQWord,
{$endif}
jitNumberFloat,
jitString,
jitBoolean,
jitNull,
jitArray,
jitObject);
TJSONFloat = Double;
TJSONStringType = {$ifdef fpc}UTF8String{$else}string{$endif};
TJSONUnicodeStringType = Unicodestring;
{$ifdef fpc}
TJSONCharType = AnsiChar;
PJSONCharType = ^TJSONCharType;
TJSONVariant = variant;
TFPJSStream = TMemoryStream;
{$else}
TJSONCharType = char;
TJSONVariant = jsvalue;
TFPJSStream = TJSArray;
{$endif}
TFormatOption = (foSingleLineArray, // Array without CR/LF : all on one line
foSingleLineObject, // Object without CR/LF : all on one line
foDoNotQuoteMembers, // Do not quote object member names.
foUseTabchar, // Use tab characters instead of spaces.
foSkipWhiteSpace, // Do not use whitespace at all
foSkipWhiteSpaceOnlyLeading // When foSkipWhiteSpace is active, skip whitespace for object members only before :
);
TFormatOptions = set of TFormatOption;
Const
DefaultIndentSize = 2;
DefaultFormat = [];
AsJSONFormat = [foSingleLineArray,foSingleLineObject]; // These options make FormatJSON behave as AsJSON
AsCompressedJSON = [foSingleLineArray,foSingleLineObject,foskipWhiteSpace]; // These options make FormatJSON behave as AsJSON with TJSONData.CompressedJSON=True
AsCompactJSON = [foSingleLineArray,foSingleLineObject,foskipWhiteSpace,foDoNotQuoteMembers]; // These options make FormatJSON behave as AsJSON with TJSONData.CompressedJSON=True and TJSONObject.UnquotedMemberNames=True
ValueJSONTypes = [jtNumber, jtString, jtBoolean, jtNull];
ActualValueJSONTypes = ValueJSONTypes - [jtNull];
StructuredJSONTypes = [jtArray,jtObject];
Type
TJSONNumberType = (
ntFloat,
ntInteger
{$ifdef fpc}
,ntInt64
,ntQWord
{$endif}
);
type
TAddStr=object
FStr:PChar;
FLen:SizeInt;
Procedure AddStr(Const S:RawByteString);
Procedure AddChar(C:AnsiChar);
Procedure Reset; inline;
Procedure Free; inline;
function GetStr:RawByteString; inline;
end;
TUtf8AddStr=object(TAddStr)
FSP:SizeUInt;
Procedure AddChar(C:AnsiChar); inline;
Procedure AddWideChar(C:WideChar);
Procedure Reset; inline;
end;
Function StringToJSONString(const S : TJSONStringType;Strict:Boolean=False):TJSONStringType; inline;
procedure _StringToJSONString(Var FAddStr:TAddStr;const S:TJSONStringType;Strict:Boolean=False); inline;
procedure __StringToJSONString(Var FAddStr:TAddStr;P:PJSONCharType;Len:SizeInt;Strict:Boolean=False);
Function JSONStringToString(const S :TJSONStringType):TJSONStringType; inline;
procedure _JSONStringToString(Var FAddStr:TUtf8AddStr;const S:TJSONStringType); inline;
procedure __JSONStringToString(Var FAddStr:TUtf8AddStr;P:PJSONCharType;Len:SizeInt);
Function JSONTypeName(JSONType:TJSONType):String;
implementation
Uses typinfo;
Procedure TAddStr.AddStr(Const S:RawByteString);
Var
i:SizeInt;
begin
if Length(S)>0 then
For i:=1 to Length(S) do
AddChar(S[i]);
end;
Procedure TAddStr.AddChar(C:AnsiChar);
Var
i,MemLen:SizeInt;
begin
if (FStr=nil) then
begin
MemLen:=0
end else
begin
MemLen:=MemSize(FStr);
end;
i:=FLen;
FLen:=FLen+1;
if (MemLen<FLen) then
begin
Case FLen of
0..SizeOf(Pointer)*4:
FStr:=ReAllocMem(FStr,SizeOf(Pointer)*4);
else
begin
FStr:=ReAllocMem(FStr,i+(i div 2));
end;
end;
end;
FStr[i]:=C;
end;
Procedure TUtf8AddStr.AddChar(C:AnsiChar); inline;
begin
FSP:=0;
inherited;
end;
Procedure TAddStr.Reset; inline;
begin
FLen:=0;
end;
Procedure TAddStr.Free; inline;
begin
FreeMem(FStr);
end;
function TAddStr.GetStr:RawByteString; inline;
begin
SetLength(Result,FLen);
Move(FStr^,Result[1],FLen);
end;
Procedure TUtf8AddStr.AddWideChar(C:WideChar);
Var
lw:longword;
begin
lw:=Ord(C);
if FSP<>0 then
begin
case lw of
$dc00..$dfff:
{High Surrogates 2}
begin
{ $d7c0 is ($d800 - ($10000 shr 10)) }
lw:=(longword(FSP-$d7c0) shl 10) + (lw xor $dc00);
inherited AddChar(AnsiChar($f0 or (lw shr 18)));
inherited AddChar(AnsiChar($80 or ((lw shr 12) and $3f)));
inherited AddChar(AnsiChar($80 or ((lw shr 6) and $3f)));
inherited AddChar(AnsiChar($80 or (lw and $3f)));
end;
end;
FSP:=0;
end else
begin
case lw of
0..$7f:
begin
inherited AddChar(AnsiChar(lw));
end;
$80..$7ff:
begin
inherited AddChar(AnsiChar($c0 or (lw shr 6)));
inherited AddChar(AnsiChar($80 or (lw and $3f)));
end;
$800..$d7ff,$e000..$ffff:
begin
inherited AddChar(AnsiChar($e0 or (lw shr 12)));
inherited AddChar(AnsiChar($80 or ((lw shr 6) and $3f)));
inherited AddChar(AnsiChar($80 or (lw and $3f)));
end;
$d800..$dbff:
{High Surrogates 1}
begin
FSP:=lw;
end;
end;
end;
end;
Procedure TUtf8AddStr.Reset; inline;
begin
inherited;
FSP:=0;
end;
function StringToJSONString(const S:TJSONStringType;Strict:Boolean=False):TJSONStringType; inline;
Var
FAddStr:TAddStr;
begin
FAddStr:=Default(TAddStr);
_StringToJSONString(FAddStr,S,Strict);
Result:=FAddStr.GetStr;
FAddStr.Free;
end;
procedure _StringToJSONString(Var FAddStr:TAddStr;const S:TJSONStringType;Strict:Boolean=False); inline;
begin
__StringToJSONString(FAddStr,PJSONCharType(S),Length(S),Strict);
end;
procedure __StringToJSONString(Var FAddStr:TAddStr;P:PJSONCharType;Len:SizeInt;Strict:Boolean=False);
Var
I:SizeInt;
C,T:AnsiChar;
begin
I:=0;
if Strict then T:='/' else T:=#0;
While (I<Len) do
begin
C:=AnsiChar(P^);
if (C in ['"',T,'\',#0..#31]) then
begin
FAddStr.AddChar('\');
Case C of
'\',
'/',
'"' : FAddStr.AddChar(C);
#8 : FAddStr.AddChar('b');
#9 : FAddStr.AddChar('t');
#10 : FAddStr.AddChar('n');
#12 : FAddStr.AddChar('f');
#13 : FAddStr.AddChar('r');
else
begin
FAddStr.AddChar('u');
FAddStr.AddStr(HexStr(Ord(C),4));
end;
end;
end else
begin
FAddStr.AddChar(c);
end;
Inc(I);
Inc(P);
end;
end;
Function JSONStringToString(const S:TJSONStringType):TJSONStringType; inline;
Var
FAddStr:TUtf8AddStr;
begin
FAddStr:=Default(TUtf8AddStr);
_JSONStringToString(FAddStr,S);
Result:=FAddStr.GetStr;
FAddStr.Free;
end;
procedure _JSONStringToString(Var FAddStr:TUtf8AddStr;const S:TJSONStringType); inline;
begin
__JSONStringToString(FAddStr,PJSONCharType(S),Length(S));
end;
procedure __JSONStringToString(Var FAddStr:TUtf8AddStr;P:PJSONCharType;Len:SizeInt);
Const
DifLo=Byte('a')-$A;
DifHi=Byte('A')-$A;
Var
I,State:SizeInt;
w:Word;
begin
State:=0;
I:=0;
While (I<Len) do
begin
Case State of
0:begin
if (P^='\') then
begin
State:=1;
end else
begin
FAddStr.AddChar(P^);
end;
end;
1:begin
Case P^ of
'b':FAddStr.AddChar(#8);
't':FAddStr.AddChar(#9);
'n':FAddStr.AddChar(#10);
'f':FAddStr.AddChar(#12);
'r':FAddStr.AddChar(#13);
'u':begin
State:=2;
w:=0;
Inc(I);
Inc(P);
Continue;
end;
else
FAddStr.AddChar(P^);
end;
State:=0;
end;
2..5:
begin
Case P^ of
'0'..'9':w:=(w shl 4) or (PByte(P)^ and $F);
'a'..'f':w:=(w shl 4) or (PByte(P)^-DifLo);
'A'..'F':w:=(w shl 4) or (PByte(P)^-DifHi);
else w:=(w shl 4);
end;
Inc(State);
if (State=6) then
begin
FAddStr.AddWideChar(WideChar(W));
State:=0;
end;
end;
end;
Inc(I);
Inc(P);
end;
end;
function JSONTypeName(JSONType: TJSONType): String;
begin
Result:=GetEnumName(TypeInfo(TJSONType),Ord(JSONType));
end;
end.

View File

@ -0,0 +1,348 @@
{
This file is part of the Free Component Library
JSON SAX-like Reader
Copyright (c) 2007 by Michael Van Canneyt michael@freepascal.org
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
//modifed by Red_prig
{$mode objfpc}
{$h+}
unit Ujsonreader;
interface
uses
Classes, SysUtils, UfpJSON, Ujsonscanner;
Type
{ TBaseJSONReader }
TBaseJSONReader = Class(TObject)
Private
FScanner : TJSONScanner;
function GetO(AIndex: TJSONOption): Boolean;
function GetOptions: TJSONOptions; inline;
procedure SetO(AIndex: TJSONOption; AValue: Boolean);
procedure SetOptions(AValue: TJSONOptions);
Protected
procedure DoError(const Msg: String);
Procedure DoParse(AtCurrent,AllowEOF: Boolean);
function GetNextToken: TJSONToken;
function CurrentTokenString: RawByteString;
function CurrentToken: TJSONToken; inline;
Procedure KeyValue(Const AKey : TJSONStringType); virtual; abstract;
Procedure StringValue(Const AValue : TJSONStringType);virtual; abstract;
Procedure NullValue; virtual; abstract;
Procedure FloatValue(Const AValue : Double); virtual; abstract;
Procedure BooleanValue(Const AValue : Boolean); virtual; abstract;
Procedure NumberValue(Const AValue : TJSONStringType); virtual; abstract;
Procedure IntegerValue(Const AValue : integer); virtual; abstract;
Procedure Int64Value(Const AValue : int64); virtual; abstract;
Procedure QWordValue(Const AValue : QWord); virtual; abstract;
Procedure StartArray; virtual; abstract;
Procedure StartObject; virtual; abstract;
Procedure EndArray; virtual; abstract;
Procedure EndObject; virtual; abstract;
Procedure ParseArray;
Procedure ParseObject;
Procedure ParseNumber;
Procedure DoExecute;
Property Scanner : TJSONScanner read FScanner;
Public
Constructor Create(Source : TStream; AUseUTF8 : Boolean = True); overload;deprecated 'use options form instead';
Constructor Create(Source : TJSONStringType; AUseUTF8 : Boolean = True); overload;deprecated 'use options form instead';
constructor Create(Source: TStream; AOptions: TJSONOptions); overload;
constructor Create(const Source: RawByteString; AOptions: TJSONOptions); overload;
destructor Destroy();override;
// Parsing options
Property Options : TJSONOptions Read GetOptions Write SetOptions;
end;
EJSONParser = Class(EParserError);
implementation
Resourcestring
SErrUnexpectedEOF = 'Unexpected EOF encountered.';
SErrUnexpectedToken = 'Unexpected token (%s) encountered.';
SErrExpectedColon = 'Expected colon (:), got token "%s".';
//SErrEmptyElement = 'Empty element encountered.';
SErrExpectedElementName = 'Expected element name, got token "%s"';
SExpectedCommaorBraceClose = 'Expected , or ], got token "%s".';
SErrInvalidNumber = 'Number is not an integer or real number: %s';
SErrNoScanner = 'No scanner. No source specified ?';
{ TBaseJSONReader }
Procedure TBaseJSONReader.DoExecute;
begin
if (FScanner=Nil) then
DoError(SErrNoScanner);
DoParse(False,True);
end;
{
Consume next token and convert to JSON data structure.
If AtCurrent is true, the current token is used. If false,
a token is gotten from the scanner.
If AllowEOF is false, encountering a tkEOF will result in an exception.
}
function TBaseJSONReader.CurrentToken: TJSONToken;
begin
Result:=FScanner.CurToken;
end;
function TBaseJSONReader.CurrentTokenString: RawByteString;
begin
If CurrentToken in [tkString,tkIdentifier,tkNumber,tkComment] then
Result:=FScanner.CurTokenString
else
Result:=TokenInfos[CurrentToken];
end;
procedure TBaseJSONReader.DoParse(AtCurrent, AllowEOF: Boolean);
var
T : TJSONToken;
begin
If not AtCurrent then
T:=GetNextToken
else
T:=FScanner.CurToken;
Case T of
tkEof : If Not AllowEof then
DoError(SErrUnexpectedEOF);
tkNull : NullValue;
tkTrue,
tkFalse : BooleanValue(t=tkTrue);
tkString : if (joUTF8 in Options) and (DefaultSystemCodePage<>CP_UTF8) then
StringValue(TJSONStringType(UTF8Decode(CurrentTokenString)))
else
StringValue(CurrentTokenString);
tkCurlyBraceOpen :
ParseObject;
tkCurlyBraceClose :
DoError(SErrUnexpectedToken);
tkSQuaredBraceOpen :
ParseArray;
tkSQuaredBraceClose :
DoError(SErrUnexpectedToken);
tkNumber :
ParseNumber;
tkComma :
DoError(SErrUnexpectedToken);
tkIdentifier :
DoError(SErrUnexpectedToken);
end;
end;
// Creates the correct JSON number type, based on the current token.
procedure TBaseJSONReader.ParseNumber;
Var
I : Integer;
I64 : Int64;
QW : QWord;
F : TJSONFloat;
S : String;
begin
S:=CurrentTokenString;
NumberValue(S);
I:=0;
if TryStrToQWord(S,QW) then
begin
if QW>qword(high(Int64)) then
QWordValue(QW)
else
if QW>MaxInt then
begin
I64 := QW;
Int64Value(I64);
end
else
begin
I:=QW;
IntegerValue(I);
end
end
else
begin
If TryStrToInt64(S,I64) then
if (I64>Maxint) or (I64<-MaxInt) then
Int64Value(I64)
Else
begin
I:=I64;
IntegerValue(I);
end
else
begin
I:=0;
Val(S,F,I);
If (I<>0) then
DoError(SErrInvalidNumber);
FloatValue(F);
end;
end;
end;
function TBaseJSONReader.GetO(AIndex: TJSONOption): Boolean;
begin
Result:=AIndex in Options;
end;
function TBaseJSONReader.GetOptions: TJSONOptions;
begin
Result:=FScanner.Options
end;
procedure TBaseJSONReader.SetO(AIndex: TJSONOption; AValue: Boolean);
begin
if aValue then
FScanner.Options:=FScanner.Options+[AINdex]
else
FScanner.Options:=FScanner.Options-[AINdex]
end;
procedure TBaseJSONReader.SetOptions(AValue: TJSONOptions);
begin
FScanner.Options:=AValue;
end;
// Current token is {, on exit current token is }
Procedure TBaseJSONReader.ParseObject;
Var
T : TJSONtoken;
LastComma : Boolean;
begin
LastComma:=False;
StartObject;
T:=GetNextToken;
While T<>tkCurlyBraceClose do
begin
If (T<>tkString) and (T<>tkIdentifier) then
DoError(SErrExpectedElementName);
KeyValue(CurrentTokenString);
T:=GetNextToken;
If (T<>tkColon) then
DoError(SErrExpectedColon);
DoParse(False,False);
T:=GetNextToken;
If Not (T in [tkComma,tkCurlyBraceClose]) then
DoError(SExpectedCommaorBraceClose);
If T=tkComma then
begin
T:=GetNextToken;
LastComma:=(t=tkCurlyBraceClose);
end;
end;
If LastComma and ((joStrict in Options) or not (joIgnoreTrailingComma in Options)) then // Test for ,} case
DoError(SErrUnExpectedToken);
EndObject;
end;
// Current token is [, on exit current token is ]
Procedure TBaseJSONReader.ParseArray;
Var
T : TJSONtoken;
LastComma : Boolean;
S : TJSONOPTions;
begin
StartArray;
LastComma:=False;
Repeat
T:=GetNextToken;
If (T<>tkSquaredBraceClose) then
begin
DoParse(True,False);
T:=GetNextToken;
If Not (T in [tkComma,tkSquaredBraceClose]) then
DoError(SExpectedCommaorBraceClose);
LastComma:=(t=TkComma);
end;
Until (T=tkSquaredBraceClose);
S:=Options;
If LastComma and ((joStrict in S) or not (joIgnoreTrailingComma in S)) then // Test for ,] case
DoError(SErrUnExpectedToken);
EndArray;
end;
// Get next token, discarding whitespace
function TBaseJSONReader.GetNextToken: TJSONToken;
begin
Repeat
Result:=FScanner.FetchToken;
Until (Not (Result in [tkComment,tkWhiteSpace]));
end;
procedure TBaseJSONReader.DoError(const Msg: String);
Var
S : String;
begin
S:=Format(Msg,[CurrentTokenString]);
S:=Format('Error at line %d, Pos %d:',[FScanner.CurRow,FSCanner.CurColumn])+S;
Raise EJSONParser.Create(S);
end;
constructor TBaseJSONReader.Create(Source: TStream; AUseUTF8 : Boolean = True);
begin
Inherited Create;
FScanner:=TJSONScanner.Create(Source,[joUTF8]);
if AUseUTF8 then
Options:=Options + [joUTF8];
end;
constructor TBaseJSONReader.Create(Source: TJSONStringType; AUseUTF8 : Boolean = True);
begin
Inherited Create;
FScanner:=TJSONScanner.Create(Source,[joUTF8]);
if AUseUTF8 then
Options:=Options + [joUTF8];
end;
constructor TBaseJSONReader.Create(Source: TStream; AOptions: TJSONOptions);
begin
FScanner:=TJSONScanner.Create(Source,AOptions);
end;
constructor TBaseJSONReader.Create(const Source: RawByteString; AOptions: TJSONOptions);
begin
FScanner:=TJSONScanner.Create(Source,AOptions);
end;
destructor TBaseJSONReader.Destroy();
begin
FreeAndNil(FScanner);
inherited Destroy();
end;
end.

View File

@ -0,0 +1,568 @@
{
This file is part of the Free Component Library
JSON source lexical scanner
Copyright (c) 2007 by Michael Van Canneyt michael@freepascal.org
See the file COPYING.FPC, included in this distribution,
for details about the copyright.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**********************************************************************}
//modifed by Red_prig
{$mode objfpc}
{$h+}
{$ifdef fpc}
{$define UsePChar}
{$endif}
unit Ujsonscanner;
interface
uses SysUtils, Classes,bufstream,Ufpjson;
resourcestring
SErrInvalidCharacter = 'Invalid character at line %d, pos %d: ''%s''';
SUnterminatedComment = 'Unterminated comment at line %d, pos %d: ''%s''';
SErrOpenString = 'string exceeds end of line %d';
type
TJSONToken = (
tkEOF,
tkWhitespace,
tkString,
tkNumber,
tkTrue,
tkFalse,
tkNull,
// Simple (one-character) tokens
tkComma, // ','
tkColon, // ':'
tkCurlyBraceOpen, // '{'
tkCurlyBraceClose, // '}'
tkSquaredBraceOpen, // '['
tkSquaredBraceClose, // ']'
tkIdentifier, // Any Javascript identifier
tkComment,
tkUnknown
);
EScannerError = class(EParserError);
TJSONOption = (joUTF8,joStrict,joComments,joIgnoreTrailingComma);
TJSONOptions = set of TJSONOption;
Const
DefaultOptions = [joUTF8];
Type
TJSONSInFlag=Set of (ifFreeSource,ifPrevChar,ifEOF);
{ TJSONScanner }
TJSONScanner = class
private
FSource:TStream;
FInFlag:TJSONSInFlag;
FCurRow,FCurColumn: Integer;
FCurToken: TJSONToken;
FCurTokenString: RawBytestring;
FCurChar:AnsiChar;
FOptions : TJSONOptions;
FAddStr:TUtf8AddStr;
function GetO(AIndex: TJSONOption): Boolean;
procedure SetO(AIndex: TJSONOption; AValue: Boolean);
protected
procedure Error(const Msg: string);overload;
procedure Error(const Msg: string;
Const Args: array of {$ifdef pas2js}jsvalue{$else}const{$endif});overload;
function DoFetchToken: TJSONToken; inline;
public
{$ifdef fpc}
constructor Create(Source : TStream; AUseUTF8 : Boolean = True); overload; deprecated 'use options form instead';
constructor Create(const Source : RawByteString; AUseUTF8 : Boolean = True); overload; deprecated 'use options form instead';
constructor Create(Source: TStream; AOptions: TJSONOptions); overload;
{$endif}
constructor Create(const Source: RawByteString; AOptions: TJSONOptions); overload;
destructor Destroy; override;
function FetchToken: TJSONToken;
function NextChar:Boolean;
Procedure StepPrev; inline;
//property CurLine: RawBytestring read FCurLine;
property CurRow: Integer read FCurRow;
property CurColumn: Integer read FCurColumn;
property CurToken: TJSONToken read FCurToken;
property CurTokenString: RawBytestring read FCurTokenString;
// Use strict JSON: " for strings, object members are strings, not identifiers
Property Strict : Boolean Index joStrict Read GetO Write SetO ; deprecated 'use options instead';
// if set to TRUE, then strings will be converted to UTF8 ansistrings, not system codepage ansistrings.
Property UseUTF8 : Boolean index joUTF8 Read GetO Write SetO; deprecated 'Use options instead';
// Parsing options
Property Options : TJSONOptions Read FOptions Write FOptions;
end;
const
TokenInfos: array[TJSONToken] of RawBytestring = (
'EOF',
'Whitespace',
'String',
'Number',
'True',
'False',
'Null',
',',
':',
'{',
'}',
'[',
']',
'identifier',
'comment',
''
);
implementation
{$ifdef fpc}
constructor TJSONScanner.Create(Source : TStream; AUseUTF8 : Boolean = True);
Var
O : TJSONOptions;
begin
O:=DefaultOptions;
if AUseUTF8 then
Include(O,joUTF8)
else
Exclude(O,joUTF8);
Create(Source,O);
end;
constructor TJSONScanner.Create(const Source : RawByteString; AUseUTF8 : Boolean = True);
Var
O : TJSONOptions;
begin
O:=DefaultOptions;
if AUseUTF8 then
Include(O,joUTF8)
else
Exclude(O,joUTF8);
Create(Source,O);
end;
constructor TJSONScanner.Create(Source: TStream; AOptions: TJSONOptions);
begin
FAddStr:=Default(TUtf8AddStr);
if Source.InheritsFrom(THandleStream) then
begin
FSource:=TReadBufStream.Create(Source,4*1024);
FInFlag:=[ifFreeSource];
end else
begin
FSource:=Source;
end;
FOptions:=AOptions;
end;
{$endif}
constructor TJSONScanner.Create(const Source: RawByteString; AOptions: TJSONOptions);
begin
FAddStr:=Default(TUtf8AddStr);
FSource:=TStringStream.Create(Source);
FInFlag:=[ifFreeSource];
FOptions:=AOptions;
end;
destructor TJSONScanner.Destroy;
begin
FAddStr.Free;
if ifFreeSource in FInFlag then
begin
FreeAndNil(FSource);
end;
Inherited;
end;
function TJSONScanner.NextChar:Boolean;
begin
if ifEOF in FInFlag then
begin
Result:=False;
end;
if ifPrevChar in FInFlag then
begin
Result:=True;
Exclude(FInFlag,ifPrevChar);
end else
begin
Result:=FSource.Read(FCurChar,1)=1;
if Result then
begin
Inc(FCurColumn);
end else
begin
FCurToken:=tkEOF;
Include(FInFlag,ifEOF);
end;
end;
end;
Procedure TJSONScanner.StepPrev; inline;
begin
Include(FInFlag,ifPrevChar);
end;
function TJSONScanner.FetchToken: TJSONToken;
begin
Result:=DoFetchToken;
end;
procedure TJSONScanner.Error(const Msg: string);
begin
raise EScannerError.Create(Msg);
end;
procedure TJSONScanner.Error(const Msg: string;
const Args: array of {$ifdef pas2js}jsvalue{$else}const{$endif});
begin
raise EScannerError.CreateFmt(Msg, Args);
end;
function TJSONScanner.DoFetchToken: TJSONToken;
function CheckNextLine:Boolean; inline;
begin
Result:=False;
if not NextChar then Exit;
case FCurChar of
#13:begin //next line
Inc(FCurRow);
if not NextChar then Exit;
FCurColumn:=0;
case FCurChar of
#10:FCurColumn:=0;
else
StepPrev;
end;
end;
#10:begin //next line
Inc(FCurRow);
if not NextChar then Exit;
FCurColumn:=0;
case FCurChar of
#13:FCurColumn:=0;
else
StepPrev;
end;
end;
end;
Result:=True;
end;
var
it : TJSONToken;
I : Integer;
tstart,tcol, u2: Integer;
C , c2: char;
begin
if not NextChar then Exit(tkEOF);
if (FCurRow=0) then
begin
FCurRow:=1;
if (FCurColumn=1) then
begin
if (FCurChar=#$EF) then
begin
if not NextChar then Exit(tkEOF);
if (FCurChar=#$BB) then
begin
if not NextChar then Exit(tkEOF);
if (FCurChar=#$BF) then
begin
if not NextChar then Exit(tkEOF);
end;
end;
end;
end;
FCurColumn:=1;
end;
FCurTokenString := '';
case FCurChar of
#13:begin //next line
Result := tkWhitespace;
Inc(FCurRow);
if not NextChar then Exit;
FCurColumn:=0;
case FCurChar of
#10:FCurColumn:=0;
else
StepPrev;
end;
end;
#10:begin //next line
Result := tkWhitespace;
Inc(FCurRow);
if not NextChar then Exit;
FCurColumn:=0;
case FCurChar of
#13:FCurColumn:=0;
else
StepPrev;
end;
end;
#0,#9, ' ':
begin
Result := tkWhitespace;
repeat
if not CheckNextLine then Exit;
until not (FCurChar in [#0,#9, ' ']);
StepPrev;
end;
'"','''':
begin
C:=FCurChar;
If (C='''') and (joStrict in Options) then
Error(SErrInvalidCharacter, [CurRow,CurColumn,FCurChar]);
if not NextChar then Exit(tkEOF);
FAddStr.Reset;
while not (FCurChar in [#0,#13,#10,C]) do
begin
if (FCurChar='\') then
begin
if not NextChar then
begin
Error(SErrOpenString,[FCurRow]);
Exit(tkEOF);
end;
Case FCurChar of
't' : FAddStr.AddChar(#9);
'b' : FAddStr.AddChar(#8);
'n' : FAddStr.AddChar(#10);
'r' : FAddStr.AddChar(#13);
'f' : FAddStr.AddChar(#12);
'u' : begin
u2:=0;
For I:=1 to 4 do
begin
if not NextChar then Exit(tkEOF);
c2:=FCurChar;
Case c2 of
'0'..'9': u2:=u2*16+ord(c2)-ord('0');
'A'..'F': u2:=u2*16+ord(c2)-ord('A')+10;
'a'..'f': u2:=u2*16+ord(c2)-ord('a')+10;
else
Error(SErrInvalidCharacter, [CurRow,CurColumn,FCurChar]);
end;
end;
FAddStr.AddWideChar(WideChar(u2));
end;
else
FAddStr.AddChar(FCurChar);
end;
end else
begin
FAddStr.AddChar(FCurChar);
end;
if not NextChar then Exit(tkEOF);
end;
if (FCurChar in [#0,#13,#10]) then
Error(SErrOpenString,[FCurRow]);
Result:=tkString;
FCurTokenString:=FAddStr.GetStr;
end;
',':
begin
Result := tkComma;
end;
'0'..'9','.','-':
begin
FAddStr.Reset;
FAddStr.AddChar(FCurChar);
while NextChar do
begin
case FCurChar of
'.':
begin
FAddStr.AddChar(FCurChar);
if not NextChar then Break;
//FAddStr.AddChar(FCurChar);
if FCurChar in ['0'..'9', 'e', 'E'] then
begin
//if not NextChar then Break;
repeat
FAddStr.AddChar(FCurChar);
if not NextChar then Break;
until not (FCurChar in ['0'..'9', 'e', 'E','-','+']);
end;
StepPrev;
break;
end;
'0'..'9':FAddStr.AddChar(FCurChar);
'e', 'E':
begin
FAddStr.AddChar(FCurChar);
if not NextChar then Break;
FAddStr.AddChar(FCurChar);
if FCurChar in ['-','+'] then
begin
if not NextChar then Break;
end;
while FCurChar in ['0'..'9'] do
begin
FAddStr.AddChar(FCurChar);
if not NextChar then Break;
end;
StepPrev;
break;
end;
else
if not (FCurChar in [#0,'}',']',',',#9,' ',#13,#10]) then
Error(SErrInvalidCharacter, [CurRow,CurColumn,FCurChar]);
StepPrev;
break;
end;
end;
FCurTokenString:=FAddStr.GetStr;
If (FCurTokenString[1]='.') then
FCurTokenString:='0'+FCurTokenString;
Result := tkNumber;
end;
':':
begin
Result := tkColon;
end;
'{':
begin
Result := tkCurlyBraceOpen;
end;
'}':
begin
Result := tkCurlyBraceClose;
end;
'[':
begin
Result := tkSquaredBraceOpen;
end;
']':
begin
Result := tkSquaredBraceClose;
end;
'/' :
begin
if Not (joComments in Options) then
Error(SErrInvalidCharacter, [CurRow,CurCOlumn,FCurChar]);
FAddStr.Reset;
FAddStr.AddChar(FCurChar);
Case FCurChar of
'/' : begin
i:=FCurRow;
While (i=FCurRow) do
begin
if not CheckNextLine then Break;
FAddStr.AddChar(FCurChar);
end;
FCurTokenString:=FAddStr.GetStr;
end;
'*' :
begin
if not CheckNextLine then
begin
Error(SUnterminatedComment, [CurRow,CurCOlumn,FCurChar]);
Exit(tkEOF);
end;
repeat
FAddStr.AddChar(FCurChar);
c:=FCurChar;
if not CheckNextLine then
begin
Error(SUnterminatedComment, [CurRow,CurCOlumn,FCurChar]);
Break;
end;
until (c='*') and (FCurChar='/');
FCurTokenString:=FAddStr.GetStr;
end;
else
Error(SErrInvalidCharacter, [CurRow,CurCOlumn,FCurChar]);
end;
Result:=tkComment;
end;
'a'..'z','A'..'Z','_':
begin
FAddStr.Reset;
tStart:=CurRow;
tcol :=CurColumn;
repeat
FAddStr.AddChar(FCurChar);
if not NextChar then Break;
until not (FCurChar in ['A'..'Z', 'a'..'z', '0'..'9', '_']);
StepPrev;
FCurTokenString:=FAddStr.GetStr;
for it := tkTrue to tkNull do
if CompareText(FCurTokenString, TokenInfos[it]) = 0 then
begin
Result := it;
FCurToken := Result;
exit;
end;
if (joStrict in Options) then
Error(SErrInvalidCharacter, [tStart,tcol,FCurTokenString])
else
Result:=tkIdentifier;
end;
else
Error(SErrInvalidCharacter, [CurRow,CurColumn,FCurChar]);
end;
FCurToken:=Result;
end;
function TJSONScanner.GetO(AIndex: TJSONOption): Boolean;
begin
Result:=AIndex in FOptions;
end;
procedure TJSONScanner.SetO(AIndex: TJSONOption; AValue: Boolean);
begin
If AValue then
Include(Foptions,AIndex)
else
Exclude(Foptions,AIndex)
end;
end.