blob: 873ac75151d91830313900ce62aee2055b4ac59e [file] [log] [blame] [raw]
#!/usr/bin/env node
import fs from 'fs/promises';
import * as cheerio from 'cheerio';
const JVMS_SPECIFICATION = './vendor/jvms.html';
const VARIADIC_MAPPINGS = {
'<n>': ['0', '1', '2', '3'],
'<d>': ['0', '1'],
'<f>': ['0, 1', '2'],
'<i>': ['m1', '0', '1', '2', '3', '4', '5'],
'<l>': ['0', '1'],
'<op>': ['g', 'l'],
'<cond>': ['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.Element>, $: 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 = `<p>Instruction ${instruction.name}: ${instruction.tooltip}</p><p>Format: ${instruction.format.join(' ')}</p>${instruction.stack[0] && `<p>Operand Stack: ${instruction.stack[0]} ${instruction.stack[1]}</p>`}<p>${instruction.description}</p>`;
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));