├── .gitignore ├── examples └── main.scm ├── replit.nix ├── default.nix ├── src ├── prompt.js ├── interp.js └── languages │ ├── roy │ └── index.js │ ├── unlambda │ └── index.js │ ├── bloop │ └── index.js │ ├── emoticon │ └── index.js │ ├── scheme │ └── index.js │ └── qbasic │ └── index.js ├── package.json ├── README.md ├── yarn.lock ├── yarn.nix ├── .replit ├── replbox.js └── vendor ├── unlambda.js ├── bloop.js └── emoticon.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .npmrc 4 | 5 | result 6 | result-* 7 | -------------------------------------------------------------------------------- /examples/main.scm: -------------------------------------------------------------------------------- 1 | (define (fib n) 2 | (if (< n 2) n (+ (fib (- n 1)) 3 | (fib (- n 2))))) 4 | (fib 9) 5 | -------------------------------------------------------------------------------- /replit.nix: -------------------------------------------------------------------------------- 1 | { pkgs }: { 2 | deps = [ 3 | pkgs.yarn2nix 4 | pkgs.nodejs-16_x 5 | pkgs.nodePackages.typescript-language-server 6 | pkgs.yarn 7 | pkgs.replitPackages.jest 8 | ]; 9 | } -------------------------------------------------------------------------------- /default.nix: -------------------------------------------------------------------------------- 1 | with (import {}); 2 | rec { 3 | muellshack = mkYarnPackage { 4 | name = "replbox"; 5 | src = ./.; 6 | packageJSON = ./package.json; 7 | yarnLock = ./yarn.lock; 8 | yarnNix = ./yarn.nix; 9 | }; 10 | } 11 | -------------------------------------------------------------------------------- /src/prompt.js: -------------------------------------------------------------------------------- 1 | const readline = require('readline'); 2 | 3 | const rl = readline.createInterface({ 4 | input: process.stdin, 5 | output: process.stdout, 6 | terminal: false, 7 | }); 8 | 9 | function prompt(prefix, callback) { 10 | process.stdout.write(prefix); 11 | const cb = line => { 12 | callback(null, line); 13 | rl.removeListener('line', cb); 14 | }; 15 | rl.on('line', cb); 16 | } 17 | 18 | module.exports = { 19 | prompt, 20 | }; 21 | -------------------------------------------------------------------------------- /src/interp.js: -------------------------------------------------------------------------------- 1 | const { prompt } = require('./prompt.js'); 2 | 3 | function stdin(callback) { 4 | prompt('', (err, text) => { 5 | callback(text); 6 | }); 7 | } 8 | 9 | function stdout(args) { 10 | if (args) { 11 | process.stdout.write(`${args}`); 12 | } 13 | } 14 | 15 | function stderr(args) { 16 | if (args) { 17 | console.error(`\x1b[0;31m${args}\x1b[0m`); 18 | } 19 | } 20 | 21 | module.exports = { 22 | stdout, 23 | stderr, 24 | stdin, 25 | }; 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@replit/replbox", 3 | "version": "3.0.0", 4 | "description": "A sandboxed browser REPL", 5 | "bin": "replbox.js", 6 | "scripts": { 7 | "prettier": "prettier --write \"src/**/*.js\"", 8 | "replbox": "node replbox.js" 9 | }, 10 | "author": "amjad@repl.it", 11 | "license": "UNLICENSED", 12 | "devDependencies": { 13 | "prettier": "^1.18.2" 14 | }, 15 | "dependencies": { 16 | "argparse": "^2.0.1", 17 | "biwascheme": "^0.7.5", 18 | "underscore": "1.2.2" 19 | }, 20 | "prettier": { 21 | "trailingComma": "all", 22 | "tabWidth": 2, 23 | "semi": true, 24 | "singleQuote": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Replbox 2 | 3 | ***NOTE***: this package is deprecated. There will be more interesting interpreter support in Replit coming soon! 4 | 5 | --- 6 | 7 | A set of interpreters that have been historically used to run things on the client. It is the successor of https://github.com/replit-archive/jsrepl. 8 | 9 | After a migration away from client-evaluated things, we turned this into a CLI package to do the migration in a backwards compatible fashion, in addition to supporting languages that only have a JS-based interpreter. 10 | 11 | Example usage `.replit`: 12 | 13 | ```toml 14 | entrypoint = "main.scm" 15 | 16 | [interpreter] 17 | command = [ 18 | "replit-replbox", 19 | "--ps1", "\u0001\u001b[33m\u0002\u0001\u001b[00m\u0002 ", 20 | "-i", 21 | "scheme" 22 | ] 23 | ``` 24 | -------------------------------------------------------------------------------- /src/languages/roy/index.js: -------------------------------------------------------------------------------- 1 | const { roy } = require('../../../vendor/roy'); 2 | const interp = require('../../interp'); 3 | 4 | const header = `Roy 0.1.3 5 | Copyright (C) 2011 Brian McKenna`; 6 | 7 | function evaluate(code, callback) { 8 | let compiled; 9 | try { 10 | compiled = roy.compile(code).output; 11 | } catch (e) { 12 | callback(e.stack, null); 13 | return; 14 | } 15 | 16 | try { 17 | callback(null, eval(compiled)); 18 | } catch (e) { 19 | callback(e.message, null); 20 | } 21 | } 22 | 23 | function checkLine(command) { 24 | try { 25 | roy.compile(command); 26 | return false; 27 | } catch (e) { 28 | if (/[[{(]$/.test(command)) { 29 | // An opening brace, bracket or paren; indent. 30 | return 1; 31 | } 32 | return 0; 33 | } 34 | } 35 | 36 | module.exports = { 37 | header, 38 | evaluate, 39 | checkLine, 40 | }; 41 | -------------------------------------------------------------------------------- /src/languages/unlambda/index.js: -------------------------------------------------------------------------------- 1 | const { Unlambda } = require('../../../vendor/unlambda'); 2 | const interp = require('../../interp'); 3 | 4 | const header = `Unlambda v2.0 (unlambda-coffee) 5 | Copyright (c) 2011 Max Shawabkeh`; 6 | 7 | function evaluate(code, callback) { 8 | let parsed; 9 | try { 10 | parsed = Unlambda.parse(code); 11 | } catch (e) { 12 | callback(e.message, null); 13 | return; 14 | } 15 | 16 | Unlambda.eval( 17 | parsed, 18 | data => callback(null, data), 19 | interp.stdin, 20 | interp.stdout, 21 | interp.stderr, 22 | ); 23 | } 24 | 25 | function checkLine(command) { 26 | if (/`$/.test(command)) { 27 | return 0; 28 | return Messenger.checkLineEnd(0); 29 | } 30 | 31 | try { 32 | Unlambda.parse(command); 33 | Messenger.checkLineEnd(false); 34 | } catch (e) { 35 | Messenger.checkLineEnd(0); 36 | } 37 | 38 | // ¯\_(ツ)_/¯ 39 | return undefined; 40 | } 41 | 42 | module.exports = { 43 | header, 44 | evaluate, 45 | checkLine, 46 | }; 47 | -------------------------------------------------------------------------------- /src/languages/bloop/index.js: -------------------------------------------------------------------------------- 1 | const BFloop = require('../../../vendor/bloop'); 2 | const interp = require('../../interp'); 3 | 4 | BFloop.init(interp.stdout); 5 | 6 | const header = `BlooPjs 7 | Copyright (c) 2005 Tim Cameron Ryan 8 | Based on Perl code by John Cowan, 1994`; 9 | 10 | function evaluate(code, callback) { 11 | try { 12 | const compiledCode = BFloop.compile(code); 13 | const result = eval(compiledCode); 14 | callback(null, result); 15 | } catch (e) { 16 | callback(e.message, null); 17 | } 18 | } 19 | 20 | function checkLine(command) { 21 | console.log('Checkline ', command); 22 | const rOpen = /BLOCK\s+(\d+)\s*:\s*BEGIN/gi; 23 | const rClose = /BLOCK\s+(\d+)\s*:\s*END/gi; 24 | 25 | const match = function(code) { 26 | const opens = code.match(rOpen) || []; 27 | const closes = code.match(rClose) || []; 28 | return opens.length - closes.length; 29 | }; 30 | 31 | if (match(command) <= 0) { 32 | return false; 33 | } 34 | 35 | const count = match(command.split('\n').slice(-1)[0]); 36 | 37 | if (count > 0) { 38 | return 1; 39 | } 40 | 41 | return 0; 42 | } 43 | 44 | module.exports = { 45 | header, 46 | evaluate, 47 | checkLine, 48 | }; 49 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | argparse@^2.0.1: 6 | version "2.0.1" 7 | resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" 8 | integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== 9 | 10 | biwascheme@^0.7.5: 11 | version "0.7.5" 12 | resolved "https://registry.yarnpkg.com/biwascheme/-/biwascheme-0.7.5.tgz#9c69abd7782f644ef547b62c42f62eb59ae19ffc" 13 | integrity sha512-43ZoNvQaQ9vbMbu7Ld0+ff9aqq3fk0jztY0p4fy6UIZUaRAuiK9ulkjtIJG7zRBkD/ZclOGUXbG13SA0jYy2NQ== 14 | dependencies: 15 | optparse "1.0.5" 16 | 17 | optparse@1.0.5: 18 | version "1.0.5" 19 | resolved "https://registry.yarnpkg.com/optparse/-/optparse-1.0.5.tgz#75e75a96506611eb1c65ba89018ff08a981e2c16" 20 | integrity sha1-dedallBmEescZbqJAY/wipgeLBY= 21 | 22 | prettier@^1.18.2: 23 | version "1.19.1" 24 | resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb" 25 | integrity sha512-s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew== 26 | 27 | underscore@1.2.2: 28 | version "1.2.2" 29 | resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.2.2.tgz#74dd40e9face84e724eb2edae945b8aedc233ba3" 30 | integrity sha1-dN1A6frOhOck6y7a6UW4rtwjO6M= 31 | -------------------------------------------------------------------------------- /yarn.nix: -------------------------------------------------------------------------------- 1 | { fetchurl, fetchgit, linkFarm, runCommand, gnutar }: rec { 2 | offline_cache = linkFarm "offline" packages; 3 | packages = [ 4 | { 5 | name = "argparse___argparse_2.0.1.tgz"; 6 | path = fetchurl { 7 | name = "argparse___argparse_2.0.1.tgz"; 8 | url = "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz"; 9 | sha512 = "8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="; 10 | }; 11 | } 12 | { 13 | name = "biwascheme___biwascheme_0.7.5.tgz"; 14 | path = fetchurl { 15 | name = "biwascheme___biwascheme_0.7.5.tgz"; 16 | url = "https://registry.yarnpkg.com/biwascheme/-/biwascheme-0.7.5.tgz"; 17 | sha512 = "43ZoNvQaQ9vbMbu7Ld0+ff9aqq3fk0jztY0p4fy6UIZUaRAuiK9ulkjtIJG7zRBkD/ZclOGUXbG13SA0jYy2NQ=="; 18 | }; 19 | } 20 | { 21 | name = "optparse___optparse_1.0.5.tgz"; 22 | path = fetchurl { 23 | name = "optparse___optparse_1.0.5.tgz"; 24 | url = "https://registry.yarnpkg.com/optparse/-/optparse-1.0.5.tgz"; 25 | sha1 = "dedallBmEescZbqJAY/wipgeLBY="; 26 | }; 27 | } 28 | { 29 | name = "prettier___prettier_1.19.1.tgz"; 30 | path = fetchurl { 31 | name = "prettier___prettier_1.19.1.tgz"; 32 | url = "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz"; 33 | sha512 = "s7PoyDv/II1ObgQunCbB9PdLmUcBZcnWOcxDh7O0N/UwDEsHyqkW+Qh28jW+mVuCdx7gLB0BotYI1Y6uI9iyew=="; 34 | }; 35 | } 36 | { 37 | name = "underscore___underscore_1.2.2.tgz"; 38 | path = fetchurl { 39 | name = "underscore___underscore_1.2.2.tgz"; 40 | url = "https://registry.yarnpkg.com/underscore/-/underscore-1.2.2.tgz"; 41 | sha1 = "dN1A6frOhOck6y7a6UW4rtwjO6M="; 42 | }; 43 | } 44 | ]; 45 | } 46 | -------------------------------------------------------------------------------- /.replit: -------------------------------------------------------------------------------- 1 | entrypoint = "examples/main.scm" 2 | 3 | [interpreter] 4 | command = [ 5 | "node", 6 | "replbox.js", 7 | "--ps1", "\u0001\u001b[33m\u0002\u0001\u001b[00m\u0002 ", 8 | "-i", 9 | "scheme" 10 | ] 11 | 12 | 13 | [env] 14 | XDG_CONFIG_HOME = "/home/runner/.config" 15 | PATH = "/home/runner/$REPL_SLUG/.config/npm/node_global/bin:/home/runner/$REPL_SLUG/node_modules/.bin" 16 | npm_config_prefix = "/home/runner/$REPL_SLUG/.config/npm/node_global" 17 | 18 | [nix] 19 | channel = "stable-22_05" 20 | 21 | [packager] 22 | language = "nodejs" 23 | 24 | [packager.features] 25 | packageSearch = true 26 | guessImports = true 27 | enabledForHosting = false 28 | 29 | [debugger] 30 | support = true 31 | 32 | [debugger.interactive] 33 | transport = "localhost:0" 34 | startCommand = ["dap-node"] 35 | 36 | [debugger.interactive.initializeMessage] 37 | command = "initialize" 38 | type = "request" 39 | 40 | [debugger.interactive.initializeMessage.arguments] 41 | clientID = "replit" 42 | clientName = "replit.com" 43 | columnsStartAt1 = true 44 | linesStartAt1 = true 45 | locale = "en-us" 46 | pathFormat = "path" 47 | supportsInvalidatedEvent = true 48 | supportsProgressReporting = true 49 | supportsRunInTerminalRequest = true 50 | supportsVariablePaging = true 51 | supportsVariableType = true 52 | 53 | [debugger.interactive.launchMessage] 54 | command = "launch" 55 | type = "request" 56 | 57 | [debugger.interactive.launchMessage.arguments] 58 | console = "externalTerminal" 59 | cwd = "." 60 | pauseForSourceMap = false 61 | program = "./index.js" 62 | request = "launch" 63 | sourceMaps = true 64 | stopOnEntry = false 65 | type = "pwa-node" 66 | 67 | [unitTest] 68 | language = "nodejs" 69 | 70 | [languages] 71 | 72 | [languages.javascript] 73 | pattern = "**/{*.js,*.jsx,*.ts,*.tsx}" 74 | 75 | [languages.javascript.languageServer] 76 | start = "typescript-language-server --stdio" -------------------------------------------------------------------------------- /src/languages/emoticon/index.js: -------------------------------------------------------------------------------- 1 | const Emoticon = require('../../../vendor/emoticon'); 2 | const interp = require('../../interp'); 3 | 4 | const header = `Emoticon v1.5 (emoticoffee) 5 | Copyright (c) 2011 Amjad Masad`; 6 | 7 | const interpreter = new Emoticon.Interpreter({ 8 | source: [], 9 | input: interp.stdin, 10 | print: interp.stdout, 11 | }); 12 | 13 | function evaluate(code, callback) { 14 | interpreter.result = env => { 15 | let resultEnv = ''; 16 | for (const listName in env) { 17 | const list = env[listName]; 18 | let listStr = list.toString(); 19 | let len = listStr.length - 74; 20 | len = len > 0 ? len : 0; 21 | listStr = listStr.slice(len); 22 | if (len > 0) { 23 | listStr = `...${listStr}`; 24 | } 25 | resultEnv += `\n${listName}: ${listStr}`; 26 | } 27 | callback(null, resultEnv); 28 | }; 29 | 30 | try { 31 | const parsed = new Emoticon.Parser(code); 32 | interpreter.lists.Z = interpreter.lists.Z.concat(parsed); 33 | interpreter.run(); 34 | } catch (e) { 35 | callback(e.message, null); 36 | } 37 | } 38 | 39 | function countParens(str) { 40 | const tokens = new Emoticon.Parser(str); 41 | let parens = 0; 42 | 43 | for (const token of tokens) { 44 | if (token.mouth) { 45 | if (token.mouth === '(') { 46 | parens++; 47 | } else if (token.mouth === ')') { 48 | parens--; 49 | } 50 | } 51 | } 52 | 53 | return parens; 54 | } 55 | 56 | function checkLine(code) { 57 | const ret = r => Messenger.checkLineEnd(r); 58 | 59 | if (countParens(code) <= 0) { 60 | return ret(false); 61 | } 62 | const lastLinesParens = countParens(code.split('\n').pop()); 63 | if (lastLinesParens > 0) { 64 | return ret(1); 65 | } else if (lastLinesParens > 0) { 66 | return ret(lastLinesParens); 67 | } 68 | 69 | return ret(0); 70 | } 71 | 72 | module.exports = { 73 | header, 74 | evaluate, 75 | checkLine, 76 | }; 77 | -------------------------------------------------------------------------------- /src/languages/scheme/index.js: -------------------------------------------------------------------------------- 1 | const BiwaScheme = require('biwascheme'); 2 | 3 | const interp = require('../../interp'); 4 | 5 | const Port = BiwaScheme.Port; 6 | 7 | Port.current_input = new Port.CustomInput(interp.stdin); 8 | Port.current_output = new Port.CustomOutput(interp.stdout); 9 | Port.current_error = new Port.CustomOutput(interp.stderr); 10 | const interpreter = new BiwaScheme.Interpreter(); 11 | 12 | const header = `BiwaScheme Interpreter version ${BiwaScheme.VERSION} 13 | Copyright (C) 2007-2014 Yutaka HARA and the BiwaScheme team`; 14 | 15 | function evaluate(code, callback) { 16 | try { 17 | interpreter.on_error = error => callback(error.message, null); 18 | interpreter.evaluate(code, newState => { 19 | // When the result is JS undefined then this eval was an error and the 20 | // error callback has been already called. 21 | // Scheme seems to return a result even on error. 22 | if (newState !== undefined) { 23 | let result = ''; 24 | 25 | if (newState !== null && newState !== BiwaScheme.undef) { 26 | result = BiwaScheme.to_write(newState); 27 | } 28 | 29 | callback(null, result); 30 | } 31 | }); 32 | } catch (e) { 33 | callback(e.message, null); 34 | } 35 | } 36 | 37 | function checkLine(command) { 38 | function countParens(str) { 39 | const { tokens } = new BiwaScheme.Parser(str); 40 | let parens = 0; 41 | for (const token of tokens) { 42 | switch (token) { 43 | case '[': 44 | case '(': 45 | parens++; 46 | break; 47 | case ']': 48 | case ')': 49 | parens--; 50 | break; 51 | } 52 | } 53 | return parens; 54 | } 55 | 56 | if (countParens(command) <= 0) { 57 | // All S-exps closed or extra closing parens; don't continue. 58 | return false; 59 | } 60 | 61 | const parensLastLine = countParens(command.split('\n').pop()); 62 | if (parensLastLine > 0) { 63 | // A new S-exp opened on the last line; indent one level. 64 | return 1; 65 | } else if (parensLastLine < 0) { 66 | // Some S-exps were closed; realign with the outermost closed S-exp. 67 | return parensLastLine; 68 | } 69 | return 0; 70 | } 71 | 72 | module.exports = { 73 | header, 74 | evaluate, 75 | checkLine, 76 | }; 77 | -------------------------------------------------------------------------------- /replbox.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const util = require('util'); 4 | 5 | const { ArgumentParser } = require('argparse'); 6 | 7 | const { prompt } = require('./src/prompt.js'); 8 | const asyncPrompt = util.promisify(prompt); 9 | const { stderr } = require('./src/interp.js'); 10 | 11 | const parser = new ArgumentParser({ 12 | description: 'Replbox is a single frontend to several language interpreters', 13 | add_help: true, 14 | }); 15 | 16 | let defaultPrompt = process.env['PS1']; 17 | if (defaultPrompt === undefined) { 18 | defaultPrompt = '\x1b[0;33m > '; 19 | } 20 | 21 | parser.add_argument('-v', '--version', { action: 'version', version: '3.0.0' }); 22 | parser.add_argument('--ps1', { 23 | help: 'The prompt to display', 24 | default: defaultPrompt, 25 | }); 26 | parser.add_argument('--result', { 27 | help: 'The prompt to display with results', 28 | default: '\x1b[0;32m=> ', 29 | }); 30 | parser.add_argument('-i', { 31 | help: 'Drop to an interpreter after interpreting files', 32 | action: 'store_true', 33 | }); 34 | parser.add_argument('lang', { 35 | help: 'The language to interpret', 36 | choices: ['bloop', 'emoticon', 'qbasic', 'roy', 'scheme', 'unlambda'], 37 | }); 38 | parser.add_argument('file', { 39 | help: 'A file to pass to the interpreter', 40 | nargs: '?', 41 | }); 42 | 43 | const args = parser.parse_args(); 44 | 45 | const { header, evaluate } = require(`./src/languages/${args.lang}`); 46 | const asyncEval = util.promisify(evaluate); 47 | console.log(header); 48 | 49 | (async () => { 50 | if (args.file) { 51 | const fs = require('fs'); 52 | try { 53 | const content = fs.readFileSync(args.file, { 54 | encoding: 'utf8', 55 | flag: 'r', 56 | }); 57 | 58 | try { 59 | const result = await asyncEval(content); 60 | if (result !== undefined) { 61 | console.log(`${args.result}${result}\x1b[0m`); 62 | } 63 | } catch (e) { 64 | stderr(e); 65 | } 66 | 67 | if (!args.i) { 68 | process.exit(0); 69 | } 70 | } catch (e) { 71 | console.error(`Failed to open ${args.file}: ${e.message}`); 72 | process.exit(1); 73 | } 74 | } 75 | 76 | while (1) { 77 | const code = await asyncPrompt(args.ps1); 78 | process.stdout.write('\x1b[0m'); 79 | try { 80 | const result = await asyncEval(code); 81 | if (result !== undefined) { 82 | console.log(`${args.result}${result}\x1b[0m`); 83 | } 84 | } catch (e) { 85 | stderr(e); 86 | } 87 | } 88 | })(); 89 | -------------------------------------------------------------------------------- /src/languages/qbasic/index.js: -------------------------------------------------------------------------------- 1 | const QBasic = require('../../../vendor/qb'); 2 | const interp = require('../../interp'); 3 | 4 | const virtual_machine = new QBasic.VirtualMachine({ 5 | print: interp.stdout, 6 | // remove the ending \n 7 | input: interp.stdin, 8 | error: interp.stderr, 9 | }); 10 | 11 | const header = `QBasic (qb.js) 12 | Copyright (c) 2010 Steve Hanov`; 13 | 14 | function evaluate(code, callback) { 15 | return virtual_machine.run(code, () => { 16 | if (virtual_machine.stack.length) { 17 | return callback(null, virtual_machine.stack.pop().toString()); 18 | } 19 | return callback(null, undefined); 20 | }); 21 | } 22 | 23 | function checkLine(command) { 24 | let i; 25 | 26 | QBasic.Program.prototype.createParser(); 27 | 28 | const parser = QBasic.Program.parser; 29 | if (parser.parse(`${command}\n`) === !null) { 30 | return false; 31 | } 32 | 33 | const tokenizer = parser.tokenizer; 34 | 35 | const lines = (function() { 36 | let j; 37 | let len; 38 | 39 | const ref = command.split('\n'); 40 | 41 | const results = []; 42 | for (j = 0, len = ref.length; j < len; j++) { 43 | i = ref[j]; 44 | results.push(`${i}\n`); 45 | } 46 | return results; 47 | })(); 48 | 49 | const countBlocks = function(lines2, partial) { 50 | let firstToken; 51 | let j; 52 | let len; 53 | let line; 54 | let secondToken; 55 | let token; 56 | let topBlock; 57 | 58 | if (partial == null) { 59 | partial = false; 60 | } 61 | 62 | const openBlocks = []; 63 | 64 | for (j = 0, len = lines2.length; j < len; j++) { 65 | line = lines2[j]; 66 | if (parser.parse(line)) { 67 | continue; 68 | } 69 | tokenizer.setText(line); 70 | token = tokenizer.nextToken(0, 0); 71 | if (!token) { 72 | continue; 73 | } 74 | firstToken = token.text; 75 | token = tokenizer.nextToken(0, token.locus.position + token.text.length); 76 | if (!token) { 77 | continue; 78 | } 79 | secondToken = token.text; 80 | topBlock = openBlocks[openBlocks.length - 1]; 81 | switch (firstToken) { 82 | case 'SUB': 83 | case 'FUNCTION': 84 | case 'FOR': 85 | case 'IF': 86 | case 'SELECT': 87 | case 'WHILE': 88 | openBlocks.push(firstToken); 89 | break; 90 | case 'DO': 91 | openBlocks.push(secondToken === 'WHILE' ? 'DOWHILE' : 'DO'); 92 | break; 93 | case 'ELSE': 94 | if (partial && openBlocks.length === 0) { 95 | openBlocks.push('IF'); 96 | } else if (topBlock !== 'IF') { 97 | return -1; 98 | } 99 | break; 100 | case 'WEND': 101 | if (topBlock === 'WHILE') { 102 | openBlocks.pop(); 103 | } else { 104 | return -1; 105 | } 106 | break; 107 | // this should never be reached because of the previous 'FOR' case 108 | // case 'FOR': 109 | // if (topBlock === 'NEXT') { 110 | // openBlocks.pop(); 111 | // } else { 112 | // return -1; 113 | // } 114 | // break; 115 | case 'LOOP': 116 | if (secondToken === 'WHILE' || secondToken === 'UNTIL') { 117 | if (topBlock === 'DO') { 118 | openBlocks.pop(); 119 | } else { 120 | return -1; 121 | } 122 | } else if (topBlock === 'DOWHILE') { 123 | openBlocks.pop(); 124 | } else { 125 | return -1; 126 | } 127 | break; 128 | case 'END': 129 | if (topBlock === secondToken) { 130 | openBlocks.pop(); 131 | } else { 132 | return -1; 133 | } 134 | } 135 | } 136 | return openBlocks.length; 137 | }; 138 | if (countBlocks(lines) <= 0) { 139 | return false; 140 | } else if (countBlocks([lines.slice(-1)[0]], true) > 0) { 141 | return 1; 142 | } 143 | return 0; 144 | } 145 | 146 | module.exports = { 147 | header, 148 | evaluate, 149 | checkLine, 150 | }; 151 | -------------------------------------------------------------------------------- /vendor/unlambda.js: -------------------------------------------------------------------------------- 1 | // commit: 46d247ad22720cbc9b79757b9762eeeae80a222a 2 | var CALL_LIMIT, call, call_current, parse, runEval, unparse; 3 | 4 | CALL_LIMIT = 500; 5 | 6 | call_current = 0; 7 | 8 | call = function(target, arg) { 9 | if (call_current >= CALL_LIMIT) { 10 | call_current = 0; 11 | setTimeout((function() { 12 | return target(arg); 13 | }), 0); 14 | } else { 15 | call_current++; 16 | target(arg); 17 | } 18 | return null; 19 | }; 20 | 21 | runEval = function(program, result, input, output, error) { 22 | var Eval, apply, register; 23 | register = void 0; 24 | apply = function(arg1, arg, continuation) { 25 | var action, closure, f1, f2, func; 26 | func = arg1[0], closure = arg1[1]; 27 | switch (func) { 28 | case '.': 29 | output(closure); 30 | call(continuation, arg); 31 | break; 32 | case 'r': 33 | output('\n'); 34 | call(continuation, arg); 35 | break; 36 | case 'i': 37 | call(continuation, arg); 38 | break; 39 | case 'k': 40 | call(continuation, ['k1', arg]); 41 | break; 42 | case 'k1': 43 | call(continuation, closure); 44 | break; 45 | case 's': 46 | call(continuation, ['s1', arg]); 47 | break; 48 | case 's1': 49 | call(continuation, ['s2', [closure, arg]]); 50 | break; 51 | case 's2': 52 | f1 = closure[0], f2 = closure[1]; 53 | apply(f1, arg, function(r1) { 54 | return apply(f2, arg, function(r2) { 55 | return apply(r1, r2, continuation); 56 | }); 57 | }); 58 | break; 59 | case 'v': 60 | call(continuation, ['v', null]); 61 | break; 62 | case 'd1': 63 | Eval(['`', [closure, arg]], function(value) { 64 | return call(continuation, value); 65 | }); 66 | break; 67 | case 'e': 68 | result(arg); 69 | break; 70 | case '@': 71 | input(function(value_read) { 72 | var action; 73 | register = value_read != null ? value_read[0] : void 0; 74 | action = register != null ? 'i' : 'v'; 75 | return Eval(['`', [arg, [action, null]]], continuation); 76 | }); 77 | break; 78 | case '|': 79 | if (register != null) { 80 | Eval(['`', [arg, ['.', register]]], continuation); 81 | } else { 82 | Eval(['`', [arg, ['v', null]]], continuation); 83 | } 84 | break; 85 | case '?': 86 | action = register === closure ? 'i' : 'v'; 87 | Eval(['`', [arg, [action, null]]], continuation); 88 | break; 89 | case 'c': 90 | Eval(['`', [arg, ['c1', continuation]]], continuation); 91 | break; 92 | case 'c1': 93 | call(closure, arg); 94 | break; 95 | default: 96 | error(new Error('Unknown function: ' + func)); 97 | } 98 | return null; 99 | }; 100 | Eval = function(arg1, continuation) { 101 | var arg, closure, func; 102 | func = arg1[0], closure = arg1[1]; 103 | if (func === '`') { 104 | func = closure[0], arg = closure[1]; 105 | Eval(func, function(evaled_func) { 106 | if (evaled_func[0] === 'd') { 107 | return call(continuation, ['d1', arg]); 108 | } else { 109 | return Eval(arg, function(evaled_arg) { 110 | return apply(evaled_func, evaled_arg, function(res) { 111 | return call(continuation, res); 112 | }); 113 | }); 114 | } 115 | }); 116 | } else { 117 | call(continuation, [func, closure]); 118 | } 119 | return null; 120 | }; 121 | Eval(program, function(value) { 122 | return result(value); 123 | }); 124 | return null; 125 | }; 126 | 127 | parse = function(program) { 128 | var doParse; 129 | doParse = function() { 130 | var match, result; 131 | if (program.length === 0) { 132 | throw Error('Unexpected end of input.'); 133 | } 134 | if (program[0] === '`') { 135 | program = program.slice(1); 136 | result = ['`', [doParse(), doParse()]]; 137 | } else if (/^[rksivdce@|]/.test(program)) { 138 | result = [program[0], null]; 139 | program = program.slice(1); 140 | } else if (/^[.?]./.test(program)) { 141 | result = [program[0], program[1]]; 142 | program = program.slice(2); 143 | } else if (match = program.match(/^(\s+|#.*)/)) { 144 | program = program.slice(match[0].length); 145 | result = doParse(); 146 | } else { 147 | throw new Error('Invalid character at: ' + program); 148 | } 149 | return result; 150 | }; 151 | return doParse(); 152 | }; 153 | 154 | unparse = function(arg1) { 155 | var closure, op; 156 | op = arg1[0], closure = arg1[1]; 157 | switch (op) { 158 | case 'r': 159 | case 'i': 160 | case 'k': 161 | case 's': 162 | case 'v': 163 | case 'd': 164 | case 'c': 165 | case 'e': 166 | case '@': 167 | case '|': 168 | return op; 169 | case 'c1': 170 | return ''; 171 | case '.': 172 | case '?': 173 | return op + closure; 174 | case 'k1': 175 | case 's1': 176 | case 'd1': 177 | return '`' + op[0] + unparse(closure); 178 | case '`': 179 | return '`' + unparse(closure[0]) + unparse(closure[1]); 180 | case 's2': 181 | return '``s' + unparse(closure[0]) + unparse(closure[1]); 182 | default: 183 | throw new Error('Unparse: unknown function: ' + op); 184 | } 185 | }; 186 | 187 | this.Unlambda = { 188 | parse: parse, 189 | unparse: unparse, 190 | "eval": runEval 191 | }; 192 | -------------------------------------------------------------------------------- /vendor/bloop.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @preserve 3 | * BlooPJS 4 | * Copyright (c) 2011 Tim Ryan 5 | * Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php 6 | */ 7 | /* spiffy links */ 8 | 9 | // http://www.amazon.com/gp/product/offer-listing/0394756827/ref=dp_pb_a//102-3710615-5652939?condition=all 10 | // http://cgibin.erols.com/ziring/cgi-bin/cep/cep.pl?_key=FLooP 11 | // http://cgibin.erols.com/ziring/cgi-bin/cep/cep.pl?_key=BLooP 12 | // http://www.pdc.kth.se/~jas/retro/retromuseum.html 13 | // http://c2.com/cgi/wiki?BloopFloopAndGloop 14 | 15 | /* parsing variables */ 16 | const BFloop = (function () { 17 | var BlooP = "BlooP"; 18 | 19 | var parseCode = null; 20 | var codeLines = null; 21 | var ns = null; 22 | var val = null; 23 | 24 | var code = ""; 25 | var computedCode = ""; 26 | 27 | var formals = []; 28 | var blocks = []; 29 | 30 | var LOOP_UNUSED = null; 31 | var LOOP_USED = 1; 32 | var LOOP_NEW_ABORTABLE = 2; 33 | var LOOP_ABORTABLE = 3; 34 | var LOOP_ABORTED = 4; 35 | 36 | /* handlers */ 37 | var noop = function () {}; 38 | var outputFunc, warningFunc, noticeFunc; 39 | outputFunc = errorFunc = warningFunc = noticeFunc = noop; 40 | 41 | 42 | function init(outputHandler, warningHandler, noticeHandler) { 43 | outputFunc = outputHandler || noop; 44 | warningFunc = warningHandler || noop; 45 | noticeFunc = noticeHandler || noop; 46 | } 47 | 48 | 49 | function compile(codeInput) { 50 | // clear existing variables 51 | parseCode = codeLines = computedCode = code = ""; 52 | 53 | parseCode = codeInput; 54 | if (!parseCode) return false; 55 | parseCode = parseCode.toUpperCase(); 56 | parseCode = parseCode.replace(/\?/g, "q"); 57 | parseCode = parseCode.replace(/-/g, "_"); 58 | // For examples from the book and the web to be copy pasted easily 59 | // we allow unicode x (multiplaction) and unicode arrow (assignment) 60 | parseCode = parseCode.replace(new RegExp(String.fromCharCode(8656), 'g'), '<='); 61 | parseCode = parseCode.replace(new RegExp(String.fromCharCode(215), 'g'), '*'); 62 | 63 | // get number of lines in code 64 | codeLines = parseCode.split("\n").length + 1; 65 | 66 | // parse all functions 67 | while (!parseCode.match(/^$/)) { 68 | parse(); 69 | computedCode += code; 70 | } 71 | 72 | return computedCode; 73 | } 74 | 75 | /* compiler output functions */ 76 | 77 | function error(warning) { 78 | throw new Error("Error: Line " + (codeLines - parseCode.split("\n").length) + ":\n" + warning); 79 | } 80 | 81 | function warn(warning) { 82 | warningFunc("Warning: Line " + (codeLines - parseCode.split("\n").length) + ":\n" + warning); 83 | } 84 | 85 | function notice(warning) { 86 | noticeFunc("Notice: Line " + (codeLines - parseCode.split("\n").length) + ":\n" + warning); 87 | } 88 | 89 | /*********************/ 90 | /* Parsing Functions */ 91 | /*********************/ 92 | 93 | function scan() { 94 | val = null; 95 | 96 | MATCH: 97 | while (true) { 98 | var breakS = 99 | [/^''/, 100 | /^<=/, 101 | /^[+*!=<>(){}":;,.-]/, 102 | /^\[/, 103 | /^\]/, 104 | /^DEFINE\b/, 105 | /^PROCEDURE\b/, 106 | /^BLOCK\b/, 107 | /^LOOP\b/, 108 | /^AT\b/, 109 | /^MOST\b/, 110 | /^TIMES\b/, 111 | /^MU_LOOP\b/, 112 | /^CELL\b/, 113 | /^OUTPUT\b/, 114 | /^YES\b/, 115 | /^NO\b/, 116 | /^QUIT\b/, 117 | /^ABORT\b/, 118 | /^IF\b/, 119 | /^THEN\b/, 120 | /^AND\b/, 121 | /^OR\b/, 122 | /^PRINT\b/]; 123 | 124 | if (parseCode.match(/^$/)) return false; 125 | 126 | if (parseCode.match(/^\/\*.*?\*\//)) { 127 | parseCode = parseCode.replace(/^\/\*.*\*\//, ""); 128 | continue MATCH; 129 | } 130 | 131 | if (parseCode.match(/^BLOCK\s+(\d+)\s*:\s*BEGIN/)) { 132 | ns = "BEGIN"; 133 | val = parseCode.match(/^BLOCK\s+(\d+)\s*:\s*BEGIN/)[1]; 134 | parseCode = parseCode.replace(/^BLOCK\s+(\d+)\s*:\s*BEGIN/, ""); 135 | break MATCH; 136 | } 137 | 138 | if (parseCode.match(/^BLOCK\s+(\d+)\s*:\s*END/)) { 139 | ns = "END"; 140 | val = parseCode.match(/^BLOCK\s+(\d+)\s*:\s*END/)[1]; 141 | parseCode = parseCode.replace(/^BLOCK\s+(\d+)\s*:\s*END/, ""); 142 | break MATCH; 143 | } 144 | 145 | for (var i=0, max=breakS.length; i") { 484 | code += " > "; 485 | } else { 486 | break; 487 | } 488 | scan(); 489 | } 490 | } 491 | } 492 | 493 | function term() { 494 | while (true) { 495 | factor(); 496 | if (ns != "+") break; 497 | scan(); 498 | code += " + "; 499 | } 500 | } 501 | 502 | function factor() { 503 | while (true) { 504 | primary(); 505 | if (ns != "*") break; 506 | scan(); 507 | code += " * "; 508 | } 509 | } 510 | 511 | function primary() { 512 | if (ns == "CELL") { 513 | cell(); 514 | } else if (ns == "OUTPUT") { 515 | code += "output"; 516 | scan(); 517 | } else if (ns == "ID") { 518 | var id = val; 519 | scan(); 520 | 521 | if (ns == "[") { 522 | scan(); 523 | code += id + "("; 524 | while (true) { 525 | expression() 526 | if (ns != ",") break; 527 | scan(); 528 | code += ", "; 529 | } 530 | need("]"); 531 | code += ")"; 532 | return; 533 | } 534 | 535 | for (var i=0, max=formals.length; i\[\]VD@PQ7L#${}\\\/()|3E*])(\s|$)/; 48 | rNumber = /^-?\d+/; 49 | rSpace = /^[ \t\v]+/; 50 | rNewLine = /^(\n)/; 51 | rComment = /^\*\*([^*]|\*[^*])*\*\*/; 52 | rWord = /^([^\s]+)\s*/; 53 | rObfusc = /^\^_(_)?\^/; 54 | rMustache = /^[8|B|:][\)|\(|\[|\]][\-|`|~]/; 55 | source = []; 56 | while (code) { 57 | if (match = code.match(rObfusc)) { 58 | match = match[0]; 59 | bObfuscatedMode = !bObfuscatedMode; 60 | } else if (match = code.match(rSpace)) { 61 | match = match[0]; 62 | } else if (match = code.match(rNewLine)) { 63 | match = match[0]; 64 | } else if (match = code.match(rComment)) { 65 | match = match[0]; 66 | } else if (match = code.match(rEmoticon)) { 67 | match = match[1]; 68 | token = new Instruction(match, 'emoticon'); 69 | source.push(token); 70 | } else if (match = code.match(rMustache)) { 71 | match = match[0]; 72 | token = new Instruction(match, 'mustache'); 73 | source.push(token); 74 | } else if (bObfuscatedMode === false) { 75 | if (match = code.match(rNumber)) { 76 | match = match[0]; 77 | token = new Instruction(parseInt(match), 'data'); 78 | source.push(token); 79 | } else if (match = code.match(rWord)) { 80 | match = match[1]; 81 | token = new Instruction(match, 'data'); 82 | source.push(token); 83 | } 84 | } else if (bObfuscatedMode === true) { 85 | if (match = code.match(rWord)) { 86 | match = match[1]; 87 | } 88 | } 89 | code = code.slice(match.length); 90 | } 91 | return source; 92 | } 93 | 94 | return Parser; 95 | 96 | })(); 97 | 98 | Interpreter = (function() { 99 | function Interpreter(arg) { 100 | var source; 101 | source = arg.source, this.print = arg.print, this.input = arg.input, this.result = arg.result, this.logger = arg.logger; 102 | source.unshift('START'); 103 | this.lists = { 104 | X: [1], 105 | Z: source, 106 | A: [':'], 107 | G: [], 108 | S: [' '], 109 | E: [], 110 | ':': [] 111 | }; 112 | } 113 | 114 | Interpreter.prototype.debug = function() { 115 | var i, log, ref, v; 116 | if (this.logger == null) { 117 | return false; 118 | } 119 | this.logger("step " + (this.left('X'))); 120 | log = ''; 121 | ref = this.lists; 122 | for (i in ref) { 123 | v = ref[i]; 124 | log += ("\n" + i + ": ") + v.toString(); 125 | } 126 | return this.logger(log); 127 | }; 128 | 129 | Interpreter.prototype.closestDivideOrClose = function(index) { 130 | var list; 131 | list = this.lists['Z']; 132 | while (index < list.length) { 133 | if (list[index].mouth === ')') { 134 | return index; 135 | } else if (list[index].mouth === '|') { 136 | this.lists['G'][0] = 'IF'; 137 | return index; 138 | } 139 | index++; 140 | } 141 | return infinity; 142 | }; 143 | 144 | Interpreter.prototype.closestCloser = function(index) { 145 | var list; 146 | list = this.lists['Z']; 147 | while (index < list.length) { 148 | if (list[index].mouth === ')') { 149 | return index; 150 | } 151 | index++; 152 | } 153 | return infinity; 154 | }; 155 | 156 | Interpreter.prototype.left = function(listName) { 157 | return this.lists[listName][0]; 158 | }; 159 | 160 | Interpreter.prototype.right = function(listName) { 161 | return this.lists[listName][this.lists[listName].length - 1]; 162 | }; 163 | 164 | Interpreter.prototype.putRight = function(listName, dataItem) { 165 | return this.lists[listName].push(dataItem); 166 | }; 167 | 168 | Interpreter.prototype.putLeft = function(listName, dataItem) { 169 | return this.lists[listName].unshift(dataItem); 170 | }; 171 | 172 | Interpreter.prototype.currentList = function() { 173 | return this.left('A'); 174 | }; 175 | 176 | Interpreter.prototype.clone = function(listName) { 177 | var j, len, list, results, v; 178 | list = this.lists[listName]; 179 | if (list.map != null) { 180 | return list.map(function(x) { 181 | return x; 182 | }); 183 | } 184 | results = []; 185 | for (j = 0, len = list.length; j < len; j++) { 186 | v = list[j]; 187 | results.push(v); 188 | } 189 | return results; 190 | }; 191 | 192 | Interpreter.prototype.run = function() { 193 | var cont, i; 194 | cont = true; 195 | i = 0; 196 | while (cont && typeof cont !== "function" && i < 30000) { 197 | i++; 198 | this.debug(); 199 | cont = this.step(); 200 | } 201 | if (typeof cont === "function") { 202 | cont(); 203 | } else { 204 | if (typeof this.result === "function") { 205 | this.result(this.lists); 206 | } 207 | } 208 | return this.lists; 209 | }; 210 | 211 | Interpreter.prototype.step = function() { 212 | var instruction, ret; 213 | instruction = this.lists['Z'][this.left('X')]; 214 | if (!instruction) { 215 | return false; 216 | } 217 | if (!(instruction instanceof Instruction)) { 218 | instruction = new Parser(instruction)[0]; 219 | } 220 | if (instruction.type === 'data') { 221 | this.putRight(this.currentList(), instruction.value); 222 | this.lists['X'][0]++; 223 | } else if (instruction.type === 'mustache') { 224 | this.putRight(this.currentList(), this.parseMustache(instruction.value)); 225 | this.lists['X'][0]++; 226 | } else if (instruction.type === 'emoticon') { 227 | ret = this.execute(instruction); 228 | this.lists['X'][0]++; 229 | return ret; 230 | } 231 | return true; 232 | }; 233 | 234 | Interpreter.prototype.parseMustache = function(mustache) { 235 | switch (mustache) { 236 | case ':)`': 237 | return 'a'; 238 | case '8)`': 239 | return 'b'; 240 | case 'B)`': 241 | return 'c'; 242 | case ':]`': 243 | return 'd'; 244 | case '8]`': 245 | return 'e'; 246 | case 'B]`': 247 | return 'f'; 248 | case ':[`': 249 | return 'g'; 250 | case '8[`': 251 | return 'h'; 252 | case 'B[`': 253 | return 'i'; 254 | case ':(`': 255 | return 'j'; 256 | case '8(`': 257 | return 'k'; 258 | case 'B(`': 259 | return 'l'; 260 | case ':)-': 261 | return 'm'; 262 | case '8)-': 263 | return 'n'; 264 | case 'B)-': 265 | return 'o'; 266 | case ':]-': 267 | return 'p'; 268 | case '8]-': 269 | return 'q'; 270 | case 'B]-': 271 | return 'r'; 272 | case ':[-': 273 | return 's'; 274 | case '8[-': 275 | return 't'; 276 | case 'B[-': 277 | return 'u'; 278 | case ':(-': 279 | return 'v'; 280 | case '8(-': 281 | return 'w'; 282 | case 'B(-': 283 | return 'x'; 284 | case ':)~': 285 | return 'y'; 286 | case '8)~': 287 | return 'z'; 288 | case 'B)~': 289 | return '0'; 290 | case ':]~': 291 | return '1'; 292 | case '8]~': 293 | return '2'; 294 | case 'B]~': 295 | return '3'; 296 | case ':[~': 297 | return '4'; 298 | case '8[~': 299 | return '5'; 300 | case 'B[~': 301 | return '6'; 302 | case ':(~': 303 | return '7'; 304 | case '8(~': 305 | return '8'; 306 | case 'B(~': 307 | return '9'; 308 | } 309 | return ''; 310 | }; 311 | 312 | Interpreter.prototype.execute = function(instruction) { 313 | var AssertCount, condition, count, currFace, currList, currentList, face, insertIndex, isReplace, item, j, k, l, len, len1, list, marker, mouth, nextInstruction, nose, numToReplace, numToRotate, operand1, operand2, pull, put, ref, ref1, ref2, replaced, tmp, v, x; 314 | mouth = instruction.mouth; 315 | nose = instruction.nose; 316 | face = instruction.face; 317 | AssertCount = (function(_this) { 318 | return function(count, listName) { 319 | if (_this.lists[listName].length < count) { 320 | throw new RuntimeError("List '" + listName + "' needs to have at least #" + count + " items to execute " + instruction + " at " + (_this.left('X'))); 321 | } 322 | }; 323 | })(this); 324 | if (face.length === 1 && face[0] === ':') { 325 | list = this.lists[':']; 326 | } else if (face.length === 2 && face[1] === ':' && face[0] in this.lists) { 327 | face = face[0]; 328 | list = this.lists[face]; 329 | } else { 330 | if (!this.lists[face]) { 331 | list = this.lists[face] = []; 332 | } else { 333 | list = this.lists[face]; 334 | } 335 | } 336 | currFace = this.currentList(); 337 | currList = this.lists[currFace]; 338 | switch (mouth) { 339 | case 'O': 340 | this.lists['A'][0] = face; 341 | break; 342 | case 'C': 343 | currList.unshift(list.length); 344 | break; 345 | case '<': 346 | AssertCount(1, currFace); 347 | this.putLeft(face, currList.shift()); 348 | break; 349 | case '>': 350 | AssertCount(1, currFace); 351 | this.putRight(face, currList.pop()); 352 | break; 353 | case '[': 354 | AssertCount(1, currFace); 355 | this.putLeft(face, this.left(currFace)); 356 | break; 357 | case ']': 358 | AssertCount(1, currFace); 359 | this.putRight(face, this.right(currFace)); 360 | break; 361 | case 'V': 362 | AssertCount(2, ':'); 363 | numToReplace = this.lists[':'].shift(); 364 | insertIndex = this.lists[':'].shift(); 365 | currentList = this.clone(currFace); 366 | while (currentList.length) { 367 | item = currentList.shift(); 368 | isReplace = numToReplace > 0 ? 1 : 0; 369 | numToReplace--; 370 | replaced = list.splice(insertIndex, isReplace, item); 371 | insertIndex++; 372 | if (isReplace) { 373 | this.putRight(':', replaced[0]); 374 | } 375 | } 376 | break; 377 | case 'D': 378 | this.lists[face] = list = this.clone(currFace); 379 | break; 380 | case '@': 381 | AssertCount(1, currFace); 382 | numToRotate = this.left(currFace); 383 | for (x = j = ref = numToRotate; ref <= 1 ? j <= 1 : j >= 1; x = ref <= 1 ? ++j : --j) { 384 | this.putLeft(face, list.pop()); 385 | } 386 | break; 387 | case 'P': 388 | AssertCount(1, face); 389 | this.print(list[0].toString()); 390 | break; 391 | case 'Q': 392 | AssertCount(1, face); 393 | this.print(list.shift().toString()); 394 | break; 395 | case '7': 396 | AssertCount(1, face); 397 | tmp = []; 398 | ref1 = list.shift().split(''); 399 | for (k = 0, len = ref1.length; k < len; k++) { 400 | v = ref1[k]; 401 | tmp.push(v); 402 | } 403 | this.lists[face] = list = tmp.concat(list); 404 | break; 405 | case 'L': 406 | AssertCount(1, face); 407 | tmp = []; 408 | ref2 = list.pop().split(''); 409 | for (l = 0, len1 = ref2.length; l < len1; l++) { 410 | v = ref2[l]; 411 | tmp.push(v); 412 | } 413 | this.lists[face] = list.concat(tmp); 414 | break; 415 | case '#': 416 | count = this.left(currFace); 417 | tmp = isNaN(count) ? list.splice(0, list.length) : list.splice(0, count); 418 | tmp = nose === '~' ? tmp.join(' ') : tmp.join(''); 419 | list.unshift(tmp); 420 | break; 421 | case '$': 422 | count = this.left(currFace); 423 | tmp = list.splice(-count, count); 424 | tmp = nose === '~' ? tmp.join(' ') : tmp.join(''); 425 | list.push(tmp); 426 | break; 427 | case '{': 428 | case '}': 429 | AssertCount(2, face); 430 | put = function(item) { 431 | if (mouth === '{') { 432 | return list.unshift(item); 433 | } else { 434 | return list.push(item); 435 | } 436 | }; 437 | pull = function() { 438 | if (mouth === '{') { 439 | return list.shift(); 440 | } else { 441 | return list.pop(); 442 | } 443 | }; 444 | operand1 = pull(); 445 | operand2 = pull(); 446 | switch (nose) { 447 | case '+': 448 | put(operand1 + operand2); 449 | break; 450 | case '-': 451 | put(operand1 - operand2); 452 | break; 453 | case 'x': 454 | put(operand1 * operand2); 455 | break; 456 | case '/': 457 | put(operand1 / operand2); 458 | break; 459 | case '\\': 460 | put(operand1 % operand2); 461 | } 462 | break; 463 | case '\\': 464 | case '/': 465 | put = (function(_this) { 466 | return function(item) { 467 | if (mouth === '\\') { 468 | return _this.lists[':'].unshift(item.toString().toUpperCase()); 469 | } else { 470 | return _this.lists[':'].push(item.toString().toUpperCase()); 471 | } 472 | }; 473 | })(this); 474 | operand1 = mouth === '\\' ? this.left(currFace) : this.right(currFace); 475 | operand2 = mouth === '\\' ? this.left(face) : this.right(face); 476 | switch (nose) { 477 | case '=': 478 | put(operand1 === operand2); 479 | break; 480 | case '>': 481 | put(operand1 > operand2); 482 | break; 483 | case '<': 484 | put(operand1 < operand2); 485 | break; 486 | case '~': 487 | put(operand1 !== operand2); 488 | } 489 | break; 490 | case '(': 491 | this.lists['G'].push(this.left('X')); 492 | break; 493 | case ')': 494 | marker = this.lists['G'].pop(); 495 | nextInstruction = marker === 'IF' ? this.left('X') : marker - 1; 496 | this.lists['X'][0] = nextInstruction; 497 | break; 498 | case '|': 499 | this.lists['X'][0] = this.closestCloser(this.left('X')); 500 | break; 501 | case '3': 502 | case 'E': 503 | condition = this.left(':'); 504 | if (condition === 'TRUE') { 505 | this.lists['X'][0] = this.closestDivideOrClose(this.left('X')); 506 | } 507 | if (mouth === 'E' && condition === 'TRUE' || condition === 'FALSE') { 508 | this.lists[':'].shift(); 509 | } 510 | break; 511 | case '*': 512 | return (function(_this) { 513 | return function() { 514 | return _this.input(function(result) { 515 | var len2, m, word; 516 | result = result.split(/[ \t\v]+/); 517 | for (m = 0, len2 = result.length; m < len2; m++) { 518 | word = result[m]; 519 | _this.putRight(currFace, word); 520 | } 521 | return _this.run(); 522 | }); 523 | }; 524 | })(this); 525 | } 526 | return true; 527 | }; 528 | 529 | return Interpreter; 530 | 531 | })(); 532 | 533 | module.exports = { 534 | Parser: Parser, 535 | Interpreter: Interpreter 536 | }; 537 | --------------------------------------------------------------------------------