watparser.pas 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  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. Result := sc.Next;
  112. if not Result then begin
  113. ErrorUnexpectedEof(sc);
  114. Exit;
  115. end;
  116. case sc.token of
  117. weNumber: num:=sc.resInt32;
  118. weIdent: id:=sc.resText;
  119. else
  120. ErrorExpectButFound(sc, 'index', TokenStr[sc.token]);
  121. Result := false;
  122. end;
  123. Result := true;
  124. if Result then sc.Next;
  125. end;
  126. function TokenTypeToValType(t: TWatToken; out tp: byte): Boolean;
  127. begin
  128. Result:=true;
  129. case t of
  130. wei32: tp:=valtype_i32;
  131. wei64: tp:=valtype_i64;
  132. wef32: tp:=valtype_f32;
  133. wef64: tp:=valtype_f64;
  134. else
  135. tp:=0;
  136. Result:=false;
  137. end;
  138. end;
  139. procedure ParseParam(sc: TWatScanner; out id: string; out tp: byte; allowIdent: Boolean = true; consumeClose: Boolean = true);
  140. begin
  141. tp:=0;
  142. id:='';
  143. if allowIdent and (sc.token=weIdent) then begin
  144. id:=sc.resText;
  145. sc.Next;
  146. end;
  147. if not TokenTypeToValType(sc.token, tp) then
  148. ErrorExpectButFound(sc, WAT_DEFTYPES, TokenStr[sc.token]);
  149. sc.Next;
  150. if consumeClose then
  151. ConsumeToken(sc, weCloseBrace);
  152. end;
  153. procedure ParseNumOrIdx(sc: TWatScanner; out num: integer; out idx: string);
  154. begin
  155. if sc.token = weIdent then begin
  156. idx := sc.resText;
  157. num := -1;
  158. end else if sc.token = weNumber then begin
  159. idx := '';
  160. num := sc.resInt32;
  161. end else
  162. ErrorExpectButFound(sc, 'number');
  163. sc.Next;
  164. end;
  165. procedure ParseInstrList(sc: TWatScanner; dst: TWasmInstrList);
  166. var
  167. ci : TWasmInstr;
  168. begin
  169. while sc.token=weInstr do begin
  170. ci := dst.AddInstr(sc.instrCode);
  171. sc.Next;
  172. case INST_FLAGS[ci.code].Param of
  173. ipNone:; // do nothing
  174. ipLeb:
  175. ParseNumOrIdx(sc, ci.operandNum, ci.operandIdx);
  176. ipi32,ipi64,ipf32,ipf64:
  177. begin
  178. if sc.token<>weNumber then
  179. ErrorExpectButFound(sc, 'number');
  180. ci.operandText := sc.resText;
  181. sc.Next;
  182. end;
  183. //ip2Leb, // memory arguments, ask for offset + align
  184. //ipTable, // a complex structure... used for br_table only
  185. //ipResType // result type used for blocks, such as If, block or loop
  186. end;
  187. end;
  188. end;
  189. procedure ParseFunc(sc: TWatScanner; dst: TWasmFunc);
  190. var
  191. nm : integer;
  192. id : string;
  193. p : TWasmParam;
  194. tk : TWatToken;
  195. begin
  196. if sc.token=weFunc then sc.Next;
  197. if sc.token=weIdent then begin
  198. dst.id:=sc.resText;
  199. sc.Next;
  200. end;
  201. ConsumeAnyOpenToken(sc, tk);
  202. if tk = weType then begin
  203. if not ParseNumOfId(sc, nm, id) then Exit;
  204. if nm>=0 then dst.functype.typeNum:=nm
  205. else dst.functype.typeIdx:=id;
  206. ConsumeAnyOpenToken(sc, tk);
  207. end;
  208. while tk = weParam do begin
  209. p:=dst.functype.AddParam;
  210. sc.Next;
  211. ParseParam(sc, p.id, p.tp);
  212. ConsumeAnyOpenToken(sc, tk);
  213. end;
  214. while tk = weResult do begin
  215. p:=dst.functype.AddResult;
  216. sc.Next;
  217. ParseParam(sc, p.id, p.tp, false);
  218. ConsumeAnyOpenToken(sc, tk);
  219. end;
  220. while tk = weLocal do begin
  221. p:=dst.AddLocal;
  222. sc.Next;
  223. ParseParam(sc, p.id, p.tp);
  224. ConsumeAnyOpenToken(sc, tk);
  225. end;
  226. if not (sc.token in [weInstr, weCloseBrace]) then
  227. ErrorExpectButFound(sc, 'identifier');
  228. ParseInstrList(sc, dst.instr);
  229. ConsumeToken(sc, weCloseBrace);
  230. end;
  231. procedure ParseExport(sc: TWatScanner; dst: TWasmExport);
  232. begin
  233. if sc.token=weExport then
  234. sc.Next;
  235. if sc.token<>weString then
  236. ErrorExpectButFound(sc, 'string');
  237. dst.name := sc.resWasmString;
  238. sc.Next;
  239. ConsumeAnyOpenToken(sc);
  240. case sc.token of
  241. weFunc: dst.exportType:=EXPDESC_FUNC;
  242. weTable: dst.exportType:=EXPDESC_TABLE;
  243. weMemory: dst.exportType:=EXPDESC_MEM;
  244. weGlobal: dst.exportType:=EXPDESC_GLOBAL;
  245. else
  246. ErrorExpectButFound(sc, 'func');
  247. end;
  248. sc.Next;
  249. case sc.token of
  250. weNumber:
  251. dst.exportNum := sc.resInt32;
  252. weIdent:
  253. dst.exportIdx := sc.resText;
  254. else
  255. ErrorExpectButFound(sc, 'index');
  256. end;
  257. sc.Next;
  258. ConsumeToken(sc, weCloseBrace);
  259. ConsumeToken(sc, weCloseBrace);
  260. end;
  261. procedure ParseImport(sc: TWatScanner; dst: TWasmImport);
  262. var
  263. tk : TWatToken;
  264. begin
  265. if sc.token=weImport then
  266. sc.Next;
  267. if sc.token<>weString then
  268. ErrorExpectButFound(sc, 'string');
  269. dst.module := sc.resWasmString;
  270. sc.Next;
  271. if sc.token<>weString then
  272. ErrorExpectButFound(sc, 'string');
  273. dst.name := sc.resWasmString;
  274. sc.Next;
  275. ConsumeAnyOpenToken(sc, tk);
  276. case tk of
  277. weAsmSymbol: ;
  278. weFunc: begin
  279. ParseFunc(sc, dst.AddFunc);
  280. end;
  281. else
  282. ErrorExpectButFound(sc, 'importdesc', TokenStr[sc.token]);
  283. end;
  284. ConsumeToken(sc, weCloseBrace);
  285. end;
  286. procedure ConsumeAsmSym(sc: TWatScanner; dst: TAsmSymList);
  287. begin
  288. dst.Push(sc.asmCmd, sc.resText);
  289. sc.Next;
  290. end;
  291. procedure ParseModuleInt(sc: TWatScanner; dst: TWasmModule);
  292. var
  293. tk : TWatToken;
  294. symlist : TAsmSymList;
  295. f : TWasmFunc;
  296. imp : TWasmImport;
  297. begin
  298. if not ConsumeOpenToken(sc, weModule) then
  299. ErrorExpectButFound(sc, 'module');
  300. symlist := TAsmSymList.Create;
  301. try
  302. sc.Next;
  303. ConsumeAnyOpenToken(sc, tk);
  304. while tk <> weCloseBrace do begin
  305. case tk of
  306. weAsmSymbol:
  307. ConsumeAsmSym(sc, symlist);
  308. weImport: begin
  309. imp:=dst.AddImport;
  310. symlist.ToLinkInfo(imp.LinkInfo);
  311. ParseImport(sc, imp);
  312. symlist.Clear;
  313. end;
  314. weFunc: begin
  315. f:=dst.AddFunc;
  316. symlist.ToLinkInfo(f.LinkInfo);
  317. ParseFunc(sc, f);
  318. symlist.Clear;
  319. end;
  320. weExport:
  321. begin
  322. ParseExport(sc, dst.AddExport);
  323. symlist.Clear;
  324. end;
  325. else
  326. ErrorExpectButFound(sc, 'func', TokenStr[sc.token]);
  327. end;
  328. ConsumeAnyOpenToken(sc, tk);
  329. end;
  330. ConsumeToken(sc, weCloseBrace);
  331. finally
  332. symlist.Free;
  333. end;
  334. end;
  335. function ParseModule(sc: TWatScanner; dst: TWasmModule; var errMsg: string): Boolean;
  336. var
  337. res : TParseResult;
  338. begin
  339. Result := ParseModule(sc, dst, res);
  340. if not Result then begin
  341. errMsg:=Format('line: %d, pos: %d, %s', [res.line, res.pos, res.error]);
  342. end else
  343. errMsg:='';
  344. end;
  345. procedure GetLineAndPos(const buf: string; ofs: integer; out line, pos: integer);
  346. var
  347. i: integer;
  348. ll: integer;
  349. begin
  350. i:=1;
  351. line:=1;
  352. ll:=1;
  353. while (i<=length(buf)) and (i<ofs) do begin
  354. if (buf[i]=#13) or (buf[i]=#10) then begin
  355. inc(line);
  356. if (i<=length(buf)) and (i<ofs) and ((buf[i]=#13) or (buf[i]=#10)) and (buf[i] <> buf[i-1]) then
  357. inc(i);
  358. ll:=i;
  359. end;
  360. inc(i);
  361. end;
  362. pos:=ofs - ll;
  363. end;
  364. function ParseModule(sc: TWatScanner; dst: TWasmModule; out err: TParseResult): Boolean;
  365. begin
  366. try
  367. err.error:='';
  368. err.pos:=0;
  369. err.line:=0;
  370. err.offset:=0;
  371. ParseModuleInt(sc, dst);
  372. Result:=true;
  373. except
  374. on x: EParserError do begin
  375. err.error := x.Message;
  376. err.offset := x.offset;
  377. GetLineAndPos(sc.buf, x.offset, err.line, err.pos);
  378. Result:=false;
  379. end;
  380. end;
  381. end;
  382. { TAsmSymList }
  383. procedure TAsmSymList.Push(const AName, AValue: string);
  384. var
  385. i : integer;
  386. begin
  387. for i:=0 to count-1 do
  388. if syms[i].name = Aname then begin
  389. syms[i].value := AValue;
  390. Exit;
  391. end;
  392. if count=length(syms) then begin
  393. if count=0 then SetLength(syms, 4)
  394. else SetLength(syms, count*2);
  395. end;
  396. syms[count].name:=AName;
  397. syms[count].value:=Avalue;
  398. inc(count);
  399. end;
  400. procedure TAsmSymList.Clear;
  401. begin
  402. count:=0;
  403. end;
  404. procedure TAsmSymList.ToLinkInfo(var AInfo: TLinkInfo);
  405. var
  406. i : integer;
  407. begin
  408. for i:=0 to count-1 do begin
  409. if syms[i].name = 'name' then
  410. AInfo.Name := syms[i].value
  411. else if syms[i].name = 'weak' then
  412. AInfo.Binding := lbWeak
  413. else if syms[i].name = 'local' then
  414. AInfo.Binding := lbLocal
  415. else if syms[i].name = 'hidden' then
  416. Ainfo.isHidden := true
  417. else if syms[i].name = 'undef' then
  418. AInfo.isUndefined := true
  419. else if syms[i].name = 'nostrip' then
  420. AInfo.NoStrip := true
  421. else if syms[i].name = 'forhost' then
  422. AInfo.Binding := lbForHost;
  423. end;
  424. end;
  425. { EParserError }
  426. constructor EParserError.Create(const amsg: string; aofs: integer);
  427. begin
  428. inherited Create(amsg);
  429. offset:=aofs;
  430. end;
  431. end.