{ This file is part of wasmbin - a collection of WebAssembly binary utils. Copyright (C) 2019, 2020 Dmitry Boyarintsev 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 . 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 (idx0 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 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.