watparser.pas 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542
  1. unit watparser;
  2. {$mode delphi}{$H+}
  3. interface
  4. uses
  5. SysUtils, Classes, wasmtext, wasmmodule, watscanner, wasmbincode, wasmbin;
  6. type
  7. TParseResult = record
  8. error : string;
  9. line : integer;
  10. pos : integer;
  11. offset : integer;
  12. end;
  13. const
  14. TokenStr : array[TWatToken] of string = (
  15. 'uknown', 'error',
  16. 'index',
  17. 'string', 'number', '(', ')',
  18. 'assembler symbol',
  19. 'instruction',
  20. 'func',
  21. 'param', 'result',
  22. 'module', 'mut', 'funcref',
  23. 'i32', 'i64',
  24. 'f32', 'f64',
  25. 'type',
  26. 'import', 'global', 'table', 'memory', 'local', 'export',
  27. 'elem', 'data', 'offset'
  28. );
  29. //function ConsumeToken(sc: TWatScanner; tk: TWatToken): Boolean;
  30. function ParseModule(sc: TWatScanner; dst: TWasmModule; var errMsg: string): Boolean; overload;
  31. function ParseModule(sc: TWatScanner; dst: TWasmModule; out err: TParseResult): Boolean; overload;
  32. implementation
  33. type
  34. // used to stop the recursive parsing
  35. { EParserError }
  36. EParserError = class(Exception)
  37. offset : integer;
  38. constructor Create(const amsg: string; aofs: integer);
  39. end;
  40. TAsmSym = record
  41. name : string;
  42. value : string;
  43. end;
  44. { TAsmSymList }
  45. TAsmSymList = class(TObject)
  46. syms : array of TAsmSym;
  47. count : integer;
  48. procedure Push(const AName, AValue: string);
  49. procedure Clear;
  50. procedure ToLinkInfo(var AInfo: TLinkInfo);
  51. end;
  52. const
  53. WAT_DEFTYPES='iN or fN';
  54. procedure ParseError(sc: TWatScanner; const errMsg: string);
  55. begin
  56. raise EParserError.Create(errMsg, sc.ofs);
  57. end;
  58. procedure ErrorExpectButFound(sc: TWatScanner; const expected: string; const butfound: string =''); overload;
  59. var
  60. r : string;
  61. begin
  62. if butfound = '' then r := sc.resText
  63. else r := butfound;
  64. ParseError(sc, 'expected "'+expected+'", but "'+r+'" found');
  65. end;
  66. procedure ErrorUnexpectedEof(sc: TWatScanner);
  67. begin
  68. ParseError(sc, 'unexpected end of file');
  69. end;
  70. procedure ConsumeAnyOpenToken(sc: TWatScanner; out tk: TWatToken;
  71. out hadOpenBrace: Boolean); overload;
  72. begin
  73. hadOpenBrace := sc.token = weOpenBrace;
  74. if hadOpenBrace then sc.Next;
  75. tk:=sc.token;
  76. end;
  77. procedure ConsumeAnyOpenToken(sc: TWatScanner); overload;
  78. var
  79. tk: TWatToken;
  80. op: Boolean;
  81. begin
  82. ConsumeAnyOpenToken(sc, tk, op);
  83. end;
  84. procedure ConsumeAnyOpenToken(sc: TWatScanner; out tk: TWatToken); overload;
  85. var
  86. op: Boolean;
  87. begin
  88. ConsumeAnyOpenToken(sc, tk, op);
  89. end;
  90. function ConsumeOpenToken(sc: TWatScanner; tk: TWatToken): Boolean;
  91. begin
  92. sc.Next;
  93. Result := (sc.token=weOpenBrace) or (sc.Token=tk);
  94. if Result and (sc.token=weOpenBrace) then begin
  95. sc.Next;
  96. Result := (sc.Token=tk);
  97. end;
  98. end;
  99. function ConsumeToken(sc: TWatScanner; tk: TWatToken): Boolean;
  100. begin
  101. Result:=sc.token =tk;
  102. if not Result then
  103. ErrorExpectButFound(sc,TokenStr[tk])
  104. else
  105. sc.Next;
  106. end;
  107. function ParseNumOfId(sc: TWatScanner; out num: integer; out id: string): Boolean;
  108. begin
  109. num:=-1;
  110. id:='';
  111. case sc.token of
  112. weNumber: num:=sc.resInt32;
  113. weIdent: id:=sc.resText;
  114. else
  115. ErrorExpectButFound(sc, 'index', TokenStr[sc.token]);
  116. Result := false;
  117. end;
  118. Result := true;
  119. if Result then sc.Next;
  120. end;
  121. function TokenTypeToValType(t: TWatToken; out tp: byte): Boolean;
  122. begin
  123. Result:=true;
  124. case t of
  125. wei32: tp:=valtype_i32;
  126. wei64: tp:=valtype_i64;
  127. wef32: tp:=valtype_f32;
  128. wef64: tp:=valtype_f64;
  129. else
  130. tp:=0;
  131. Result:=false;
  132. end;
  133. end;
  134. procedure ParseParam(sc: TWatScanner; out id: string; out tp: byte; allowIdent: Boolean = true; consumeClose: Boolean = true);
  135. begin
  136. tp:=0;
  137. id:='';
  138. if allowIdent and (sc.token=weIdent) then begin
  139. id:=sc.resText;
  140. sc.Next;
  141. end;
  142. if not TokenTypeToValType(sc.token, tp) then
  143. ErrorExpectButFound(sc, WAT_DEFTYPES, TokenStr[sc.token]);
  144. sc.Next;
  145. if consumeClose then
  146. ConsumeToken(sc, weCloseBrace);
  147. end;
  148. procedure ParseNumOrIdx(sc: TWatScanner; out num: integer; out idx: string);
  149. begin
  150. if sc.token = weIdent then begin
  151. idx := sc.resText;
  152. num := -1;
  153. end else if sc.token = weNumber then begin
  154. idx := '';
  155. num := sc.resInt32;
  156. end else
  157. ErrorExpectButFound(sc, 'number');
  158. sc.Next;
  159. end;
  160. procedure ParseFuncParamResult(sc: TWatScanner; dst: TWasmFuncType);
  161. var
  162. tk : TWatToken;
  163. nm : integer;
  164. id : string;
  165. p : TWasmParam;
  166. begin
  167. tk := sc.token;
  168. if tk = weType then begin
  169. sc.Next;
  170. if not ParseNumOfId(sc, nm, id) then
  171. Exit;
  172. if nm>=0 then dst.typeNum:=nm
  173. else dst.typeIdx:=id;
  174. ConsumeAnyOpenToken(sc, tk);
  175. end;
  176. while tk = weParam do begin
  177. p:=dst.AddParam;
  178. sc.Next;
  179. ParseParam(sc, p.id, p.tp);
  180. ConsumeAnyOpenToken(sc, tk);
  181. end;
  182. while tk = weResult do begin
  183. p:=dst.AddResult;
  184. sc.Next;
  185. ParseParam(sc, p.id, p.tp, false);
  186. ConsumeAnyOpenToken(sc, tk);
  187. end;
  188. end;
  189. procedure ParseInstrList(sc: TWatScanner; dst: TWasmInstrList);
  190. var
  191. ci : TWasmInstr;
  192. ft : TWasmFuncType;
  193. begin
  194. while sc.token=weInstr do begin
  195. ci := dst.AddInstr(sc.instrCode);
  196. sc.Next;
  197. case INST_FLAGS[ci.code].Param of
  198. ipNone:; // do nothing
  199. ipLeb:
  200. ParseNumOrIdx(sc, ci.operandNum, ci.operandIdx);
  201. ipi32,ipi64,ipf32,ipf64,ipi32OrFunc:
  202. begin
  203. if (INST_FLAGS[ci.code].Param = ipi32OrFunc) and (sc.token = weIdent) then
  204. ci.operandText := sc.resText
  205. else if sc.token<>weNumber then
  206. ErrorExpectButFound(sc, 'number');
  207. ci.operandText := sc.resText;
  208. sc.Next;
  209. end;
  210. ipCallType:
  211. begin
  212. // call_indirect operator consists of 2 parameters
  213. // 1 - type call
  214. // 2 - table reference index. Which should always be zero.
  215. ConsumeToken(sc, weOpenBrace);
  216. ft := ci.addInstType;
  217. ParseFuncParamResult(sc, ft);
  218. ci.operandNum := 0; // table reference index
  219. end;
  220. //ip2Leb, // memory arguments, ask for offset + align
  221. //ipTable, // a complex structure... used for br_table only
  222. //ipResType // result type used for blocks, such as If, block or loop
  223. end;
  224. end;
  225. end;
  226. procedure ParseFunc(sc: TWatScanner; dst: TWasmFunc);
  227. var
  228. tk : TWatToken;
  229. p : TWasmParam;
  230. begin
  231. if sc.token=weFunc then sc.Next;
  232. if sc.token=weIdent then begin
  233. dst.id:=sc.resText;
  234. sc.Next;
  235. end;
  236. ConsumeAnyOpenToken(sc, tk);
  237. if tk in [weType, weParam, weResult] then
  238. ParseFuncParamResult(sc, dst.functype);
  239. while tk = weLocal do begin
  240. p:=dst.AddLocal;
  241. sc.Next;
  242. ParseParam(sc, p.id, p.tp);
  243. ConsumeAnyOpenToken(sc, tk);
  244. end;
  245. if not (sc.token in [weInstr, weCloseBrace]) then
  246. ErrorExpectButFound(sc, 'identifier');
  247. ParseInstrList(sc, dst.instr);
  248. ConsumeToken(sc, weCloseBrace);
  249. end;
  250. procedure ParseExport(sc: TWatScanner; dst: TWasmExport);
  251. begin
  252. if sc.token=weExport then
  253. sc.Next;
  254. if sc.token<>weString then
  255. ErrorExpectButFound(sc, 'string');
  256. dst.name := sc.resWasmString;
  257. sc.Next;
  258. ConsumeAnyOpenToken(sc);
  259. case sc.token of
  260. weFunc: dst.exportType:=EXPDESC_FUNC;
  261. weTable: dst.exportType:=EXPDESC_TABLE;
  262. weMemory: dst.exportType:=EXPDESC_MEM;
  263. weGlobal: dst.exportType:=EXPDESC_GLOBAL;
  264. else
  265. ErrorExpectButFound(sc, 'func');
  266. end;
  267. sc.Next;
  268. case sc.token of
  269. weNumber:
  270. dst.exportNum := sc.resInt32;
  271. weIdent:
  272. dst.exportIdx := sc.resText;
  273. else
  274. ErrorExpectButFound(sc, 'index');
  275. end;
  276. sc.Next;
  277. ConsumeToken(sc, weCloseBrace);
  278. ConsumeToken(sc, weCloseBrace);
  279. end;
  280. procedure ParseImport(sc: TWatScanner; dst: TWasmImport);
  281. var
  282. tk : TWatToken;
  283. begin
  284. if sc.token=weImport then
  285. sc.Next;
  286. if sc.token<>weString then
  287. ErrorExpectButFound(sc, 'string');
  288. dst.module := sc.resWasmString;
  289. sc.Next;
  290. if sc.token<>weString then
  291. ErrorExpectButFound(sc, 'string');
  292. dst.name := sc.resWasmString;
  293. sc.Next;
  294. ConsumeAnyOpenToken(sc, tk);
  295. case tk of
  296. weAsmSymbol: ;
  297. weFunc: begin
  298. ParseFunc(sc, dst.AddFunc);
  299. end;
  300. else
  301. ErrorExpectButFound(sc, 'importdesc', TokenStr[sc.token]);
  302. end;
  303. ConsumeToken(sc, weCloseBrace);
  304. end;
  305. procedure ConsumeAsmSym(sc: TWatScanner; dst: TAsmSymList);
  306. begin
  307. dst.Push(sc.asmCmd, sc.resText);
  308. sc.Next;
  309. end;
  310. function ParseId(sc: TWatScanner; var id: TWasmId): boolean;
  311. begin
  312. Result := ParseNumOfId(sc, id.idNum, id.id);
  313. end;
  314. procedure ParseTable(sc: TWatScanner; dst: TWasmTable);
  315. begin
  316. sc.Next;
  317. ParseId(sc, dst.id);
  318. ConsumeToken(sc, weFuncRef);
  319. ConsumeToken(sc, weCloseBrace);
  320. end;
  321. procedure ParseModuleInt(sc: TWatScanner; dst: TWasmModule);
  322. var
  323. tk : TWatToken;
  324. symlist : TAsmSymList;
  325. f : TWasmFunc;
  326. imp : TWasmImport;
  327. begin
  328. if not ConsumeOpenToken(sc, weModule) then
  329. ErrorExpectButFound(sc, 'module');
  330. symlist := TAsmSymList.Create;
  331. try
  332. sc.Next;
  333. ConsumeAnyOpenToken(sc, tk);
  334. while tk <> weCloseBrace do begin
  335. case tk of
  336. weAsmSymbol:
  337. ConsumeAsmSym(sc, symlist);
  338. weImport: begin
  339. imp:=dst.AddImport;
  340. symlist.ToLinkInfo(imp.LinkInfo);
  341. ParseImport(sc, imp);
  342. symlist.Clear;
  343. end;
  344. weTable: begin
  345. ParseTable(sc, dst.AddTable)
  346. end;
  347. weFunc: begin
  348. f:=dst.AddFunc;
  349. symlist.ToLinkInfo(f.LinkInfo);
  350. ParseFunc(sc, f);
  351. symlist.Clear;
  352. end;
  353. weExport:
  354. begin
  355. ParseExport(sc, dst.AddExport);
  356. symlist.Clear;
  357. end;
  358. else
  359. ErrorExpectButFound(sc, 'func', TokenStr[sc.token]);
  360. end;
  361. ConsumeAnyOpenToken(sc, tk);
  362. end;
  363. ConsumeToken(sc, weCloseBrace);
  364. finally
  365. symlist.Free;
  366. end;
  367. end;
  368. function ParseModule(sc: TWatScanner; dst: TWasmModule; var errMsg: string): Boolean;
  369. var
  370. res : TParseResult;
  371. begin
  372. Result := ParseModule(sc, dst, res);
  373. if not Result then begin
  374. errMsg:=Format('line: %d, pos: %d, %s', [res.line, res.pos, res.error]);
  375. end else
  376. errMsg:='';
  377. end;
  378. procedure GetLineAndPos(const buf: string; ofs: integer; out line, pos: integer);
  379. var
  380. i: integer;
  381. ll: integer;
  382. begin
  383. i:=1;
  384. line:=1;
  385. ll:=1;
  386. while (i<=length(buf)) and (i<ofs) do begin
  387. if (buf[i]=#13) or (buf[i]=#10) then begin
  388. inc(line);
  389. if (i<=length(buf)) and (i<ofs) and ((buf[i]=#13) or (buf[i]=#10)) and (buf[i] <> buf[i-1]) then
  390. inc(i);
  391. ll:=i;
  392. end;
  393. inc(i);
  394. end;
  395. pos:=ofs - ll;
  396. end;
  397. function ParseModule(sc: TWatScanner; dst: TWasmModule; out err: TParseResult): Boolean;
  398. begin
  399. try
  400. err.error:='';
  401. err.pos:=0;
  402. err.line:=0;
  403. err.offset:=0;
  404. ParseModuleInt(sc, dst);
  405. Result:=true;
  406. except
  407. on x: EParserError do begin
  408. err.error := x.Message;
  409. err.offset := x.offset;
  410. GetLineAndPos(sc.buf, x.offset, err.line, err.pos);
  411. Result:=false;
  412. end;
  413. end;
  414. end;
  415. { TAsmSymList }
  416. procedure TAsmSymList.Push(const AName, AValue: string);
  417. var
  418. i : integer;
  419. begin
  420. for i:=0 to count-1 do
  421. if syms[i].name = Aname then begin
  422. syms[i].value := AValue;
  423. Exit;
  424. end;
  425. if count=length(syms) then begin
  426. if count=0 then SetLength(syms, 4)
  427. else SetLength(syms, count*2);
  428. end;
  429. syms[count].name:=AName;
  430. syms[count].value:=Avalue;
  431. inc(count);
  432. end;
  433. procedure TAsmSymList.Clear;
  434. begin
  435. count:=0;
  436. end;
  437. procedure TAsmSymList.ToLinkInfo(var AInfo: TLinkInfo);
  438. var
  439. i : integer;
  440. begin
  441. for i:=0 to count-1 do begin
  442. if syms[i].name = 'name' then
  443. AInfo.Name := syms[i].value
  444. else if syms[i].name = 'weak' then
  445. AInfo.Binding := lbWeak
  446. else if syms[i].name = 'local' then
  447. AInfo.Binding := lbLocal
  448. else if syms[i].name = 'hidden' then
  449. Ainfo.isHidden := true
  450. else if syms[i].name = 'undef' then
  451. AInfo.isUndefined := true
  452. else if syms[i].name = 'nostrip' then
  453. AInfo.NoStrip := true
  454. else if syms[i].name = 'forhost' then
  455. AInfo.Binding := lbForHost;
  456. end;
  457. end;
  458. { EParserError }
  459. constructor EParserError.Create(const amsg: string; aofs: integer);
  460. begin
  461. inherited Create(amsg);
  462. offset:=aofs;
  463. end;
  464. end.