UNPKG

microvium

Version:

A compact, embeddable scripting engine for microcontrollers for executing small scripts written in a subset of JavaScript.

204 lines 9.13 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.stringifyValue = exports.stringifyAllocation = exports.stringifyOperand = exports.stringifyGeneralOperation = exports.stringifyReturnOperation = exports.stringifyOperation = exports.formatSourceLoc = exports.stringifyOperationLine = exports.stringifyComments = exports.stringifyBlock = exports.stringifyFunction = exports.stringifyUnit = void 0; const utils_1 = require("./utils"); const lodash_1 = __importDefault(require("lodash")); function stringifyUnit(unit, opts = {}) { return `unit ${(0, utils_1.stringifyIdentifier)(unit.sourceFilename)};\n\nentry ${(0, utils_1.stringifyIdentifier)(unit.entryFunctionID)};\n\n${ // Global variables unit.freeVariables.length ? unit.freeVariables.map(g => `external ${(0, utils_1.stringifyIdentifier)(g)} from free-variable ${(0, utils_1.stringifyStringLiteral)(g)};\n`).join('') + '\n' : ''}${ // Imports Object.keys(unit.moduleImports).length > 0 ? unit.moduleImports.map(({ variableName, source: specifier }) => `external ${variableName !== undefined ? (0, utils_1.stringifyIdentifier)(variableName) : '<unnamed>'} from import ${(0, utils_1.stringifyStringLiteral)(specifier)};\n`).join('') + '\n' : ''}${ // Module-level variables unit.moduleVariables.length ? unit.moduleVariables.map(g => `global ${(0, utils_1.stringifyIdentifier)(g)};\n`).join('') + '\n' : ''}${ // Functions [...Object.values(unit.functions)] .map(f => stringifyFunction(f, '', opts)) .join('\n\n')}`; } exports.stringifyUnit = stringifyUnit; function stringifyFunction(func, indent, opts = {}) { let blocks = func.blocks; return `${func.comments && opts.showComments !== false ? func.comments.map(c => `// ${c}\n`).join('') : ''}function ${(0, utils_1.stringifyIdentifier)(func.id)}() {${blocksInOrder(blocks, func.entryBlockID) .map(b => stringifyBlock(b, indent + ' ', opts)) .join('')}\n${indent}}`; } exports.stringifyFunction = stringifyFunction; function blocksInOrder(blocks, entryBlockID) { if (!(entryBlockID in blocks)) { return (0, utils_1.invalidOperation)('Malformed function'); } const { [entryBlockID]: firstBlock, ...otherBlocks } = blocks; const result = [ firstBlock, ...lodash_1.default.sortBy(Object.values(otherBlocks), b => { const m = b.id.match(/^block(\d+)$/); if (!m) return b.id; return parseInt(m[1]); }) ]; (0, utils_1.hardAssert)(!result.some(b => !b)); return result; } function stringifyBlock(block, indent, opts = {}) { return `${stringifyComments(indent, block.comments, opts)}\n${indent}${block.id}:${block.operations .map(o => stringifyOperationLine(o, indent + ' ', opts)) .join('')}`; } exports.stringifyBlock = stringifyBlock; function stringifyComments(indent, comments, opts = {}) { if (!comments || opts.showComments === false) { return ''; } return comments .flatMap(c => c.split('\n')) .map(s => s.trim()) .map(c => `\n${indent}// ${c}`) .join(''); } exports.stringifyComments = stringifyComments; function stringifyOperationLine(operation, indent, opts = {}) { const loc = operation.sourceLoc; let line = `${indent}${stringifyOperation(operation)};`; let commentCol = 40; if (opts.showStackDepth || opts.showVariableNameHints || opts.commentSourceLocations) { line = line.padEnd(commentCol, ' '); line += ' //'; commentCol += 3; } if (opts.showStackDepth) { line += ' '; if (operation.stackDepthAfter !== undefined) line += operation.stackDepthAfter; commentCol += 3; line = line.padEnd(commentCol, ' '); } if (opts.showVariableNameHints) { line += ' '; if (operation.nameHint) line += operation.nameHint; commentCol += 15; line = line.padEnd(commentCol, ' '); } if (opts.commentSourceLocations) { line += ' '; if (loc) line += `${loc.filename}:${loc.line}:${loc.column + 1}`; commentCol += 30; } line = `${stringifyComments(indent, operation.comments, opts)}\n${line}`; return line; } exports.stringifyOperationLine = stringifyOperationLine; function formatSourceLoc(loc) { return `${loc.filename}:${loc.line}:${loc.column + 1}`; } exports.formatSourceLoc = formatSourceLoc; function stringifyOperation(operation) { switch (operation.opcode) { case 'Return': return stringifyReturnOperation(operation); default: return stringifyGeneralOperation(operation); } } exports.stringifyOperation = stringifyOperation; function stringifyReturnOperation(operation) { if (operation.staticInfo?.returnUndefined) { return stringifyGeneralOperation(operation) + ' // return undefined'; } else { return stringifyGeneralOperation(operation); } } exports.stringifyReturnOperation = stringifyReturnOperation; function stringifyGeneralOperation(operation) { return `${operation.opcode}(${operation.operands .map(stringifyOperand) .join(', ')})`; } exports.stringifyGeneralOperation = stringifyGeneralOperation; function stringifyOperand(operand) { switch (operand.type) { case 'LabelOperand': return `@${operand.targetBlockId}`; case 'LiteralOperand': return 'lit ' + stringifyValue(operand.literal); case 'CountOperand': return 'count ' + operand.count; case 'FlagOperand': return 'flag ' + (operand.flag ? 'true' : 'false'); case 'IndexOperand': return 'index ' + operand.index; case 'NameOperand': return `name '${operand.name}'`; case 'OpOperand': return `op '${operand.subOperation}'`; default: return (0, utils_1.assertUnreachable)(operand); } } exports.stringifyOperand = stringifyOperand; function stringifyAllocation(allocation) { switch (allocation.type) { case 'ArrayAllocation': return `[${allocation.items .map(v => `\n ${v ? stringifyValue(v) : ''},`) .join('')}\n]`; case 'ObjectAllocation': return `{${(0, utils_1.entriesInOrder)(allocation.properties) .map(([k, v]) => `\n ${(0, utils_1.stringifyIdentifier)(k)}: ${stringifyValue(v)},`) .join('')}\n}`; case 'Uint8ArrayAllocation': return `Uint8Array { ${allocation.bytes .map(b => `0x${b.toString(16).padStart(2, '0')}`) .join(', ')} }`; case 'ClosureAllocation': return `Closure [${allocation.slots .map(v => `\n ${v ? stringifyValue(v) : ''},`) .join('')}\n]`; default: return (0, utils_1.assertUnreachable)(allocation); } } exports.stringifyAllocation = stringifyAllocation; function stringifyValue(value) { switch (value.type) { case 'DeletedValue': return 'deleted'; case 'UndefinedValue': return 'undefined'; case 'NullValue': return 'null'; case 'NoOpFunction': return `no-op-function`; case 'BooleanValue': return value.value ? 'true' : 'false'; case 'NumberValue': { if (Object.is(value.value, -0)) { return '-0'; } else if (value.value === Infinity) { return 'Infinity'; } else if (value.value === -Infinity) { return '-Infinity'; } else if (isNaN(value.value)) { return 'NaN'; } else { return JSON.stringify(value.value); } } case 'StringValue': return (0, utils_1.stringifyStringLiteral)(value.value); case 'HostFunctionValue': return `host function ${value.value}`; case 'FunctionValue': return `&function ${(0, utils_1.stringifyIdentifier)(value.value)}`; case 'ClassValue': return `class (${stringifyValue(value.constructorFunc)}, ${stringifyValue(value.staticProps)})`; case 'ReferenceValue': return `&allocation ${value.value}`; case 'EphemeralFunctionValue': return `&ephemeral ${value.value}`; case 'EphemeralObjectValue': return `&ephemeral ${value.value}`; case 'ProgramAddressValue': return `&prog ${value.funcId}[${value.blockId}]`; case 'ResumePoint': return `resume point (${value.address.funcId}, ${value.address.blockId}, ${value.address.operationIndex})`; default: return (0, utils_1.assertUnreachable)(value); } } exports.stringifyValue = stringifyValue; //# sourceMappingURL=stringify-il.js.map