tablegen.js 141 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410
  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. const isNegative = value === '-';
  268. value += this.peek();
  269. this._next();
  270. value += this.peek();
  271. this._next();
  272. while (this._position < this._text.length && /[01]/.test(this.peek())) {
  273. value += this.peek();
  274. this._next();
  275. }
  276. const binaryDigits = isNegative ? value.substring(3) : value.substring(2);
  277. const result = parseInt(binaryDigits, 2);
  278. return new tablegen.Token('number', isNegative ? -result : result, location);
  279. }
  280. // Decimal
  281. while (this._position < this._text.length && this._isDigit(this.peek())) {
  282. value += this.peek();
  283. this._next();
  284. }
  285. return new tablegen.Token('number', parseInt(value, 10), location);
  286. }
  287. _readIdentifier(location) {
  288. let value = '';
  289. while (this._position < this._text.length && this._isIdentifierChar(this.peek())) {
  290. value += this.peek();
  291. this._next();
  292. }
  293. while (this.peek() === '.' && this._isIdentifierStart(this.peek(1))) {
  294. value += this.peek(); // add dot
  295. this._next();
  296. while (this._position < this._text.length && this._isIdentifierChar(this.peek())) {
  297. value += this.peek();
  298. this._next();
  299. }
  300. }
  301. const type = this._keywords.has(value) ? 'keyword' : 'id';
  302. return new tablegen.Token(type, value, location);
  303. }
  304. };
  305. tablegen.Value = class {
  306. constructor(type, value) {
  307. this.type = type; // 'int', 'string', 'bit', 'bits', 'list', 'dag', 'code', 'def'
  308. this.value = value;
  309. }
  310. };
  311. tablegen.DAG = class {
  312. constructor(operator, operands) {
  313. this.operator = operator; // string or Value
  314. this.operands = operands; // array of {value, name}
  315. }
  316. };
  317. tablegen.Type = class {
  318. constructor(name) {
  319. this.name = name;
  320. this.args = [];
  321. }
  322. toString() {
  323. if (this.args.length === 0) {
  324. return this.name;
  325. }
  326. return `${this.name}<${this.args.map((a) => a.toString()).join(', ')}>`;
  327. }
  328. };
  329. tablegen.RecordVal = class {
  330. constructor(name, type, value) {
  331. this.name = name;
  332. this.type = type; // tablegen.Type
  333. this.value = value; // tablegen.Value or null
  334. }
  335. };
  336. tablegen.Record = class {
  337. constructor(name, parser = null) {
  338. this.name = name;
  339. this.parents = [];
  340. this.fields = new Map();
  341. this.templateArgs = [];
  342. this.templateBindings = new Map(); // parameter name -> bound value
  343. this.location = null;
  344. this.parser = parser;
  345. }
  346. getValue(name) {
  347. return this.fields.get(name) || null;
  348. }
  349. // Unified resolution function that handles both parent field copying and post-inheritance resolution
  350. // Inspired by LLVM's Resolver pattern to avoid code duplication
  351. _resolveInit(init, resolver, visited = new Set()) {
  352. if (!init || typeof init !== 'object') {
  353. return init;
  354. }
  355. const key = JSON.stringify(init);
  356. if (visited.has(key)) {
  357. return init;
  358. }
  359. visited.add(key);
  360. // Resolve def/id type references (template parameters or field access)
  361. if ((init.type === 'def' || init.type === 'id') && typeof init.value === 'string') {
  362. // Handle field access syntax (e.g., "meta.mnemonic" or "addrKind.name")
  363. if (init.value.includes('.')) {
  364. const [baseName, ...fieldParts] = init.value.split('.');
  365. // Try to resolve the base
  366. let current = resolver.resolveReference(baseName);
  367. if (current) {
  368. // Walk through field access chain
  369. for (const fieldName of fieldParts) {
  370. // Handle def reference
  371. if (current && current.type === 'def' && typeof current.value === 'string') {
  372. const defName = current.value;
  373. const def = this.parser.getDef(defName) || this.parser.getClass(defName);
  374. if (def) {
  375. const field = def.getValue(fieldName);
  376. if (field && field.value) {
  377. current = field.value;
  378. } else {
  379. return init; // Field not found
  380. }
  381. } else {
  382. return init; // Def not found
  383. }
  384. } else if (current && current.type === 'dag' && current.value) {
  385. // Handle DAG (anonymous class instantiation)
  386. const className = current.value.operator;
  387. const templateArgs = current.value.operands.map((op) => op.value);
  388. // Instantiate an anonymous class with template arguments
  389. // Used for resolving field access like meta.mnemonic where meta is ROCDL_TrLoadOpMeta<...>
  390. let instantiated = null;
  391. const baseClass = this.parser.getClass(className);
  392. if (baseClass) {
  393. // Create an anonymous record that inherits from the class
  394. instantiated = new tablegen.Record(`<anonymous ${className}>`, this.parser);
  395. instantiated.parents = [{ name: className, args: templateArgs }];
  396. this.parser.addSubClass(instantiated);
  397. instantiated.resolveReferences();
  398. }
  399. if (instantiated) {
  400. const field = instantiated.getValue(fieldName);
  401. if (field && field.value) {
  402. current = field.value;
  403. } else {
  404. return init;
  405. }
  406. } else {
  407. return init;
  408. }
  409. } else {
  410. return init; // Can't resolve further
  411. }
  412. }
  413. // Successfully resolved the field access chain
  414. return this._resolveInit(current, resolver, visited);
  415. }
  416. } else {
  417. // Simple reference without dots
  418. const resolved = resolver.resolveReference(init.value);
  419. if (resolved) {
  420. return this._resolveInit(resolved, resolver, visited);
  421. }
  422. }
  423. }
  424. // Recursively resolve nested structures
  425. if (init.type === 'dag' && init.value) {
  426. return {
  427. type: 'dag',
  428. value: {
  429. operator: init.value.operator,
  430. operands: init.value.operands.map((op) => ({
  431. value: this._resolveInit(op.value, resolver, visited),
  432. name: op.name
  433. }))
  434. }
  435. };
  436. }
  437. if (init.type === 'list' && Array.isArray(init.value)) {
  438. return {
  439. type: 'list',
  440. value: init.value.map((v) => this._resolveInit(v, resolver, visited))
  441. };
  442. }
  443. if (init.type === 'bang' && init.value) {
  444. return {
  445. type: 'bang',
  446. value: {
  447. op: init.value.op,
  448. args: init.value.args.map((arg) => this._resolveInit(arg, resolver, visited))
  449. }
  450. };
  451. }
  452. if (init.type === 'concat' && Array.isArray(init.value)) {
  453. const resolvedParts = init.value.map((v) => this._resolveInit(v, resolver, visited));
  454. // Flatten nested concats recursively: if a resolved part is itself a concat,
  455. // extract its parts to avoid nested concat structures that cause double evaluation
  456. const flattenedParts = [];
  457. const flattenConcat = (part) => {
  458. if (part && part.type === 'concat' && Array.isArray(part.value)) {
  459. for (const subPart of part.value) {
  460. flattenConcat(subPart);
  461. }
  462. } else {
  463. flattenedParts.push(part);
  464. }
  465. };
  466. for (const part of resolvedParts) {
  467. flattenConcat(part);
  468. }
  469. return {
  470. type: 'concat',
  471. value: flattenedParts
  472. };
  473. }
  474. // For other types, return shallow copy for parent field copying, as-is for post-inheritance
  475. return resolver.shouldCopy ? { ...init } : init;
  476. }
  477. // Helper to deep copy a field and resolve parameter references in a specific context
  478. _copyAndResolveField(field, bindings, parentClass) {
  479. const resolver = {
  480. shouldCopy: true,
  481. resolveReference: (name) => bindings.get(name) || parentClass.templateBindings.get(name) || null
  482. };
  483. return {
  484. name: field.name,
  485. type: field.type,
  486. value: this._resolveInit(field.value, resolver)
  487. };
  488. }
  489. // Resolve template parameter references in field values
  490. // This matches C++ Record::resolveReferences() behavior
  491. // After inheriting from a templated class, substitute all template parameter
  492. // references in field values with their bound values from templateBindings
  493. resolveReferences() {
  494. if (this.templateBindings.size === 0) {
  495. return; // No template parameters to substitute
  496. }
  497. const findTemplateBinding = (paramName, record = this, visited = new Set()) => {
  498. if (visited.has(record.name)) {
  499. return null;
  500. }
  501. visited.add(record.name);
  502. if (record.templateBindings.has(paramName)) {
  503. return record.templateBindings.get(paramName);
  504. }
  505. for (const parent of record.parents) {
  506. const parentClass = this.parser.classes.get(parent.name);
  507. if (parentClass) {
  508. const paramIndex = parentClass.templateArgs.findIndex((arg) => arg.name === paramName);
  509. if (paramIndex !== -1 && parent.args && parent.args[paramIndex]) {
  510. return parent.args[paramIndex];
  511. }
  512. const binding = findTemplateBinding(paramName, parentClass, visited);
  513. if (binding) {
  514. return binding;
  515. }
  516. }
  517. }
  518. return null;
  519. };
  520. const resolver = {
  521. shouldCopy: false,
  522. resolveReference: (name) => {
  523. const binding = findTemplateBinding(name);
  524. if (binding) {
  525. return binding;
  526. }
  527. const field = this.getValue(name);
  528. if (field && field.value) {
  529. return field.value;
  530. }
  531. return null;
  532. }
  533. };
  534. for (const [, field] of this.fields) {
  535. if (field.value) {
  536. const resolved = this._resolveInit(field.value, resolver);
  537. if (resolved !== field.value) {
  538. field.value = resolved;
  539. }
  540. }
  541. }
  542. }
  543. getValueAsString(fieldName) {
  544. const field = this.getValue(fieldName);
  545. if (!field || !field.value) {
  546. return null;
  547. }
  548. const evaluated = this.evaluateValue(field.value);
  549. if (typeof evaluated === 'string') {
  550. return evaluated;
  551. }
  552. return null;
  553. }
  554. getValueAsBit(fieldName) {
  555. const field = this.getValue(fieldName);
  556. if (!field || !field.value) {
  557. return null;
  558. }
  559. const evaluated = this.evaluateValue(field.value);
  560. if (typeof evaluated === 'boolean') {
  561. return evaluated;
  562. }
  563. if (typeof evaluated === 'number') {
  564. return evaluated !== 0;
  565. }
  566. return null;
  567. }
  568. getValueAsDag(fieldName) {
  569. const field = this.getValue(fieldName);
  570. if (!field || !field.value) {
  571. return null;
  572. }
  573. const evaluated = this.evaluateValue(field.value);
  574. if (evaluated && typeof evaluated === 'object' && evaluated.operator) {
  575. return evaluated;
  576. }
  577. return null;
  578. }
  579. getValueAsDef(fieldName) {
  580. const field = this.getValue(fieldName);
  581. if (!field || !field.value) {
  582. return null;
  583. }
  584. if (field.value.type === 'def' && typeof field.value.value === 'string') {
  585. const defName = field.value.value;
  586. const candidates = [];
  587. for (const def of this.parser.defs) {
  588. if (def.name === defName) {
  589. candidates.push(def);
  590. }
  591. }
  592. for (const cls of this.parser.classes.values()) {
  593. if (cls.name === defName) {
  594. candidates.push(cls);
  595. }
  596. }
  597. if (candidates.length === 0) {
  598. return null;
  599. }
  600. if (candidates.length === 1) {
  601. return candidates[0];
  602. }
  603. const sourceFile = this.location?.file || '';
  604. if (sourceFile) {
  605. const sourceParts = sourceFile.split('/');
  606. let [bestMatch] = candidates;
  607. let bestPrefixLength = -1;
  608. for (const candidate of candidates) {
  609. const candidateFile = candidate.location?.file || '';
  610. const candidateParts = candidateFile.split('/');
  611. let prefixLength = 0;
  612. for (let i = 0; i < Math.min(sourceParts.length, candidateParts.length); i++) {
  613. if (sourceParts[i] !== candidateParts[i]) {
  614. break;
  615. }
  616. prefixLength++;
  617. }
  618. if (prefixLength > bestPrefixLength) {
  619. bestPrefixLength = prefixLength;
  620. bestMatch = candidate;
  621. }
  622. }
  623. return bestMatch;
  624. }
  625. return candidates[0];
  626. }
  627. return null;
  628. }
  629. isEnumAttr() {
  630. const enumBaseClasses = [
  631. 'EnumAttr', // Wrapper for dialect-specific enums
  632. 'IntEnumAttr', 'I32EnumAttr', 'I64EnumAttr',
  633. 'BitEnumAttr', 'I8BitEnumAttr', 'I16BitEnumAttr', 'I32BitEnumAttr', 'I64BitEnumAttr',
  634. 'IntEnum', 'I32IntEnum', 'I64IntEnum', // Integer enum types
  635. 'BitEnum', 'I8BitEnum', 'I16BitEnum', 'I32BitEnum', 'I64BitEnum' // Bit enum types
  636. ];
  637. const checkParents = (record, visited = new Set()) => {
  638. if (record && !visited.has(record.name)) {
  639. visited.add(record.name);
  640. if (enumBaseClasses.includes(record.name)) {
  641. return true;
  642. }
  643. for (const parent of record.parents) {
  644. const parentClass = this.parser.getClass(parent.name);
  645. if (parentClass && checkParents(parentClass, visited)) {
  646. return true;
  647. }
  648. }
  649. }
  650. return false;
  651. };
  652. return checkParents(this);
  653. }
  654. isEnumProp() {
  655. const enumPropBaseClasses = [
  656. 'EnumProp', // Property wrapping an enum
  657. 'EnumPropWithAttrForm' // Property with attribute form
  658. ];
  659. const checkParents = (record, visited = new Set()) => {
  660. if (record && !visited.has(record.name)) {
  661. visited.add(record.name);
  662. if (enumPropBaseClasses.includes(record.name)) {
  663. return true;
  664. }
  665. for (const parent of record.parents) {
  666. const parentClass = this.parser.getClass(parent.name);
  667. if (parentClass && checkParents(parentClass, visited)) {
  668. return true;
  669. }
  670. }
  671. }
  672. return false;
  673. };
  674. return checkParents(this);
  675. }
  676. // Get enum cases from an enum attribute or property definition
  677. getEnumCases() {
  678. if (!this.isEnumAttr() && !this.isEnumProp()) {
  679. return null;
  680. }
  681. // Handle EnumProp - the first argument is the underlying enum
  682. if (this.isEnumProp()) {
  683. for (const parent of this.parents) {
  684. if (parent.name === 'EnumProp' || parent.name === 'EnumPropWithAttrForm') {
  685. if (parent.args && parent.args.length >= 1) {
  686. const [enumInfoArg] = parent.args;
  687. if (enumInfoArg && enumInfoArg.type === 'def' && typeof enumInfoArg.value === 'string') {
  688. const enumName = enumInfoArg.value;
  689. const underlyingEnum = this.parser.getDef(enumName) || this.parser.getClass(enumName);
  690. if (underlyingEnum) {
  691. // Recursively get cases from the underlying enum
  692. return underlyingEnum.getEnumCases();
  693. }
  694. }
  695. }
  696. }
  697. // Recursively search parent classes
  698. const parentClass = this.parser.getClass(parent.name);
  699. if (parentClass && parentClass.isEnumProp()) {
  700. const cases = parentClass.getEnumCases();
  701. if (cases) {
  702. return cases;
  703. }
  704. }
  705. }
  706. }
  707. // Helper to search for EnumAttr in the parent hierarchy
  708. const findEnumAttrParent = (record, visited = new Set()) => {
  709. if (!record || visited.has(record.name)) {
  710. return null;
  711. }
  712. visited.add(record.name);
  713. for (const parent of record.parents) {
  714. if (parent.name === 'EnumAttr') {
  715. return parent;
  716. }
  717. // Recursively search parent classes
  718. const parentClass = this.parser.getClass(parent.name);
  719. if (parentClass) {
  720. const found = findEnumAttrParent(parentClass, visited);
  721. if (found) {
  722. return found;
  723. }
  724. }
  725. }
  726. return null;
  727. };
  728. const enumAttrParent = findEnumAttrParent(this);
  729. // Pattern 0: Handle wrapper classes like OpenMP_EnumAttr, GPU_EnumAttr, etc.
  730. // These are classes that inherit from EnumAttr and take the underlying enum as first arg.
  731. // When we call findEnumAttrParent, it returns EnumAttr's template params (e.g., "enumInfo"),
  732. // not the actual values. So we need to look at this.parents to find the wrapper class
  733. // and extract the actual enum from its args.
  734. for (const parent of this.parents) {
  735. const parentClass = this.parser.getClass(parent.name);
  736. if (parentClass && parentClass !== this) {
  737. // Check if this parent class inherits from EnumAttr
  738. const parentEnumAttr = findEnumAttrParent(parentClass);
  739. if (parentEnumAttr && parent.args && parent.args.length >= 1) {
  740. // The first arg of wrapper classes is typically the underlying enum
  741. for (const arg of parent.args) {
  742. if (arg.type === 'def' && typeof arg.value === 'string') {
  743. const potentialEnum = this.parser.getDef(arg.value) || this.parser.getClass(arg.value);
  744. if (potentialEnum && potentialEnum.isEnumAttr()) {
  745. const cases = potentialEnum.getEnumCases();
  746. if (cases && cases.length > 0) {
  747. return cases;
  748. }
  749. }
  750. }
  751. }
  752. }
  753. }
  754. }
  755. // Pattern 1a: EnumAttr<Dialect, EnumInfo (as def), name>
  756. // The 2nd argument is a reference to the underlying enum (e.g., GPU_Dimension)
  757. if (enumAttrParent && enumAttrParent.args && enumAttrParent.args.length >= 2) {
  758. const [dialectArg, enumInfoArg] = enumAttrParent.args;
  759. if (enumInfoArg && enumInfoArg.type === 'def' && typeof enumInfoArg.value === 'string') {
  760. // Get the expected namespace from the dialect
  761. let expectedNamespace = null;
  762. if (dialectArg && dialectArg.type === 'def') {
  763. const dialectDef = this.parser.getDef(dialectArg.value) || this.parser.getClass(dialectArg.value);
  764. if (dialectDef) {
  765. expectedNamespace = dialectDef.getValueAsString('cppNamespace');
  766. }
  767. }
  768. // Find the enum with the matching name and namespace
  769. // Try all defs with this name in case of conflicts (different namespaces)
  770. const enumName = enumInfoArg.value;
  771. let underlyingEnum = null;
  772. if (expectedNamespace) {
  773. // Normalize namespace for comparison (handle both "vector" and "::mlir::vector" formats)
  774. const normalizeNamespace = (ns) => {
  775. if (!ns) {
  776. return null;
  777. }
  778. // Remove leading :: and extract the last component
  779. const parts = ns.replace(/^::/, '').split('::');
  780. return parts[parts.length - 1];
  781. };
  782. const normalizedExpected = normalizeNamespace(expectedNamespace);
  783. // Search through all defs to find one with matching name AND namespace
  784. for (const def of this.parser.defs) {
  785. if (def.name === enumName) {
  786. const defNamespace = def.getValueAsString('cppNamespace');
  787. const normalizedDef = normalizeNamespace(defNamespace);
  788. if (normalizedDef === normalizedExpected) {
  789. underlyingEnum = def;
  790. break;
  791. }
  792. }
  793. }
  794. }
  795. // Fallback to regular lookup if namespace matching fails
  796. if (!underlyingEnum) {
  797. underlyingEnum = this.parser.getDef(enumName) || this.parser.getClass(enumName);
  798. }
  799. if (underlyingEnum) {
  800. return underlyingEnum.getEnumCases();
  801. }
  802. }
  803. // Pattern 1b: EnumAttr<Dialect, EnumInfo (as DAG), name>
  804. // The 2nd argument is a DAG that instantiates an enum class template
  805. // e.g., EnumAttr<SPIRV_Dialect, SPIRV_I32Enum<"Scope", "desc", [cases]>, "scope">
  806. if (enumInfoArg && enumInfoArg.type === 'dag' && enumInfoArg.value) {
  807. // The DAG operator is the enum class template (e.g., SPIRV_I32Enum)
  808. // We need to find the actual cases by looking at this record's parent args
  809. // which should have the instantiated template parameters
  810. // Search through this record's parents to find one with the cases list
  811. for (const parent of this.parents) {
  812. if (parent.args && parent.args.length >= 3) {
  813. // Look for a list argument that contains enum case defs
  814. for (const arg of parent.args) {
  815. if (arg.type === 'list' && Array.isArray(arg.value)) {
  816. // Check if this looks like an enum case list
  817. const [firstItem] = arg.value;
  818. if (firstItem && firstItem.type === 'def') {
  819. // Try to extract cases from this list
  820. const cases = [];
  821. for (const caseValue of arg.value) {
  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. if (cases.length > 0) {
  833. return cases;
  834. }
  835. }
  836. }
  837. }
  838. }
  839. }
  840. }
  841. }
  842. // Pattern 2: I64EnumAttr<name, summary, [cases]>
  843. // Cases are in the 3rd template argument
  844. for (const parent of this.parents) {
  845. // The 3rd argument should be the cases list
  846. if (parent.args && parent.args.length >= 3) {
  847. const [,,casesArg] = parent.args;
  848. if (casesArg && casesArg.type === 'list' && Array.isArray(casesArg.value)) {
  849. const cases = [];
  850. for (const caseValue of casesArg.value) {
  851. // Each case can be either a DAG or a def reference
  852. if (caseValue.type === 'dag' && caseValue.value) {
  853. // DAG format: I64EnumAttrCase<"symbol", value, "string">
  854. // The string representation is in the 3rd operand, or 1st if only 2 operands
  855. const operands = caseValue.value.operands;
  856. if (operands && operands.length > 0) {
  857. // Try the 3rd operand first (string), fall back to 1st (symbol)
  858. const strOperand = operands.length >= 3 ? operands[2] : operands[0];
  859. if (strOperand && strOperand.value) {
  860. const str = this.evaluateValue(strOperand.value);
  861. if (str && typeof str === 'string') {
  862. cases.push(str);
  863. }
  864. }
  865. }
  866. } else if (caseValue.type === 'def' && typeof caseValue.value === 'string') {
  867. // Def reference format
  868. const caseDef = this.parser.getDef(caseValue.value) || this.parser.getClass(caseValue.value);
  869. if (caseDef) {
  870. const str = caseDef.getValueAsString('str');
  871. if (str) {
  872. cases.push(str);
  873. }
  874. }
  875. }
  876. }
  877. return cases.length > 0 ? cases : null;
  878. }
  879. }
  880. }
  881. // Pattern 3: LLVM_EnumAttr<name, cppType, summary, [cases]>
  882. // Cases are in the 4th template argument
  883. for (const parent of this.parents) {
  884. if (parent.args && parent.args.length >= 4) {
  885. const [,,,casesArg] = parent.args;
  886. if (casesArg && casesArg.type === 'list' && Array.isArray(casesArg.value)) {
  887. const cases = [];
  888. for (const caseValue of casesArg.value) {
  889. // Each case can be either a DAG or a def reference
  890. if (caseValue.type === 'dag' && caseValue.value) {
  891. // DAG format: I32EnumAttrCase<"symbol", value>
  892. // The first operand is the symbol name
  893. const operands = caseValue.value.operands;
  894. if (operands && operands.length > 0) {
  895. const strOperand = operands[0];
  896. if (strOperand && strOperand.value) {
  897. const str = this.evaluateValue(strOperand.value);
  898. if (str && typeof str === 'string') {
  899. cases.push(str);
  900. }
  901. }
  902. }
  903. } else if (caseValue.type === 'def' && typeof caseValue.value === 'string') {
  904. const caseDef = this.parser.getDef(caseValue.value) || this.parser.getClass(caseValue.value);
  905. if (caseDef) {
  906. const str = caseDef.getValueAsString('str');
  907. if (str) {
  908. cases.push(str);
  909. }
  910. }
  911. }
  912. }
  913. return cases.length > 0 ? cases : null;
  914. }
  915. }
  916. }
  917. return null;
  918. }
  919. evaluateValue(value, visited = new Set()) {
  920. if (!value) {
  921. return null;
  922. }
  923. // Handle named values (e.g., { name: 'clauses', value: { type: 'list', ... } })
  924. // These come from named arguments in template instantiation
  925. if (!value.type && value.name && value.value) {
  926. return this.evaluateValue(value.value, visited);
  927. }
  928. switch (value.type) {
  929. case 'string':
  930. return value.value.replace(/^"|"$/g, '');
  931. case 'code':
  932. return value.value;
  933. case 'int':
  934. return parseInt(value.value, 10);
  935. case 'bit':
  936. return value.value === 'true' || value.value === '1';
  937. case 'list':
  938. return value.value.map((v) => this.evaluateValue(v, visited));
  939. case 'concat': {
  940. const parts = value.value.map((v) => this.evaluateValue(v, visited)).filter((v) => v !== null && v !== undefined && v !== '');
  941. return parts.join('');
  942. }
  943. case 'id': {
  944. const fieldName = value.value;
  945. // Guard against circular references
  946. const idKey = `id:${fieldName}`;
  947. if (visited.has(idKey)) {
  948. return null;
  949. }
  950. visited.add(idKey);
  951. if (this.templateBindings.has(fieldName)) {
  952. return this.evaluateValue(this.templateBindings.get(fieldName), visited);
  953. }
  954. const field = this.getValue(fieldName);
  955. if (field && field.value) {
  956. return this.evaluateValue(field.value, visited);
  957. }
  958. return null;
  959. }
  960. case 'def': {
  961. const defName = typeof value.value === 'string' ? value.value : value.value.value;
  962. // Handle TableGen builtin constants
  963. if (defName === 'true') {
  964. return true;
  965. }
  966. if (defName === 'false') {
  967. return false;
  968. }
  969. // Guard against circular references
  970. const defKey = `def:${defName}`;
  971. if (visited.has(defKey)) {
  972. return defName; // Return the def name itself to break the cycle
  973. }
  974. visited.add(defKey);
  975. if (defName.includes('.')) {
  976. const parts = defName.split('.');
  977. const [baseName, ...fieldPath] = parts;
  978. let baseValue = null;
  979. if (this.templateBindings.has(baseName)) {
  980. baseValue = this.evaluateValue(this.templateBindings.get(baseName), visited);
  981. } else {
  982. const field = this.getValue(baseName);
  983. if (field && field.value) {
  984. baseValue = this.evaluateValue(field.value, visited);
  985. }
  986. }
  987. if (typeof baseValue === 'string') {
  988. const def = this.parser.getDef(baseValue) || this.parser.getClass(baseValue);
  989. if (def) {
  990. let current = def;
  991. for (const fieldName of fieldPath) {
  992. const field = current.getValue(fieldName);
  993. if (!field || !field.value) {
  994. return null;
  995. }
  996. const evaluated = current.evaluateValue(field.value, visited);
  997. // If it's another def name, continue navigation
  998. if (typeof evaluated === 'string' && (this.parser.getDef(evaluated) || this.parser.getClass(evaluated))) {
  999. current = this.parser.getDef(evaluated) || this.parser.getClass(evaluated);
  1000. } else {
  1001. return evaluated;
  1002. }
  1003. }
  1004. return null;
  1005. }
  1006. }
  1007. return null;
  1008. }
  1009. if (this.templateBindings.has(defName)) {
  1010. return this.evaluateValue(this.templateBindings.get(defName), visited);
  1011. }
  1012. const field = this.getValue(defName);
  1013. if (field && field.value) {
  1014. return this.evaluateValue(field.value, visited);
  1015. }
  1016. const def = this.parser.getDef(defName) || this.parser.getClass(defName);
  1017. return def ? defName : null;
  1018. }
  1019. case 'bang': {
  1020. const { op, args } = value.value;
  1021. switch (op) {
  1022. case 'if': {
  1023. if (args.length < 2) {
  1024. return null;
  1025. }
  1026. const condition = this.evaluateValue(args[0], visited);
  1027. if (condition) {
  1028. return args.length > 1 ? this.evaluateValue(args[1], visited) : null;
  1029. }
  1030. return args.length > 2 ? this.evaluateValue(args[2], visited) : null;
  1031. }
  1032. case 'empty': {
  1033. if (args.length < 1) {
  1034. return true;
  1035. }
  1036. const val = this.evaluateValue(args[0], visited);
  1037. if (Array.isArray(val)) {
  1038. return val.length === 0;
  1039. }
  1040. if (typeof val === 'string') {
  1041. return val.length === 0;
  1042. }
  1043. return val === null || val === undefined;
  1044. }
  1045. case 'interleave': {
  1046. if (args.length < 2) {
  1047. return '';
  1048. }
  1049. const list = this.evaluateValue(args[0], visited);
  1050. const separator = this.evaluateValue(args[1], visited);
  1051. if (!Array.isArray(list)) {
  1052. return '';
  1053. }
  1054. return list.filter((x) => x !== null && x !== '').join(separator || '');
  1055. }
  1056. case 'not':
  1057. if (args.length < 1) {
  1058. return true;
  1059. }
  1060. return !this.evaluateValue(args[0], visited);
  1061. case 'or':
  1062. for (const arg of args) {
  1063. if (this.evaluateValue(arg, visited)) {
  1064. return true;
  1065. }
  1066. }
  1067. return false;
  1068. case 'and':
  1069. for (const arg of args) {
  1070. if (!this.evaluateValue(arg, visited)) {
  1071. return false;
  1072. }
  1073. }
  1074. return true;
  1075. case 'foldl': {
  1076. // !foldl(init, list, acc, item, expr)
  1077. // Fold left: iterate over list, accumulating results
  1078. if (args.length < 5) {
  1079. return null;
  1080. }
  1081. let accumulator = this.evaluateValue(args[0], visited); // init value
  1082. const list = this.evaluateValue(args[1], visited); // list to fold over
  1083. // args[2] is the accumulator variable name (not evaluated)
  1084. // args[3] is the item variable name (not evaluated)
  1085. // args[4] is the expression to evaluate
  1086. if (!Array.isArray(list)) {
  1087. return accumulator;
  1088. }
  1089. for (const item of list) {
  1090. const accName = args[2].value; // variable name
  1091. const itemName = args[3].value; // variable name
  1092. const prevAcc = this.fields.get(accName);
  1093. const prevItem = this.fields.get(itemName);
  1094. let accValue = null;
  1095. if (accumulator && typeof accumulator === 'object' && accumulator.operator) {
  1096. // It's a DAG object
  1097. accValue = new tablegen.Value('dag', accumulator);
  1098. } else if (Array.isArray(accumulator)) {
  1099. // It's a list
  1100. accValue = new tablegen.Value('list', accumulator);
  1101. } else if (typeof accumulator === 'string') {
  1102. accValue = new tablegen.Value('string', accumulator);
  1103. } else if (typeof accumulator === 'number') {
  1104. accValue = new tablegen.Value('int', accumulator);
  1105. } else {
  1106. accValue = accumulator;
  1107. }
  1108. this.fields.set(accName, new tablegen.RecordVal(accName, null, accValue));
  1109. let itemValue = item;
  1110. if (typeof item === 'string') {
  1111. itemValue = new tablegen.Value('def', item);
  1112. } else if (typeof item === 'number') {
  1113. itemValue = new tablegen.Value('int', item);
  1114. } else if (typeof item === 'boolean') {
  1115. itemValue = new tablegen.Value('bit', item ? '1' : '0');
  1116. } else if (item && typeof item === 'object' && !item.type) {
  1117. itemValue = new tablegen.Value('dag', item);
  1118. }
  1119. this.fields.set(itemName, new tablegen.RecordVal(itemName, null, itemValue));
  1120. accumulator = this.evaluateValue(args[4], new Set()); // Fresh visited set for each iteration
  1121. if (prevAcc) {
  1122. this.fields.set(accName, prevAcc);
  1123. } else {
  1124. this.fields.delete(accName);
  1125. }
  1126. if (prevItem) {
  1127. this.fields.set(itemName, prevItem);
  1128. } else {
  1129. this.fields.delete(itemName);
  1130. }
  1131. }
  1132. return accumulator;
  1133. }
  1134. case 'foreach': {
  1135. // !foreach(item, list, expr)
  1136. // Map: iterate over list, transforming each item
  1137. if (args.length < 3) {
  1138. return [];
  1139. }
  1140. const itemName = args[0].value;
  1141. const list = this.evaluateValue(args[1], visited);
  1142. const results = [];
  1143. if (Array.isArray(list)) {
  1144. for (const item of list) {
  1145. const prevItem = this.fields.get(itemName);
  1146. let itemValue = item;
  1147. if (typeof item === 'string') {
  1148. itemValue = new tablegen.Value('def', item);
  1149. } else if (typeof item === 'number') {
  1150. itemValue = new tablegen.Value('int', item);
  1151. } else if (typeof item === 'boolean') {
  1152. itemValue = new tablegen.Value('bit', item ? '1' : '0');
  1153. } else if (item && typeof item === 'object' && !item.type) {
  1154. itemValue = new tablegen.Value('dag', item);
  1155. }
  1156. this.fields.set(itemName, new tablegen.RecordVal(itemName, null, itemValue));
  1157. const result = this.evaluateValue(args[2], new Set()); // Fresh visited set for each iteration
  1158. results.push(result);
  1159. if (prevItem) {
  1160. this.fields.set(itemName, prevItem);
  1161. } else {
  1162. this.fields.delete(itemName);
  1163. }
  1164. }
  1165. }
  1166. return results;
  1167. }
  1168. case 'filter': {
  1169. // !filter(item, list, predicate)
  1170. // Filter: keep items where predicate is true
  1171. if (args.length < 3) {
  1172. return [];
  1173. }
  1174. const itemName = args[0].value;
  1175. const list = this.evaluateValue(args[1], visited);
  1176. const results = [];
  1177. if (Array.isArray(list)) {
  1178. for (const item of list) {
  1179. const prevItem = this.fields.get(itemName);
  1180. // Wrap item in a Value so it can be used in expressions
  1181. let itemValue = item;
  1182. if (typeof item === 'string') {
  1183. // If it's a def name, wrap it as a 'def' Value
  1184. itemValue = new tablegen.Value('def', item);
  1185. } else if (typeof item === 'number') {
  1186. itemValue = new tablegen.Value('int', item);
  1187. } else if (typeof item === 'boolean') {
  1188. itemValue = new tablegen.Value('bit', item ? '1' : '0');
  1189. } else if (item && typeof item === 'object' && !item.type) {
  1190. // If it's a raw object (like a DAG), wrap it
  1191. itemValue = new tablegen.Value('dag', item);
  1192. }
  1193. // If item is already a Value, use it as is
  1194. this.fields.set(itemName, new tablegen.RecordVal(itemName, null, itemValue));
  1195. const keep = this.evaluateValue(args[2], new Set()); // Fresh visited set for each iteration
  1196. if (keep) {
  1197. results.push(item);
  1198. }
  1199. if (prevItem) {
  1200. this.fields.set(itemName, prevItem);
  1201. } else {
  1202. this.fields.delete(itemName);
  1203. }
  1204. }
  1205. }
  1206. return results;
  1207. }
  1208. case 'con': {
  1209. // !con(dag1, dag2, ...)
  1210. // Concatenate dags - merge operands from multiple dags
  1211. if (args.length === 0) {
  1212. return new tablegen.DAG('ins', []);
  1213. }
  1214. let operator = 'ins';
  1215. const allOperands = [];
  1216. for (const arg of args) {
  1217. let dagToProcess = null;
  1218. const evaluated = this.evaluateValue(arg, visited);
  1219. if (evaluated && typeof evaluated === 'object') {
  1220. if (evaluated.operator && evaluated.operands) {
  1221. dagToProcess = evaluated;
  1222. } else if (evaluated.type === 'dag' && evaluated.value) {
  1223. dagToProcess = evaluated.value;
  1224. }
  1225. }
  1226. if (!dagToProcess && arg.type === 'dag') {
  1227. dagToProcess = arg.value;
  1228. }
  1229. if (dagToProcess && dagToProcess.operands) {
  1230. if (operator === 'ins' && dagToProcess.operator) {
  1231. operator = dagToProcess.operator;
  1232. }
  1233. allOperands.push(...dagToProcess.operands);
  1234. }
  1235. }
  1236. return new tablegen.DAG(operator, allOperands);
  1237. }
  1238. case 'listconcat': {
  1239. // !listconcat(list1, list2, ...)
  1240. // Concatenate multiple lists into one
  1241. const result = [];
  1242. for (const arg of args) {
  1243. const list = this.evaluateValue(arg, visited);
  1244. if (Array.isArray(list)) {
  1245. result.push(...list);
  1246. }
  1247. }
  1248. return result;
  1249. }
  1250. case 'listremove': {
  1251. // !listremove(list, items_to_remove)
  1252. // Remove all occurrences of items_to_remove from list
  1253. if (args.length < 2) {
  1254. return [];
  1255. }
  1256. const list = this.evaluateValue(args[0], visited);
  1257. const toRemove = this.evaluateValue(args[1], visited);
  1258. if (!Array.isArray(list)) {
  1259. return [];
  1260. }
  1261. const removeSet = new Set();
  1262. if (Array.isArray(toRemove)) {
  1263. for (const item of toRemove) {
  1264. removeSet.add(JSON.stringify(item));
  1265. }
  1266. } else {
  1267. removeSet.add(JSON.stringify(toRemove));
  1268. }
  1269. return list.filter((item) => !removeSet.has(JSON.stringify(item)));
  1270. }
  1271. case 'cast': {
  1272. // !cast<type>(value)
  1273. // Cast value to type - for now just convert to string
  1274. if (args.length < 1) {
  1275. return null;
  1276. }
  1277. const val = this.evaluateValue(args[0], visited);
  1278. if (val === null || val === undefined) {
  1279. return null;
  1280. }
  1281. return String(val);
  1282. }
  1283. case 'eq': {
  1284. // !eq(a, b)
  1285. // Return true if a equals b
  1286. if (args.length < 2) {
  1287. return false;
  1288. }
  1289. const a = this.evaluateValue(args[0], visited);
  1290. const b = this.evaluateValue(args[1], visited);
  1291. return a === b;
  1292. }
  1293. case 'ne': {
  1294. // !ne(a, b)
  1295. // Return true if a not equals b
  1296. if (args.length < 2) {
  1297. return false;
  1298. }
  1299. const a = this.evaluateValue(args[0], visited);
  1300. const b = this.evaluateValue(args[1], visited);
  1301. return a !== b;
  1302. }
  1303. case 'lt': {
  1304. // !lt(a, b)
  1305. // Return true if a < b
  1306. if (args.length < 2) {
  1307. return false;
  1308. }
  1309. const a = this.evaluateValue(args[0], visited);
  1310. const b = this.evaluateValue(args[1], visited);
  1311. return a < b;
  1312. }
  1313. case 'le': {
  1314. // !le(a, b)
  1315. // Return true if a <= b
  1316. if (args.length < 2) {
  1317. return false;
  1318. }
  1319. const a = this.evaluateValue(args[0], visited);
  1320. const b = this.evaluateValue(args[1], visited);
  1321. return a <= b;
  1322. }
  1323. case 'gt': {
  1324. // !gt(a, b)
  1325. // Return true if a > b
  1326. if (args.length < 2) {
  1327. return false;
  1328. }
  1329. const a = this.evaluateValue(args[0], visited);
  1330. const b = this.evaluateValue(args[1], visited);
  1331. return a > b;
  1332. }
  1333. case 'ge': {
  1334. // !ge(a, b)
  1335. // Return true if a >= b
  1336. if (args.length < 2) {
  1337. return false;
  1338. }
  1339. const a = this.evaluateValue(args[0], visited);
  1340. const b = this.evaluateValue(args[1], visited);
  1341. return a >= b;
  1342. }
  1343. case 'range': {
  1344. // !range(n) or !range(start, end) or !range(start, end, step)
  1345. // Generate a list of integers
  1346. if (args.length === 0) {
  1347. return [];
  1348. }
  1349. let start = 0;
  1350. let end = 0;
  1351. let step = 1;
  1352. if (args.length === 1) {
  1353. end = this.evaluateValue(args[0], visited);
  1354. } else if (args.length === 2) {
  1355. start = this.evaluateValue(args[0], visited);
  1356. end = this.evaluateValue(args[1], visited);
  1357. } else {
  1358. start = this.evaluateValue(args[0], visited);
  1359. end = this.evaluateValue(args[1], visited);
  1360. step = this.evaluateValue(args[2], visited);
  1361. }
  1362. const result = [];
  1363. if (step > 0) {
  1364. for (let i = start; i < end; i += step) {
  1365. result.push(i);
  1366. }
  1367. } else if (step < 0) {
  1368. for (let i = start; i > end; i += step) {
  1369. result.push(i);
  1370. }
  1371. }
  1372. return result;
  1373. }
  1374. case 'listsplat': {
  1375. // !listsplat(element, n)
  1376. // Create a list with n copies of element
  1377. if (args.length < 2) {
  1378. return [];
  1379. }
  1380. const [element] = args; // Don't evaluate yet, keep as Value
  1381. const count = this.evaluateValue(args[1], visited);
  1382. const result = [];
  1383. for (let i = 0; i < count; i++) {
  1384. result.push(element);
  1385. }
  1386. return result;
  1387. }
  1388. case 'cond': {
  1389. // !cond(condition1: value1, condition2: value2, ..., true: defaultValue)
  1390. // Evaluate conditions in order, return first matching value
  1391. for (let i = 0; i < args.length; i++) {
  1392. const arg = args[i];
  1393. if (arg.condition) {
  1394. const condition = this.evaluateValue(arg.condition, visited);
  1395. if (condition === true || condition === 1) {
  1396. return this.evaluateValue(arg.value, visited);
  1397. }
  1398. }
  1399. }
  1400. return null;
  1401. }
  1402. case 'dag': {
  1403. // !dag(operator, operands_list, names_list)
  1404. // Construct a DAG from operator, operands, and names
  1405. if (args.length < 2) {
  1406. return new tablegen.DAG('ins', []);
  1407. }
  1408. const operatorArg = this.evaluateValue(args[0], visited);
  1409. const operator = typeof operatorArg === 'string' ? operatorArg : 'ins';
  1410. const operandsList = this.evaluateValue(args[1], visited);
  1411. const namesList = args.length > 2 ? this.evaluateValue(args[2], visited) : [];
  1412. const operands = [];
  1413. if (Array.isArray(operandsList)) {
  1414. for (let i = 0; i < operandsList.length; i++) {
  1415. const value = operandsList[i];
  1416. const name = Array.isArray(namesList) && i < namesList.length ? namesList[i] : '';
  1417. operands.push({ value, name });
  1418. }
  1419. }
  1420. return new tablegen.DAG(operator, operands);
  1421. }
  1422. case 'tolower': {
  1423. // !tolower(string)
  1424. // Convert string to lowercase
  1425. if (args.length < 1) {
  1426. return null;
  1427. }
  1428. const str = this.evaluateValue(args[0], visited);
  1429. if (typeof str === 'string') {
  1430. return str.toLowerCase();
  1431. }
  1432. return null;
  1433. }
  1434. case 'toupper': {
  1435. // !toupper(string)
  1436. // Convert string to uppercase
  1437. if (args.length < 1) {
  1438. return null;
  1439. }
  1440. const str = this.evaluateValue(args[0], visited);
  1441. if (typeof str === 'string') {
  1442. return str.toUpperCase();
  1443. }
  1444. return null;
  1445. }
  1446. case 'strconcat': {
  1447. // !strconcat(str1, str2, ...)
  1448. // Concatenate strings
  1449. const parts = [];
  1450. for (const arg of args) {
  1451. const val = this.evaluateValue(arg, visited);
  1452. if (val !== null && val !== undefined) {
  1453. parts.push(String(val));
  1454. }
  1455. }
  1456. return parts.join('');
  1457. }
  1458. case 'subst': {
  1459. // !subst(pattern, replacement, string)
  1460. // Replace all occurrences of pattern with replacement in string
  1461. if (args.length < 3) {
  1462. return null;
  1463. }
  1464. const pattern = this.evaluateValue(args[0], visited);
  1465. const replacement = this.evaluateValue(args[1], visited);
  1466. const str = this.evaluateValue(args[2], visited);
  1467. if (typeof str === 'string' && typeof pattern === 'string') {
  1468. const rep = replacement !== null && replacement !== undefined ? String(replacement) : '';
  1469. // Use split/join for global replacement
  1470. return str.split(pattern).join(rep);
  1471. }
  1472. return str;
  1473. }
  1474. default:
  1475. return null;
  1476. }
  1477. }
  1478. case 'dag': {
  1479. const dag = value.value;
  1480. const evaluatedOperands = dag.operands.map((operand) => {
  1481. if (!operand.value) {
  1482. return operand;
  1483. }
  1484. const valType = operand.value.type;
  1485. if (valType === 'def' || valType === 'id') {
  1486. const refName = operand.value.value;
  1487. if (this.templateBindings.has(refName)) {
  1488. const evaluated = this.evaluateValue(operand.value, visited);
  1489. if (typeof evaluated === 'string') {
  1490. return {
  1491. value: { type: 'def', value: evaluated },
  1492. name: operand.name
  1493. };
  1494. }
  1495. }
  1496. }
  1497. return operand;
  1498. });
  1499. return new tablegen.DAG(dag.operator, evaluatedOperands);
  1500. }
  1501. case 'uninitialized':
  1502. return null;
  1503. default:
  1504. return null;
  1505. }
  1506. }
  1507. };
  1508. tablegen.Reader = class {
  1509. constructor() {
  1510. this._paths = [];
  1511. this._includes = new Set();
  1512. this._defs = new Map();
  1513. this._defvars = new Map();
  1514. this.defs = [];
  1515. this.classes = new Map();
  1516. this.multiclasses = new Map();
  1517. }
  1518. async parse(files, paths) {
  1519. this._paths = paths || [];
  1520. for (const file of files) {
  1521. // eslint-disable-next-line no-await-in-loop
  1522. await this._parseFile(file);
  1523. }
  1524. }
  1525. getDef(name) {
  1526. // Check if this is a defvar alias and resolve it
  1527. if (this._defvars.has(name)) {
  1528. const alias = this._defvars.get(name);
  1529. if (alias && alias.type === 'def' && typeof alias.value === 'string') {
  1530. return this.getDef(alias.value) || this._defs.get(name);
  1531. }
  1532. }
  1533. return this._defs.get(name);
  1534. }
  1535. getClass(name) {
  1536. // Check if this is a defvar alias and resolve it
  1537. if (this._defvars.has(name)) {
  1538. const alias = this._defvars.get(name);
  1539. if (alias && alias.type === 'def' && typeof alias.value === 'string') {
  1540. return this.getClass(alias.value) || this.classes.get(name);
  1541. }
  1542. }
  1543. return this.classes.get(name);
  1544. }
  1545. // Add a subclass to a record, processing template parameters and copying fields
  1546. // This mirrors LLVM's TGParser::AddSubClass behavior
  1547. addSubClass(record) {
  1548. // Track which fields were explicitly defined in the def via 'let' statements
  1549. // These should never be overwritten by parent class fields
  1550. const explicitFields = new Set(record.fields.keys());
  1551. // Step 1: Build initial template bindings for this record from its immediate parents
  1552. const recordBindings = new Map();
  1553. for (const parent of record.parents) {
  1554. const parentClass = this.classes.get(parent.name);
  1555. if (parentClass && parentClass.templateArgs && parentClass.templateArgs.length > 0) {
  1556. const templateArgs = parent.args || [];
  1557. for (let i = 0; i < parentClass.templateArgs.length; i++) {
  1558. const param = parentClass.templateArgs[i];
  1559. let boundValue = null;
  1560. // Check for named argument, positional argument, or default value
  1561. const namedArg = templateArgs.find((arg) => arg.name === param.name);
  1562. if (namedArg) {
  1563. boundValue = namedArg.value;
  1564. } else if (i < templateArgs.length) {
  1565. const arg = templateArgs[i];
  1566. boundValue = arg.name ? arg.value : arg;
  1567. } else if (param.defaultValue) {
  1568. boundValue = param.defaultValue;
  1569. }
  1570. if (boundValue) {
  1571. recordBindings.set(param.name, boundValue);
  1572. }
  1573. }
  1574. }
  1575. }
  1576. record.templateBindings = recordBindings;
  1577. // Step 2: Process parents and flatten fields
  1578. // Helper to resolve Init values using a set of bindings
  1579. // Uses record._resolveInit to avoid code duplication
  1580. const resolveInitValue = (value, bindings) => {
  1581. const resolver = {
  1582. shouldCopy: false,
  1583. resolveReference: (name) => bindings.get(name) || null
  1584. };
  1585. return record._resolveInit(value, resolver);
  1586. };
  1587. const processParent = (parent, currentBindings, visited = new Set()) => {
  1588. if (visited.has(parent.name)) {
  1589. return;
  1590. }
  1591. visited.add(parent.name);
  1592. const parentClass = this.classes.get(parent.name);
  1593. if (!parentClass) {
  1594. return;
  1595. }
  1596. const parentBindings = new Map();
  1597. if (parentClass.templateArgs && parent.args) {
  1598. // Handle named and positional arguments correctly
  1599. // Named arguments have {name, value} format and should match by name
  1600. // Positional arguments should fill remaining slots in order
  1601. const templateArgs = parent.args;
  1602. for (let i = 0; i < parentClass.templateArgs.length; i++) {
  1603. const param = parentClass.templateArgs[i];
  1604. let boundValue = null;
  1605. // Check for named argument first
  1606. const namedArg = templateArgs.find((arg) => arg.name === param.name);
  1607. if (namedArg) {
  1608. boundValue = namedArg.value;
  1609. } else if (i < templateArgs.length) {
  1610. // Positional argument - use if it's not a named arg for another param
  1611. const arg = templateArgs[i];
  1612. boundValue = arg.name ? arg.value : arg;
  1613. } else if (param.defaultValue) {
  1614. boundValue = param.defaultValue;
  1615. }
  1616. if (boundValue) {
  1617. // When resolving default values, also check parentBindings for previously bound template params
  1618. // This handles cases like: class Foo<string sym, string str = sym>
  1619. // where 'str' default value references 'sym' which was bound earlier
  1620. const combinedBindings = new Map([...currentBindings, ...parentBindings]);
  1621. const resolvedArg = resolveInitValue(boundValue, combinedBindings);
  1622. parentBindings.set(param.name, resolvedArg);
  1623. }
  1624. }
  1625. }
  1626. for (const grandparent of parentClass.parents) {
  1627. processParent(grandparent, parentBindings, visited);
  1628. }
  1629. for (const [fieldName, field] of parentClass.fields) {
  1630. // Only protect fields that were explicitly defined in the def via 'let'
  1631. // Fields inherited from grandparents should be overwritten by parent class fields
  1632. if (explicitFields.has(fieldName)) {
  1633. // Check if the explicit field is empty/uninitialized - if so, copy from parent
  1634. const existingField = record.fields.get(fieldName);
  1635. const existingIsUninit = existingField.value && existingField.value.type === 'uninitialized';
  1636. const existingIsEmptyDag = existingField.value && existingField.value.type === 'dag' && existingField.value.value && existingField.value.value.operands && existingField.value.value.operands.length === 0;
  1637. const existingIsEmptyString = existingField.value && existingField.value.type === 'string' && existingField.value.value === '';
  1638. const existingIsFalseBit = existingField.value && existingField.value.type === 'int' && existingField.value.value === 0 && existingField.type && existingField.type.name === 'bit';
  1639. if (existingIsUninit || existingIsEmptyDag || existingIsEmptyString || existingIsFalseBit) {
  1640. const resolvedField = record._copyAndResolveField(field, parentBindings, parentClass);
  1641. record.fields.set(fieldName, resolvedField);
  1642. }
  1643. // Otherwise, keep the explicit field (child's 'let' wins)
  1644. } else {
  1645. // Field is not explicit - copy from parent (overwrites grandparent values)
  1646. const resolvedField = record._copyAndResolveField(field, parentBindings, parentClass);
  1647. record.fields.set(fieldName, resolvedField);
  1648. }
  1649. }
  1650. };
  1651. for (const parent of record.parents) {
  1652. processParent(parent, recordBindings, new Set());
  1653. }
  1654. }
  1655. async access(file) {
  1656. try {
  1657. await fs.access(file);
  1658. return true;
  1659. } catch {
  1660. // continue regardless of error
  1661. }
  1662. return false;
  1663. }
  1664. async _parseFile(file) {
  1665. let location = null;
  1666. for (const current of this._paths) {
  1667. const test = path.join(current, file);
  1668. // eslint-disable-next-line no-await-in-loop
  1669. if (await this.access(test)) {
  1670. location = path.resolve(test);
  1671. break;
  1672. }
  1673. }
  1674. if (!location) {
  1675. throw new tablegen.Error(`File not found '${file}'.`);
  1676. }
  1677. if (!this._includes.has(location)) {
  1678. this._includes.add(location);
  1679. const content = await fs.readFile(location, 'utf-8');
  1680. this._tokenizer = new tablegen.Tokenizer(content, location);
  1681. while (!this._match('eof')) {
  1682. const token = this._tokenizer.current();
  1683. if (token.type === 'keyword') {
  1684. switch (token.value) {
  1685. case 'include':
  1686. // eslint-disable-next-line no-await-in-loop
  1687. await this._parseInclude();
  1688. break;
  1689. case 'class': this._parseClass(); break;
  1690. case 'def': this._parseDef(); break;
  1691. case 'defm': this._parseDefm(); break;
  1692. case 'let': this._parseLet(); break;
  1693. case 'multiclass': this._parseMulticlass(); break;
  1694. case 'defvar': this._parseDefvar(); break;
  1695. case 'defset': this._parseDefset(); break;
  1696. case 'foreach': this._parseForeach(); break;
  1697. default:
  1698. this._read();
  1699. break;
  1700. }
  1701. } else {
  1702. this._read();
  1703. }
  1704. }
  1705. }
  1706. }
  1707. async _parseInclude() {
  1708. this._read();
  1709. const file = this._expect('string');
  1710. const tokenizer = this._tokenizer;
  1711. await this._parseFile(file);
  1712. this._tokenizer = tokenizer;
  1713. }
  1714. _parseClass() {
  1715. this._read();
  1716. let name = null;
  1717. if (this._match('id')) {
  1718. name = this._expect('id');
  1719. } else if (this._match('number')) {
  1720. name = String(this._expect('number'));
  1721. } else {
  1722. throw new tablegen.Error(`Expected class name but got '${this._tokenizer.current().type}' at ${this._tokenizer.location()}`);
  1723. }
  1724. const record = new tablegen.Record(name, this);
  1725. record.location = this._tokenizer.location();
  1726. if (this._match('<')) {
  1727. record.templateArgs = this._parseTemplateParams();
  1728. }
  1729. if (this._match(':')) {
  1730. record.parents = this._parseParentClassList();
  1731. }
  1732. // Don't process parent classes for class definitions - only for defs/instances
  1733. // Classes are templates that get instantiated later with concrete template arguments
  1734. // Processing parents here with unbound template parameters causes incorrect resolutions
  1735. // this.addSubClass(record);
  1736. if (this._match('{')) {
  1737. this._parseRecordBody(record);
  1738. }
  1739. this.classes.set(name, record);
  1740. }
  1741. _parseDef() {
  1742. this._read();
  1743. let name = '';
  1744. if (this._match('id')) {
  1745. name = this._read();
  1746. } else if (this._match('number')) {
  1747. name = this._read().toString();
  1748. }
  1749. const def = new tablegen.Record(name, this);
  1750. def.location = this._tokenizer.location();
  1751. if (this._match(':')) {
  1752. def.parents = this._parseParentClassList();
  1753. }
  1754. if (this._match('{')) {
  1755. this._parseRecordBody(def);
  1756. }
  1757. this.addSubClass(def);
  1758. def.resolveReferences();
  1759. if (name) {
  1760. this._defs.set(name, def);
  1761. this.defs.push(def);
  1762. }
  1763. }
  1764. _parseDefm() {
  1765. this._read(); // consume 'defm'
  1766. // Parse the defm name prefix (may be empty or a concatenated name)
  1767. const namePrefix = [];
  1768. while (!this._match(':') && !this._match(';') && !this._match('eof')) {
  1769. if (this._match('id')) {
  1770. const value = this._read();
  1771. if (this._eat('.')) {
  1772. const field = this._expect('id');
  1773. namePrefix.push({ type: 'field_access', base: value, field });
  1774. } else {
  1775. namePrefix.push({ type: 'id', value });
  1776. }
  1777. } else if (this._match('string')) {
  1778. namePrefix.push({ type: 'string', value: this._read() });
  1779. } else if (this._match('number')) {
  1780. namePrefix.push({ type: 'number', value: this._read() });
  1781. } else if (this._eat('#')) {
  1782. namePrefix.push({ type: 'concat' });
  1783. } else if (this._match('!')) {
  1784. const bangValue = this._parseValue();
  1785. if (bangValue && bangValue.type === 'bang') {
  1786. namePrefix.push({ type: 'bang', value: bangValue.value });
  1787. }
  1788. } else {
  1789. break;
  1790. }
  1791. }
  1792. // Parse the parent multiclass references
  1793. let parents = [];
  1794. if (this._match(':')) {
  1795. parents = this._parseParentClassList();
  1796. }
  1797. this._eat(';');
  1798. // Instantiate each parent multiclass
  1799. for (const parent of parents) {
  1800. this._instantiateMulticlass(parent, namePrefix, new Map());
  1801. }
  1802. }
  1803. _instantiateMulticlass(parent, namePrefix, substitutions) {
  1804. const multiclass = this.multiclasses.get(parent.name);
  1805. if (!multiclass) {
  1806. // Might be a regular class, not a multiclass - skip
  1807. return;
  1808. }
  1809. // Build template argument bindings
  1810. const bindings = new Map(substitutions);
  1811. if (multiclass.templateArgs && parent.args) {
  1812. for (let i = 0; i < multiclass.templateArgs.length && i < parent.args.length; i++) {
  1813. const param = multiclass.templateArgs[i];
  1814. const arg = parent.args[i];
  1815. // Handle both {name, value} format (named arg) and tablegen.Value format (positional arg)
  1816. // For named args like {name: 'foo', value: <Value>}, use the value property
  1817. // For positional args that are already Values, use them directly
  1818. let argValue = null;
  1819. if (arg && arg.name && arg.value !== undefined) {
  1820. // Named argument: {name: 'foo', value: <Value>}
  1821. argValue = arg.value;
  1822. } else if (arg && arg.type !== undefined) {
  1823. // This is a tablegen.Value object, use it directly
  1824. argValue = arg;
  1825. } else {
  1826. argValue = arg;
  1827. }
  1828. bindings.set(param.name, argValue);
  1829. }
  1830. }
  1831. // Process inherited parent multiclasses first
  1832. for (const mcParent of multiclass.parents) {
  1833. const resolvedParent = this._resolveMulticlassParent(mcParent, bindings);
  1834. this._instantiateMulticlass(resolvedParent, namePrefix, bindings);
  1835. }
  1836. // Process entries
  1837. for (const entry of multiclass.entries) {
  1838. if (entry.type === 'def') {
  1839. this._instantiateMulticlassDef(entry.data, namePrefix, bindings);
  1840. } else if (entry.type === 'defm') {
  1841. // Nested defm - instantiate the referenced multiclass(es)
  1842. for (const defmParent of entry.data.parents) {
  1843. const resolvedParent = this._resolveMulticlassParent(defmParent, bindings);
  1844. const combinedPrefix = [...namePrefix, ...entry.data.nameTemplate];
  1845. this._instantiateMulticlass(resolvedParent, combinedPrefix, bindings);
  1846. }
  1847. } else if (entry.type === 'foreach') {
  1848. this._resolveMulticlassForeach(entry.data, namePrefix, bindings);
  1849. } else if (entry.type === 'defvar') {
  1850. const value = this._evaluateDefvar(entry.data.value, bindings);
  1851. bindings.set(entry.data.name, value);
  1852. }
  1853. }
  1854. }
  1855. _resolveMulticlassParent(parent, bindings) {
  1856. // Resolve template arguments in parent reference
  1857. const resolvedArgs = [];
  1858. if (parent.args) {
  1859. for (const arg of parent.args) {
  1860. const argValue = arg.value === undefined ? arg : arg.value;
  1861. if (argValue && argValue.type === 'def' && bindings.has(argValue.value)) {
  1862. resolvedArgs.push({ value: bindings.get(argValue.value) });
  1863. } else {
  1864. resolvedArgs.push(arg);
  1865. }
  1866. }
  1867. }
  1868. return { name: parent.name, args: resolvedArgs };
  1869. }
  1870. _resolveMulticlassForeach(loop, namePrefix, bindings) {
  1871. if (loop.isConditional) {
  1872. const conditionResult = this._evaluateCondition(loop.condition, bindings);
  1873. if (conditionResult === false || conditionResult === null) {
  1874. return;
  1875. }
  1876. for (const entry of loop.entries) {
  1877. if (entry.type === 'def') {
  1878. this._instantiateMulticlassDef(entry.data, namePrefix, bindings);
  1879. } else if (entry.type === 'foreach') {
  1880. this._resolveMulticlassForeach(entry.data, namePrefix, bindings);
  1881. } else if (entry.type === 'defvar') {
  1882. const value = this._evaluateDefvar(entry.data.value, bindings);
  1883. bindings.set(entry.data.name, value);
  1884. }
  1885. }
  1886. return;
  1887. }
  1888. if (!loop.listValue || loop.listValue.length === 0) {
  1889. return;
  1890. }
  1891. for (const listItem of loop.listValue) {
  1892. const currentBindings = new Map(bindings);
  1893. if (loop.iterVarName) {
  1894. currentBindings.set(loop.iterVarName, listItem);
  1895. }
  1896. for (const entry of loop.entries) {
  1897. if (entry.type === 'def') {
  1898. this._instantiateMulticlassDef(entry.data, namePrefix, currentBindings);
  1899. } else if (entry.type === 'foreach') {
  1900. this._resolveMulticlassForeach(entry.data, namePrefix, currentBindings);
  1901. } else if (entry.type === 'defvar') {
  1902. const value = this._evaluateDefvar(entry.data.value, currentBindings);
  1903. currentBindings.set(entry.data.name, value);
  1904. }
  1905. }
  1906. }
  1907. }
  1908. _resolveTemplateValue(value, bindings) {
  1909. if (!value) {
  1910. return value;
  1911. }
  1912. if (value.type === 'string' || value.type === 'int' || value.type === 'code') {
  1913. return value;
  1914. }
  1915. if (value.type === 'def' || value.type === 'id') {
  1916. const name = typeof value.value === 'string' ? value.value : (value.value && value.value.value);
  1917. if (name && bindings.has(name)) {
  1918. return bindings.get(name);
  1919. }
  1920. return value;
  1921. }
  1922. if (value.type === 'bang') {
  1923. const resolved = this._evaluateDefvar(value, bindings);
  1924. return resolved;
  1925. }
  1926. if (value.type === 'list' && Array.isArray(value.value)) {
  1927. return new tablegen.Value('list', value.value.map((v) => this._resolveTemplateValue(v, bindings)));
  1928. }
  1929. return value;
  1930. }
  1931. _instantiateMulticlassDef(template, namePrefix, bindings) {
  1932. // Build the full name from prefix + template name
  1933. const fullNameTemplate = [...namePrefix, ...template.nameTemplate];
  1934. // Resolve name template to get the actual def name
  1935. let name = '';
  1936. for (const part of fullNameTemplate) {
  1937. if (part.type === 'concat') {
  1938. continue;
  1939. }
  1940. if (part.type === 'string') {
  1941. name += String(part.value).replace(/^"|"$/g, '');
  1942. } else if (part.type === 'number') {
  1943. name += String(part.value);
  1944. } else if (part.type === 'id') {
  1945. if (bindings.has(part.value)) {
  1946. const resolved = bindings.get(part.value);
  1947. if (resolved && resolved.type === 'string') {
  1948. name += String(resolved.value).replace(/^"|"$/g, '');
  1949. } else if (resolved && resolved.type === 'int') {
  1950. name += String(resolved.value);
  1951. } else if (typeof resolved === 'string') {
  1952. name += resolved;
  1953. } else {
  1954. name += part.value;
  1955. }
  1956. } else {
  1957. name += part.value;
  1958. }
  1959. } else if (part.type === 'field_access') {
  1960. const baseName = part.base;
  1961. if (bindings.has(baseName)) {
  1962. const baseValue = bindings.get(baseName);
  1963. if (baseValue && baseValue.type === 'record_instance' && baseValue.value && baseValue.value.fields) {
  1964. const fieldValue = baseValue.value.fields.get(part.field);
  1965. if (fieldValue && fieldValue.type === 'string') {
  1966. name += String(fieldValue.value).replace(/^"|"$/g, '');
  1967. }
  1968. }
  1969. }
  1970. } else if (part.type === 'bang') {
  1971. const evaluated = this._evaluateDefvar({ type: 'bang', value: part.value }, bindings);
  1972. if (evaluated && evaluated.type === 'string') {
  1973. name += String(evaluated.value).replace(/^"|"$/g, '');
  1974. }
  1975. }
  1976. }
  1977. // Skip if already defined (avoid duplicates)
  1978. if (this._defs.has(name)) {
  1979. return;
  1980. }
  1981. // Create the def record
  1982. const def = new tablegen.Record(name, this);
  1983. def.location = this._tokenizer ? this._tokenizer.location() : null;
  1984. // Store template bindings in the def for later resolution
  1985. def.templateBindings = new Map(bindings);
  1986. // Resolve parent classes with template bindings
  1987. const resolvedParents = [];
  1988. for (const parent of template.parents) {
  1989. const resolvedArgs = [];
  1990. if (parent.args) {
  1991. for (const arg of parent.args) {
  1992. // arg could be:
  1993. // - A tablegen.Value object: {type: '...', value: ...}
  1994. // - A named arg: {name: '...', value: <Value>}
  1995. let argValue = null;
  1996. let argName = null;
  1997. if (arg && arg.type !== undefined) {
  1998. // This is a tablegen.Value, use it directly
  1999. argValue = arg;
  2000. argName = null;
  2001. } else if (arg && arg.name && arg.value !== undefined) {
  2002. // This is a named arg, extract the value
  2003. argValue = arg.value;
  2004. argName = arg.name;
  2005. } else {
  2006. argValue = arg;
  2007. argName = null;
  2008. }
  2009. // Resolve the argument value with template bindings
  2010. const resolved = this._resolveTemplateValue(argValue, bindings);
  2011. // Push in format expected by addSubClass: named args as {name, value}, others as direct Value
  2012. if (argName) {
  2013. resolvedArgs.push({ name: argName, value: resolved });
  2014. } else {
  2015. resolvedArgs.push(resolved);
  2016. }
  2017. }
  2018. }
  2019. resolvedParents.push({ name: parent.name, args: resolvedArgs });
  2020. }
  2021. def.parents = resolvedParents;
  2022. // Copy body fields and resolve them
  2023. if (template.bodyFields) {
  2024. for (const [fieldName, field] of template.bodyFields) {
  2025. const resolvedValue = this._resolveTemplateValue(field.value, bindings);
  2026. def.fields.set(fieldName, new tablegen.RecordVal(fieldName, field.type, resolvedValue));
  2027. }
  2028. }
  2029. this.addSubClass(def);
  2030. def.resolveReferences();
  2031. if (name) {
  2032. this._defs.set(name, def);
  2033. this.defs.push(def);
  2034. }
  2035. }
  2036. _parseMulticlass() {
  2037. this._read(); // consume 'multiclass'
  2038. const name = this._expect('id');
  2039. const multiclass = {
  2040. name,
  2041. templateArgs: [],
  2042. parents: [],
  2043. entries: []
  2044. };
  2045. // Parse template parameters
  2046. if (this._match('<')) {
  2047. multiclass.templateArgs = this._parseTemplateParams();
  2048. }
  2049. // Parse parent multiclass references
  2050. if (this._match(':')) {
  2051. multiclass.parents = this._parseParentClassList();
  2052. }
  2053. // Parse multiclass body
  2054. if (this._eat('{')) {
  2055. while (!this._match('}') && !this._match('eof')) {
  2056. const token = this._tokenizer.current();
  2057. if (token.type === 'keyword') {
  2058. switch (token.value) {
  2059. case 'def':
  2060. multiclass.entries.push({ type: 'def', data: this._parseDefTemplate() });
  2061. break;
  2062. case 'defm':
  2063. multiclass.entries.push({ type: 'defm', data: this._parseDefmTemplate() });
  2064. break;
  2065. case 'foreach':
  2066. multiclass.entries.push({ type: 'foreach', data: this._parseForeachTemplate() });
  2067. break;
  2068. case 'if':
  2069. multiclass.entries.push({ type: 'foreach', data: this._parseIfAsLoop() });
  2070. break;
  2071. case 'let':
  2072. this._parseLet();
  2073. break;
  2074. case 'defvar':
  2075. multiclass.entries.push({ type: 'defvar', data: this._parseDefvarTemplate() });
  2076. break;
  2077. default:
  2078. this._read();
  2079. break;
  2080. }
  2081. } else {
  2082. this._read();
  2083. }
  2084. }
  2085. this._expect('}');
  2086. }
  2087. this.multiclasses.set(name, multiclass);
  2088. }
  2089. _parseDefmTemplate() {
  2090. this._read(); // consume 'defm'
  2091. const nameTemplate = [];
  2092. // Parse name template (similar to _parseDefTemplate but for nested defm)
  2093. while (!this._match(':') && !this._match(';') && !this._match('eof')) {
  2094. if (this._match('id')) {
  2095. const value = this._read();
  2096. if (this._eat('.')) {
  2097. const field = this._expect('id');
  2098. nameTemplate.push({ type: 'field_access', base: value, field });
  2099. } else {
  2100. nameTemplate.push({ type: 'id', value });
  2101. }
  2102. } else if (this._match('string')) {
  2103. nameTemplate.push({ type: 'string', value: this._read() });
  2104. } else if (this._match('number')) {
  2105. nameTemplate.push({ type: 'number', value: this._read() });
  2106. } else if (this._eat('#')) {
  2107. nameTemplate.push({ type: 'concat' });
  2108. } else if (this._match('!')) {
  2109. const bangValue = this._parseValue();
  2110. if (bangValue && bangValue.type === 'bang') {
  2111. nameTemplate.push({ type: 'bang', value: bangValue.value });
  2112. }
  2113. } else {
  2114. break;
  2115. }
  2116. }
  2117. let parents = [];
  2118. if (this._match(':')) {
  2119. parents = this._parseParentClassList();
  2120. }
  2121. this._eat(';');
  2122. return { nameTemplate, parents };
  2123. }
  2124. _parseLet() {
  2125. this._read(); // consume 'let'
  2126. // Skip until we find 'in' keyword, '{', or ';'
  2127. while (!this._match('eof') && !this._match('keyword', 'in') && !this._match('{') && !this._match(';')) {
  2128. this._read();
  2129. }
  2130. if (this._match('keyword', 'in')) {
  2131. this._read(); // consume 'in'
  2132. // After 'in', we can have:
  2133. // - '{' for a block of statements
  2134. // - ';' for no body
  2135. // - A statement like 'def', 'defm', etc.
  2136. if (this._match('{')) {
  2137. this._read();
  2138. // Parse the contents of the let block - don't skip them!
  2139. // This handles 'let opDocGroup = ... in { def X, def Y, ... }'
  2140. this._parseLetBlockBody();
  2141. this._expect('}');
  2142. } else if (this._eat(';')) {
  2143. // Just a let declaration, no body
  2144. }
  2145. // Otherwise, fall through to let the main loop handle the next statement
  2146. // (e.g., 'let ... in def X' - the def will be parsed by the main loop)
  2147. } else {
  2148. this._eat('{');
  2149. this._eat(';');
  2150. }
  2151. }
  2152. _parseLetBlockBody() {
  2153. // Parse statements inside a 'let ... in { ... }' block
  2154. while (!this._match('}') && !this._match('eof')) {
  2155. const token = this._tokenizer.current();
  2156. if (token.type === 'keyword') {
  2157. switch (token.value) {
  2158. case 'def': this._parseDef(); break;
  2159. case 'defm': this._parseDefm(); break;
  2160. case 'let': this._parseLet(); break;
  2161. case 'multiclass': this._parseMulticlass(); break;
  2162. case 'defvar': this._parseDefvar(); break;
  2163. case 'defset': this._parseDefset(); break;
  2164. case 'foreach': this._parseForeach(); break;
  2165. case 'class': this._parseClass(); break;
  2166. default:
  2167. this._read();
  2168. break;
  2169. }
  2170. } else {
  2171. this._read();
  2172. }
  2173. }
  2174. }
  2175. _parseDefvar() {
  2176. this._read(); // consume 'defvar'
  2177. const name = this._expect('id'); // variable name
  2178. this._expect('=');
  2179. // Parse and store the value - needed for resolving type aliases like
  2180. // defvar Util_GlobalRefAttr = FlatSymbolRefAttr;
  2181. const value = this._parseValue();
  2182. this._expect(';');
  2183. // Store defvar for alias resolution (e.g., type constraint lookups)
  2184. if (name && value) {
  2185. this._defvars.set(name, value);
  2186. }
  2187. }
  2188. _parseDefset() {
  2189. this._read();
  2190. let depth = 0;
  2191. while (!this._match('eof')) {
  2192. if (this._eat('{')) {
  2193. depth++;
  2194. } else if (this._eat('}')) {
  2195. depth--;
  2196. if (depth === 0) {
  2197. break;
  2198. }
  2199. } else {
  2200. this._read();
  2201. }
  2202. }
  2203. }
  2204. _parseForeach() {
  2205. const location = this._tokenizer.location();
  2206. this._read();
  2207. const iterVarName = this._expect('id');
  2208. this._expect('=');
  2209. const listValue = this._parseForeachListValue();
  2210. this._expect('keyword', 'in');
  2211. const loop = { location, iterVarName, listValue, entries: [] };
  2212. if (this._match('{')) {
  2213. this._read();
  2214. this._parseForeachBody(loop);
  2215. this._expect('}');
  2216. } else {
  2217. this._parseForeachBodyStatement(loop);
  2218. }
  2219. this._resolveForeachLoop(loop, new Map());
  2220. }
  2221. _parseForeachListValue() {
  2222. const values = [];
  2223. if (this._eat('[')) {
  2224. while (!this._match(']') && !this._match('eof')) {
  2225. const value = this._parseListItem();
  2226. if (value && value.type === 'dag') {
  2227. const instantiated = this._instantiateClassTemplate(value.value);
  2228. if (instantiated) {
  2229. values.push(instantiated);
  2230. } else {
  2231. values.push(value);
  2232. }
  2233. } else {
  2234. values.push(value);
  2235. }
  2236. this._eat(',');
  2237. }
  2238. this._expect(']');
  2239. } else if (this._eat('!')) {
  2240. const op = this._expect('id');
  2241. if (op === 'range' && this._eat('(')) {
  2242. const args = [];
  2243. while (!this._match(')') && !this._match('eof')) {
  2244. args.push(this._parseValue());
  2245. this._eat(',');
  2246. }
  2247. this._expect(')');
  2248. if (args.length >= 1) {
  2249. let start = 0;
  2250. let end = 0;
  2251. if (args.length === 1) {
  2252. end = this._evaluateSimpleValue(args[0]);
  2253. } else {
  2254. start = this._evaluateSimpleValue(args[0]);
  2255. end = this._evaluateSimpleValue(args[1]);
  2256. }
  2257. for (let i = start; i < end; i++) {
  2258. values.push(new tablegen.Value('int', i));
  2259. }
  2260. }
  2261. } else {
  2262. while (!this._match('keyword', 'in') && !this._match('eof')) {
  2263. this._read();
  2264. }
  2265. }
  2266. } else if (this._eat('{')) {
  2267. const start = this._expect('number');
  2268. if (this._eat('-') || this._eat('...')) {
  2269. const end = this._expect('number');
  2270. for (let i = start; i <= end; i++) {
  2271. values.push(new tablegen.Value('int', i));
  2272. }
  2273. } else {
  2274. // Single value: {5} means just the value 5
  2275. values.push(new tablegen.Value('int', start));
  2276. }
  2277. this._expect('}');
  2278. } else {
  2279. while (!this._match('keyword', 'in') && !this._match('eof')) {
  2280. this._read();
  2281. }
  2282. }
  2283. return values;
  2284. }
  2285. _instantiateClassTemplate(dag) {
  2286. if (!dag || !dag.operator) {
  2287. return null;
  2288. }
  2289. const className = typeof dag.operator === 'string' ? dag.operator : dag.operator.value;
  2290. const classRecord = this.classes.get(className);
  2291. if (!classRecord) {
  2292. return null;
  2293. }
  2294. const fields = new Map();
  2295. const bindings = new Map();
  2296. if (classRecord.templateArgs && dag.operands) {
  2297. for (let i = 0; i < classRecord.templateArgs.length && i < dag.operands.length; i++) {
  2298. const paramName = classRecord.templateArgs[i].name;
  2299. const argValue = dag.operands[i].value;
  2300. bindings.set(paramName, argValue);
  2301. }
  2302. }
  2303. for (const [fieldName, field] of classRecord.fields) {
  2304. let resolvedValue = field.value;
  2305. if (resolvedValue && resolvedValue.type === 'def' && bindings.has(resolvedValue.value)) {
  2306. resolvedValue = bindings.get(resolvedValue.value);
  2307. } else if (resolvedValue && resolvedValue.type === 'bang') {
  2308. resolvedValue = this._evaluateBangOp(resolvedValue, bindings);
  2309. }
  2310. fields.set(fieldName, resolvedValue);
  2311. }
  2312. return new tablegen.Value('record_instance', { className, fields });
  2313. }
  2314. _evaluateBangOp(value, bindings) {
  2315. if (!value || value.type !== 'bang') {
  2316. return value;
  2317. }
  2318. const { op, args } = value.value;
  2319. if (op === 'tolower' && args && args.length === 1) {
  2320. let [arg] = args;
  2321. if (arg.type === 'def' && bindings.has(arg.value)) {
  2322. arg = bindings.get(arg.value);
  2323. }
  2324. if (arg.type === 'string') {
  2325. const str = String(arg.value).replace(/^"|"$/g, '');
  2326. return new tablegen.Value('string', str.toLowerCase());
  2327. }
  2328. }
  2329. return value;
  2330. }
  2331. _evaluateSimpleValue(value) {
  2332. if (!value) {
  2333. return 0;
  2334. }
  2335. if (value.type === 'int') {
  2336. return typeof value.value === 'number' ? value.value : parseInt(value.value, 10);
  2337. }
  2338. if (typeof value === 'number') {
  2339. return value;
  2340. }
  2341. return 0;
  2342. }
  2343. _parseForeachBody(loop) {
  2344. while (!this._match('}') && !this._match('eof')) {
  2345. this._parseForeachBodyStatement(loop);
  2346. }
  2347. }
  2348. _parseForeachBodyStatement(loop) {
  2349. const token = this._tokenizer.current();
  2350. if (token.type === 'keyword') {
  2351. switch (token.value) {
  2352. case 'def':
  2353. loop.entries.push({ type: 'def', data: this._parseDefTemplate() });
  2354. break;
  2355. case 'defm':
  2356. this._parseDefm();
  2357. break;
  2358. case 'let':
  2359. this._parseLet();
  2360. break;
  2361. case 'defvar':
  2362. loop.entries.push({ type: 'defvar', data: this._parseDefvarTemplate() });
  2363. break;
  2364. case 'foreach':
  2365. loop.entries.push({ type: 'foreach', data: this._parseForeachTemplate() });
  2366. break;
  2367. case 'if':
  2368. loop.entries.push({ type: 'foreach', data: this._parseIfAsLoop() });
  2369. break;
  2370. default:
  2371. this._read();
  2372. break;
  2373. }
  2374. } else {
  2375. this._read();
  2376. }
  2377. }
  2378. _parseIfAsLoop() {
  2379. const location = this._tokenizer.location();
  2380. this._read();
  2381. const condition = this._parseValue();
  2382. this._expect('keyword', 'then');
  2383. const loop = {
  2384. location,
  2385. iterVarName: null,
  2386. listValue: [],
  2387. entries: [],
  2388. condition,
  2389. hasDefvar: false,
  2390. isConditional: true
  2391. };
  2392. if (this._match('{')) {
  2393. this._read();
  2394. this._parseForeachBody(loop);
  2395. this._expect('}');
  2396. } else {
  2397. this._parseForeachBodyStatement(loop);
  2398. }
  2399. if (this._match('keyword', 'else')) {
  2400. this._read();
  2401. if (this._match('{')) {
  2402. this._read();
  2403. let depth = 1;
  2404. while (depth > 0 && !this._match('eof')) {
  2405. if (this._eat('{')) {
  2406. depth++;
  2407. } else if (this._eat('}')) {
  2408. depth--;
  2409. } else {
  2410. this._read();
  2411. }
  2412. }
  2413. } else {
  2414. // Single statement else: skip until semicolon
  2415. this._skipUntil([';']);
  2416. this._eat(';');
  2417. }
  2418. }
  2419. return loop;
  2420. }
  2421. _parseDefTemplate() {
  2422. this._read();
  2423. const nameTemplate = [];
  2424. while (!this._match(':') && !this._match('{') && !this._match(';') && !this._match('eof')) {
  2425. if (this._match('id')) {
  2426. const value = this._read();
  2427. if (this._eat('.')) {
  2428. const field = this._expect('id');
  2429. nameTemplate.push({ type: 'field_access', base: value, field });
  2430. } else {
  2431. nameTemplate.push({ type: 'id', value });
  2432. }
  2433. } else if (this._match('string')) {
  2434. nameTemplate.push({ type: 'string', value: this._read() });
  2435. } else if (this._match('number')) {
  2436. nameTemplate.push({ type: 'number', value: this._read() });
  2437. } else if (this._eat('#')) {
  2438. nameTemplate.push({ type: 'concat' });
  2439. } else if (this._match('!')) {
  2440. const bangValue = this._parseValue();
  2441. if (bangValue && bangValue.type === 'bang') {
  2442. nameTemplate.push({ type: 'bang', value: bangValue.value });
  2443. }
  2444. } else {
  2445. break;
  2446. }
  2447. }
  2448. let parents = [];
  2449. if (this._match(':')) {
  2450. parents = this._parseParentClassList();
  2451. }
  2452. let bodyFields = new Map();
  2453. if (this._match('{')) {
  2454. this._read(); // consume '{'
  2455. bodyFields = this._parseRecordBodyFields();
  2456. this._expect('}');
  2457. }
  2458. this._eat(';');
  2459. return { nameTemplate, parents, bodyFields };
  2460. }
  2461. _parseForeachTemplate() {
  2462. const location = this._tokenizer.location();
  2463. this._read();
  2464. const iterVarName = this._expect('id');
  2465. this._expect('=');
  2466. const listValue = this._parseForeachListValue();
  2467. this._expect('keyword', 'in');
  2468. const loop = { location, iterVarName, listValue, entries: [] };
  2469. if (this._match('{')) {
  2470. this._read();
  2471. this._parseForeachBody(loop);
  2472. this._expect('}');
  2473. } else {
  2474. this._parseForeachBodyStatement(loop);
  2475. }
  2476. return loop;
  2477. }
  2478. _parseDefvarTemplate() {
  2479. this._read();
  2480. const name = this._expect('id');
  2481. this._expect('=');
  2482. const value = this._parseValue();
  2483. this._expect(';');
  2484. return { name, value };
  2485. }
  2486. _parseRecordBodyFields() {
  2487. const fields = new Map();
  2488. while (!this._match('}') && !this._match('eof')) {
  2489. if (this._match('keyword', 'let')) {
  2490. this._read();
  2491. const name = this._expect('id');
  2492. this._expect('=');
  2493. const value = this._parseValue();
  2494. this._eat(';');
  2495. fields.set(name, { name, type: null, value });
  2496. } else if (this._match('keyword', 'field')) {
  2497. this._read();
  2498. const type = this._parseType();
  2499. const name = this._expect('id');
  2500. let value = null;
  2501. if (this._eat('=')) {
  2502. value = this._parseValue();
  2503. }
  2504. this._eat(';');
  2505. fields.set(name, { name, type, value });
  2506. } else if (this._match('keyword', 'assert') || this._match('keyword', 'dump')) {
  2507. // Skip assert and dump statements
  2508. this._skipUntil([';']);
  2509. this._eat(';');
  2510. } else if (this._match('id') || this._match('keyword')) {
  2511. // Type followed by field name
  2512. const type = this._parseType();
  2513. const name = this._expect('id');
  2514. let value = null;
  2515. if (this._eat('=')) {
  2516. value = this._parseValue();
  2517. }
  2518. this._eat(';');
  2519. fields.set(name, { name, type, value });
  2520. } else {
  2521. this._read();
  2522. }
  2523. }
  2524. return fields;
  2525. }
  2526. _resolveForeachLoop(loop, substitutions) {
  2527. if (loop.entries.length === 0) {
  2528. return;
  2529. }
  2530. if (loop.isConditional) {
  2531. const conditionResult = this._evaluateCondition(loop.condition, substitutions);
  2532. if (conditionResult === false || conditionResult === null) {
  2533. return;
  2534. }
  2535. for (const entry of loop.entries) {
  2536. if (entry.type === 'def') {
  2537. this._instantiateDef(entry.data, substitutions);
  2538. } else if (entry.type === 'foreach') {
  2539. this._resolveForeachLoop(entry.data, substitutions);
  2540. } else if (entry.type === 'defvar') {
  2541. const value = this._evaluateDefvar(entry.data.value, substitutions);
  2542. substitutions.set(entry.data.name, value);
  2543. }
  2544. }
  2545. return;
  2546. }
  2547. if (loop.listValue.length === 0) {
  2548. return;
  2549. }
  2550. for (const listItem of loop.listValue) {
  2551. const currentSubs = new Map(substitutions);
  2552. if (loop.iterVarName) {
  2553. currentSubs.set(loop.iterVarName, listItem);
  2554. }
  2555. for (const entry of loop.entries) {
  2556. if (entry.type === 'def') {
  2557. this._instantiateDef(entry.data, currentSubs);
  2558. } else if (entry.type === 'foreach') {
  2559. this._resolveForeachLoop(entry.data, currentSubs);
  2560. } else if (entry.type === 'defvar') {
  2561. const value = this._evaluateDefvar(entry.data.value, currentSubs);
  2562. currentSubs.set(entry.data.name, value);
  2563. }
  2564. }
  2565. }
  2566. }
  2567. _evaluateCondition(condition, substitutions) {
  2568. if (!condition) {
  2569. return null;
  2570. }
  2571. if (condition.type === 'bang' && condition.value) {
  2572. const { op, args } = condition.value;
  2573. if (op === 'ne' && args.length === 2) {
  2574. const a = this._evaluateSimpleExpr(args[0], substitutions);
  2575. const b = this._evaluateSimpleExpr(args[1], substitutions);
  2576. if (a !== null && b !== null) {
  2577. return a !== b;
  2578. }
  2579. }
  2580. if (op === 'eq' && args.length === 2) {
  2581. const a = this._evaluateSimpleExpr(args[0], substitutions);
  2582. const b = this._evaluateSimpleExpr(args[1], substitutions);
  2583. if (a !== null && b !== null) {
  2584. return a === b;
  2585. }
  2586. }
  2587. }
  2588. // Can't evaluate complex conditions
  2589. return null;
  2590. }
  2591. // Evaluate a simple expression for condition evaluation
  2592. _evaluateSimpleExpr(expr, substitutions) {
  2593. if (!expr) {
  2594. return null;
  2595. }
  2596. if (expr.type === 'string') {
  2597. return String(expr.value).replace(/^"|"$/g, '');
  2598. }
  2599. if (expr.type === 'int') {
  2600. return typeof expr.value === 'number' ? expr.value : parseInt(expr.value, 10);
  2601. }
  2602. if ((expr.type === 'def' || expr.type === 'id') && typeof expr.value === 'string') {
  2603. // Handle field access like "largeT.name"
  2604. if (expr.value.includes('.')) {
  2605. const [baseName, ...fieldParts] = expr.value.split('.');
  2606. let current = substitutions.has(baseName) ? substitutions.get(baseName) : null;
  2607. if (current) {
  2608. for (const fieldName of fieldParts) {
  2609. // Handle record_instance (from _instantiateClassTemplate)
  2610. if (current && current.type === 'record_instance' && current.value && current.value.fields) {
  2611. const fieldValue = current.value.fields.get(fieldName);
  2612. if (fieldValue) {
  2613. current = fieldValue;
  2614. } else {
  2615. return null;
  2616. }
  2617. } else {
  2618. return null;
  2619. }
  2620. }
  2621. return this._evaluateSimpleExpr(current, substitutions);
  2622. }
  2623. } else if (substitutions.has(expr.value)) {
  2624. return this._evaluateSimpleExpr(substitutions.get(expr.value), substitutions);
  2625. }
  2626. }
  2627. return null;
  2628. }
  2629. _evaluateDefvar(value, substitutions) {
  2630. if (!value) {
  2631. return new tablegen.Value('string', '');
  2632. }
  2633. if (value.type === 'string') {
  2634. return value;
  2635. }
  2636. if (value.type === 'int') {
  2637. return value;
  2638. }
  2639. if ((value.type === 'def' || value.type === 'id') && substitutions.has(value.value)) {
  2640. return substitutions.get(value.value);
  2641. }
  2642. if (value.type === 'concat') {
  2643. const parts = value.value.map((part) => this._evaluateDefvar(part, substitutions));
  2644. let result = '';
  2645. for (const part of parts) {
  2646. if (part.type === 'string') {
  2647. result += String(part.value).replace(/^"|"$/g, '');
  2648. } else if (part.type === 'int') {
  2649. result += String(part.value);
  2650. } else if (part.type === 'def' || part.type === 'id') {
  2651. result += String(part.value);
  2652. }
  2653. }
  2654. return new tablegen.Value('string', result);
  2655. }
  2656. if (value.type === 'bang' && value.value) {
  2657. const { op, args } = value.value;
  2658. if (op === 'cast' && args && args.length > 0) {
  2659. const arg = this._evaluateDefvar(args[0], substitutions);
  2660. if (arg.type === 'int') {
  2661. return new tablegen.Value('string', String(arg.value));
  2662. }
  2663. return arg;
  2664. }
  2665. if (op === 'toupper' && args && args.length > 0) {
  2666. const arg = this._evaluateDefvar(args[0], substitutions);
  2667. if (arg.type === 'string') {
  2668. return new tablegen.Value('string', String(arg.value).toUpperCase());
  2669. }
  2670. return arg;
  2671. }
  2672. if (op === 'tolower' && args && args.length > 0) {
  2673. const arg = this._evaluateDefvar(args[0], substitutions);
  2674. if (arg.type === 'string') {
  2675. return new tablegen.Value('string', String(arg.value).toLowerCase());
  2676. }
  2677. return arg;
  2678. }
  2679. if (op === 'strconcat' && args && args.length > 0) {
  2680. let result = '';
  2681. for (const arg of args) {
  2682. const evaluated = this._evaluateDefvar(arg, substitutions);
  2683. if (evaluated && evaluated.type === 'string') {
  2684. result += String(evaluated.value).replace(/^"|"$/g, '');
  2685. } else if (evaluated && evaluated.type === 'int') {
  2686. result += String(evaluated.value);
  2687. }
  2688. }
  2689. return new tablegen.Value('string', result);
  2690. }
  2691. }
  2692. return value;
  2693. }
  2694. _instantiateDef(template, substitutions) {
  2695. let name = '';
  2696. for (const part of template.nameTemplate) {
  2697. if (part.type === 'concat') {
  2698. continue;
  2699. } else if (part.type === 'field_access') {
  2700. if (substitutions.has(part.base)) {
  2701. const subValue = substitutions.get(part.base);
  2702. name += this._getFieldValue(subValue, part.field);
  2703. } else {
  2704. name += `${part.base}.${part.field}`;
  2705. }
  2706. } else if (part.type === 'id') {
  2707. if (substitutions.has(part.value)) {
  2708. const subValue = substitutions.get(part.value);
  2709. name += this._valueToString(subValue);
  2710. } else {
  2711. name += part.value;
  2712. }
  2713. } else if (part.type === 'string') {
  2714. name += part.value;
  2715. } else if (part.type === 'number') {
  2716. name += String(part.value);
  2717. } else if (part.type === 'bang') {
  2718. const evaluated = this._evaluateDefvar(new tablegen.Value('bang', part.value), substitutions);
  2719. name += this._valueToString(evaluated);
  2720. }
  2721. }
  2722. const def = new tablegen.Record(name, this);
  2723. def.location = this._tokenizer.location();
  2724. def.parents = template.parents.map((parent) => ({
  2725. name: parent.name,
  2726. args: parent.args ? parent.args.map((arg) => this._substituteValue(arg, substitutions)) : []
  2727. }));
  2728. for (const [fieldName, field] of template.bodyFields) {
  2729. const resolvedValue = field.value ? this._substituteValue(field.value, substitutions) : null;
  2730. def.fields.set(fieldName, new tablegen.RecordVal(fieldName, field.type, resolvedValue));
  2731. }
  2732. this.addSubClass(def);
  2733. def.resolveReferences();
  2734. if (name) {
  2735. this._defs.set(name, def);
  2736. this.defs.push(def);
  2737. }
  2738. }
  2739. _valueToString(value) {
  2740. if (!value) {
  2741. return '';
  2742. }
  2743. if (typeof value === 'string') {
  2744. return value;
  2745. }
  2746. if (typeof value === 'number') {
  2747. return String(value);
  2748. }
  2749. if (value.type === 'string') {
  2750. return String(value.value).replace(/^"|"$/g, '');
  2751. }
  2752. if (value.type === 'int') {
  2753. return String(value.value);
  2754. }
  2755. if (value.type === 'id' || value.type === 'def') {
  2756. return String(value.value);
  2757. }
  2758. return '';
  2759. }
  2760. _getFieldValue(value, fieldName) {
  2761. if (!value) {
  2762. return '';
  2763. }
  2764. if (value.type === 'record_instance' && value.value && value.value.fields) {
  2765. const fieldValue = value.value.fields.get(fieldName);
  2766. return this._valueToString(fieldValue);
  2767. }
  2768. return '';
  2769. }
  2770. _substituteValue(value, substitutions) {
  2771. if (!value) {
  2772. return value;
  2773. }
  2774. if (value.type === 'def' || value.type === 'id') {
  2775. const varName = value.value;
  2776. if (substitutions.has(varName)) {
  2777. return substitutions.get(varName);
  2778. }
  2779. if (typeof varName === 'string' && varName.includes('.')) {
  2780. const [base, field] = varName.split('.', 2);
  2781. if (substitutions.has(base)) {
  2782. const baseValue = substitutions.get(base);
  2783. const fieldValue = this._getFieldValue(baseValue, field);
  2784. if (fieldValue) {
  2785. return new tablegen.Value('string', fieldValue);
  2786. }
  2787. }
  2788. }
  2789. }
  2790. if (value.type === 'list' && Array.isArray(value.value)) {
  2791. return {
  2792. type: 'list',
  2793. value: value.value.map((v) => this._substituteValue(v, substitutions))
  2794. };
  2795. }
  2796. if (value.type === 'dag' && value.value) {
  2797. return {
  2798. type: 'dag',
  2799. value: {
  2800. operator: value.value.operator,
  2801. operands: value.value.operands.map((op) => ({
  2802. value: this._substituteValue(op.value, substitutions),
  2803. name: op.name
  2804. }))
  2805. }
  2806. };
  2807. }
  2808. if (value.type === 'concat' && Array.isArray(value.value)) {
  2809. return {
  2810. type: 'concat',
  2811. value: value.value.map((v) => this._substituteValue(v, substitutions))
  2812. };
  2813. }
  2814. return value;
  2815. }
  2816. _parseTemplateParams() {
  2817. this._read(); // <
  2818. const params = [];
  2819. while (!this._match('>') && !this._match('eof')) {
  2820. const type = this._parseType();
  2821. const name = this._expect('id');
  2822. let defaultValue = null;
  2823. if (this._match('=')) {
  2824. this._read();
  2825. defaultValue = this._parseValue();
  2826. }
  2827. params.push({ name, type, defaultValue });
  2828. if (this._match(',')) {
  2829. this._read();
  2830. }
  2831. }
  2832. this._expect('>');
  2833. return params;
  2834. }
  2835. _parseParentClassList() {
  2836. this._read();
  2837. const parents = [];
  2838. while (!this._match('{') && !this._match(';') && !this._match('eof')) {
  2839. const parent = this._parseType();
  2840. parents.push(parent);
  2841. if (!this._eat(',')) {
  2842. break;
  2843. }
  2844. }
  2845. return parents;
  2846. }
  2847. _parseRecordBody(record) {
  2848. this._read();
  2849. while (!this._match('}') && !this._match('eof')) {
  2850. if (this._match('keyword', 'let')) {
  2851. this._read();
  2852. const name = this._expect('id');
  2853. this._expect('=');
  2854. const value = this._parseValue();
  2855. const field = new tablegen.RecordVal(name, null, value);
  2856. record.fields.set(name, field);
  2857. this._eat(';');
  2858. } else if (this._match('keyword', 'defvar')) {
  2859. this._read();
  2860. const name = this._expect('id');
  2861. this._expect('=');
  2862. const value = this._parseValue();
  2863. const field = new tablegen.RecordVal(name, null, value);
  2864. record.fields.set(name, field);
  2865. this._eat(';');
  2866. } else if (this._match('keyword', 'assert')) {
  2867. this._read();
  2868. this._parseValue(); // condition
  2869. this._eat(',');
  2870. this._parseValue(); // message
  2871. this._eat(';');
  2872. } else if (this._match('keyword', 'bit') || this._match('keyword', 'bits') || this._match('keyword', 'int') ||
  2873. this._match('keyword', 'string') || this._match('keyword', 'list') || this._match('keyword', 'dag') ||
  2874. this._match('keyword', 'code') || this._match('id')) {
  2875. const type = this._parseType();
  2876. // Skip if next token is not an id (handles edge cases in complex nested structures)
  2877. if (!this._match('id')) {
  2878. if (this._match(',') || this._match('>') || this._match(')') || this._match(']')) {
  2879. continue;
  2880. }
  2881. }
  2882. const name = this._expect('id');
  2883. let value = null;
  2884. if (this._eat('=')) {
  2885. value = this._parseValue();
  2886. }
  2887. const field = new tablegen.RecordVal(name, type, value);
  2888. record.fields.set(name, field);
  2889. this._eat(';');
  2890. } else {
  2891. this._read();
  2892. }
  2893. }
  2894. this._expect('}');
  2895. }
  2896. _parseType() {
  2897. let typeName = '';
  2898. if (this._match('keyword', 'bit') || this._match('keyword', 'bits') || this._match('keyword', 'int') ||
  2899. this._match('keyword', 'string') || this._match('keyword', 'list') || this._match('keyword', 'dag') ||
  2900. this._match('keyword', 'code')) {
  2901. typeName = this._read();
  2902. } else if (this._match('id')) {
  2903. typeName = this._read();
  2904. } else if (this._match('number')) {
  2905. typeName = this._read().toString();
  2906. if (this._match('id')) {
  2907. typeName += this._read();
  2908. }
  2909. } else {
  2910. throw new tablegen.Error(`Expected type at ${this._tokenizer.location()}`);
  2911. }
  2912. const type = new tablegen.Type(typeName);
  2913. if (this._eat('<')) {
  2914. type.args = this._parseTemplateArgList();
  2915. }
  2916. return type;
  2917. }
  2918. _parseTemplateArgList() {
  2919. // Parse template arguments directly from token stream
  2920. // Supports both positional (arg1, arg2) and named (name=value) arguments
  2921. const args = [];
  2922. while (!this._match('>') && !this._match('eof')) {
  2923. // Check if this is a named argument: id = value
  2924. if (this._match('id')) {
  2925. const name = this._read();
  2926. if (this._match('=')) {
  2927. // Named argument
  2928. this._read(); // Consume '='
  2929. const value = this._parseValue();
  2930. args.push({ name, value });
  2931. } else {
  2932. // Positional argument that starts with an id
  2933. // Reconstruct the value - id might be part of concat, field access, etc.
  2934. let value = new tablegen.Value('def', name);
  2935. // Handle < > for template instantiation - manually parse with depth tracking
  2936. if (this._eat('<')) {
  2937. const nestedArgs = [];
  2938. let depth = 1;
  2939. let currentArg = [];
  2940. while (depth > 0 && !this._match('eof')) {
  2941. if (this._match('<')) {
  2942. currentArg.push(this._read());
  2943. depth++;
  2944. } else if (this._match('>')) {
  2945. if (depth === 1) {
  2946. // End of this template arg list
  2947. if (currentArg.length > 0) {
  2948. // Parse accumulated tokens properly
  2949. const argStr = currentArg.join(' ');
  2950. // Try to parse as number first
  2951. if (/^-?\d+$/.test(argStr)) {
  2952. nestedArgs.push(new tablegen.Value('int', argStr));
  2953. } else {
  2954. nestedArgs.push(new tablegen.Value('def', argStr));
  2955. }
  2956. }
  2957. this._read(); // consume the >
  2958. depth--;
  2959. } else {
  2960. currentArg.push(this._read());
  2961. depth--;
  2962. }
  2963. } else if (this._match(',') && depth === 1) {
  2964. // Argument separator at current depth
  2965. if (currentArg.length > 0) {
  2966. // Parse accumulated tokens properly
  2967. const argStr = currentArg.join(' ');
  2968. // Try to parse as number first
  2969. if (/^-?\d+$/.test(argStr)) {
  2970. nestedArgs.push(new tablegen.Value('int', argStr));
  2971. } else {
  2972. nestedArgs.push(new tablegen.Value('def', argStr));
  2973. }
  2974. currentArg = [];
  2975. }
  2976. this._read(); // consume comma
  2977. } else {
  2978. // Regular token - add to current arg
  2979. currentArg.push(this._read());
  2980. }
  2981. }
  2982. // Convert to DAG operands
  2983. const operands = nestedArgs.map((arg) => ({ value: arg, name: null }));
  2984. value = new tablegen.Value('dag', new tablegen.DAG(name, operands));
  2985. }
  2986. // Handle field access
  2987. if (this._eat('.')) {
  2988. const field = this._expect('id');
  2989. if (value.type === 'def') {
  2990. value = new tablegen.Value('def', `${name}.${field}`);
  2991. }
  2992. }
  2993. // Handle :: suffix
  2994. if (this._eat('::')) {
  2995. const suffix = this._expect('id');
  2996. if (value.type === 'def') {
  2997. value = new tablegen.Value('def', `${name}::${suffix}`);
  2998. }
  2999. }
  3000. // Handle # concatenation
  3001. if (this._match('#')) {
  3002. const values = [value];
  3003. while (this._match('#')) {
  3004. this._read();
  3005. values.push(this._parsePrimaryValue());
  3006. }
  3007. value = new tablegen.Value('concat', values);
  3008. }
  3009. args.push(value);
  3010. }
  3011. } else {
  3012. const value = this._parseValue();
  3013. args.push(value);
  3014. }
  3015. if (!this._eat(',')) {
  3016. break;
  3017. }
  3018. }
  3019. this._expect('>');
  3020. return args;
  3021. }
  3022. _parseValue() {
  3023. const values = [];
  3024. values.push(this._parsePrimaryValue());
  3025. while (this._match('#') || (values[values.length - 1] && values[values.length - 1].type === 'string' && this._match('string'))) {
  3026. if (this._match('#')) {
  3027. this._read();
  3028. // Handle trailing # before ; (malformed TableGen but seen in some files)
  3029. if (this._match(';') || this._match(',') || this._match(')') || this._match(']') || this._match('}') || this._match('eof')) {
  3030. break;
  3031. }
  3032. }
  3033. values.push(this._parsePrimaryValue());
  3034. }
  3035. if (values.length === 1) {
  3036. return values[0];
  3037. }
  3038. return new tablegen.Value('concat', values);
  3039. }
  3040. _parseListItem() {
  3041. // Handle $variable as a standalone value in list/dag context
  3042. if (this._eat('$')) {
  3043. const name = this._expect('id');
  3044. return new tablegen.Value('var', name);
  3045. }
  3046. // Special handling for dag-like constructs (id followed by <...>)
  3047. // These need to be parsed as DAGs for trait information
  3048. if (this._match('id')) {
  3049. const name = this._read();
  3050. if (this._eat('<')) {
  3051. const templateArgs = this._parseTemplateArgList();
  3052. const operands = templateArgs.map((arg) => {
  3053. if (arg && typeof arg === 'object' && arg.name && arg.value) {
  3054. return { value: arg.value, name: arg.name };
  3055. }
  3056. return { value: arg, name: null };
  3057. });
  3058. const result = new tablegen.Value('dag', new tablegen.DAG(name, operands));
  3059. if (this._eat('.')) {
  3060. result.field = this._expect('id');
  3061. }
  3062. return result;
  3063. }
  3064. // Not a template instantiation, but might be an identifier with suffixes
  3065. // Put the token back conceptually by creating a def value and checking for suffixes
  3066. let result = new tablegen.Value('def', name);
  3067. // Check for subscripts, field access, etc.
  3068. while (this._eat('[')) {
  3069. const index = this._parseValue();
  3070. this._expect(']');
  3071. result = new tablegen.Value('bang', { op: 'subscript', args: [result, index], field: null });
  3072. }
  3073. if (this._eat('.')) {
  3074. const field = this._expect('id');
  3075. if (result.type === 'def') {
  3076. result = new tablegen.Value('def', `${result.value}.${field}`);
  3077. } else {
  3078. result.value.field = field;
  3079. }
  3080. }
  3081. return result;
  3082. }
  3083. return this._parseValue();
  3084. }
  3085. _parsePrimaryValue() {
  3086. if (this._match('string')) {
  3087. const value = this._read();
  3088. return new tablegen.Value('string', value);
  3089. }
  3090. if (this._match('number')) {
  3091. const value = this._read();
  3092. return new tablegen.Value('int', value);
  3093. }
  3094. if (this._match('true') || this._match('false')) {
  3095. const value = this._read();
  3096. return new tablegen.Value('bit', value);
  3097. }
  3098. if (this._match('code')) {
  3099. const value = this._read();
  3100. return new tablegen.Value('code', value);
  3101. }
  3102. if (this._eat('[')) {
  3103. const items = [];
  3104. while (!this._match(']') && !this._match('eof')) {
  3105. items.push(this._parseListItem());
  3106. this._eat(',');
  3107. }
  3108. this._expect(']');
  3109. if (this._match('<')) {
  3110. this._skip('<', '>');
  3111. }
  3112. return new tablegen.Value('list', items);
  3113. }
  3114. if (this._eat('(')) {
  3115. let operator = null;
  3116. let operatorName = null;
  3117. if (this._match('id')) {
  3118. operator = this._read();
  3119. // Handle template arguments on the operator: (NativeCodeCallVoid<...> ...)
  3120. if (this._match('<')) {
  3121. this._skip('<', '>');
  3122. }
  3123. // Handle operator binding: (OpQ:$op ...)
  3124. if (this._eat(':') && this._eat('$')) {
  3125. if (this._match('id') || this._match('keyword')) {
  3126. operatorName = this._read();
  3127. }
  3128. }
  3129. }
  3130. const operands = [];
  3131. while (!this._match(')') && !this._match('eof')) {
  3132. if (this._eat(',')) {
  3133. continue;
  3134. }
  3135. // Use _parseListItem() to handle template instantiations like Arg<TTG_MemDescType>
  3136. const value = this._parseListItem();
  3137. let name = null;
  3138. if (this._eat(':') && this._eat('$')) {
  3139. if (this._match('id') || this._match('keyword')) {
  3140. name = this._read();
  3141. }
  3142. }
  3143. const operand = { value, name };
  3144. operands.push(operand);
  3145. this._eat(',');
  3146. }
  3147. this._expect(')');
  3148. const dag = new tablegen.DAG(operator, operands);
  3149. if (operatorName) {
  3150. dag.operatorName = operatorName;
  3151. }
  3152. return new tablegen.Value('dag', dag);
  3153. }
  3154. if (this._eat('{')) {
  3155. const fields = new Map();
  3156. while (!this._match('}') && !this._match('eof')) {
  3157. const name = this._expect('id');
  3158. this._expect('=');
  3159. const value = this._parseValue();
  3160. fields.set(name, value);
  3161. this._eat(',');
  3162. }
  3163. this._expect('}');
  3164. return new tablegen.Value('record', fields);
  3165. }
  3166. if (this._eat('!')) {
  3167. let op = null;
  3168. if (this._match('id') || this._match('keyword')) {
  3169. op = this._read();
  3170. } else {
  3171. throw new tablegen.Error(`Expected operator after '!' but got '${this._tokenizer.current().type}' at ${this._tokenizer.location()}`);
  3172. }
  3173. if (this._match('<')) {
  3174. this._skip('<', '>');
  3175. }
  3176. const args = [];
  3177. if (this._eat('(')) {
  3178. if (op === 'cond') {
  3179. while (!this._match(')') && !this._match('eof')) {
  3180. const condition = this._parseValue();
  3181. this._expect(':');
  3182. const value = this._parseValue();
  3183. args.push({ condition, value });
  3184. this._eat(',');
  3185. }
  3186. } else {
  3187. while (!this._match(')') && !this._match('eof')) {
  3188. args.push(this._parseValue());
  3189. this._eat(',');
  3190. }
  3191. }
  3192. this._expect(')');
  3193. }
  3194. let field = null;
  3195. if (this._eat('.')) {
  3196. field = this._expect('id');
  3197. }
  3198. return new tablegen.Value('bang', { op, args, field });
  3199. }
  3200. if (this._match('id') || this._match('keyword')) {
  3201. const value = this._read();
  3202. let result = new tablegen.Value('def', value);
  3203. // Handle various suffixes: templates, subscripts, field access, scope resolution
  3204. while (true) {
  3205. if (this._match('<')) {
  3206. // Template arguments are skipped here - they're parsed properly in
  3207. // DAG context by _parseListItem() which creates DAG values with template args
  3208. this._skip('<', '>');
  3209. } else if (this._eat('[')) {
  3210. // Array subscripting: x[0]
  3211. const index = this._parseValue();
  3212. this._expect(']');
  3213. result = new tablegen.Value('bang', { op: 'subscript', args: [result, index], field: null });
  3214. } else if (this._eat('.')) {
  3215. // Field access: x.field or x[0].field
  3216. const field = this._expect('id');
  3217. if (result.type === 'def') {
  3218. result = new tablegen.Value('def', `${result.value}.${field}`);
  3219. } else {
  3220. // For subscript results, add field access
  3221. result.value.field = field;
  3222. }
  3223. } else if (this._eat('::')) {
  3224. // Scope resolution
  3225. const suffix = this._expect('id');
  3226. if (result.type === 'def') {
  3227. result = new tablegen.Value('def', `${result.value}::${suffix}`);
  3228. }
  3229. break;
  3230. } else {
  3231. break;
  3232. }
  3233. }
  3234. return result;
  3235. }
  3236. if (this._eat('?')) {
  3237. return new tablegen.Value('uninitialized', null);
  3238. }
  3239. if (this._eat('$')) {
  3240. const name = this._expect('id');
  3241. return new tablegen.Value('var', name);
  3242. }
  3243. throw new tablegen.Error(`Unexpected value at ${this._tokenizer.location()}`);
  3244. }
  3245. _read() {
  3246. return this._tokenizer.read().value;
  3247. }
  3248. _match(type, value) {
  3249. const token = this._tokenizer.current();
  3250. return token.type === type && (!value || token.value === value);
  3251. }
  3252. _eat(type, value) {
  3253. if (this._match(type, value)) {
  3254. this._read();
  3255. return true;
  3256. }
  3257. return false;
  3258. }
  3259. _expect(type, value) {
  3260. if (this._match(type, value)) {
  3261. return this._read();
  3262. }
  3263. const token = this._tokenizer.current();
  3264. throw new tablegen.Error(`Expected '${type}' but got '${token.type}' at ${this._tokenizer.location()}`);
  3265. }
  3266. _skip(open, close) {
  3267. if (!this._match(open)) {
  3268. return;
  3269. }
  3270. this._read(); // consume opening token
  3271. let depth = 1;
  3272. while (depth > 0 && !this._match('eof')) {
  3273. if (this._match(open)) {
  3274. depth++;
  3275. } else if (this._match(close)) {
  3276. depth--;
  3277. }
  3278. this._read();
  3279. }
  3280. }
  3281. _skipUntil(types) {
  3282. while (!this._match('eof') && !types.includes(this._tokenizer.current().type)) {
  3283. this._read();
  3284. }
  3285. }
  3286. };
  3287. tablegen.Error = class extends Error {
  3288. constructor(message) {
  3289. super(message);
  3290. this.name = 'TableGen Error';
  3291. }
  3292. };
  3293. export const Reader = tablegen.Reader;