diff --git a/package-lock.json b/package-lock.json index 55ad6303..85ecfd03 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8172,6 +8172,11 @@ "pify": "^3.0.0" } }, + "malbolge-vm": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/malbolge-vm/-/malbolge-vm-1.0.2.tgz", + "integrity": "sha1-AXFAPb2Js4qi10yUzalcqRGo9ms=" + }, "map-age-cleaner": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", diff --git a/package.json b/package.json index cb59db38..d6922d49 100644 --- a/package.json +++ b/package.json @@ -113,6 +113,7 @@ "lodash": "^4.17.11", "loglevel": "^1.6.1", "loglevel-message-prefix": "^3.0.0", + "malbolge-vm": "^1.0.2", "moment": "^2.23.0", "moment-timezone": "^0.5.23", "ngeohash": "^0.6.3", diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json index 8235ab10..a6e765ab 100755 --- a/src/core/config/Categories.json +++ b/src/core/config/Categories.json @@ -311,6 +311,12 @@ "TCP/IP Checksum" ] }, + { + "name": "Esoteric Programming", + "ops": [ + "Malbolge" + ] + }, { "name": "Code tidy", "ops": [ diff --git a/src/core/operations/Malbolge.mjs b/src/core/operations/Malbolge.mjs new file mode 100644 index 00000000..24820f83 --- /dev/null +++ b/src/core/operations/Malbolge.mjs @@ -0,0 +1,90 @@ +/** + * @author Karsten Silkenbäumer [github.com/kassi] + * @copyright Karsten Silkenbäumer 2019 + * @license Apache-2.0 + */ + +import Operation from "../Operation"; +import OperationError from "../errors/OperationError"; +import mb from "malbolge-vm"; + +/** + * Malbolge operation + */ +class Malbolge extends Operation { + /** + * Malbolge constructor + */ + constructor () { + super(); + + this.name = "Malbolge"; + this.module = "Default"; + this.description = "Malbolge, invented by Ben Olmstead in 1998, is an esoteric programming language designed to be as difficult to program in as possible. The first ‘Hello, world!’ program written in it was produced by a Lisp program using a local beam search of the space of all possible programs. It is modeled as a virtual machine based on ternary digits."; + this.infoURL = "http://esolangs.org/wiki/Malbolge"; + this.inputType = "string"; + this.outputType = "string"; + this.args = [ + { + "name": "User Input (line by line)", + "type": "text", + "value": "" + } + ]; + this.patterns = [ + { + match: "^(?:[\\x21-\\x7e])?$", + flags: "i", + args: [] + } + ]; + } + + /** + * @param {String} input - A Malbolge program + * @param {Object[]} args + * @returns {String} + */ + run (input, args) { + if (input.length === 0) { + return ""; + } + + const [userInputString] = args, + vm = mb.load(input), + userInputArray = userInputString.split("").map(e => e.charCodeAt(0)); + + let userInput = null, + output = "", + loop = true, + temp; + + while (loop) { + try { + while ((temp = mb.step(vm, userInput)) !== mb.EXIT) { + userInput = null; + + if (temp !== null) { + output += String.fromCharCode(temp); + } + } + loop = false; + } catch (err) { + if (err === mb.WANTS_INPUT) { + if (userInputArray.length) { + userInput = userInputArray.shift(); + continue; + } + output += "Error: Input required"; + loop = false; + } else { + throw new OperationError(err); + } + } + } + + return output; + } +} + +export default Malbolge; diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs index fb68ed9c..d3f6c6b2 100644 --- a/tests/operations/index.mjs +++ b/tests/operations/index.mjs @@ -57,6 +57,7 @@ import "./tests/JWTSign"; import "./tests/JWTVerify"; import "./tests/MS"; import "./tests/Magic"; +import "./tests/Malbolge"; import "./tests/MorseCode"; import "./tests/NetBIOS"; import "./tests/OTP"; diff --git a/tests/operations/tests/Malbolge.mjs b/tests/operations/tests/Malbolge.mjs new file mode 100644 index 00000000..1a12d62f --- /dev/null +++ b/tests/operations/tests/Malbolge.mjs @@ -0,0 +1,44 @@ +/** + * Malbolge tests. + * + * @author Karsten Silkenbäumer [github.com/kassi] + * @copyright Karsten Silkenbäumer 2019 + * @license Apache-2.0 + */ +import TestRegister from "../TestRegister"; + +TestRegister.addTests([ + { + name: "Malbolge: no program", + input: "", + expectedOutput: "", + recipeConfig: [ + { + op: "Malbolge", + args: [""], + }, + ], + }, + { + name: "Malbolge: simple program", + input: "('&%:9]!~}|z2Vxwv-,POqponl$Hjig%eB@@>}=", + expectedOutput: "Hello World!", + recipeConfig: [ + { + op: "Malbolge", + args: [""], + }, + ], + }, + { + name: "Malbolge: simple cat with infinite user input", + input: "(=BA#9\"=<;:3y7x54-21q/p-,+*)\"!h%B0/.\n~P<\n<:(8&\n66#\"!~}|{zyxwvu\ngJ%", + expectedOutput: "First line\nSecond Line\nError: Input required", + recipeConfig: [ + { + op: "Malbolge", + args: ["First line\nSecond Line\n"], + }, + ], + }, +]);