Add offsets to commands
This commit is contained in:
parent
39890cdc5a
commit
2b6b6a96fb
|
@ -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
104
gir.js
|
@ -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);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue