tablegen.js 108 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714
  1. import * as fs from 'fs/promises';
  2. import * as path from 'path';
  3. const tablegen = {};
  4. tablegen.Location = class {
  5. constructor(file, line, column) {
  6. this.file = file;
  7. this.line = line;
  8. this.column = column;
  9. }
  10. toString() {
  11. return `${this.file}:${this.line}:${this.column}`;
  12. }
  13. };
  14. tablegen.Token = class {
  15. constructor(type, value, location) {
  16. this.type = type;
  17. this.value = value;
  18. this.location = location;
  19. }
  20. };
  21. tablegen.Tokenizer = class {
  22. constructor(text, file) {
  23. this._text = text;
  24. this._file = file;
  25. this._position = 0;
  26. this._line = 1;
  27. this._column = 1;
  28. this._keywords = new Set([
  29. 'assert', 'bit', 'bits', 'class', 'code', 'dag', 'def', 'defm', 'defset', 'defvar',
  30. 'dump', 'else', 'false', 'field', 'foreach', 'if', 'in', 'include', 'int',
  31. 'let', 'list', 'multiclass', 'string', 'then', 'true'
  32. ]);
  33. this._token = this._tokenize();
  34. }
  35. location() {
  36. return new tablegen.Location(this._file, this._line, this._column);
  37. }
  38. current() {
  39. return this._token;
  40. }
  41. peek(offset = 0) {
  42. const pos = this._position + offset;
  43. return pos < this._text.length ? this._text[pos] : null;
  44. }
  45. read() {
  46. const prev = this._token;
  47. this._token = this._tokenize();
  48. return prev;
  49. }
  50. _tokenize() {
  51. this._skipWhitespace();
  52. if (this._position >= this._text.length) {
  53. return new tablegen.Token('eof', null, this.location());
  54. }
  55. const location = this.location();
  56. const c = this.peek();
  57. if (c === '"') {
  58. return this._readString(location);
  59. }
  60. if (c === '[' && this.peek(1) === '{') {
  61. return this._readCodeBlock(location);
  62. }
  63. if (c === ':' && this.peek(1) === ':') {
  64. this._next();
  65. this._next();
  66. return new tablegen.Token('::', '::', location);
  67. }
  68. if (c === '.' && this.peek(1) === '.' && this.peek(2) === '.') {
  69. this._next();
  70. this._next();
  71. this._next();
  72. return new tablegen.Token('...', '...', location);
  73. }
  74. if (c === '-' && this.peek(1) === '>') {
  75. this._next();
  76. this._next();
  77. return new tablegen.Token('->', '->', location);
  78. }
  79. if (c === '=' && this.peek(1) === '=') {
  80. this._next();
  81. this._next();
  82. return new tablegen.Token('==', '==', location);
  83. }
  84. if (c === '!' && this.peek(1) === '=') {
  85. this._next();
  86. this._next();
  87. return new tablegen.Token('!=', '!=', location);
  88. }
  89. if (c === '<' && this.peek(1) === '<') {
  90. this._next();
  91. this._next();
  92. return new tablegen.Token('<<', '<<', location);
  93. }
  94. if (this._isDigit(c)) {
  95. let pos = 1;
  96. while (this.peek(pos) && this._isDigit(this.peek(pos))) {
  97. pos++;
  98. }
  99. if (this.peek(pos) && /[a-zA-Z_]/.test(this.peek(pos))) {
  100. return this._readIdentifier(location);
  101. }
  102. return this._readNumber(location);
  103. }
  104. if (c === '-' && this._isDigit(this.peek(1))) {
  105. return this._readNumber(location);
  106. }
  107. if (this._isIdentifierStart(c)) {
  108. return this._readIdentifier(location);
  109. }
  110. if ('{}[]()<>,;:=!#$.?^'.includes(c)) {
  111. this._next();
  112. return new tablegen.Token(c, c, location);
  113. }
  114. throw new tablegen.Error(`Unexpected character '${c}' at ${location}`);
  115. }
  116. _next() {
  117. if (this._position < this._text.length) {
  118. if (this._text[this._position] === '\n') {
  119. this._line++;
  120. this._column = 1;
  121. } else {
  122. this._column++;
  123. }
  124. this._position++;
  125. }
  126. }
  127. _skipWhitespace() {
  128. while (this._position < this._text.length) {
  129. const c = this.peek();
  130. if (/\s/.test(c)) {
  131. this._next();
  132. continue;
  133. }
  134. if (c === '/' && this.peek(1) === '/') {
  135. this._next();
  136. this._next();
  137. while (this._position < this._text.length && this.peek() !== '\n') {
  138. this._next();
  139. }
  140. continue;
  141. }
  142. if (c === '/' && this.peek(1) === '*') {
  143. this._next();
  144. this._next();
  145. let depth = 1;
  146. while (this._position < this._text.length && depth > 0) {
  147. if (this.peek() === '/' && this.peek(1) === '*') {
  148. this._next();
  149. this._next();
  150. depth++;
  151. } else if (this.peek() === '*' && this.peek(1) === '/') {
  152. this._next();
  153. this._next();
  154. depth--;
  155. } else {
  156. this._next();
  157. }
  158. }
  159. continue;
  160. }
  161. break;
  162. }
  163. }
  164. _isDigit(c) {
  165. return c !== null && /[0-9]/.test(c);
  166. }
  167. _isIdentifierStart(c) {
  168. return c !== null && /[a-zA-Z_]/.test(c);
  169. }
  170. _isIdentifierChar(c) {
  171. return c !== null && /[a-zA-Z0-9_]/.test(c);
  172. }
  173. _readString(location) {
  174. let value = '';
  175. this._next(); // opening "
  176. while (this._position < this._text.length && this.peek() !== '"') {
  177. if (this.peek() === '\\') {
  178. this._next();
  179. const c = this.peek();
  180. switch (c) {
  181. case 'n': value += '\n'; break;
  182. case 't': value += '\t'; break;
  183. case 'r': value += '\r'; break;
  184. case '\\': value += '\\'; break;
  185. case '"': value += '"'; break;
  186. default: value += c; break;
  187. }
  188. this._next();
  189. } else {
  190. value += this.peek();
  191. this._next();
  192. }
  193. }
  194. if (this.peek() === '"') {
  195. this._next();
  196. }
  197. return new tablegen.Token('string', value, location);
  198. }
  199. _readCodeBlock(location) {
  200. let value = '';
  201. this._next(); // [
  202. this._next(); // {
  203. let depth = 1;
  204. let lineStart = true;
  205. let lineContent = '';
  206. while (this._position < this._text.length && depth > 0) {
  207. const c = this.peek();
  208. const next = this.peek(1);
  209. if (c === '[' && next === '{') {
  210. const trimmedLine = lineContent.trim();
  211. if (lineStart || trimmedLine === '' || /^(let|def|class)\s/.test(trimmedLine)) {
  212. depth++;
  213. }
  214. value += c;
  215. this._next();
  216. value += next;
  217. this._next();
  218. lineContent += c + next;
  219. } else if (c === '}' && next === ']') {
  220. depth--;
  221. if (depth === 0) {
  222. this._next();
  223. this._next();
  224. break;
  225. }
  226. value += c;
  227. this._next();
  228. value += next;
  229. this._next();
  230. lineContent += c + next;
  231. } else {
  232. if (c === '\n') {
  233. lineStart = true;
  234. lineContent = '';
  235. } else if (c !== ' ' && c !== '\t') {
  236. lineStart = false;
  237. }
  238. if (c !== '\n') {
  239. lineContent += c;
  240. }
  241. value += c;
  242. this._next();
  243. }
  244. }
  245. return new tablegen.Token('code', value.trim(), location);
  246. }
  247. _readNumber(location) {
  248. let value = '';
  249. if (this.peek() === '-') {
  250. value += this.peek();
  251. this._next();
  252. }
  253. // Hexadecimal
  254. if (this.peek() === '0' && this.peek(1) === 'x') {
  255. value += this.peek();
  256. this._next();
  257. value += this.peek();
  258. this._next();
  259. while (this._position < this._text.length && /[0-9a-fA-F]/.test(this.peek())) {
  260. value += this.peek();
  261. this._next();
  262. }
  263. return new tablegen.Token('number', parseInt(value, 16), location);
  264. }
  265. // Binary
  266. if (this.peek() === '0' && this.peek(1) === 'b') {
  267. value += this.peek();
  268. this._next();
  269. value += this.peek();
  270. this._next();
  271. while (this._position < this._text.length && /[01]/.test(this.peek())) {
  272. value += this.peek();
  273. this._next();
  274. }
  275. return new tablegen.Token('number', parseInt(value.substring(2), 2), location);
  276. }
  277. // Decimal
  278. while (this._position < this._text.length && this._isDigit(this.peek())) {
  279. value += this.peek();
  280. this._next();
  281. }
  282. return new tablegen.Token('number', parseInt(value, 10), location);
  283. }
  284. _readIdentifier(location) {
  285. let value = '';
  286. while (this._position < this._text.length && this._isIdentifierChar(this.peek())) {
  287. value += this.peek();
  288. this._next();
  289. }
  290. while (this.peek() === '.' && this._isIdentifierStart(this.peek(1))) {
  291. value += this.peek(); // add dot
  292. this._next();
  293. while (this._position < this._text.length && this._isIdentifierChar(this.peek())) {
  294. value += this.peek();
  295. this._next();
  296. }
  297. }
  298. const type = this._keywords.has(value) ? 'keyword' : 'id';
  299. return new tablegen.Token(type, value, location);
  300. }
  301. };
  302. tablegen.Value = class {
  303. constructor(type, value) {
  304. this.type = type; // 'int', 'string', 'bit', 'bits', 'list', 'dag', 'code', 'def'
  305. this.value = value;
  306. }
  307. };
  308. tablegen.DAG = class {
  309. constructor(operator, operands) {
  310. this.operator = operator; // string or Value
  311. this.operands = operands; // array of {value, name}
  312. }
  313. };
  314. tablegen.Type = class {
  315. constructor(name) {
  316. this.name = name;
  317. this.args = [];
  318. }
  319. toString() {
  320. if (this.args.length === 0) {
  321. return this.name;
  322. }
  323. return `${this.name}<${this.args.map((a) => a.toString()).join(', ')}>`;
  324. }
  325. };
  326. tablegen.RecordVal = class {
  327. constructor(name, type, value) {
  328. this.name = name;
  329. this.type = type; // tablegen.Type
  330. this.value = value; // tablegen.Value or null
  331. }
  332. };
  333. tablegen.Record = class {
  334. constructor(name, parser = null) {
  335. this.name = name;
  336. this.parents = [];
  337. this.fields = new Map();
  338. this.templateArgs = [];
  339. this.templateBindings = new Map(); // parameter name -> bound value
  340. this.location = null;
  341. this.parser = parser;
  342. }
  343. getValue(name) {
  344. return this.fields.get(name) || null;
  345. }
  346. // Unified resolution function that handles both parent field copying and post-inheritance resolution
  347. // Inspired by LLVM's Resolver pattern to avoid code duplication
  348. _resolveInit(init, resolver, visited = new Set()) {
  349. if (!init || typeof init !== 'object') {
  350. return init;
  351. }
  352. const key = JSON.stringify(init);
  353. if (visited.has(key)) {
  354. return init;
  355. }
  356. visited.add(key);
  357. // Resolve def/id type references (template parameters or field access)
  358. if ((init.type === 'def' || init.type === 'id') && typeof init.value === 'string') {
  359. // Handle field access syntax (e.g., "meta.mnemonic" or "addrKind.name")
  360. if (init.value.includes('.')) {
  361. const [baseName, ...fieldParts] = init.value.split('.');
  362. // Try to resolve the base
  363. let current = resolver.resolveReference(baseName);
  364. if (current) {
  365. // Walk through field access chain
  366. for (const fieldName of fieldParts) {
  367. // Handle def reference
  368. if (current && current.type === 'def' && typeof current.value === 'string') {
  369. const defName = current.value;
  370. const def = this.parser.getDef(defName) || this.parser.getClass(defName);
  371. if (def) {
  372. const field = def.getValue(fieldName);
  373. if (field && field.value) {
  374. current = field.value;
  375. } else {
  376. return init; // Field not found
  377. }
  378. } else {
  379. return init; // Def not found
  380. }
  381. } else if (current && current.type === 'dag' && current.value) {
  382. // Handle DAG (anonymous class instantiation)
  383. const className = current.value.operator;
  384. const templateArgs = current.value.operands.map((op) => op.value);
  385. // Instantiate an anonymous class with template arguments
  386. // Used for resolving field access like meta.mnemonic where meta is ROCDL_TrLoadOpMeta<...>
  387. let instantiated = null;
  388. const baseClass = this.parser.getClass(className);
  389. if (baseClass) {
  390. // Create an anonymous record that inherits from the class
  391. instantiated = new tablegen.Record(`<anonymous ${className}>`, this.parser);
  392. instantiated.parents = [{ name: className, args: templateArgs }];
  393. this.parser.addSubClass(instantiated);
  394. instantiated.resolveReferences();
  395. }
  396. if (instantiated) {
  397. const field = instantiated.getValue(fieldName);
  398. if (field && field.value) {
  399. current = field.value;
  400. } else {
  401. return init;
  402. }
  403. } else {
  404. return init;
  405. }
  406. } else {
  407. return init; // Can't resolve further
  408. }
  409. }
  410. // Successfully resolved the field access chain
  411. return this._resolveInit(current, resolver, visited);
  412. }
  413. } else {
  414. // Simple reference without dots
  415. const resolved = resolver.resolveReference(init.value);
  416. if (resolved) {
  417. return this._resolveInit(resolved, resolver, visited);
  418. }
  419. }
  420. }
  421. // Recursively resolve nested structures
  422. if (init.type === 'dag' && init.value) {
  423. return {
  424. type: 'dag',
  425. value: {
  426. operator: init.value.operator,
  427. operands: init.value.operands.map((op) => ({
  428. value: this._resolveInit(op.value, resolver, visited),
  429. name: op.name
  430. }))
  431. }
  432. };
  433. }
  434. if (init.type === 'list' && Array.isArray(init.value)) {
  435. return {
  436. type: 'list',
  437. value: init.value.map((v) => this._resolveInit(v, resolver, visited))
  438. };
  439. }
  440. if (init.type === 'bang' && init.value) {
  441. return {
  442. type: 'bang',
  443. value: {
  444. op: init.value.op,
  445. args: init.value.args.map((arg) => this._resolveInit(arg, resolver, visited))
  446. }
  447. };
  448. }
  449. if (init.type === 'concat' && Array.isArray(init.value)) {
  450. const resolvedParts = init.value.map((v) => this._resolveInit(v, resolver, visited));
  451. // Flatten nested concats recursively: if a resolved part is itself a concat,
  452. // extract its parts to avoid nested concat structures that cause double evaluation
  453. const flattenedParts = [];
  454. const flattenConcat = (part) => {
  455. if (part && part.type === 'concat' && Array.isArray(part.value)) {
  456. for (const subPart of part.value) {
  457. flattenConcat(subPart);
  458. }
  459. } else {
  460. flattenedParts.push(part);
  461. }
  462. };
  463. for (const part of resolvedParts) {
  464. flattenConcat(part);
  465. }
  466. return {
  467. type: 'concat',
  468. value: flattenedParts
  469. };
  470. }
  471. // For other types, return shallow copy for parent field copying, as-is for post-inheritance
  472. return resolver.shouldCopy ? { ...init } : init;
  473. }
  474. // Helper to deep copy a field and resolve parameter references in a specific context
  475. _copyAndResolveField(field, bindings, parentClass) {
  476. const resolver = {
  477. shouldCopy: true,
  478. resolveReference: (name) => bindings.get(name) || parentClass.templateBindings.get(name) || null
  479. };
  480. return {
  481. name: field.name,
  482. type: field.type,
  483. value: this._resolveInit(field.value, resolver)
  484. };
  485. }
  486. // Resolve template parameter references in field values
  487. // This matches C++ Record::resolveReferences() behavior
  488. // After inheriting from a templated class, substitute all template parameter
  489. // references in field values with their bound values from templateBindings
  490. resolveReferences() {
  491. if (this.templateBindings.size === 0) {
  492. return; // No template parameters to substitute
  493. }
  494. const findTemplateBinding = (paramName, record = this, visited = new Set()) => {
  495. if (visited.has(record.name)) {
  496. return null;
  497. }
  498. visited.add(record.name);
  499. if (record.templateBindings.has(paramName)) {
  500. return record.templateBindings.get(paramName);
  501. }
  502. for (const parent of record.parents) {
  503. const parentClass = this.parser.classes.get(parent.name);
  504. if (parentClass) {
  505. const paramIndex = parentClass.templateArgs.findIndex((arg) => arg.name === paramName);
  506. if (paramIndex !== -1 && parent.args && parent.args[paramIndex]) {
  507. return parent.args[paramIndex];
  508. }
  509. const binding = findTemplateBinding(paramName, parentClass, visited);
  510. if (binding) {
  511. return binding;
  512. }
  513. }
  514. }
  515. return null;
  516. };
  517. const resolver = {
  518. shouldCopy: false,
  519. resolveReference: (name) => {
  520. const binding = findTemplateBinding(name);
  521. if (binding) {
  522. return binding;
  523. }
  524. const field = this.getValue(name);
  525. if (field && field.value) {
  526. return field.value;
  527. }
  528. return null;
  529. }
  530. };
  531. for (const [, field] of this.fields) {
  532. if (field.value) {
  533. const resolved = this._resolveInit(field.value, resolver);
  534. if (resolved !== field.value) {
  535. field.value = resolved;
  536. }
  537. }
  538. }
  539. }
  540. getValueAsString(fieldName) {
  541. const field = this.getValue(fieldName);
  542. if (!field || !field.value) {
  543. return null;
  544. }
  545. const evaluated = this.evaluateValue(field.value);
  546. if (typeof evaluated === 'string') {
  547. return evaluated;
  548. }
  549. return null;
  550. }
  551. getValueAsBit(fieldName) {
  552. const field = this.getValue(fieldName);
  553. if (!field || !field.value) {
  554. return null;
  555. }
  556. const evaluated = this.evaluateValue(field.value);
  557. if (typeof evaluated === 'boolean') {
  558. return evaluated;
  559. }
  560. if (typeof evaluated === 'number') {
  561. return evaluated !== 0;
  562. }
  563. return null;
  564. }
  565. getValueAsDag(fieldName) {
  566. const field = this.getValue(fieldName);
  567. if (!field || !field.value) {
  568. return null;
  569. }
  570. const evaluated = this.evaluateValue(field.value);
  571. if (evaluated && typeof evaluated === 'object' && evaluated.operator) {
  572. return evaluated;
  573. }
  574. return null;
  575. }
  576. getValueAsDef(fieldName) {
  577. const field = this.getValue(fieldName);
  578. if (!field || !field.value) {
  579. return null;
  580. }
  581. if (field.value.type === 'def' && typeof field.value.value === 'string') {
  582. const defName = field.value.value;
  583. return this.parser.getDef(defName) || this.parser.getClass(defName);
  584. }
  585. return null;
  586. }
  587. isEnumAttr() {
  588. const enumBaseClasses = [
  589. 'EnumAttr', // Wrapper for dialect-specific enums
  590. 'IntEnumAttr', 'I32EnumAttr', 'I64EnumAttr',
  591. 'BitEnumAttr', 'I8BitEnumAttr', 'I16BitEnumAttr', 'I32BitEnumAttr', 'I64BitEnumAttr',
  592. 'IntEnum', 'I32IntEnum', 'I64IntEnum', // Integer enum types
  593. 'BitEnum', 'I8BitEnum', 'I16BitEnum', 'I32BitEnum', 'I64BitEnum' // Bit enum types
  594. ];
  595. const checkParents = (record, visited = new Set()) => {
  596. if (record && !visited.has(record.name)) {
  597. visited.add(record.name);
  598. if (enumBaseClasses.includes(record.name)) {
  599. return true;
  600. }
  601. for (const parent of record.parents) {
  602. const parentClass = this.parser.getClass(parent.name);
  603. if (parentClass && checkParents(parentClass, visited)) {
  604. return true;
  605. }
  606. }
  607. }
  608. return false;
  609. };
  610. return checkParents(this);
  611. }
  612. isEnumProp() {
  613. const enumPropBaseClasses = [
  614. 'EnumProp', // Property wrapping an enum
  615. 'EnumPropWithAttrForm' // Property with attribute form
  616. ];
  617. const checkParents = (record, visited = new Set()) => {
  618. if (record && !visited.has(record.name)) {
  619. visited.add(record.name);
  620. if (enumPropBaseClasses.includes(record.name)) {
  621. return true;
  622. }
  623. for (const parent of record.parents) {
  624. const parentClass = this.parser.getClass(parent.name);
  625. if (parentClass && checkParents(parentClass, visited)) {
  626. return true;
  627. }
  628. }
  629. }
  630. return false;
  631. };
  632. return checkParents(this);
  633. }
  634. // Get enum cases from an enum attribute or property definition
  635. getEnumCases() {
  636. if (!this.isEnumAttr() && !this.isEnumProp()) {
  637. return null;
  638. }
  639. // Handle EnumProp - the first argument is the underlying enum
  640. if (this.isEnumProp()) {
  641. for (const parent of this.parents) {
  642. if (parent.name === 'EnumProp' || parent.name === 'EnumPropWithAttrForm') {
  643. if (parent.args && parent.args.length >= 1) {
  644. const [enumInfoArg] = parent.args;
  645. if (enumInfoArg && enumInfoArg.type === 'def' && typeof enumInfoArg.value === 'string') {
  646. const enumName = enumInfoArg.value;
  647. const underlyingEnum = this.parser.getDef(enumName) || this.parser.getClass(enumName);
  648. if (underlyingEnum) {
  649. // Recursively get cases from the underlying enum
  650. return underlyingEnum.getEnumCases();
  651. }
  652. }
  653. }
  654. }
  655. // Recursively search parent classes
  656. const parentClass = this.parser.getClass(parent.name);
  657. if (parentClass && parentClass.isEnumProp()) {
  658. const cases = parentClass.getEnumCases();
  659. if (cases) {
  660. return cases;
  661. }
  662. }
  663. }
  664. }
  665. // Helper to search for EnumAttr in the parent hierarchy
  666. const findEnumAttrParent = (record, visited = new Set()) => {
  667. if (!record || visited.has(record.name)) {
  668. return null;
  669. }
  670. visited.add(record.name);
  671. for (const parent of record.parents) {
  672. if (parent.name === 'EnumAttr') {
  673. return parent;
  674. }
  675. // Recursively search parent classes
  676. const parentClass = this.parser.getClass(parent.name);
  677. if (parentClass) {
  678. const found = findEnumAttrParent(parentClass, visited);
  679. if (found) {
  680. return found;
  681. }
  682. }
  683. }
  684. return null;
  685. };
  686. const enumAttrParent = findEnumAttrParent(this);
  687. // Pattern 1a: EnumAttr<Dialect, EnumInfo (as def), name>
  688. // The 2nd argument is a reference to the underlying enum (e.g., GPU_Dimension)
  689. if (enumAttrParent && enumAttrParent.args && enumAttrParent.args.length >= 2) {
  690. const [dialectArg, enumInfoArg] = enumAttrParent.args;
  691. if (enumInfoArg && enumInfoArg.type === 'def' && typeof enumInfoArg.value === 'string') {
  692. // Get the expected namespace from the dialect
  693. let expectedNamespace = null;
  694. if (dialectArg && dialectArg.type === 'def') {
  695. const dialectDef = this.parser.getDef(dialectArg.value) || this.parser.getClass(dialectArg.value);
  696. if (dialectDef) {
  697. expectedNamespace = dialectDef.getValueAsString('cppNamespace');
  698. }
  699. }
  700. // Find the enum with the matching name and namespace
  701. // Try all defs with this name in case of conflicts (different namespaces)
  702. const enumName = enumInfoArg.value;
  703. let underlyingEnum = null;
  704. if (expectedNamespace) {
  705. // Normalize namespace for comparison (handle both "vector" and "::mlir::vector" formats)
  706. const normalizeNamespace = (ns) => {
  707. if (!ns) {
  708. return null;
  709. }
  710. // Remove leading :: and extract the last component
  711. const parts = ns.replace(/^::/, '').split('::');
  712. return parts[parts.length - 1];
  713. };
  714. const normalizedExpected = normalizeNamespace(expectedNamespace);
  715. // Search through all defs to find one with matching name AND namespace
  716. for (const def of this.parser.defs) {
  717. if (def.name === enumName) {
  718. const defNamespace = def.getValueAsString('cppNamespace');
  719. const normalizedDef = normalizeNamespace(defNamespace);
  720. if (normalizedDef === normalizedExpected) {
  721. underlyingEnum = def;
  722. break;
  723. }
  724. }
  725. }
  726. }
  727. // Fallback to regular lookup if namespace matching fails
  728. if (!underlyingEnum) {
  729. underlyingEnum = this.parser.getDef(enumName) || this.parser.getClass(enumName);
  730. }
  731. if (underlyingEnum) {
  732. return underlyingEnum.getEnumCases();
  733. }
  734. }
  735. // Pattern 1b: EnumAttr<Dialect, EnumInfo (as DAG), name>
  736. // The 2nd argument is a DAG that instantiates an enum class template
  737. // e.g., EnumAttr<SPIRV_Dialect, SPIRV_I32Enum<"Scope", "desc", [cases]>, "scope">
  738. if (enumInfoArg && enumInfoArg.type === 'dag' && enumInfoArg.value) {
  739. // The DAG operator is the enum class template (e.g., SPIRV_I32Enum)
  740. // We need to find the actual cases by looking at this record's parent args
  741. // which should have the instantiated template parameters
  742. // Search through this record's parents to find one with the cases list
  743. for (const parent of this.parents) {
  744. if (parent.args && parent.args.length >= 3) {
  745. // Look for a list argument that contains enum case defs
  746. for (const arg of parent.args) {
  747. if (arg.type === 'list' && Array.isArray(arg.value)) {
  748. // Check if this looks like an enum case list
  749. const [firstItem] = arg.value;
  750. if (firstItem && firstItem.type === 'def') {
  751. // Try to extract cases from this list
  752. const cases = [];
  753. for (const caseValue of arg.value) {
  754. if (caseValue.type === 'def' && typeof caseValue.value === 'string') {
  755. const caseDef = this.parser.getDef(caseValue.value) || this.parser.getClass(caseValue.value);
  756. if (caseDef) {
  757. const str = caseDef.getValueAsString('str');
  758. if (str) {
  759. cases.push(str);
  760. }
  761. }
  762. }
  763. }
  764. if (cases.length > 0) {
  765. return cases;
  766. }
  767. }
  768. }
  769. }
  770. }
  771. }
  772. }
  773. }
  774. // Pattern 2: I64EnumAttr<name, summary, [cases]>
  775. // Cases are in the 3rd template argument
  776. for (const parent of this.parents) {
  777. // The 3rd argument should be the cases list
  778. if (parent.args && parent.args.length >= 3) {
  779. const [,,casesArg] = parent.args;
  780. if (casesArg && casesArg.type === 'list' && Array.isArray(casesArg.value)) {
  781. const cases = [];
  782. for (const caseValue of casesArg.value) {
  783. // Each case can be either a DAG or a def reference
  784. if (caseValue.type === 'dag' && caseValue.value) {
  785. // DAG format: I64EnumAttrCase<"symbol", value, "string">
  786. // The string representation is in the 3rd operand, or 1st if only 2 operands
  787. const operands = caseValue.value.operands;
  788. if (operands && operands.length > 0) {
  789. // Try the 3rd operand first (string), fall back to 1st (symbol)
  790. const strOperand = operands.length >= 3 ? operands[2] : operands[0];
  791. if (strOperand && strOperand.value) {
  792. const str = this.evaluateValue(strOperand.value);
  793. if (str && typeof str === 'string') {
  794. cases.push(str);
  795. }
  796. }
  797. }
  798. } else if (caseValue.type === 'def' && typeof caseValue.value === 'string') {
  799. // Def reference format
  800. const caseDef = this.parser.getDef(caseValue.value) || this.parser.getClass(caseValue.value);
  801. if (caseDef) {
  802. const str = caseDef.getValueAsString('str');
  803. if (str) {
  804. cases.push(str);
  805. }
  806. }
  807. }
  808. }
  809. return cases.length > 0 ? cases : null;
  810. }
  811. }
  812. }
  813. // Pattern 3: LLVM_EnumAttr<name, cppType, summary, [cases]>
  814. // Cases are in the 4th template argument
  815. for (const parent of this.parents) {
  816. if (parent.args && parent.args.length >= 4) {
  817. const [,,,casesArg] = parent.args;
  818. if (casesArg && casesArg.type === 'list' && Array.isArray(casesArg.value)) {
  819. const cases = [];
  820. for (const caseValue of casesArg.value) {
  821. // Each case is a def reference
  822. if (caseValue.type === 'def' && typeof caseValue.value === 'string') {
  823. const caseDef = this.parser.getDef(caseValue.value) || this.parser.getClass(caseValue.value);
  824. if (caseDef) {
  825. const str = caseDef.getValueAsString('str');
  826. if (str) {
  827. cases.push(str);
  828. }
  829. }
  830. }
  831. }
  832. return cases.length > 0 ? cases : null;
  833. }
  834. }
  835. }
  836. return null;
  837. }
  838. evaluateValue(value) {
  839. if (!value) {
  840. return null;
  841. }
  842. // Handle named values (e.g., { name: 'clauses', value: { type: 'list', ... } })
  843. // These come from named arguments in template instantiation
  844. if (!value.type && value.name && value.value) {
  845. return this.evaluateValue(value.value);
  846. }
  847. switch (value.type) {
  848. case 'string':
  849. return value.value.replace(/^"|"$/g, '');
  850. case 'code':
  851. return value.value;
  852. case 'int':
  853. return parseInt(value.value, 10);
  854. case 'bit':
  855. return value.value === 'true' || value.value === '1';
  856. case 'list':
  857. return value.value.map((v) => this.evaluateValue(v));
  858. case 'concat': {
  859. const parts = value.value.map((v) => this.evaluateValue(v)).filter((v) => v !== null && v !== undefined && v !== '');
  860. return parts.join('');
  861. }
  862. case 'id': {
  863. const fieldName = value.value;
  864. if (this.templateBindings.has(fieldName)) {
  865. return this.evaluateValue(this.templateBindings.get(fieldName));
  866. }
  867. const field = this.getValue(fieldName);
  868. if (field && field.value) {
  869. return this.evaluateValue(field.value);
  870. }
  871. return null;
  872. }
  873. case 'def': {
  874. const defName = typeof value.value === 'string' ? value.value : value.value.value;
  875. if (defName.includes('.')) {
  876. const parts = defName.split('.');
  877. const [baseName, ...fieldPath] = parts;
  878. let baseValue = null;
  879. if (this.templateBindings.has(baseName)) {
  880. baseValue = this.evaluateValue(this.templateBindings.get(baseName));
  881. } else {
  882. const field = this.getValue(baseName);
  883. if (field && field.value) {
  884. baseValue = this.evaluateValue(field.value);
  885. }
  886. }
  887. if (typeof baseValue === 'string') {
  888. const def = this.parser.getDef(baseValue) || this.parser.getClass(baseValue);
  889. if (def) {
  890. let current = def;
  891. for (const fieldName of fieldPath) {
  892. const field = current.getValue(fieldName);
  893. if (!field || !field.value) {
  894. return null;
  895. }
  896. const evaluated = current.evaluateValue(field.value);
  897. // If it's another def name, continue navigation
  898. if (typeof evaluated === 'string' && (this.parser.getDef(evaluated) || this.parser.getClass(evaluated))) {
  899. current = this.parser.getDef(evaluated) || this.parser.getClass(evaluated);
  900. } else {
  901. return evaluated;
  902. }
  903. }
  904. return null;
  905. }
  906. }
  907. return null;
  908. }
  909. if (this.templateBindings.has(defName)) {
  910. return this.evaluateValue(this.templateBindings.get(defName));
  911. }
  912. const field = this.getValue(defName);
  913. if (field && field.value) {
  914. return this.evaluateValue(field.value);
  915. }
  916. const def = this.parser.getDef(defName) || this.parser.getClass(defName);
  917. return def ? defName : null;
  918. }
  919. case 'bang': {
  920. const { op, args } = value.value;
  921. switch (op) {
  922. case 'if': {
  923. if (args.length < 2) {
  924. return null;
  925. }
  926. const condition = this.evaluateValue(args[0]);
  927. if (condition) {
  928. return args.length > 1 ? this.evaluateValue(args[1]) : null;
  929. }
  930. return args.length > 2 ? this.evaluateValue(args[2]) : null;
  931. }
  932. case 'empty': {
  933. if (args.length < 1) {
  934. return true;
  935. }
  936. const val = this.evaluateValue(args[0]);
  937. if (Array.isArray(val)) {
  938. return val.length === 0;
  939. }
  940. if (typeof val === 'string') {
  941. return val.length === 0;
  942. }
  943. return val === null || val === undefined;
  944. }
  945. case 'interleave': {
  946. if (args.length < 2) {
  947. return '';
  948. }
  949. const list = this.evaluateValue(args[0]);
  950. const separator = this.evaluateValue(args[1]);
  951. if (!Array.isArray(list)) {
  952. return '';
  953. }
  954. return list.filter((x) => x !== null && x !== '').join(separator || '');
  955. }
  956. case 'not':
  957. if (args.length < 1) {
  958. return true;
  959. }
  960. return !this.evaluateValue(args[0]);
  961. case 'or':
  962. for (const arg of args) {
  963. if (this.evaluateValue(arg)) {
  964. return true;
  965. }
  966. }
  967. return false;
  968. case 'and':
  969. for (const arg of args) {
  970. if (!this.evaluateValue(arg)) {
  971. return false;
  972. }
  973. }
  974. return true;
  975. case 'foldl': {
  976. // !foldl(init, list, acc, item, expr)
  977. // Fold left: iterate over list, accumulating results
  978. if (args.length < 5) {
  979. return null;
  980. }
  981. let accumulator = this.evaluateValue(args[0]); // init value
  982. const list = this.evaluateValue(args[1]); // list to fold over
  983. // args[2] is the accumulator variable name (not evaluated)
  984. // args[3] is the item variable name (not evaluated)
  985. // args[4] is the expression to evaluate
  986. if (!Array.isArray(list)) {
  987. return accumulator;
  988. }
  989. for (const item of list) {
  990. const accName = args[2].value; // variable name
  991. const itemName = args[3].value; // variable name
  992. const prevAcc = this.fields.get(accName);
  993. const prevItem = this.fields.get(itemName);
  994. let accValue = null;
  995. if (accumulator && typeof accumulator === 'object' && accumulator.operator) {
  996. // It's a DAG object
  997. accValue = new tablegen.Value('dag', accumulator);
  998. } else if (Array.isArray(accumulator)) {
  999. // It's a list
  1000. accValue = new tablegen.Value('list', accumulator);
  1001. } else if (typeof accumulator === 'string') {
  1002. accValue = new tablegen.Value('string', accumulator);
  1003. } else if (typeof accumulator === 'number') {
  1004. accValue = new tablegen.Value('int', accumulator);
  1005. } else {
  1006. accValue = accumulator;
  1007. }
  1008. this.fields.set(accName, new tablegen.RecordVal(accName, null, accValue));
  1009. let itemValue = item;
  1010. if (typeof item === 'string') {
  1011. itemValue = new tablegen.Value('def', item);
  1012. } else if (typeof item === 'number') {
  1013. itemValue = new tablegen.Value('int', item);
  1014. } else if (typeof item === 'boolean') {
  1015. itemValue = new tablegen.Value('bit', item ? '1' : '0');
  1016. } else if (item && typeof item === 'object' && !item.type) {
  1017. itemValue = new tablegen.Value('dag', item);
  1018. }
  1019. this.fields.set(itemName, new tablegen.RecordVal(itemName, null, itemValue));
  1020. accumulator = this.evaluateValue(args[4]);
  1021. if (prevAcc) {
  1022. this.fields.set(accName, prevAcc);
  1023. } else {
  1024. this.fields.delete(accName);
  1025. }
  1026. if (prevItem) {
  1027. this.fields.set(itemName, prevItem);
  1028. } else {
  1029. this.fields.delete(itemName);
  1030. }
  1031. }
  1032. return accumulator;
  1033. }
  1034. case 'foreach': {
  1035. // !foreach(item, list, expr)
  1036. // Map: iterate over list, transforming each item
  1037. if (args.length < 3) {
  1038. return [];
  1039. }
  1040. const itemName = args[0].value;
  1041. const list = this.evaluateValue(args[1]);
  1042. const results = [];
  1043. if (Array.isArray(list)) {
  1044. for (const item of list) {
  1045. const prevItem = this.fields.get(itemName);
  1046. let itemValue = item;
  1047. if (typeof item === 'string') {
  1048. itemValue = new tablegen.Value('def', item);
  1049. } else if (typeof item === 'number') {
  1050. itemValue = new tablegen.Value('int', item);
  1051. } else if (typeof item === 'boolean') {
  1052. itemValue = new tablegen.Value('bit', item ? '1' : '0');
  1053. } else if (item && typeof item === 'object' && !item.type) {
  1054. itemValue = new tablegen.Value('dag', item);
  1055. }
  1056. this.fields.set(itemName, new tablegen.RecordVal(itemName, null, itemValue));
  1057. const result = this.evaluateValue(args[2]);
  1058. results.push(result);
  1059. if (prevItem) {
  1060. this.fields.set(itemName, prevItem);
  1061. } else {
  1062. this.fields.delete(itemName);
  1063. }
  1064. }
  1065. }
  1066. return results;
  1067. }
  1068. case 'filter': {
  1069. // !filter(item, list, predicate)
  1070. // Filter: keep items where predicate is true
  1071. if (args.length < 3) {
  1072. return [];
  1073. }
  1074. const itemName = args[0].value;
  1075. const list = this.evaluateValue(args[1]);
  1076. const results = [];
  1077. if (Array.isArray(list)) {
  1078. for (const item of list) {
  1079. const prevItem = this.fields.get(itemName);
  1080. // Wrap item in a Value so it can be used in expressions
  1081. let itemValue = item;
  1082. if (typeof item === 'string') {
  1083. // If it's a def name, wrap it as a 'def' Value
  1084. itemValue = new tablegen.Value('def', item);
  1085. } else if (typeof item === 'number') {
  1086. itemValue = new tablegen.Value('int', item);
  1087. } else if (typeof item === 'boolean') {
  1088. itemValue = new tablegen.Value('bit', item ? '1' : '0');
  1089. } else if (item && typeof item === 'object' && !item.type) {
  1090. // If it's a raw object (like a DAG), wrap it
  1091. itemValue = new tablegen.Value('dag', item);
  1092. }
  1093. // If item is already a Value, use it as is
  1094. this.fields.set(itemName, new tablegen.RecordVal(itemName, null, itemValue));
  1095. const keep = this.evaluateValue(args[2]);
  1096. if (keep) {
  1097. results.push(item);
  1098. }
  1099. if (prevItem) {
  1100. this.fields.set(itemName, prevItem);
  1101. } else {
  1102. this.fields.delete(itemName);
  1103. }
  1104. }
  1105. }
  1106. return results;
  1107. }
  1108. case 'con': {
  1109. // !con(dag1, dag2, ...)
  1110. // Concatenate dags - merge operands from multiple dags
  1111. if (args.length === 0) {
  1112. return new tablegen.DAG('ins', []);
  1113. }
  1114. let operator = 'ins';
  1115. const allOperands = [];
  1116. for (const arg of args) {
  1117. let dagToProcess = null;
  1118. const evaluated = this.evaluateValue(arg);
  1119. if (evaluated && typeof evaluated === 'object') {
  1120. if (evaluated.operator && evaluated.operands) {
  1121. dagToProcess = evaluated;
  1122. } else if (evaluated.type === 'dag' && evaluated.value) {
  1123. dagToProcess = evaluated.value;
  1124. }
  1125. }
  1126. if (!dagToProcess && arg.type === 'dag') {
  1127. dagToProcess = arg.value;
  1128. }
  1129. if (dagToProcess && dagToProcess.operands) {
  1130. if (operator === 'ins' && dagToProcess.operator) {
  1131. operator = dagToProcess.operator;
  1132. }
  1133. allOperands.push(...dagToProcess.operands);
  1134. }
  1135. }
  1136. return new tablegen.DAG(operator, allOperands);
  1137. }
  1138. case 'listconcat': {
  1139. // !listconcat(list1, list2, ...)
  1140. // Concatenate multiple lists into one
  1141. const result = [];
  1142. for (const arg of args) {
  1143. const list = this.evaluateValue(arg);
  1144. if (Array.isArray(list)) {
  1145. result.push(...list);
  1146. }
  1147. }
  1148. return result;
  1149. }
  1150. case 'listremove': {
  1151. // !listremove(list, items_to_remove)
  1152. // Remove all occurrences of items_to_remove from list
  1153. if (args.length < 2) {
  1154. return [];
  1155. }
  1156. const list = this.evaluateValue(args[0]);
  1157. const toRemove = this.evaluateValue(args[1]);
  1158. if (!Array.isArray(list)) {
  1159. return [];
  1160. }
  1161. const removeSet = new Set();
  1162. if (Array.isArray(toRemove)) {
  1163. for (const item of toRemove) {
  1164. removeSet.add(JSON.stringify(item));
  1165. }
  1166. } else {
  1167. removeSet.add(JSON.stringify(toRemove));
  1168. }
  1169. return list.filter((item) => !removeSet.has(JSON.stringify(item)));
  1170. }
  1171. case 'cast': {
  1172. // !cast<type>(value)
  1173. // Cast value to type - for now just convert to string
  1174. if (args.length < 1) {
  1175. return null;
  1176. }
  1177. const val = this.evaluateValue(args[0]);
  1178. if (val === null || val === undefined) {
  1179. return null;
  1180. }
  1181. return String(val);
  1182. }
  1183. case 'eq': {
  1184. // !eq(a, b)
  1185. // Return true if a equals b
  1186. if (args.length < 2) {
  1187. return false;
  1188. }
  1189. const a = this.evaluateValue(args[0]);
  1190. const b = this.evaluateValue(args[1]);
  1191. return a === b;
  1192. }
  1193. case 'ne': {
  1194. // !ne(a, b)
  1195. // Return true if a not equals b
  1196. if (args.length < 2) {
  1197. return false;
  1198. }
  1199. const a = this.evaluateValue(args[0]);
  1200. const b = this.evaluateValue(args[1]);
  1201. return a !== b;
  1202. }
  1203. case 'lt': {
  1204. // !lt(a, b)
  1205. // Return true if a < b
  1206. if (args.length < 2) {
  1207. return false;
  1208. }
  1209. const a = this.evaluateValue(args[0]);
  1210. const b = this.evaluateValue(args[1]);
  1211. return a < b;
  1212. }
  1213. case 'le': {
  1214. // !le(a, b)
  1215. // Return true if a <= b
  1216. if (args.length < 2) {
  1217. return false;
  1218. }
  1219. const a = this.evaluateValue(args[0]);
  1220. const b = this.evaluateValue(args[1]);
  1221. return a <= b;
  1222. }
  1223. case 'gt': {
  1224. // !gt(a, b)
  1225. // Return true if a > b
  1226. if (args.length < 2) {
  1227. return false;
  1228. }
  1229. const a = this.evaluateValue(args[0]);
  1230. const b = this.evaluateValue(args[1]);
  1231. return a > b;
  1232. }
  1233. case 'ge': {
  1234. // !ge(a, b)
  1235. // Return true if a >= b
  1236. if (args.length < 2) {
  1237. return false;
  1238. }
  1239. const a = this.evaluateValue(args[0]);
  1240. const b = this.evaluateValue(args[1]);
  1241. return a >= b;
  1242. }
  1243. case 'range': {
  1244. // !range(n) or !range(start, end) or !range(start, end, step)
  1245. // Generate a list of integers
  1246. if (args.length === 0) {
  1247. return [];
  1248. }
  1249. let start = 0;
  1250. let end = 0;
  1251. let step = 1;
  1252. if (args.length === 1) {
  1253. end = this.evaluateValue(args[0]);
  1254. } else if (args.length === 2) {
  1255. start = this.evaluateValue(args[0]);
  1256. end = this.evaluateValue(args[1]);
  1257. } else {
  1258. start = this.evaluateValue(args[0]);
  1259. end = this.evaluateValue(args[1]);
  1260. step = this.evaluateValue(args[2]);
  1261. }
  1262. const result = [];
  1263. if (step > 0) {
  1264. for (let i = start; i < end; i += step) {
  1265. result.push(i);
  1266. }
  1267. } else if (step < 0) {
  1268. for (let i = start; i > end; i += step) {
  1269. result.push(i);
  1270. }
  1271. }
  1272. return result;
  1273. }
  1274. case 'listsplat': {
  1275. // !listsplat(element, n)
  1276. // Create a list with n copies of element
  1277. if (args.length < 2) {
  1278. return [];
  1279. }
  1280. const [element] = args; // Don't evaluate yet, keep as Value
  1281. const count = this.evaluateValue(args[1]);
  1282. const result = [];
  1283. for (let i = 0; i < count; i++) {
  1284. result.push(element);
  1285. }
  1286. return result;
  1287. }
  1288. case 'cond': {
  1289. // !cond(condition1: value1, condition2: value2, ..., true: defaultValue)
  1290. // Evaluate conditions in order, return first matching value
  1291. for (let i = 0; i < args.length; i++) {
  1292. const arg = args[i];
  1293. if (arg.condition) {
  1294. const condition = this.evaluateValue(arg.condition);
  1295. if (condition === true || condition === 1) {
  1296. return this.evaluateValue(arg.value);
  1297. }
  1298. }
  1299. }
  1300. return null;
  1301. }
  1302. case 'dag': {
  1303. // !dag(operator, operands_list, names_list)
  1304. // Construct a DAG from operator, operands, and names
  1305. if (args.length < 2) {
  1306. return new tablegen.DAG('ins', []);
  1307. }
  1308. const operatorArg = this.evaluateValue(args[0]);
  1309. const operator = typeof operatorArg === 'string' ? operatorArg : 'ins';
  1310. const operandsList = this.evaluateValue(args[1]);
  1311. const namesList = args.length > 2 ? this.evaluateValue(args[2]) : [];
  1312. const operands = [];
  1313. if (Array.isArray(operandsList)) {
  1314. for (let i = 0; i < operandsList.length; i++) {
  1315. const value = operandsList[i];
  1316. const name = Array.isArray(namesList) && i < namesList.length ? namesList[i] : '';
  1317. operands.push({ value, name });
  1318. }
  1319. }
  1320. return new tablegen.DAG(operator, operands);
  1321. }
  1322. default:
  1323. return null;
  1324. }
  1325. }
  1326. case 'dag':
  1327. return value.value;
  1328. case 'uninitialized':
  1329. return null;
  1330. default:
  1331. return null;
  1332. }
  1333. }
  1334. };
  1335. tablegen.Reader = class {
  1336. constructor() {
  1337. this._paths = [];
  1338. this._includes = new Set();
  1339. this._defs = new Map();
  1340. this.defs = [];
  1341. this.classes = new Map();
  1342. }
  1343. async parse(files, paths) {
  1344. this._paths = paths || [];
  1345. for (const file of files) {
  1346. // eslint-disable-next-line no-await-in-loop
  1347. await this._parseFile(file);
  1348. }
  1349. }
  1350. getDef(name) {
  1351. return this._defs.get(name);
  1352. }
  1353. getClass(name) {
  1354. return this.classes.get(name);
  1355. }
  1356. // Add a subclass to a record, processing template parameters and copying fields
  1357. // This mirrors LLVM's TGParser::AddSubClass behavior
  1358. addSubClass(record) {
  1359. // Track which fields were explicitly defined in the def via 'let' statements
  1360. // These should never be overwritten by parent class fields
  1361. const explicitFields = new Set(record.fields.keys());
  1362. // Step 1: Build initial template bindings for this record from its immediate parents
  1363. const recordBindings = new Map();
  1364. for (const parent of record.parents) {
  1365. const parentClass = this.classes.get(parent.name);
  1366. if (parentClass && parentClass.templateArgs && parentClass.templateArgs.length > 0) {
  1367. const templateArgs = parent.args || [];
  1368. for (let i = 0; i < parentClass.templateArgs.length; i++) {
  1369. const param = parentClass.templateArgs[i];
  1370. let boundValue = null;
  1371. // Check for named argument, positional argument, or default value
  1372. const namedArg = templateArgs.find((arg) => arg.name === param.name);
  1373. if (namedArg) {
  1374. boundValue = namedArg.value;
  1375. } else if (i < templateArgs.length) {
  1376. const arg = templateArgs[i];
  1377. boundValue = arg.name ? arg.value : arg;
  1378. } else if (param.defaultValue) {
  1379. boundValue = param.defaultValue;
  1380. }
  1381. if (boundValue) {
  1382. recordBindings.set(param.name, boundValue);
  1383. }
  1384. }
  1385. }
  1386. }
  1387. record.templateBindings = recordBindings;
  1388. // Step 2: Process parents and flatten fields
  1389. // Helper to resolve Init values using a set of bindings
  1390. // Uses record._resolveInit to avoid code duplication
  1391. const resolveInitValue = (value, bindings) => {
  1392. const resolver = {
  1393. shouldCopy: false,
  1394. resolveReference: (name) => bindings.get(name) || null
  1395. };
  1396. return record._resolveInit(value, resolver);
  1397. };
  1398. const processParent = (parent, currentBindings, visited = new Set()) => {
  1399. if (visited.has(parent.name)) {
  1400. return;
  1401. }
  1402. visited.add(parent.name);
  1403. const parentClass = this.classes.get(parent.name);
  1404. if (!parentClass) {
  1405. return;
  1406. }
  1407. const parentBindings = new Map();
  1408. if (parentClass.templateArgs && parent.args) {
  1409. for (let i = 0; i < parentClass.templateArgs.length && i < parent.args.length; i++) {
  1410. const paramName = parentClass.templateArgs[i].name;
  1411. const argValue = parent.args[i];
  1412. const resolvedArg = resolveInitValue(argValue, currentBindings);
  1413. parentBindings.set(paramName, resolvedArg);
  1414. }
  1415. }
  1416. for (const grandparent of parentClass.parents) {
  1417. processParent(grandparent, parentBindings, visited);
  1418. }
  1419. for (const [fieldName, field] of parentClass.fields) {
  1420. // Only protect fields that were explicitly defined in the def via 'let'
  1421. // Fields inherited from grandparents should be overwritten by parent class fields
  1422. if (explicitFields.has(fieldName)) {
  1423. // Check if the explicit field is empty/uninitialized - if so, copy from parent
  1424. const existingField = record.fields.get(fieldName);
  1425. const existingIsUninit = existingField.value?.type === 'uninitialized';
  1426. const existingIsEmptyDag = existingField.value?.type === 'dag' && existingField.value?.value?.operands?.length === 0;
  1427. const existingIsEmptyString = existingField.value?.type === 'string' && existingField.value?.value === '';
  1428. const existingIsFalseBit = existingField.value?.type === 'int' && existingField.value?.value === 0 && existingField.type?.name === 'bit';
  1429. if (existingIsUninit || existingIsEmptyDag || existingIsEmptyString || existingIsFalseBit) {
  1430. const resolvedField = record._copyAndResolveField(field, parentBindings, parentClass);
  1431. record.fields.set(fieldName, resolvedField);
  1432. }
  1433. // Otherwise, keep the explicit field (child's 'let' wins)
  1434. } else {
  1435. // Field is not explicit - copy from parent (overwrites grandparent values)
  1436. const resolvedField = record._copyAndResolveField(field, parentBindings, parentClass);
  1437. record.fields.set(fieldName, resolvedField);
  1438. }
  1439. }
  1440. };
  1441. for (const parent of record.parents) {
  1442. processParent(parent, recordBindings, new Set());
  1443. }
  1444. }
  1445. async access(file) {
  1446. try {
  1447. await fs.access(file);
  1448. return true;
  1449. } catch {
  1450. // continue regardless of error
  1451. }
  1452. return false;
  1453. }
  1454. async _parseFile(file) {
  1455. let location = null;
  1456. for (const current of this._paths) {
  1457. const test = path.join(current, file);
  1458. // eslint-disable-next-line no-await-in-loop
  1459. if (await this.access(test)) {
  1460. location = path.resolve(test);
  1461. break;
  1462. }
  1463. }
  1464. if (!location) {
  1465. throw new tablegen.Error(`File not found '${file}'.`);
  1466. }
  1467. if (!this._includes.has(location)) {
  1468. this._includes.add(location);
  1469. const content = await fs.readFile(location, 'utf-8');
  1470. this._tokenizer = new tablegen.Tokenizer(content, location);
  1471. while (!this._match('eof')) {
  1472. const token = this._tokenizer.current();
  1473. if (token.type === 'keyword') {
  1474. switch (token.value) {
  1475. case 'include':
  1476. // eslint-disable-next-line no-await-in-loop
  1477. await this._parseInclude();
  1478. break;
  1479. case 'class': this._parseClass(); break;
  1480. case 'def': this._parseDef(); break;
  1481. case 'defm': this._parseDefm(); break;
  1482. case 'let': this._parseLet(); break;
  1483. case 'multiclass': this._parseMulticlass(); break;
  1484. case 'defvar': this._parseDefvar(); break;
  1485. case 'defset': this._parseDefset(); break;
  1486. case 'foreach': this._parseForeach(); break;
  1487. default:
  1488. this._read();
  1489. break;
  1490. }
  1491. } else {
  1492. this._read();
  1493. }
  1494. }
  1495. }
  1496. }
  1497. async _parseInclude() {
  1498. this._read();
  1499. const file = this._expect('string');
  1500. const tokenizer = this._tokenizer;
  1501. await this._parseFile(file);
  1502. this._tokenizer = tokenizer;
  1503. }
  1504. _parseClass() {
  1505. this._read();
  1506. let name = null;
  1507. if (this._match('id')) {
  1508. name = this._expect('id');
  1509. } else if (this._match('number')) {
  1510. name = String(this._expect('number'));
  1511. } else {
  1512. throw new tablegen.Error(`Expected class name but got '${this._tokenizer.current().type}' at ${this._tokenizer.location()}`);
  1513. }
  1514. const record = new tablegen.Record(name, this);
  1515. record.location = this._tokenizer.location();
  1516. if (this._match('<')) {
  1517. record.templateArgs = this._parseTemplateParams();
  1518. }
  1519. if (this._match(':')) {
  1520. record.parents = this._parseParentClassList();
  1521. }
  1522. // Don't process parent classes for class definitions - only for defs/instances
  1523. // Classes are templates that get instantiated later with concrete template arguments
  1524. // Processing parents here with unbound template parameters causes incorrect resolutions
  1525. // this.addSubClass(record);
  1526. if (this._match('{')) {
  1527. this._parseRecordBody(record);
  1528. }
  1529. this.classes.set(name, record);
  1530. }
  1531. _parseDef() {
  1532. this._read();
  1533. let name = '';
  1534. if (this._match('id')) {
  1535. name = this._read();
  1536. } else if (this._match('number')) {
  1537. name = this._read().toString();
  1538. }
  1539. const def = new tablegen.Record(name, this);
  1540. def.location = this._tokenizer.location();
  1541. if (this._match(':')) {
  1542. def.parents = this._parseParentClassList();
  1543. }
  1544. if (this._match('{')) {
  1545. this._parseRecordBody(def);
  1546. }
  1547. this.addSubClass(def);
  1548. def.resolveReferences();
  1549. if (name) {
  1550. this._defs.set(name, def);
  1551. this.defs.push(def);
  1552. }
  1553. }
  1554. _parseDefm() {
  1555. this._read();
  1556. this._skipUntil([';', 'def', 'class', 'defm', 'let', 'multiclass']);
  1557. this._eat(';');
  1558. }
  1559. _parseMulticlass() {
  1560. this._read();
  1561. let depth = 0;
  1562. while (!this._match('eof')) {
  1563. if (this._eat('{')) {
  1564. depth++;
  1565. } else if (this._eat('}')) {
  1566. depth--;
  1567. if (depth === 0) {
  1568. break;
  1569. }
  1570. } else {
  1571. this._read();
  1572. }
  1573. }
  1574. }
  1575. _parseLet() {
  1576. this._read();
  1577. this._skipUntil(['in', '{', ';']);
  1578. this._eat('in');
  1579. this._eat(';');
  1580. }
  1581. _parseDefvar() {
  1582. this._read(); // consume 'defvar'
  1583. this._expect('id'); // variable name
  1584. this._expect('=');
  1585. // Parse the value (could be complex expression)
  1586. this._parseValue();
  1587. this._expect(';');
  1588. // Note: defvar defines local variables used in templates
  1589. // We don't need to store them as they're only used during TableGen evaluation
  1590. // Just consume the syntax for now
  1591. }
  1592. _parseDefset() {
  1593. this._read();
  1594. let depth = 0;
  1595. while (!this._match('eof')) {
  1596. if (this._eat('{')) {
  1597. depth++;
  1598. } else if (this._eat('}')) {
  1599. depth--;
  1600. if (depth === 0) {
  1601. break;
  1602. }
  1603. } else {
  1604. this._read();
  1605. }
  1606. }
  1607. }
  1608. _parseForeach() {
  1609. const location = this._tokenizer.location();
  1610. this._read();
  1611. const iterVarName = this._expect('id');
  1612. this._expect('=');
  1613. const listValue = this._parseForeachListValue();
  1614. this._expect('keyword', 'in');
  1615. const loop = { location, iterVarName, listValue, entries: [], hasDefvar: false };
  1616. if (this._match('{')) {
  1617. this._read();
  1618. this._parseForeachBody(loop);
  1619. this._expect('}');
  1620. } else {
  1621. this._parseForeachBodyStatement(loop);
  1622. }
  1623. this._resolveForeachLoop(loop, new Map());
  1624. }
  1625. _parseForeachListValue() {
  1626. const values = [];
  1627. if (this._eat('[')) {
  1628. while (!this._match(']') && !this._match('eof')) {
  1629. const value = this._parseListItem();
  1630. if (value && value.type === 'dag') {
  1631. const instantiated = this._instantiateClassTemplate(value.value);
  1632. if (instantiated) {
  1633. values.push(instantiated);
  1634. } else {
  1635. values.push(value);
  1636. }
  1637. } else {
  1638. values.push(value);
  1639. }
  1640. this._eat(',');
  1641. }
  1642. this._expect(']');
  1643. } else if (this._eat('!')) {
  1644. const op = this._expect('id');
  1645. if (op === 'range' && this._eat('(')) {
  1646. const args = [];
  1647. while (!this._match(')') && !this._match('eof')) {
  1648. args.push(this._parseValue());
  1649. this._eat(',');
  1650. }
  1651. this._expect(')');
  1652. if (args.length >= 1) {
  1653. let start = 0;
  1654. let end = 0;
  1655. if (args.length === 1) {
  1656. end = this._evaluateSimpleValue(args[0]);
  1657. } else {
  1658. start = this._evaluateSimpleValue(args[0]);
  1659. end = this._evaluateSimpleValue(args[1]);
  1660. }
  1661. for (let i = start; i < end; i++) {
  1662. values.push(new tablegen.Value('int', i));
  1663. }
  1664. }
  1665. } else {
  1666. while (!this._match('keyword', 'in') && !this._match('eof')) {
  1667. this._read();
  1668. }
  1669. }
  1670. } else if (this._eat('{')) {
  1671. const start = this._expect('number');
  1672. if (this._eat('-') || this._eat('...')) {
  1673. const end = this._expect('number');
  1674. for (let i = start; i <= end; i++) {
  1675. values.push(new tablegen.Value('int', i));
  1676. }
  1677. }
  1678. this._expect('}');
  1679. } else {
  1680. while (!this._match('keyword', 'in') && !this._match('eof')) {
  1681. this._read();
  1682. }
  1683. }
  1684. return values;
  1685. }
  1686. _instantiateClassTemplate(dag) {
  1687. if (!dag || !dag.operator) {
  1688. return null;
  1689. }
  1690. const className = typeof dag.operator === 'string' ? dag.operator : dag.operator.value;
  1691. const classRecord = this.classes.get(className);
  1692. if (!classRecord) {
  1693. return null;
  1694. }
  1695. const fields = new Map();
  1696. const bindings = new Map();
  1697. if (classRecord.templateArgs && dag.operands) {
  1698. for (let i = 0; i < classRecord.templateArgs.length && i < dag.operands.length; i++) {
  1699. const paramName = classRecord.templateArgs[i].name;
  1700. const argValue = dag.operands[i].value;
  1701. bindings.set(paramName, argValue);
  1702. }
  1703. }
  1704. for (const [fieldName, field] of classRecord.fields) {
  1705. let resolvedValue = field.value;
  1706. if (resolvedValue && resolvedValue.type === 'def' && bindings.has(resolvedValue.value)) {
  1707. resolvedValue = bindings.get(resolvedValue.value);
  1708. } else if (resolvedValue && resolvedValue.type === 'bang') {
  1709. resolvedValue = this._evaluateBangOp(resolvedValue, bindings);
  1710. }
  1711. fields.set(fieldName, resolvedValue);
  1712. }
  1713. return new tablegen.Value('record_instance', { className, fields });
  1714. }
  1715. _evaluateBangOp(value, bindings) {
  1716. if (!value || value.type !== 'bang') {
  1717. return value;
  1718. }
  1719. const { op, args } = value.value;
  1720. if (op === 'tolower' && args && args.length === 1) {
  1721. let [arg] = args;
  1722. if (arg.type === 'def' && bindings.has(arg.value)) {
  1723. arg = bindings.get(arg.value);
  1724. }
  1725. if (arg.type === 'string') {
  1726. const str = String(arg.value).replace(/^"|"$/g, '');
  1727. return new tablegen.Value('string', str.toLowerCase());
  1728. }
  1729. }
  1730. return value;
  1731. }
  1732. _evaluateSimpleValue(value) {
  1733. if (!value) {
  1734. return 0;
  1735. }
  1736. if (value.type === 'int') {
  1737. return typeof value.value === 'number' ? value.value : parseInt(value.value, 10);
  1738. }
  1739. if (typeof value === 'number') {
  1740. return value;
  1741. }
  1742. return 0;
  1743. }
  1744. _parseForeachBody(loop) {
  1745. while (!this._match('}') && !this._match('eof')) {
  1746. this._parseForeachBodyStatement(loop);
  1747. // If we found defvar, skip the rest of the body since we can't properly expand this loop
  1748. if (loop.hasDefvar) {
  1749. this._skipUntilClosingBrace();
  1750. return;
  1751. }
  1752. }
  1753. }
  1754. // Skip tokens until we reach the matching closing brace (but don't consume it)
  1755. _skipUntilClosingBrace() {
  1756. let depth = 1;
  1757. while (depth > 0 && !this._match('eof')) {
  1758. if (this._match('{')) {
  1759. depth++;
  1760. this._read();
  1761. } else if (this._match('}')) {
  1762. depth--;
  1763. if (depth === 0) {
  1764. // Don't consume the final } - let the caller handle it
  1765. return;
  1766. }
  1767. this._read();
  1768. } else {
  1769. this._read();
  1770. }
  1771. }
  1772. }
  1773. _parseForeachBodyStatement(loop) {
  1774. const token = this._tokenizer.current();
  1775. if (token.type === 'keyword') {
  1776. switch (token.value) {
  1777. case 'def':
  1778. loop.entries.push({ type: 'def', data: this._parseDefTemplate() });
  1779. break;
  1780. case 'defm':
  1781. this._parseDefm();
  1782. break;
  1783. case 'let':
  1784. this._parseLet();
  1785. break;
  1786. case 'defvar':
  1787. loop.hasDefvar = true;
  1788. this._parseDefvar();
  1789. break;
  1790. case 'foreach':
  1791. loop.entries.push({ type: 'foreach', data: this._parseForeachTemplate() });
  1792. break;
  1793. case 'if':
  1794. loop.entries.push({ type: 'foreach', data: this._parseIfAsLoop() });
  1795. break;
  1796. default:
  1797. this._read();
  1798. break;
  1799. }
  1800. } else {
  1801. this._read();
  1802. }
  1803. }
  1804. _parseIfAsLoop() {
  1805. const location = this._tokenizer.location();
  1806. this._read();
  1807. const condition = this._parseValue();
  1808. this._expect('keyword', 'then');
  1809. const loop = {
  1810. location,
  1811. iterVarName: null,
  1812. listValue: [],
  1813. entries: [],
  1814. condition,
  1815. hasDefvar: false,
  1816. isConditional: true
  1817. };
  1818. if (this._match('{')) {
  1819. this._read();
  1820. this._parseForeachBody(loop);
  1821. this._expect('}');
  1822. } else {
  1823. this._parseForeachBodyStatement(loop);
  1824. }
  1825. if (this._match('keyword', 'else')) {
  1826. this._read();
  1827. if (this._match('{')) {
  1828. this._read();
  1829. let depth = 1;
  1830. while (depth > 0 && !this._match('eof')) {
  1831. if (this._eat('{')) {
  1832. depth++;
  1833. } else if (this._eat('}')) {
  1834. depth--;
  1835. } else {
  1836. this._read();
  1837. }
  1838. }
  1839. }
  1840. }
  1841. return loop;
  1842. }
  1843. _parseDefTemplate() {
  1844. this._read();
  1845. const nameTemplate = [];
  1846. while (!this._match(':') && !this._match('{') && !this._match(';') && !this._match('eof')) {
  1847. if (this._match('id')) {
  1848. const value = this._read();
  1849. if (this._eat('.')) {
  1850. const field = this._expect('id');
  1851. nameTemplate.push({ type: 'field_access', base: value, field });
  1852. } else {
  1853. nameTemplate.push({ type: 'id', value });
  1854. }
  1855. } else if (this._match('string')) {
  1856. nameTemplate.push({ type: 'string', value: this._read() });
  1857. } else if (this._match('number')) {
  1858. nameTemplate.push({ type: 'number', value: this._read() });
  1859. } else if (this._eat('#')) {
  1860. nameTemplate.push({ type: 'concat' });
  1861. } else {
  1862. break;
  1863. }
  1864. }
  1865. let parents = [];
  1866. if (this._match(':')) {
  1867. parents = this._parseParentClassList();
  1868. }
  1869. let bodyFields = new Map();
  1870. if (this._match('{')) {
  1871. this._read(); // consume '{'
  1872. bodyFields = this._parseRecordBodyFields();
  1873. this._expect('}');
  1874. }
  1875. this._eat(';');
  1876. return { nameTemplate, parents, bodyFields };
  1877. }
  1878. _parseForeachTemplate() {
  1879. const location = this._tokenizer.location();
  1880. this._read();
  1881. const iterVarName = this._expect('id');
  1882. this._expect('=');
  1883. const listValue = this._parseForeachListValue();
  1884. this._expect('keyword', 'in');
  1885. const loop = { location, iterVarName, listValue, entries: [], hasDefvar: false };
  1886. if (this._match('{')) {
  1887. this._read();
  1888. this._parseForeachBody(loop);
  1889. this._expect('}');
  1890. } else {
  1891. this._parseForeachBodyStatement(loop);
  1892. }
  1893. return loop;
  1894. }
  1895. _parseRecordBodyFields() {
  1896. const fields = new Map();
  1897. while (!this._match('}') && !this._match('eof')) {
  1898. if (this._match('keyword', 'let')) {
  1899. this._read();
  1900. const name = this._expect('id');
  1901. this._expect('=');
  1902. const value = this._parseValue();
  1903. this._eat(';');
  1904. fields.set(name, { name, type: null, value });
  1905. } else if (this._match('keyword', 'field')) {
  1906. this._read();
  1907. const type = this._parseType();
  1908. const name = this._expect('id');
  1909. let value = null;
  1910. if (this._eat('=')) {
  1911. value = this._parseValue();
  1912. }
  1913. this._eat(';');
  1914. fields.set(name, { name, type, value });
  1915. } else if (this._match('keyword', 'assert') || this._match('keyword', 'dump')) {
  1916. // Skip assert and dump statements
  1917. this._skipUntil([';']);
  1918. this._eat(';');
  1919. } else if (this._match('id') || this._match('keyword')) {
  1920. // Type followed by field name
  1921. const type = this._parseType();
  1922. const name = this._expect('id');
  1923. let value = null;
  1924. if (this._eat('=')) {
  1925. value = this._parseValue();
  1926. }
  1927. this._eat(';');
  1928. fields.set(name, { name, type, value });
  1929. } else {
  1930. this._read();
  1931. }
  1932. }
  1933. return fields;
  1934. }
  1935. _resolveForeachLoop(loop, substitutions) {
  1936. if (loop.hasDefvar) {
  1937. return;
  1938. }
  1939. if (loop.entries.length === 0) {
  1940. return;
  1941. }
  1942. if (loop.isConditional) {
  1943. const conditionResult = this._evaluateCondition(loop.condition, substitutions);
  1944. if (conditionResult === false || conditionResult === null) {
  1945. return;
  1946. }
  1947. for (const entry of loop.entries) {
  1948. if (entry.type === 'def') {
  1949. this._instantiateDef(entry.data, substitutions);
  1950. } else if (entry.type === 'foreach') {
  1951. this._resolveForeachLoop(entry.data, substitutions);
  1952. }
  1953. }
  1954. return;
  1955. }
  1956. if (loop.listValue.length === 0) {
  1957. return;
  1958. }
  1959. for (const listItem of loop.listValue) {
  1960. const currentSubs = new Map(substitutions);
  1961. if (loop.iterVarName) {
  1962. currentSubs.set(loop.iterVarName, listItem);
  1963. }
  1964. for (const entry of loop.entries) {
  1965. if (entry.type === 'def') {
  1966. this._instantiateDef(entry.data, currentSubs);
  1967. } else if (entry.type === 'foreach') {
  1968. this._resolveForeachLoop(entry.data, currentSubs);
  1969. }
  1970. }
  1971. }
  1972. }
  1973. _evaluateCondition(condition, substitutions) {
  1974. if (!condition) {
  1975. return null;
  1976. }
  1977. if (condition.type === 'bang' && condition.value) {
  1978. const { op, args } = condition.value;
  1979. if (op === 'ne' && args.length === 2) {
  1980. const a = this._evaluateSimpleExpr(args[0], substitutions);
  1981. const b = this._evaluateSimpleExpr(args[1], substitutions);
  1982. if (a !== null && b !== null) {
  1983. return a !== b;
  1984. }
  1985. }
  1986. if (op === 'eq' && args.length === 2) {
  1987. const a = this._evaluateSimpleExpr(args[0], substitutions);
  1988. const b = this._evaluateSimpleExpr(args[1], substitutions);
  1989. if (a !== null && b !== null) {
  1990. return a === b;
  1991. }
  1992. }
  1993. }
  1994. // Can't evaluate complex conditions
  1995. return null;
  1996. }
  1997. // Evaluate a simple expression for condition evaluation
  1998. _evaluateSimpleExpr(expr, substitutions) {
  1999. if (!expr) {
  2000. return null;
  2001. }
  2002. if (expr.type === 'string') {
  2003. return String(expr.value).replace(/^"|"$/g, '');
  2004. }
  2005. if (expr.type === 'int') {
  2006. return typeof expr.value === 'number' ? expr.value : parseInt(expr.value, 10);
  2007. }
  2008. if ((expr.type === 'def' || expr.type === 'id') && substitutions.has(expr.value)) {
  2009. return this._evaluateSimpleExpr(substitutions.get(expr.value), substitutions);
  2010. }
  2011. return null;
  2012. }
  2013. _instantiateDef(template, substitutions) {
  2014. let name = '';
  2015. for (const part of template.nameTemplate) {
  2016. if (part.type === 'concat') {
  2017. continue;
  2018. } else if (part.type === 'field_access') {
  2019. if (substitutions.has(part.base)) {
  2020. const subValue = substitutions.get(part.base);
  2021. name += this._getFieldValue(subValue, part.field);
  2022. } else {
  2023. name += `${part.base}.${part.field}`;
  2024. }
  2025. } else if (part.type === 'id') {
  2026. if (substitutions.has(part.value)) {
  2027. const subValue = substitutions.get(part.value);
  2028. name += this._valueToString(subValue);
  2029. } else {
  2030. name += part.value;
  2031. }
  2032. } else if (part.type === 'string') {
  2033. name += part.value;
  2034. } else if (part.type === 'number') {
  2035. name += String(part.value);
  2036. }
  2037. }
  2038. const def = new tablegen.Record(name, this);
  2039. def.location = this._tokenizer.location();
  2040. def.parents = template.parents.map((parent) => ({
  2041. name: parent.name,
  2042. args: parent.args ? parent.args.map((arg) => this._substituteValue(arg, substitutions)) : []
  2043. }));
  2044. for (const [fieldName, field] of template.bodyFields) {
  2045. const resolvedValue = field.value ? this._substituteValue(field.value, substitutions) : null;
  2046. def.fields.set(fieldName, new tablegen.RecordVal(fieldName, field.type, resolvedValue));
  2047. }
  2048. this.addSubClass(def);
  2049. def.resolveReferences();
  2050. if (name) {
  2051. this._defs.set(name, def);
  2052. this.defs.push(def);
  2053. }
  2054. }
  2055. _valueToString(value) {
  2056. if (!value) {
  2057. return '';
  2058. }
  2059. if (typeof value === 'string') {
  2060. return value;
  2061. }
  2062. if (typeof value === 'number') {
  2063. return String(value);
  2064. }
  2065. if (value.type === 'string') {
  2066. return String(value.value).replace(/^"|"$/g, '');
  2067. }
  2068. if (value.type === 'int') {
  2069. return String(value.value);
  2070. }
  2071. if (value.type === 'id' || value.type === 'def') {
  2072. return String(value.value);
  2073. }
  2074. return '';
  2075. }
  2076. _getFieldValue(value, fieldName) {
  2077. if (!value) {
  2078. return '';
  2079. }
  2080. if (value.type === 'record_instance' && value.value && value.value.fields) {
  2081. const fieldValue = value.value.fields.get(fieldName);
  2082. return this._valueToString(fieldValue);
  2083. }
  2084. return '';
  2085. }
  2086. _substituteValue(value, substitutions) {
  2087. if (!value) {
  2088. return value;
  2089. }
  2090. if (value.type === 'def' || value.type === 'id') {
  2091. const varName = value.value;
  2092. if (substitutions.has(varName)) {
  2093. return substitutions.get(varName);
  2094. }
  2095. if (typeof varName === 'string' && varName.includes('.')) {
  2096. const [base, field] = varName.split('.', 2);
  2097. if (substitutions.has(base)) {
  2098. const baseValue = substitutions.get(base);
  2099. const fieldValue = this._getFieldValue(baseValue, field);
  2100. if (fieldValue) {
  2101. return new tablegen.Value('string', fieldValue);
  2102. }
  2103. }
  2104. }
  2105. }
  2106. if (value.type === 'list' && Array.isArray(value.value)) {
  2107. return {
  2108. type: 'list',
  2109. value: value.value.map((v) => this._substituteValue(v, substitutions))
  2110. };
  2111. }
  2112. if (value.type === 'dag' && value.value) {
  2113. return {
  2114. type: 'dag',
  2115. value: {
  2116. operator: value.value.operator,
  2117. operands: value.value.operands.map((op) => ({
  2118. value: this._substituteValue(op.value, substitutions),
  2119. name: op.name
  2120. }))
  2121. }
  2122. };
  2123. }
  2124. if (value.type === 'concat' && Array.isArray(value.value)) {
  2125. return {
  2126. type: 'concat',
  2127. value: value.value.map((v) => this._substituteValue(v, substitutions))
  2128. };
  2129. }
  2130. return value;
  2131. }
  2132. _parseTemplateParams() {
  2133. this._read(); // <
  2134. const params = [];
  2135. while (!this._match('>') && !this._match('eof')) {
  2136. const type = this._parseType();
  2137. const name = this._expect('id');
  2138. let defaultValue = null;
  2139. if (this._match('=')) {
  2140. this._read();
  2141. defaultValue = this._parseValue();
  2142. }
  2143. params.push({ name, type, defaultValue });
  2144. if (this._match(',')) {
  2145. this._read();
  2146. }
  2147. }
  2148. this._expect('>');
  2149. return params;
  2150. }
  2151. _parseParentClassList() {
  2152. this._read();
  2153. const parents = [];
  2154. while (!this._match('{') && !this._match(';') && !this._match('eof')) {
  2155. const parent = this._parseType();
  2156. parents.push(parent);
  2157. if (!this._eat(',')) {
  2158. break;
  2159. }
  2160. }
  2161. return parents;
  2162. }
  2163. _parseRecordBody(record) {
  2164. this._read();
  2165. while (!this._match('}') && !this._match('eof')) {
  2166. if (this._match('keyword', 'let')) {
  2167. this._read();
  2168. const name = this._expect('id');
  2169. this._expect('=');
  2170. const value = this._parseValue();
  2171. const field = new tablegen.RecordVal(name, null, value);
  2172. record.fields.set(name, field);
  2173. this._eat(';');
  2174. } else if (this._match('keyword', 'defvar')) {
  2175. this._read();
  2176. const name = this._expect('id');
  2177. this._expect('=');
  2178. const value = this._parseValue();
  2179. const field = new tablegen.RecordVal(name, null, value);
  2180. record.fields.set(name, field);
  2181. this._eat(';');
  2182. } else if (this._match('keyword', 'assert')) {
  2183. this._read();
  2184. this._parseValue(); // condition
  2185. this._eat(',');
  2186. this._parseValue(); // message
  2187. this._eat(';');
  2188. } else if (this._match('keyword', 'bit') || this._match('keyword', 'bits') || this._match('keyword', 'int') ||
  2189. this._match('keyword', 'string') || this._match('keyword', 'list') || this._match('keyword', 'dag') ||
  2190. this._match('keyword', 'code') || this._match('id')) {
  2191. const type = this._parseType();
  2192. // Skip if next token is not an id (handles edge cases in complex nested structures)
  2193. if (!this._match('id')) {
  2194. if (this._match(',') || this._match('>') || this._match(')') || this._match(']')) {
  2195. continue;
  2196. }
  2197. }
  2198. const name = this._expect('id');
  2199. let value = null;
  2200. if (this._eat('=')) {
  2201. value = this._parseValue();
  2202. }
  2203. const field = new tablegen.RecordVal(name, type, value);
  2204. record.fields.set(name, field);
  2205. this._eat(';');
  2206. } else {
  2207. this._read();
  2208. }
  2209. }
  2210. this._expect('}');
  2211. }
  2212. _parseType() {
  2213. let typeName = '';
  2214. if (this._match('keyword', 'bit') || this._match('keyword', 'bits') || this._match('keyword', 'int') ||
  2215. this._match('keyword', 'string') || this._match('keyword', 'list') || this._match('keyword', 'dag') ||
  2216. this._match('keyword', 'code')) {
  2217. typeName = this._read();
  2218. } else if (this._match('id')) {
  2219. typeName = this._read();
  2220. } else if (this._match('number')) {
  2221. typeName = this._read().toString();
  2222. if (this._match('id')) {
  2223. typeName += this._read();
  2224. }
  2225. } else {
  2226. throw new tablegen.Error(`Expected type at ${this._tokenizer.location()}`);
  2227. }
  2228. const type = new tablegen.Type(typeName);
  2229. if (this._eat('<')) {
  2230. type.args = this._parseTemplateArgList();
  2231. }
  2232. return type;
  2233. }
  2234. _parseTemplateArgList() {
  2235. // Parse template arguments directly from token stream
  2236. // Supports both positional (arg1, arg2) and named (name=value) arguments
  2237. const args = [];
  2238. while (!this._match('>') && !this._match('eof')) {
  2239. // Check if this is a named argument: id = value
  2240. if (this._match('id')) {
  2241. const name = this._read();
  2242. if (this._match('=')) {
  2243. // Named argument
  2244. this._read(); // Consume '='
  2245. const value = this._parseValue();
  2246. args.push({ name, value });
  2247. } else {
  2248. // Positional argument that starts with an id
  2249. // Reconstruct the value - id might be part of concat, field access, etc.
  2250. let value = new tablegen.Value('def', name);
  2251. // Handle < > for template instantiation - manually parse with depth tracking
  2252. if (this._eat('<')) {
  2253. const nestedArgs = [];
  2254. let depth = 1;
  2255. let currentArg = [];
  2256. while (depth > 0 && !this._match('eof')) {
  2257. if (this._match('<')) {
  2258. currentArg.push(this._read());
  2259. depth++;
  2260. } else if (this._match('>')) {
  2261. if (depth === 1) {
  2262. // End of this template arg list
  2263. if (currentArg.length > 0) {
  2264. // Parse accumulated tokens properly
  2265. const argStr = currentArg.join(' ');
  2266. // Try to parse as number first
  2267. if (/^-?\d+$/.test(argStr)) {
  2268. nestedArgs.push(new tablegen.Value('int', argStr));
  2269. } else {
  2270. nestedArgs.push(new tablegen.Value('def', argStr));
  2271. }
  2272. }
  2273. this._read(); // consume the >
  2274. depth--;
  2275. } else {
  2276. currentArg.push(this._read());
  2277. depth--;
  2278. }
  2279. } else if (this._match(',') && depth === 1) {
  2280. // Argument separator at current depth
  2281. if (currentArg.length > 0) {
  2282. // Parse accumulated tokens properly
  2283. const argStr = currentArg.join(' ');
  2284. // Try to parse as number first
  2285. if (/^-?\d+$/.test(argStr)) {
  2286. nestedArgs.push(new tablegen.Value('int', argStr));
  2287. } else {
  2288. nestedArgs.push(new tablegen.Value('def', argStr));
  2289. }
  2290. currentArg = [];
  2291. }
  2292. this._read(); // consume comma
  2293. } else {
  2294. // Regular token - add to current arg
  2295. currentArg.push(this._read());
  2296. }
  2297. }
  2298. // Convert to DAG operands
  2299. const operands = nestedArgs.map((arg) => ({ value: arg, name: null }));
  2300. value = new tablegen.Value('dag', new tablegen.DAG(name, operands));
  2301. }
  2302. // Handle field access
  2303. if (this._eat('.')) {
  2304. const field = this._expect('id');
  2305. if (value.type === 'def') {
  2306. value = new tablegen.Value('def', `${name}.${field}`);
  2307. }
  2308. }
  2309. // Handle :: suffix
  2310. if (this._eat('::')) {
  2311. const suffix = this._expect('id');
  2312. if (value.type === 'def') {
  2313. value = new tablegen.Value('def', `${name}::${suffix}`);
  2314. }
  2315. }
  2316. // Handle # concatenation
  2317. if (this._match('#')) {
  2318. const values = [value];
  2319. while (this._match('#')) {
  2320. this._read();
  2321. values.push(this._parsePrimaryValue());
  2322. }
  2323. value = new tablegen.Value('concat', values);
  2324. }
  2325. args.push(value);
  2326. }
  2327. } else {
  2328. const value = this._parseValue();
  2329. args.push(value);
  2330. }
  2331. if (!this._eat(',')) {
  2332. break;
  2333. }
  2334. }
  2335. this._expect('>');
  2336. return args;
  2337. }
  2338. _parseValue() {
  2339. const values = [];
  2340. values.push(this._parsePrimaryValue());
  2341. while (this._match('#') || (values[values.length - 1] && values[values.length - 1].type === 'string' && this._match('string'))) {
  2342. if (this._match('#')) {
  2343. this._read();
  2344. }
  2345. values.push(this._parsePrimaryValue());
  2346. }
  2347. if (values.length === 1) {
  2348. return values[0];
  2349. }
  2350. return new tablegen.Value('concat', values);
  2351. }
  2352. _parseListItem() {
  2353. // Handle $variable as a standalone value in list/dag context
  2354. if (this._eat('$')) {
  2355. const name = this._expect('id');
  2356. return new tablegen.Value('var', name);
  2357. }
  2358. // Special handling for dag-like constructs (id followed by <...>)
  2359. // These need to be parsed as DAGs for trait information
  2360. if (this._match('id')) {
  2361. const name = this._read();
  2362. if (this._eat('<')) {
  2363. const templateArgs = this._parseTemplateArgList();
  2364. const operands = templateArgs.map((arg) => {
  2365. if (arg && typeof arg === 'object' && arg.name && arg.value) {
  2366. return { value: arg.value, name: arg.name };
  2367. }
  2368. return { value: arg, name: null };
  2369. });
  2370. const result = new tablegen.Value('dag', new tablegen.DAG(name, operands));
  2371. if (this._eat('.')) {
  2372. result.field = this._expect('id');
  2373. }
  2374. return result;
  2375. }
  2376. // Not a template instantiation, but might be an identifier with suffixes
  2377. // Put the token back conceptually by creating a def value and checking for suffixes
  2378. let result = new tablegen.Value('def', name);
  2379. // Check for subscripts, field access, etc.
  2380. while (this._eat('[')) {
  2381. const index = this._parseValue();
  2382. this._expect(']');
  2383. result = new tablegen.Value('bang', { op: 'subscript', args: [result, index], field: null });
  2384. }
  2385. if (this._eat('.')) {
  2386. const field = this._expect('id');
  2387. if (result.type === 'def') {
  2388. result = new tablegen.Value('def', `${result.value}.${field}`);
  2389. } else {
  2390. result.value.field = field;
  2391. }
  2392. }
  2393. return result;
  2394. }
  2395. return this._parseValue();
  2396. }
  2397. _parsePrimaryValue() {
  2398. if (this._match('string')) {
  2399. const value = this._read();
  2400. return new tablegen.Value('string', value);
  2401. }
  2402. if (this._match('number')) {
  2403. const value = this._read();
  2404. return new tablegen.Value('int', value);
  2405. }
  2406. if (this._match('true') || this._match('false')) {
  2407. const value = this._read();
  2408. return new tablegen.Value('bit', value);
  2409. }
  2410. if (this._match('code')) {
  2411. const value = this._read();
  2412. return new tablegen.Value('code', value);
  2413. }
  2414. if (this._eat('[')) {
  2415. const items = [];
  2416. while (!this._match(']') && !this._match('eof')) {
  2417. items.push(this._parseListItem());
  2418. this._eat(',');
  2419. }
  2420. this._expect(']');
  2421. if (this._match('<')) {
  2422. this._skip('<', '>');
  2423. }
  2424. return new tablegen.Value('list', items);
  2425. }
  2426. if (this._eat('(')) {
  2427. let operator = null;
  2428. let operatorName = null;
  2429. if (this._match('id')) {
  2430. operator = this._read();
  2431. // Handle template arguments on the operator: (NativeCodeCallVoid<...> ...)
  2432. if (this._match('<')) {
  2433. this._skip('<', '>');
  2434. }
  2435. // Handle operator binding: (OpQ:$op ...)
  2436. if (this._eat(':') && this._eat('$')) {
  2437. if (this._match('id') || this._match('keyword')) {
  2438. operatorName = this._read();
  2439. }
  2440. }
  2441. }
  2442. const operands = [];
  2443. while (!this._match(')') && !this._match('eof')) {
  2444. if (this._eat(',')) {
  2445. continue;
  2446. }
  2447. // Use _parseListItem() to handle template instantiations like Arg<TTG_MemDescType>
  2448. const value = this._parseListItem();
  2449. let name = null;
  2450. if (this._eat(':') && this._eat('$')) {
  2451. if (this._match('id') || this._match('keyword')) {
  2452. name = this._read();
  2453. }
  2454. }
  2455. const operand = { value, name };
  2456. operands.push(operand);
  2457. this._eat(',');
  2458. }
  2459. this._expect(')');
  2460. const dag = new tablegen.DAG(operator, operands);
  2461. if (operatorName) {
  2462. dag.operatorName = operatorName;
  2463. }
  2464. return new tablegen.Value('dag', dag);
  2465. }
  2466. if (this._eat('{')) {
  2467. const fields = new Map();
  2468. while (!this._match('}') && !this._match('eof')) {
  2469. const name = this._expect('id');
  2470. this._expect('=');
  2471. const value = this._parseValue();
  2472. fields.set(name, value);
  2473. this._eat(',');
  2474. }
  2475. this._expect('}');
  2476. return new tablegen.Value('record', fields);
  2477. }
  2478. if (this._eat('!')) {
  2479. let op = null;
  2480. if (this._match('id') || this._match('keyword')) {
  2481. op = this._read();
  2482. } else {
  2483. throw new tablegen.Error(`Expected operator after '!' but got '${this._tokenizer.current().type}' at ${this._tokenizer.location()}`);
  2484. }
  2485. if (this._match('<')) {
  2486. this._skip('<', '>');
  2487. }
  2488. const args = [];
  2489. if (this._eat('(')) {
  2490. if (op === 'cond') {
  2491. while (!this._match(')') && !this._match('eof')) {
  2492. const condition = this._parseValue();
  2493. this._expect(':');
  2494. const value = this._parseValue();
  2495. args.push({ condition, value });
  2496. this._eat(',');
  2497. }
  2498. } else {
  2499. while (!this._match(')') && !this._match('eof')) {
  2500. args.push(this._parseValue());
  2501. this._eat(',');
  2502. }
  2503. }
  2504. this._expect(')');
  2505. }
  2506. let field = null;
  2507. if (this._eat('.')) {
  2508. field = this._expect('id');
  2509. }
  2510. return new tablegen.Value('bang', { op, args, field });
  2511. }
  2512. if (this._match('id') || this._match('keyword')) {
  2513. const value = this._read();
  2514. let result = new tablegen.Value('def', value);
  2515. // Handle various suffixes: templates, subscripts, field access, scope resolution
  2516. while (true) {
  2517. if (this._match('<')) {
  2518. // Template arguments are skipped here - they're parsed properly in
  2519. // DAG context by _parseListItem() which creates DAG values with template args
  2520. this._skip('<', '>');
  2521. } else if (this._eat('[')) {
  2522. // Array subscripting: x[0]
  2523. const index = this._parseValue();
  2524. this._expect(']');
  2525. result = new tablegen.Value('bang', { op: 'subscript', args: [result, index], field: null });
  2526. } else if (this._eat('.')) {
  2527. // Field access: x.field or x[0].field
  2528. const field = this._expect('id');
  2529. if (result.type === 'def') {
  2530. result = new tablegen.Value('def', `${result.value}.${field}`);
  2531. } else {
  2532. // For subscript results, add field access
  2533. result.value.field = field;
  2534. }
  2535. } else if (this._eat('::')) {
  2536. // Scope resolution
  2537. const suffix = this._expect('id');
  2538. if (result.type === 'def') {
  2539. result = new tablegen.Value('def', `${result.value}::${suffix}`);
  2540. }
  2541. break;
  2542. } else {
  2543. break;
  2544. }
  2545. }
  2546. return result;
  2547. }
  2548. if (this._eat('?')) {
  2549. return new tablegen.Value('uninitialized', null);
  2550. }
  2551. if (this._eat('$')) {
  2552. const name = this._expect('id');
  2553. return new tablegen.Value('var', name);
  2554. }
  2555. throw new tablegen.Error(`Unexpected value at ${this._tokenizer.location()}`);
  2556. }
  2557. _read() {
  2558. return this._tokenizer.read().value;
  2559. }
  2560. _match(type, value) {
  2561. const token = this._tokenizer.current();
  2562. return token.type === type && (!value || token.value === value);
  2563. }
  2564. _eat(type, value) {
  2565. if (this._match(type, value)) {
  2566. this._read();
  2567. return true;
  2568. }
  2569. return false;
  2570. }
  2571. _expect(type, value) {
  2572. if (this._match(type, value)) {
  2573. return this._read();
  2574. }
  2575. const token = this._tokenizer.current();
  2576. throw new tablegen.Error(`Expected '${type}' but got '${token.type}' at ${this._tokenizer.location()}`);
  2577. }
  2578. _skip(open, close) {
  2579. if (!this._match(open)) {
  2580. return;
  2581. }
  2582. this._read(); // consume opening token
  2583. let depth = 1;
  2584. while (depth > 0 && !this._match('eof')) {
  2585. if (this._match(open)) {
  2586. depth++;
  2587. } else if (this._match(close)) {
  2588. depth--;
  2589. }
  2590. this._read();
  2591. }
  2592. }
  2593. _skipUntil(types) {
  2594. while (!this._match('eof') && !types.includes(this._tokenizer.current().type)) {
  2595. this._read();
  2596. }
  2597. }
  2598. };
  2599. tablegen.Error = class extends Error {
  2600. constructor(message) {
  2601. super(message);
  2602. this.name = 'TableGen Error';
  2603. }
  2604. };
  2605. export const Reader = tablegen.Reader;