First commit
This commit is contained in:
commit
2cbc9be4e3
5 changed files with 209 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
*.swp
|
9
README.md
Normal file
9
README.md
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
Gir is an optimizing brainfuck interpreter in Javascript.
|
||||||
|
|
||||||
|
Name
|
||||||
|
----
|
||||||
|
𒌋𒀜 gir (Sumerian) - n. kiln
|
||||||
|
|
||||||
|
License
|
||||||
|
-------
|
||||||
|
Everything in the repo is under Unlicense / CC0.
|
24
UNLICENSE
Normal file
24
UNLICENSE
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
This is free and unencumbered software released into the public domain.
|
||||||
|
|
||||||
|
Anyone is free to copy, modify, publish, use, compile, sell, or
|
||||||
|
distribute this software, either in source code form or as a compiled
|
||||||
|
binary, for any purpose, commercial or non-commercial, and by any
|
||||||
|
means.
|
||||||
|
|
||||||
|
In jurisdictions that recognize copyright laws, the author or authors
|
||||||
|
of this software dedicate any and all copyright interest in the
|
||||||
|
software to the public domain. We make this dedication for the benefit
|
||||||
|
of the public at large and to the detriment of our heirs and
|
||||||
|
successors. We intend this dedication to be an overt act of
|
||||||
|
relinquishment in perpetuity of all present and future rights to this
|
||||||
|
software under copyright law.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||||
|
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
||||||
|
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
||||||
|
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
For more information, please refer to [http://unlicense.org]
|
11
gir.html
Normal file
11
gir.html
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>Gir testbench</title>
|
||||||
|
<script type="text/javascript" src="gir.js"></script>
|
||||||
|
</head>
|
||||||
|
<html>
|
||||||
|
<p>Open the JavaScript console</p>
|
||||||
|
<!-- TODO: Implement some kind of html interface -->
|
||||||
|
</html>
|
||||||
|
</html>
|
164
gir.js
Normal file
164
gir.js
Normal file
|
@ -0,0 +1,164 @@
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Use symbols for the names of the instructions
|
||||||
|
// Unsure if this helps in any way over strings, but I feel it neater
|
||||||
|
|
||||||
|
// +++++ → {type: add, value: 5}
|
||||||
|
const add = Symbol('add');
|
||||||
|
// > → {type: moveHead, value: 1}
|
||||||
|
const moveHead = Symbol('moveHead');
|
||||||
|
// . → {type: writeByte}
|
||||||
|
const writeByte = Symbol('writeByte');
|
||||||
|
// , → {type: readByte}
|
||||||
|
const readByte = Symbol('readByte');
|
||||||
|
// [-] → {type: loop, contents: [{type: add, value: -1}]}
|
||||||
|
const loop = Symbol('loop');
|
||||||
|
|
||||||
|
// TODO: Add extensions from Eldis
|
||||||
|
|
||||||
|
class ParsingError extends Error {}
|
||||||
|
|
||||||
|
// (string) → [commandObjects]
|
||||||
|
// May throw
|
||||||
|
function parse(program) {
|
||||||
|
// (string, int, bool) → {parsed: [commandObjects], lastIndex: int}
|
||||||
|
// index is the index of the next character to consume
|
||||||
|
// inLoop tells whether we're parsing a loop or the top level
|
||||||
|
// lastIndex is the last index the function consumed
|
||||||
|
function constructTree(program, index = 0, inLoop = false) {
|
||||||
|
let commands = [];
|
||||||
|
|
||||||
|
// Move this out of the loop body since we need to return
|
||||||
|
// the index of the last character we parsed
|
||||||
|
let i = index;
|
||||||
|
for(;;) {
|
||||||
|
if(i >= program.length) {
|
||||||
|
// If we're parsing a loop, we have a
|
||||||
|
// missing ]
|
||||||
|
// If we're parsing the top level, this is
|
||||||
|
// where we should exit
|
||||||
|
if(inLoop) {
|
||||||
|
throw new ParsingError('Missing ]');
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
|
||||||
|
} else if(program[i] == ']') {
|
||||||
|
// If we're parsing a loop, this is where we
|
||||||
|
// should exit
|
||||||
|
// If we're parsing the top level, we have a
|
||||||
|
// missing [
|
||||||
|
if(inLoop) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
throw new ParsingError('Missing [');
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
|
||||||
|
} else if(program[i] == '+' || program[i] == '-') {
|
||||||
|
// Fold a run of +s and -s into one node
|
||||||
|
let value = 0;
|
||||||
|
for(; i < program.length; i++) {
|
||||||
|
if(program[i] == '+') {
|
||||||
|
value++;
|
||||||
|
} else if(program[i] == '-') {
|
||||||
|
value--;
|
||||||
|
} else {
|
||||||
|
// Reached end of the run
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commands.push({type: add, value: value});
|
||||||
|
// i is not incremented, since it already
|
||||||
|
// points to a location containig a char we
|
||||||
|
// have not yet handled
|
||||||
|
|
||||||
|
} else if(program[i] == '<' || program[i] == '>') {
|
||||||
|
// Fold a run of <s and >s into one node
|
||||||
|
let value = 0;
|
||||||
|
for(; i < program.length; i++) {
|
||||||
|
if(program[i] == '>') {
|
||||||
|
value++;
|
||||||
|
} else if(program[i] == '<') {
|
||||||
|
value--;
|
||||||
|
} else {
|
||||||
|
// Reached end of the run
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
commands.push({type: moveHead, value: value});
|
||||||
|
// see +/- for why we don't increment i
|
||||||
|
|
||||||
|
} else if(program[i] == '.') {
|
||||||
|
commands.push({type: writeByte});
|
||||||
|
i++;
|
||||||
|
|
||||||
|
} else if(program[i] == ',') {
|
||||||
|
commands.push({type: readByte});
|
||||||
|
i++;
|
||||||
|
|
||||||
|
} else if(program[i] == '[') {
|
||||||
|
// Parse a loop. This is done by calling the
|
||||||
|
// same parser function recursively
|
||||||
|
// Due to this the loop appears as one node
|
||||||
|
// in the parsed result
|
||||||
|
let {parsed, lastIndex} = constructTree(
|
||||||
|
program, // Same program data
|
||||||
|
i + 1, // Start at the next char
|
||||||
|
true // We're parsing a loop
|
||||||
|
);
|
||||||
|
commands.push({type: loop, contents: parsed});
|
||||||
|
// Since lastIndex was consumed by the inner
|
||||||
|
// function, we don't want to consume it a
|
||||||
|
// second time
|
||||||
|
i = lastIndex + 1;
|
||||||
|
} else {
|
||||||
|
// All others characters are comments,
|
||||||
|
// ignore them
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {parsed: commands, lastIndex: i};
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only care about the parsed contents, since under normal
|
||||||
|
// operarion we only get out of the loop if we've reached the end
|
||||||
|
// of the program
|
||||||
|
let {parsed} = constructTree(program);
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ([commandObjects]) <io>
|
||||||
|
function prettyPrint(parsed) {
|
||||||
|
// ([commandObjects], string) <io>
|
||||||
|
function printIndented(parsed, indent = '') {
|
||||||
|
for(let command of parsed) {
|
||||||
|
let line = indent;
|
||||||
|
if(command.type == add) {
|
||||||
|
line += `add ${command.value}`;
|
||||||
|
console.log(line);
|
||||||
|
} else if(command.type == moveHead) {
|
||||||
|
line += `moveHead ${command.value}`;
|
||||||
|
console.log(line);
|
||||||
|
} else if(command.type == writeByte) {
|
||||||
|
line += 'writeByte';
|
||||||
|
console.log(line);
|
||||||
|
} else if(command.type == readByte) {
|
||||||
|
line += 'readByte';
|
||||||
|
console.log(line);
|
||||||
|
} else if(command.type == loop) {
|
||||||
|
line += 'loop';
|
||||||
|
console.log(line);
|
||||||
|
printIndented(command.contents, indent + ' ');
|
||||||
|
} else {
|
||||||
|
line += `unknown ${command.type}`;
|
||||||
|
console.log(line);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
printIndented(parsed);
|
||||||
|
}
|
Loading…
Reference in a new issue