Add offsets to commands

This commit is contained in:
Juhani Krekelä 2018-05-22 15:08:23 +03:00
parent 39890cdc5a
commit 2b6b6a96fb
2 changed files with 101 additions and 4 deletions

View File

@ -15,3 +15,4 @@ Gir supports following optimizations:
* Turn runs of +- or <> into one command
* Turn [-] or [+] into one command
* Add offsets to commands that modify tape, to reduce moving tape head

104
gir.js
View File

@ -8,17 +8,22 @@
// Unsure if this helps in any way over strings, but I feel it neater
// +++++ → {type: add, value: 5}
// Can have offset property
const add = Symbol('add');
// > → {type: moveHead, value: 1}
const moveHead = Symbol('moveHead');
// . → {type: writeByte}
// Can have offset property
const writeByte = Symbol('writeByte');
// , → {type: readByte}
// Can have offset property
const readByte = Symbol('readByte');
// [-] → {type: loop, contents: [{type: add, value: -1}]}
// Can have isBalanced property
const loop = Symbol('loop');
// [-] → {type: clear}
// Can have offset property
const clear = Symbol('clear');
// TODO: Add extensions from Eldis
@ -149,30 +154,45 @@ function parse(program) {
return parsed;
}
// ([commandObjects]) <io>
// ([commandObjects/offsetCommandObjects]) <io>
function prettyPrint(parsed) {
// ([commandObjects], string) <io>
// ([commandObjects/offsetCommandObjects], string) <io>
function printIndented(parsed, indent = '') {
for(let command of parsed) {
let line = indent;
if(command.type == add) {
line += `add ${command.value}`;
if('offset' in command) {
line += ` (${command.offset})`;
}
console.log(line);
} else if(command.type == moveHead) {
line += `moveHead ${command.value}`;
console.log(line);
} else if(command.type == writeByte) {
line += 'writeByte';
if('offset' in command) {
line += ` (${command.offset})`;
}
console.log(line);
} else if(command.type == readByte) {
line += 'readByte';
if('offset' in command) {
line += ` (${command.offset})`;
}
console.log(line);
} else if(command.type == loop) {
line += 'loop';
if('isBalanced' in command) {
line += ` (balanced: ${command.isBalanced})`;
}
console.log(line);
printIndented(command.contents, indent + ' ');
} else if(command.type == clear) {
line += 'clear';
if('offset' in command) {
line += ` (${command.offset})`;
}
console.log(line);
} else {
line += `unknown ${command.type}`;
@ -187,6 +207,8 @@ function prettyPrint(parsed) {
// Optimization passes
// ------------------------------------------------------------------
class UnknownIRError extends Error {}
// ([commandObjects]) → [commandObjects]
function joinAdjacentOps(parsed) {
// ([commandObjects], commandType) → [commandObjects]
@ -255,9 +277,83 @@ function transformClearLoops(parsed) {
return optimized;
}
// ([commandObjects]) → [commandObjects]
// ([commandObjects]) → [offsetCommandObjects]
function addOffsetProperties(parsed) {
// ([commandObjects]) → {offsetted: [offsetCommandObjects], isBalanced: bool}
function worker(parsed) {
let offsetted = [];
let isBalanced = true;
let headChange = 0;
let offset = 0;
for(let command of parsed) {
if(command.type == add) {
offsetted.push({type: add,
value: command.value,
offset: offset});
} else if(command.type == moveHead) {
offset += command.value;
} else if(command.type == writeByte) {
offsetted.push({type: writeByte,
offset: offset});
} else if(command.type == readByte) {
offsetted.push({type: readByte,
offset: offset});
} else if(command.type == clear) {
offsetted.push({type: clear,
offset: offset});
} else if(command.type == loop) {
// A loop should be self-contained
// If offset is not 0, add a moveHead
if(offset != 0) {
offsetted.push({type: moveHead,
value: offset});
// Mark we've moved the head
headChange += offset;
}
offset = 0;
// Run optimization on the loop
let result = worker(command.contents);
// We're only balanced if our loops are
isBalanced = isBalanced && result.isBalanced;
offsetted.push({type: loop,
contents: result.offsetted,
isBalanced: result.isBalanced});
// headChange's value becomes invalid if the
// loop is not balanced. However, we only
// care about its value when figuring out
// our isBalanced, which will be forced to
// false if any inner loop is not balanced
} else {
throw new UnknownIRError();
}
}
// We need to move the tape head in the end anyways, so
// generate moveHead is offseet is not 0
if(offset != 0) {
offsetted.push({type: moveHead,
value: offset});
}
// We're only balanced if relative to start of the loop ends
// up as 0
isBalanced = isBalanced && offset + headChange == 0;
return {offsetted, isBalanced};
}
return worker(parsed).offsetted;
}
// ([commandObjects]) → [offsetCommandObjects]
function optimize(parsed) {
const optimizations = [joinAdjacentOps, transformClearLoops];
const optimizations = [
joinAdjacentOps,
transformClearLoops,
addOffsetProperties
]
return optimizations.reduce((IR, optimization) =>
optimization(IR), parsed);
}