redc
Version:
Compiles RED lang into Minecraft schematics
269 lines (268 loc) • 10.9 kB
JavaScript
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);
}