microvium
Version:
A compact, embeddable scripting engine for microcontrollers for executing small scripts written in a subset of JavaScript.
204 lines • 9.13 kB
JavaScript
;
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