#!/usr/bin/env node import fs from 'fs/promises'; import * as cheerio from 'cheerio'; const JVMS_SPECIFICATION = './vendor/jvms.html'; const VARIADIC_MAPPINGS = { '': ['0', '1', '2', '3'], '': ['0', '1'], '': ['0, 1', '2'], '': ['m1', '0', '1', '2', '3', '4', '5'], '': ['0', '1'], '': ['g', 'l'], '': ['eq', 'ne'], }; type InstructionInfo = { name: string anchor: string tooltip: string format: string[] stack: [string | null, string | null] description: string } const extract = (node: cheerio.Cheerio, $: cheerio.CheerioAPI) => { const anchorElement = node.find('div.titlepage > div > div > h3.title > a[name*="jvms-6.5"]').first(); const nameElement = anchorElement.parent().find('span.emphasis > em'); const anchor = anchorElement.attr('name')!; const name = nameElement.text(); const [ operationSection, formatSection, _formsSection, operandStackSection, descriptionSection, ] = node.find('div.section').toArray().map(it => $(it)); const operation = operationSection.find('p.norm').first().text(); const format = formatSection.find('div.literallayout > p > span.emphasis > em').toArray().map(it => $(it).text()); const description = descriptionSection.find('p.norm-dynamic').first(); // rewrite links to oracle.com $(description).find('* > a[href*="jvms-"]').toArray().forEach((el) => { $(el).attr('href', `https://docs.oracle.com/javase/specs/jvms/se18/html/${$(el).attr('href')}`); }); const [stackBefore, stackAfter] = operandStackSection.find('p.norm') .toArray() .map(it => $(it)); const result: InstructionInfo[] = []; const hasVariadicMapping = Object.keys(VARIADIC_MAPPINGS).some((pat) => name.endsWith(pat)); if (hasVariadicMapping) { for (const [pattern, mappings] of Object.entries(VARIADIC_MAPPINGS)) { if (name.endsWith(pattern)) { for (const mapping of mappings) { result.push({ name: name.replace(pattern, mapping).replaceAll('<', '[').replaceAll('>', ']'), anchor, description: description.html()!, tooltip: operation, stack: [stackBefore?.html(), stackAfter?.html()], format: format.map(x => x.replaceAll('<', '[').replaceAll('>', ']')), }); } } } } else { result.push({ name, anchor, description: description.html()!, tooltip: operation, stack: [stackBefore?.html(), stackAfter?.html()], format, }); } return result; }; const main = async () => { const file = await fs.readFile(JVMS_SPECIFICATION, 'utf-8'); const $ = cheerio.load(file); const sections = $('div.section-execution'); const instructions = sections.toArray() .slice(1) // Drop 1 because the first is the "mne monic" .map(it => extract($(it), $)) .flat(); console.log('import {AssemblyInstructionInfo} from \'../base.js\';'); console.log(''); console.log('export function getAsmOpcode(opcode: string | undefined): AssemblyInstructionInfo | undefined {'); console.log(' if (!opcode) return;'); console.log(' switch (opcode.toUpperCase()) {'); for (const instruction of instructions) { console.log(` case '${instruction.name.toUpperCase()}':`); console.log(' return {'); console.log(` url: \`https://docs.oracle.com/javase/specs/jvms/se18/html/jvms-6.html#${instruction.anchor}\`,`); const body = `

Instruction ${instruction.name}: ${instruction.tooltip}

Format: ${instruction.format.join(' ')}

${instruction.stack[0] && `

Operand Stack: ${instruction.stack[0]} ${instruction.stack[1]}

`}

${instruction.description}

`; console.log(` html: \`${body.replace(/\s\s+/g, ' ')}\`,`); console.log(` tooltip: \`${instruction.tooltip.replace(/\s\s+/g, ' ')}\`,`); console.log(' };'); } console.log(' }'); console.log('}'); }; main().then(() =>{}).catch(e => console.error("Caught error", e));