123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486 |
- { This file is part of wasmbin - a collection of WebAssembly binary utils.
- Copyright (C) 2019, 2020 Dmitry Boyarintsev <[email protected]>
- Copyright (C) 2020 by the Free Pascal development team
- This source is free software; you can redistribute it and/or modify it under
- the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2 of the License, or (at your option)
- any later version.
- This code 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. See the GNU General Public License for more
- details.
- A copy of the GNU General Public License is available on the World Wide Web
- at <http://www.gnu.org/copyleft/gpl.html>. You can also obtain it by writing
- to the Free Software Foundation, Inc., 51 Franklin Street - Fifth Floor,
- Boston, MA 02110-1335, USA.
- }
- unit watscanner;
- {$mode delphi}{$H+}
- interface
- uses
- SysUtils, Classes, parseutils, wasmtext;
- type
- TWatToken = (weNone, weError,
- weIdent,
- weString, weNumber, weOpenBrace, weCloseBrace,
- weAsmSymbol,
- weInstr,
- weFunc,
- weParam, weResult,
- weModule, weMut, weFuncRef,
- wei32, wei64,
- wef32, wef64,
- weType,
- weImport, weGlobal, weTable, weMemory, weLocal, weExport,
- weElem, weData, weOffset, weAlign, weEqual
- );
- // used only for weNumber
- TWatNumberFormat = (
- wnfNo, // other than number
- wnfInteger, // 00
- wnfHex, // 0xABC
- wnfFloat, // 0.000
- wnfFloatHex // 0x000.bced
- );
- THexStr = record
- num : QWord;
- frac : QWord;
- exp : integer;
- isNeg : Boolean;
- end;
- { TWatScanner }
- TWatScanner = class(TObject)
- protected
- procedure DoComment(ofs: Integer; const cmt: string); virtual;
- function CommentIsSymbol(const cmt: string): Boolean;
- public
- buf : string;
- idx : integer;
- instrCode : byte;
- ofs : integer;
- token : TWatToken;
- numformat : TWatNumberFormat;
- resText : string;
- asmCmd : string;
- skipAsmSym : Boolean;
- procedure SetSource(const abuf: string);
- function Next: Boolean;
- function resInt32(const def: integer=-1): Integer;
- function resWasmString: string;
- end;
- const
- // see Identifiers of Textual format
- IdStart = '$';
- IdBody = AlphaNumChars
- + [ '!' ,'#' ,'$' ,'%' ,'&' ,'''' ,'*'
- ,'+' ,'-' ,'.' ,'/' ,':' ,'<' ,'='
- ,'>' ,'?' ,'@' ,'\' ,'^' ,'_' ,'`'
- ,'|' ,'~'];
- GrammarChars = AlphaNumChars+['.','_'
- ,'/' // some old instructions are like that: "f32.reinterpret/i32"
- ];
- procedure GetGrammar(const txt: string; out entity: TWatToken; out instByte: byte);
- const
- KEY_MODULE = 'module';
- KEY_FUNC = 'func';
- KEY_FUNCREF = 'funcref';
- KEY_I32 = 'i32';
- KEY_I64 = 'i64';
- KEY_F32 = 'f32';
- KEY_F64 = 'f64';
- KEY_PARAM = 'param';
- KEY_RESULT = 'result';
- KEY_MUT = 'mut';
- KEY_TYPE = 'type';
- KEY_IMPORT = 'import';
- KEY_GLOBAL = 'global';
- KEY_TABLE = 'table';
- KEY_MEMORY = 'memory';
- KEY_LOCAL = 'local';
- KEY_EXPORT = 'export';
- KEY_ELEM = 'elem';
- KEY_DATA = 'data';
- KEY_OFFSET = 'offset';
- function ScanString(const buf: string; var idx: integer): string;
- function HexFloatStrToHexStr(const t: string; out hexStr: THexStr): Boolean;
- function HexFracToSingle(const num, frac: QWord; exp: Integer; isNeg: Boolean): Single;
- function HexFloatStrToSingle(const hexstr: string): Single;
- function HexFracToDouble(const num, frac: QWord; exp: Integer; neg: Boolean): Double;
- function HexFloatStrToDouble(const hexstr: string): double;
- implementation
- procedure GetGrammar(const txt: string; out entity: TWatToken; out instByte: byte);
- begin
- instByte:=0;
- entity:=weError;
- if txt='' then Exit;
- case txt[1] of
- 'a':
- if txt='anyfunc' then entity:=weFuncRef
- else if txt = 'align' then entity:=weAlign
- else if TextToInst(txt, instByte) then entity:=weInstr;
- 'd':
- if txt=KEY_DATA then entity:=weData
- else if TextToInst(txt, instByte) then entity:=weInstr;
- 'e':
- if txt=KEY_EXPORT then entity:=weExport
- else if txt=KEY_ELEM then entity:=weElem
- else if TextToInst(txt, instByte) then entity:=weInstr;
- 'i':
- if txt=KEY_I32 then entity:=wei32
- else if txt=KEY_I64 then entity:=wei64
- else if txt=KEY_IMPORT then entity:=weImport
- else if TextToInst(txt, instByte) then entity:=weInstr;
- 'g':
- if txt=KEY_GLOBAL then entity:=weGlobal
- else if TextToInst(txt, instByte) then entity:=weInstr;
- 'f':
- if txt=KEY_FUNC then entity:=weFunc
- else if txt=KEY_FUNCREF then entity:=weFuncRef
- else if txt=KEY_F32 then entity:=wef32
- else if txt=KEY_F64 then entity:=wef64
- else if TextToInst(txt, instByte) then entity:=weInstr;
- 'l':
- if txt=KEY_LOCAL then entity:=weLocal
- else if TextToInst(txt, instByte) then entity:=weInstr;
- 'm':
- if txt=KEY_MODULE then entity:=weModule
- else if txt = KEY_MUT then entity:=weMut
- else if txt = KEY_MEMORY then entity:=weMemory
- else if TextToInst(txt, instByte) then entity:=weInstr;
- 'o':
- if txt=KEY_OFFSET then entity:=weOffset
- else if TextToInst(txt, instByte) then entity:=weInstr;
- 'p':
- if txt=KEY_PARAM then entity:=weParam
- else if TextToInst(txt, instByte) then entity:=weInstr;
- 'r':
- if txt=KEY_RESULT then entity:=weResult
- else if TextToInst(txt, instByte) then entity:=weInstr;
- 't':
- if txt=KEY_TYPE then entity:=weType
- else if txt=KEY_TABLE then entity:=weTable
- else if TextToInst(txt, instByte) then entity:=weInstr;
- else
- if TextToInst(txt, instByte) then entity:=weInstr;
- end;
- end;
- { TWatScanner }
- procedure TWatScanner.DoComment(ofs: Integer; const cmt: string);
- begin
- end;
- function TWatScanner.CommentIsSymbol(const cmt: string): Boolean;
- var
- i: integer;
- t: string;
- v: string;
- begin
- Result := false;
- if (Pos(';;',cmt)<>1) then Exit;
- i:=3;
- ScanWhile(cmt, i, SpaceChars);
- if (i>length(cmt)) or (cmt[i]<>'.') then Exit;
- inc(i);
- t := AnsiLowerCase(ScanTo(cmt, i, SpaceChars));
- ScanWhile(cmt, i, SpaceChars);
- v := ScanTo(cmt, i, SpaceChars);
- asmCmd := t;
- resText := v;
- Result := true;
- end;
- procedure TWatScanner.SetSource(const abuf: string);
- begin
- buf:=abuf;
- idx:=1;
- end;
- function ScanString(const buf: string; var idx: integer): string;
- var
- j : integer;
- begin
- if buf[idx]<>'"' then begin
- Result:='';
- Exit;
- end;
- j:=idx;
- inc(idx);
- while (buf[idx]<>'"') and (idx<length(buf)) do begin
- if buf[idx]='\' then inc(idx);
- inc(idx);
- end;
- inc(idx);
- Result:=Copy(buf, j, idx-j);
- end;
- function TWatScanner.Next: Boolean;
- var
- cmt : string;
- done: boolean;
- fmt : TCNumberFormat;
- si : integer;
- begin
- numformat := wnfNo;
- Result := idx<=length(buf);
- if not Result then Exit;
- done:=false;
- resText:='';
- while not done do begin
- ScanWhile(buf, idx, SpaceEolnChars);
- Result := idx<=length(buf);
- if not Result then Exit;
- ofs:=idx;
- if (idx<length(buf)) and (buf[idx] in [';','(']) and (buf[idx+1]=';') then begin
- if (buf[idx]=';') then begin
- // comment until the end of the line
- cmt := ScanTo(buf, idx, EoLnChars);
- ScanWhile(buf, idx, EoLnChars);
- end else
- // comment until the ;)
- cmt := ScanToSubstr(buf, idx, ';)');
- if not skipAsmSym and CommentIsSymbol(cmt) then begin
- token:=weAsmSymbol;
- done:=true;
- end else
- DoComment(ofs, cmt);
- end else begin
- done:=true;
- if buf[idx] = '(' then begin
- token:=weOpenBrace;
- inc(idx);
- end else if buf[idx]=')' then begin
- token:=weCloseBrace;
- inc(idx);
- end else if buf[idx]='=' then begin
- token:=weEqual;
- inc(idx);
- end else if buf[idx]='"' then begin
- token:=weString;
- resText:=ScanString(buf, idx);
- end else if buf[idx] = IdStart then begin
- token:=weIdent;
- resText:=ScanWhile(buf, idx, IdBody);
- end else if buf[idx] in SignNumericChars then begin
- fmt := ScanNumberC(buf, idx, resText);
- if fmt = nfError then begin
- token := weError;
- Exit;
- end else
- token:=weNumber;
- case fmt of
- nfFloat: numformat := wnfFloat;
- nfFloatHex: numFormat := wnfFloatHex;
- nfHex: numformat := wnfHex;
- else
- numformat := wnfInteger;
- end;
- end else if buf[idx] in GrammarChars then begin
- si := idx;
- resText:=ScanWhile(buf, idx, GrammarChars);
- // second try for the number
- if (resText = 'nan') or (resText = 'inf') then begin
- idx := si;
- fmt := ScanNumberC(buf, idx, resText);
- if fmt = nfError then begin
- token := weError;
- Exit;
- end else
- token:=weNumber;
- case fmt of
- nfFloat: numformat := wnfFloat;
- nfHex: numformat := wnfHex;
- else
- numformat := wnfInteger;
- end;
- end else
- GetGrammar(resText, token, instrCode);
- done:=true;
- end else begin
- token:=weError;
- inc(idx);
- done:=true;
- end;
- end;
- end;
- if resText='' then
- resText := Copy(buf, ofs, idx-ofs);
- end;
- function TWatScanner.resInt32(const def: integer=-1): Integer;
- var
- err: integer;
- begin
- Val(resText, Result, err);
- if err<>0 then Result:=def;
- end;
- function TWatScanner.resWasmString: string;
- var
- i : integer;
- j : integer;
- begin
- if token<>weString then begin
- Result:='';
- Exit;
- end;
- Result:=Copy(resText, 2, length(resText)-2);
- if Result='' then Exit;
- i:=1;
- j:=1;
- while i<=length(Result) do begin
- if Result[i]='\' then begin
- inc(i);
- if i<=length(Result) then
- case Result[i] of
- 'r': Result[j]:=#13;
- 'n': Result[j]:=#10;
- '\': Result[j]:='\';
- '"': Result[j]:='"';
- end;
- end else
- if (j<i) then Result[j]:=Result[i];
- inc(j);
- inc(i);
- end;
- SetLength(Result, j-1);
- end;
- function HexFloatStrToHexStr(const t: string; out hexStr: THexStr): Boolean;
- var
- i : integer;
- j : integer;
- err : Integer;
- const
- HexChars = ['0'..'9','a'..'f','A'..'F'];
- begin
- hexStr.isNeg:=false;
- hexStr.num:=0;
- hexStr.frac:=0;
- hexStr.exp:=0;
- if (t='') then begin
- Result:=true;
- Exit;
- end;
- i:=1;
- hexStr.isNeg:=t[i]='-';
- if (hexStr.isNeg) then inc(i);
- inc(i,2); // skipping '0x'
- j:=i;
- while (i<=length(t)) and (t[i] in HexChars) do inc(i);
- Val('$'+Copy(t, j, i-j), hexStr.num, err);
- Result:=err=0;
- if not Result then Exit;
- if (t[i]='.') then begin
- inc(i);
- j:=i;
- while (i<=length(t)) and (t[i] in HexChars) do inc(i);
- Val('$'+Copy(t, j, i-j), hexStr.frac, err);
- Result:=err=0;
- if not Result then Exit;
- end;
- Result := (i<=length(t)) and (t[i] = 'p') or (t[i]='P');
- inc(i);
- Val(Copy(t, i, length(t)), hexStr.exp, err);
- Result:=err=0;
- end;
- function HexFracToSingle(const num, frac: QWord; exp: Integer; isNeg: Boolean): Single;
- var
- x : QWord;
- nm : QWord;
- adjexp : integer;
- sr : TSingleRec;
- begin
- nm := num;
- x := frac;
- adjexp := -1;
- while (nm > 0) do begin
- x:=(x shr 1) or ((nm and 1) shl 23);
- nm := nm shr 1;
- inc(adjexp);
- end;
- sr.Exp:=127 + exp + adjexp;
- sr.Frac:=x;
- sr.Sign:=isNeg;
- Result := sr.Value;
- end;
- function HexFloatStrToSingle(const hexstr: string): Single;
- var
- st : THexStr;
- begin
- HexFloatStrToHexStr(hexstr, st);
- Result:=HexFracToSingle(st.num, st.frac, st.exp, st.isNeg);
- end;
- function HexFracToDouble(const num, frac: QWord; exp: Integer; neg: Boolean): Double;
- var
- x : QWord;
- nm : QWord;
- adjexp : integer;
- sr : TDoubleRec;
- begin
- nm := num;
- x := frac;
- adjexp := 0;
- while (nm > 1) do begin
- x:=(x shr 1) or ((nm and 1) shl 52);
- nm := nm shr 1;
- inc(adjexp);
- end;
- sr.Exp:=1023 + exp + adjexp;
- sr.Frac:=x;
- sr.Sign:=neg;
- Result := sr.Value;
- end;
- function HexFloatStrToDouble(const hexstr: string): double;
- var
- st : THexStr;
- begin
- HexFloatStrToHexStr(hexstr, st);
- Result:=HexFracToDouble(st.num, st.frac, st.exp, st.isNeg);
- end;
- end.
|