parent
c917127d8e
commit
b866f744a0
1 changed files with 226 additions and 0 deletions
@ -0,0 +1,226 @@ |
|||||||
|
#!/usr/bin/env node |
||||||
|
|
||||||
|
console.time("Compiled in"); |
||||||
|
|
||||||
|
const fs = require("fs"); |
||||||
|
const args = process.argv.slice(2, process.argv.length); |
||||||
|
|
||||||
|
if (!args[0] && args[0] !== 0) { |
||||||
|
console.error("Usage: mblf <input file> <output file>"); |
||||||
|
process.exit(1); |
||||||
|
} else if (!args[1] && args[1] !== 0) { |
||||||
|
console.error("Error: Please provide an output path."); |
||||||
|
process.exit(1); |
||||||
|
} |
||||||
|
|
||||||
|
function parseNum(number) { |
||||||
|
if (typeof number == "string") { |
||||||
|
if (number.startsWith("0x")) { |
||||||
|
return parseInt(number.slice(2, number.length), 16); |
||||||
|
} else if (number.startsWith("\"") && number.endsWith("\"")) { |
||||||
|
return number.charCodeAt(1); |
||||||
|
} else { |
||||||
|
return parseInt(number); |
||||||
|
} |
||||||
|
} else { |
||||||
|
return number; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
let bfOutput = []; |
||||||
|
let variables = []; |
||||||
|
let ptr = 0; |
||||||
|
let prevPtr = 0; |
||||||
|
|
||||||
|
function instruct(instruction, arg) { |
||||||
|
let pointedVariable; |
||||||
|
let number; |
||||||
|
switch (instruction) { |
||||||
|
case "var": |
||||||
|
if (variables.indexOf(arg) != -1) { |
||||||
|
return `${arg} already exists.`; |
||||||
|
} |
||||||
|
let emptyAddress = 0; |
||||||
|
while (!(variables[emptyAddress] === undefined)) { |
||||||
|
emptyAddress++; |
||||||
|
} |
||||||
|
variables[emptyAddress] = arg; |
||||||
|
break; |
||||||
|
case "delvar": |
||||||
|
delete variables[variables.indexOf(arg)]; |
||||||
|
break; |
||||||
|
case "point": |
||||||
|
if (bfOutput[bfOutput.length-1] !== undefined && |
||||||
|
(bfOutput[bfOutput.length-1].endsWith("<") || |
||||||
|
bfOutput[bfOutput.length-1].endsWith(">"))) { |
||||||
|
bfOutput.pop(); |
||||||
|
ptr = prevPtr; |
||||||
|
} |
||||||
|
let variable = variables.indexOf(arg); |
||||||
|
if (variable == -1) { |
||||||
|
return `${arg} is not a variable.`; |
||||||
|
} |
||||||
|
let distance = variable - ptr; |
||||||
|
prevPtr = ptr; |
||||||
|
if (distance > 0) { |
||||||
|
bfOutput.push(">".repeat(distance)); |
||||||
|
} else if (distance < 0) { |
||||||
|
bfOutput.push("<".repeat(-distance)); |
||||||
|
} |
||||||
|
ptr = variable; |
||||||
|
break; |
||||||
|
case "pointm": |
||||||
|
bfOutput.push("<+[-<+]-"); // thank you mixtela |
||||||
|
ptr = variables.indexOf(arg); |
||||||
|
break; |
||||||
|
case "add": |
||||||
|
number = parseNum(arg); |
||||||
|
if (Number.isNaN(number)) { |
||||||
|
return "Failed to parse number."; |
||||||
|
} |
||||||
|
bfOutput.push("+".repeat(number)); |
||||||
|
break; |
||||||
|
case "addb": |
||||||
|
number = parseNum(arg); |
||||||
|
if (Number.isNaN(number)) { |
||||||
|
return "Failed to parse number."; |
||||||
|
} |
||||||
|
pointedVariable = variables[ptr]; |
||||||
|
instruct("var", "__temp"); |
||||||
|
instruct("point", "__temp"); |
||||||
|
instruct("add", Math.floor(number / 16)); |
||||||
|
bfOutput.push("["); |
||||||
|
instruct("sub", "1"); |
||||||
|
instruct("point", pointedVariable); |
||||||
|
instruct("add", "16"); |
||||||
|
instruct("point", "__temp"); |
||||||
|
bfOutput.push("]"); |
||||||
|
instruct("point", pointedVariable); |
||||||
|
instruct("add", number - Math.floor(number / 16) * 16); |
||||||
|
instruct("delvar", "__temp"); |
||||||
|
break; |
||||||
|
case "addv": |
||||||
|
pointedVariable = variables[ptr]; |
||||||
|
bfOutput.push("["); |
||||||
|
instruct("sub", "1"); |
||||||
|
instruct("point", arg); |
||||||
|
instruct("add", "1"); |
||||||
|
instruct("point", pointedVariable); |
||||||
|
bfOutput.push("]"); |
||||||
|
break; |
||||||
|
case "sub": |
||||||
|
number = parseNum(arg); |
||||||
|
if (Number.isNaN(number)) { |
||||||
|
return "Failed to parse number."; |
||||||
|
} |
||||||
|
bfOutput.push("-".repeat(number)); |
||||||
|
break; |
||||||
|
case "subb": |
||||||
|
number = parseNum(arg); |
||||||
|
if (Number.isNaN(number)) { |
||||||
|
return "Failed to parse number."; |
||||||
|
} |
||||||
|
pointedVariable = variables[ptr]; |
||||||
|
instruct("var", "__temp"); |
||||||
|
instruct("point", "__temp"); |
||||||
|
instruct("add", Math.floor(number / 10)); |
||||||
|
bfOutput.push("["); |
||||||
|
instruct("sub", "1"); |
||||||
|
instruct("point", pointedVariable); |
||||||
|
instruct("sub", "10"); |
||||||
|
instruct("point", "__temp"); |
||||||
|
bfOutput.push("]"); |
||||||
|
instruct("point", pointedVariable); |
||||||
|
instruct("sub", number - Math.floor(number / 10) * 10); |
||||||
|
instruct("delvar", "__temp"); |
||||||
|
break; |
||||||
|
case "subv": |
||||||
|
pointedVariable = variables[ptr]; |
||||||
|
bfOutput.push("["); |
||||||
|
instruct("sub", "1"); |
||||||
|
instruct("point", arg); |
||||||
|
instruct("sub", "1"); |
||||||
|
instruct("point", pointedVariable); |
||||||
|
bfOutput.push("]"); |
||||||
|
break; |
||||||
|
case "copy": |
||||||
|
pointedVariable = variables[ptr]; |
||||||
|
instruct("var", "__temp"); |
||||||
|
bfOutput.push("["); |
||||||
|
instruct("sub", "1"); |
||||||
|
instruct("point", arg); |
||||||
|
instruct("add", "1"); |
||||||
|
instruct("point", "__temp"); |
||||||
|
instruct("add", "1"); |
||||||
|
instruct("point", pointedVariable); |
||||||
|
bfOutput.push("]"); |
||||||
|
instruct("point", "__temp"); |
||||||
|
bfOutput.push("["); |
||||||
|
instruct("sub", "1"); |
||||||
|
instruct("point", pointedVariable); |
||||||
|
instruct("add", "1"); |
||||||
|
instruct("point", "__temp"); |
||||||
|
bfOutput.push("]"); |
||||||
|
instruct("delvar", "__temp"); |
||||||
|
break; |
||||||
|
case "setz": |
||||||
|
bfOutput.push("[-]"); |
||||||
|
break; |
||||||
|
case "getchr": |
||||||
|
bfOutput.push(","); |
||||||
|
break; |
||||||
|
case "print": |
||||||
|
bfOutput.push("."); |
||||||
|
break; |
||||||
|
case "[": |
||||||
|
bfOutput.push("["); |
||||||
|
break; |
||||||
|
case "]": |
||||||
|
bfOutput.push("]"); |
||||||
|
break; |
||||||
|
default: |
||||||
|
if (instruction !== "") { |
||||||
|
return `${instruction} is not an instruction.` |
||||||
|
} |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
function runMblf(inputFile) { |
||||||
|
let file; |
||||||
|
try { |
||||||
|
file = fs.readFileSync(inputFile, "utf8") |
||||||
|
} catch (e) { |
||||||
|
console.error(`Error: ${e.message}`); |
||||||
|
process.exit(1); |
||||||
|
} |
||||||
|
return file |
||||||
|
.replace(/\r/gm, "") |
||||||
|
.split("\n") |
||||||
|
.forEach((item, line) => { |
||||||
|
let statement = item.split(";;")[0].trim().split(" "); |
||||||
|
if (statement[0] == "#include") { |
||||||
|
try { |
||||||
|
runMblf(item.split("\"")[1]); |
||||||
|
} catch (e) { |
||||||
|
if (e.name == "RangeError" && e.message == "Maximum call stack size exceeded") { |
||||||
|
console.error(`[${inputFile}] Error in line ${line+1}: Call stack size exceeded. Check for cyclic dependencies.`); |
||||||
|
process.exit(1); |
||||||
|
} else { |
||||||
|
throw e; |
||||||
|
} |
||||||
|
} |
||||||
|
} else { |
||||||
|
let instructAttempt = instruct(statement[0], statement.slice(1, statement.length).join(" ")); |
||||||
|
if (instructAttempt !== undefined) { |
||||||
|
console.error(`[${inputFile}] Error in line ${line+1}: ${instructAttempt}`); |
||||||
|
process.exit(1); |
||||||
|
} |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
runMblf(args[0]); |
||||||
|
fs.writeFileSync(args[1], bfOutput.join("")); |
||||||
|
|
||||||
|
console.timeEnd("Compiled in"); |
Loading…
Reference in new issue