UNPKG

redc

Version:

Compiles RED lang into Minecraft schematics

269 lines (268 loc) 10.9 kB
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) { if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) { if (ar || !(i in from)) { if (!ar) ar = Array.prototype.slice.call(from, 0, i); ar[i] = from[i]; } } return to.concat(ar || Array.prototype.slice.call(from)); }; import { WORD_SIZE, CAPACITY, REDCError } from './meta.js'; var bin = function (n) { return n.toString(2).padStart(WORD_SIZE, '0').slice(-WORD_SIZE); }; var allInstructions = [ 'FWD', 'BCK', 'FWD_IF', 'BCK_IF', 'CPY', 'CPY_DEV', 'CPY_INP', 'SET', 'SET_DEV', 'SUM', 'SUM_INT', 'OR', 'OR_INT', 'XOR', 'XOR_INT', 'AND', 'AND_INT', 'NOT', 'SHIFT', 'PTR', 'PTR_INP', 'PTR_CPY', 'PTR_CPY_DEV', 'EXIT' ]; var instructions = Object.fromEntries(allInstructions.map(function (instruction, i) { return [instruction, bin(i + 1)]; })); function checkForExhaustive(p) { console.error('[Internal error] not exhaustive switch ' + p); throw new REDCError("not exhaustive switch: \"".concat(p, "\"")); } var binaryOperations = { plus: { operate: function (a, b) { return bin(a + b); }, instruction: [instructions.SUM, instructions.SUM_INT] }, or: { operate: function (a, b) { var ab = bin(a); var bb = bin(b); var ored = ''; for (var i = 0; i < ab.length; i++) ored += (ab[i] === '1' || bb[i] === '1') ? '1' : '0'; return ored; }, instruction: [instructions.OR, instructions.OR_INT] }, and: { operate: function (a, b) { var ab = bin(a); var bb = bin(b); var anded = ''; for (var i = 0; i < ab.length; i++) anded += (ab[i] === '1' && bb[i] === '1') ? '1' : '0'; return anded; }, instruction: [instructions.AND, instructions.AND_INT] }, xor: { operate: function (a, b) { var ab = bin(a); var bb = bin(b); var xored = ''; for (var i = 0; i < ab.length; i++) { xored += (ab[i] !== bb[i] ? '1' : '0'); } return xored; }, instruction: [instructions.XOR, instructions.XOR_INT] } }; export function compile(data, options) { var _a; function getAddr(variable) { return bin(data.variables[variable].address); } function lengthOf(i) { switch (i.type) { case 'jump': if (i["if"]) return 4; return 3; case 'exit': return 1; case 'assignment': return 3; case 'and': case 'or': case 'xor': case 'plus': return (typeof i.operand1 === 'number' && typeof i.operand2 === 'number') ? 3 : 4; case 'not': case 'shift': return 3; case 'pointer': return 3; case 'ptrcopy': return 4; default: checkForExhaustive(i); } } var routines = {}; var entries = Object.entries(data.routines); entries.sort(function (a, b) { if (a[0] === data.entry) { return -1; } else if (b[0] === data.entry) { return 1; } else return 0; }); var lastPosition = 0; for (var _i = 0, entries_1 = entries; _i < entries_1.length; _i++) { var _b = entries_1[_i], name_1 = _b[0], routine = _b[1]; routines[name_1] = { instructions: routine, position: lastPosition, size: routine.reduce(function (prev, next) { return prev + lengthOf(next); }, 0) }; lastPosition += routine.reduce(function (prev, current) { return prev += lengthOf(current); }, 0); } var built = Object.values(routines).map(function (routine) { var pos = routine.position; return routine.instructions.map(function (i) { var len = lengthOf(i); pos += len; switch (i.type) { case 'jump': if (!(i.to in routines)) throw new REDCError("Unknown routine \"".concat(i.to, "\"")); var forward = true; var distance = void 0; var continuousDistance = routines[i.to].position - pos; var edgeDistance = CAPACITY + pos - routines[i.to].position; if (continuousDistance < edgeDistance) { distance = continuousDistance; } else { distance = edgeDistance; forward = false; } if (distance < 0) { distance *= -1; forward = !forward; } if (distance === 0) { return undefined; } if (i["if"]) { return [forward ? instructions.FWD_IF : instructions.BCK_IF, bin(distance >> WORD_SIZE), bin(distance), getAddr(i["if"])]; } else { return [forward ? instructions.FWD : instructions.BCK, bin(distance >> WORD_SIZE), bin(distance)]; } case 'exit': return [instructions.EXIT]; case 'assignment': var to = data.variables[i.assignedTo]; if (to.source === 'input') throw new REDCError("Variable \"".concat(i.assignedTo, "\" is allocated from input memory and cannot be assigned to.")); if (typeof i.value === 'number') { if (to.source === 'ram') { return [instructions.SET, bin(to.address), bin(i.value)]; } else if (to.source === 'device') { return [instructions.SET_DEV, bin(to.address), bin(i.value)]; } } else { var v = data.variables[i.value]; if (to.source === 'ram' && v.source === 'ram') { return [instructions.CPY, bin(v.address), bin(to.address)]; } else if (to.source === 'device' && v.source === 'ram') { return [instructions.CPY_DEV, bin(v.address), bin(to.address)]; } else if (to.source === 'ram' && v.source === 'input') { return [instructions.CPY_INP, bin(v.address), bin(to.address)]; } else throw new REDCError("Invalid assignment combination, cannot assign \"".concat(v.source, "\" (\"").concat(i.value, "\") to \"").concat(to.source, "\" (\"").concat(i.assignedTo, "\")")); } return; case 'shift': case 'not': to = data.variables[i.assignedTo]; if (to.source !== 'ram') throw new REDCError("Invalid operation, can only assign an invert operation to a variable allocated in ram"); if (typeof i.operand === 'number') { return [instructions.NOT, bin(to.address), bin(i.operand).split('').map(function (c) { return c === '0' ? '1' : '0'; }).join('')]; } else { var v = data.variables[i.operand]; if (v.source === 'ram') { return [instructions.NOT, bin(to.address), bin(v.address)]; } else throw new REDCError("Invalid operation, can only invert variables allocated in ram."); } case 'and': case 'or': case 'xor': case 'plus': var operation = binaryOperations[i.type]; to = data.variables[i.assignedTo]; var numVarOperands = 2; if (typeof i.operand1 === 'number') numVarOperands--; if (typeof i.operand2 === 'number') numVarOperands--; if (numVarOperands === 2) { return [operation.instruction[0], bin(to.address), bin(data.variables[i.operand1].address), bin(data.variables[i.operand2].address)]; } else if (numVarOperands === 1) { var _a = typeof i.operand1 === 'number' ? { num: i.operand1, variable: i.operand2 } : { num: i.operand2, variable: i.operand1 }, num = _a.num, variable = _a.variable; return [operation.instruction[1], bin(to.address), getAddr(variable), bin(num)]; } else { return [instructions.SET, bin(to.address), operation.operate(i.operand1, i.operand2)]; } case 'pointer': var instruction = i.source === 'ram' ? instructions.PTR : instructions.PTR_INP; case 'ptrcopy': var atAddr = bin(data.variables[i.at].address); var toAddr = bin(data.variables[i.to].address); return [ instruction !== null && instruction !== void 0 ? instruction : (i.source === 'ram' ? instructions.PTR_CPY : instructions.PTR_CPY_DEV), atAddr, toAddr ]; default: checkForExhaustive(i); } }).filter(function (s) { return !!s; }); }); if (options === null || options === void 0 ? void 0 : options.debug) { (_a = options.out) === null || _a === void 0 ? void 0 : _a.call(options, built.flat(1).map(function (_a) { var code = _a[0], rest = _a.slice(1); return __spreadArray([Object.entries(instructions).find(function (_a) { var v = _a[1]; return v === code; })[0].padEnd(12)], rest, true); }).map(function (i) { return i.join(' '); }).join('\n')); } return built.flat(2); }