// converts the compiler genome and the memory map into the javascript vm's // layout files. everything it writes is generated, so it goes to build/js/ and // nothing is left in the tree. // // node src/js/extract_genome.js [--genome PATH] [--map PATH] [--out DIR] // // the genome is the one make genome produced in build/core/genome.h, and the // map is src/core/memmap.h. outputs, in the out directory: // genome.json the genome words, for the vm that has no compiler inside it // layout.ts the map as typescript constants // layout.js the map as commonjs constants const fs = require('fs'); const path = require('path'); const root = path.join(__dirname, '..', '..'); const args = process.argv.slice(2); const opt = (name) => { const i = args.indexOf(name); return i >= 0 && args[i + 1] ? args[i + 1] : null; }; if (args.includes('--help') || args.includes('-h')) { console.log([ 'usage: node src/js/extract_genome.js [--genome PATH] [--map PATH] [--out DIR]', 'converts the compiler genome and memory map into the javascript layout files.', 'options:', ' --genome PATH the genome header to read; defaults to build/core/genome.h', ' --map PATH the memory map header to read; defaults to src/core/memmap.h', ' --out DIR where to write; defaults to build/js', ' --help, -h show this help and exit', ].join('\n')); process.exit(0); } const unknown = args.filter((a) => !['--genome', '--map', '--out'].includes(a)); if (unknown.length) { console.error('extract_genome: unexpected arguments: ' + unknown.join(' ') + '; use --help for usage'); process.exit(2); } const genomePath = opt('--genome') || path.join(root, 'build', 'core', 'genome.h'); const mapPath = opt('--map') || path.join(__dirname, '..', 'core', 'memmap.h'); const outDir = opt('--out') || path.join(root, 'build', 'js'); if (!fs.existsSync(genomePath)) { console.error('extract_genome: no genome at ' + genomePath + '; run `make genome` first'); process.exit(1); } fs.mkdirSync(outDir, { recursive: true }); const text = fs.readFileSync(genomePath, 'utf8'); const gorgMatch = text.match(/#define\s+GORG\s+(\d+)/); const glenMatch = text.match(/#define\s+GLEN\s+(\d+)/); const gorg = parseInt(gorgMatch[1], 10); const glen = parseInt(glenMatch[1], 10); const arrMatch = text.match(/genome\[\]\s*=\s*\{([\s\S]*?)\};/); if (!arrMatch) { console.error('extract_genome: could not find the genome array in ' + genomePath); process.exit(1); } const words = arrMatch[1] .split(',') .map(s => s.trim()) .filter(s => s.length > 0); console.log(`Parsed genome: GORG=${gorg}, GLEN=${words.length}`); if (words.length !== glen) throw new Error('genome length does not match GLEN'); // the compiler's map, as typescript constants: the js vm reads them instead of // its own copies. the values are computed here, from the map. const { mapValues } = require('./mapvalues.js'); const names = ['MW', 'PW', 'ZFP', 'GORG', 'DS0', 'DSLIM', 'RS0', 'RSLIM', 'SYSGATE', 'HEAP_LO', 'HEAP_HI']; const map = mapValues(mapPath, names); if (map.GORG !== gorg) throw new Error('genome GORG ' + gorg + ' is not the map GORG ' + map.GORG); const genomeJson = path.join(outDir, 'genome.json'); fs.writeFileSync(genomeJson, JSON.stringify({ gorg, glen, words })); console.log('saved genome to ' + genomeJson); const lines = names.map(k => ` ${k}: ${map[k]},`).join('\n'); const layoutTs = path.join(outDir, 'layout.ts'); fs.writeFileSync(layoutTs, '// generated by src/js/extract_genome.js from ' + path.relative(root, mapPath) + '. do not edit.\n' + 'export const MAP = {\n' + lines + '\n} as const;\n'); console.log('saved the map to ' + layoutTs); const layoutJs = path.join(outDir, 'layout.js'); fs.writeFileSync(layoutJs, '// generated by src/js/extract_genome.js from ' + path.relative(root, mapPath) + '. do not edit.\n' + 'exports.MAP = ' + JSON.stringify(map, null, 4) + ';\n'); console.log('saved the map to ' + layoutJs);