Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- ----- BEGIN KAOS v0.3 -----
- <!DOCTYPE HTML>
- <!--
- KAOS v0.3
- -->
- <html>
- <head>
- <meta charset="utf-8">
- <title>KAOS v0.3</title>
- <style>
- canvas {
- position: absolute;
- top: 0;
- left: 0;
- width: 100%;
- height: 100%;
- }
- </style>
- <script src="https://cdn.jsdelivr.net/processing.js/1.4.8/processing.min.js"></script>
- <script>
- var fcon;
- // Nim based module system for importing and installing applications and .nim "binaries"
- // Heavily draws inspiration and snippets from Ben Burrill's acclaimed BMS, go give him an upvote! https://www.khanacademy.org/computer-programming/bens-module-system-version-2/6070976254115840
- var modules = {
- loadProgram: function (id, callback) {
- document.loadProgram = callback;
- var scr = document.createElement("script");
- scr.src = "https://www.khanacademy.org/api/labs/scratchpads/" + id + "?callback=document.loadProgram";
- document.head.appendChild(scr);
- },
- downloadProgram: function (id, path, callback) {
- modules.loadProgram(id, function (data) {
- var f = new fcon("f", path);
- f.write(data.revision.code);
- f.init();
- callback();
- });
- }
- }
- </script>
- </head>
- <body>
- <canvas id="kaos_canvas"></canvas>
- <script>
- modules.loadProgram("5871784009596928", function (data) {
- eval(data.revision.code);
- fcon = fs.File;
- new Processing(document.getElementById("kaos_canvas"), function (processing) {
- with (processing) {
- init(modules, processing);
- }
- });
- });
- </script>
- </body>
- </html>
- ----- END KAOS v0.3 -----
- ----- BEGIN KAOS v0.3 BACKEND -----
- // jshint ignore:start
- // The filesystem object.
- // Each entry's key is its path array, and entries are unordered.
- var filesystem = {};
- // User constructor. Makes a user.
- var User = function (name, password) {
- this.name = name;
- this.password = password;
- this.home = "/home/" + name;
- };
- // Current user.
- var user = new User("guest");
- // Users. Each element is the name of a user.
- var users = ["guest", "root"];
- // Groups. Each group is simply a named array of usernames.
- var groups = {};
- // Utility functions
- util: {
- // Represents a KAOS error.
- var KAOSError = function (message) {
- this.message = message;
- };
- // If first argument is undefined, return second argument. Otherwise return first.
- // Used for default parameters in functions.
- var def = function (first, second) {
- return first === undefined ? second : first;
- };
- // Get a specific digit from an octal number.
- // Digit numbers are 0-indexed and big-endian.
- var getOctalDigit = function (number, digit) {
- // Binary magic
- return (number & (7 << (3 * digit))) >> (3 * digit);
- };
- // readable(), writable(), and executable() return a truthy value if you can read/write/execute a file, and a falsy value if you can't.
- var readable = function (file) {
- return (file.permissions & 4) ||
- (groups[file.group].indexOf(user.name) > -1 && (file.permissions & 32)) ||
- ((file.owner === user.name) && (file.permissions & 256)) ||
- user.name === "root";
- };
- var writable = function (file) {
- return (file.permissions & 2) ||
- (groups[file.group].indexOf(user.name) > -1 && (file.permissions & 16)) ||
- (file.owner === user.name && (file.permissions & 128)) ||
- user.name === "root";
- };
- var executable = function (file) {
- return (file.permissions & 1) ||
- (groups[file.group].indexOf(user.name) > -1 && (file.permissions & 8)) ||
- (file.owner === user.name && (file.permissions & 64)) ||
- user.name === "root";
- };
- // Turns a path array (normal parameters) into a displayable path string.
- // Optional: appendix, an element to be added to the end of path.
- var getPathString = function (path, appendix) {
- return ("/" + path.concat(appendix ? [appendix] : []).join("/")).replace(new RegExp("^" + user.home), "~");
- };
- // Turns a displayable path string into a path array (normal parameters).
- var getPathArray = function (pathString) {
- return pathString.replace(/^\//, "").replace(/^~/, user.home.slice(1)).split("/");
- };
- }
- // File constructor
- file: {
- /*
- Represents a file in the filesystem. Can be either a classical file or a directory.
- `type`: The type of the file. Either "f" or "d". Defaults to "f".
- `name`: The name of the file. Defaults to "untitled.txt" if type is "f" and "untitled" if type is "d".
- `parent`: The path of the parent directory.
- `permissions`: The octal permissions of the file. Defaults to 0[o]666 for type "f" and 0[o]777 for type "d".
- `owner`: The user who owns the file.
- `group`: The group which has group access to the file.
- */
- var File = function (type, name, parent, permissions, owner, group) {
- this.type = def(type, "f");
- this.parent = def(parent, []);
- if (this.parent.length && filesystem[this.parent].type !== "d") {
- throw new KAOSError(getPathString(this.parent) + ": Not a directory");
- }
- this.owner = owner;
- this.group = group;
- this.path = this.parent.concat([name]);
- this.pathString = getPathString(this.parent, name);
- if (this.type === "f") {
- this.name = name;
- this.permissions = def(permissions, 0666); // 0b110110110
- this.text = "";
- } else if (this.type === "d") {
- this.name = name;
- this.permissions = def(permissions, 0777); // 0b111111111
- this.children = [];
- } else {
- throw new KAOSError(this.pathString + ": Invalid file type: `" + type + "`");
- }
- };
- File.prototype.init = function () {
- filesystem[this.path] = this;
- };
- File.prototype.read = function () {
- if (this.type !== "f") {
- throw new KAOSError("You cannot read a directory");
- }
- if (!readable(this)) {
- throw new KAOSError("User " + user.name + " does not have read access to " + this.pathString);
- }
- return this.text;
- };
- File.prototype.write = function (text) {
- if (this.type !== "f") {
- throw new KAOSError("You cannot write to a directory");
- }
- if (!writable(this)) {
- throw new KAOSError("User " + user.name + " does not have write access to " + this.pathString);
- }
- this.text = text;
- };
- File.prototype.chmod = function (permissions) {
- if (this.type !== "f") {
- throw new KAOSError("You cannot write to a directory");
- }
- if (user.name !== this.owner) {
- throw new KAOSError("User " + user.name + " does not own " + this.pathString);
- }
- this.permissions = permissions;
- };
- File.prototype.ls = function () {
- if (!readable(this)) {
- throw new KAOSError("User " + user.name + " does not have read access to " + this.pathString);
- }
- if (this.type === "f") {
- return [this];
- }
- return this.children;
- };
- }
- // Nim language, transpiled from es6 using babel and customized to the above filesystem
- nim: {
- var defaultFunctions = {
- "print": {
- args: 1,
- type: "string",
- run: function (a, o) {
- return [a[0], a[0]];
- }
- },
- "epoch": {
- args: 0,
- type: "number",
- run: function (a, o) {
- return ["", Date.now()];
- }
- }
- };
- // Utility functions that need to be taken outside of constructors, maybe recursive stuff
- var utils = {
- // Get responses for HTTP error codes
- getErrorResponse: function (code) {
- return code.toString();
- },
- // Split partially tokenized block into more blocks on subs/sube
- splitIntoBlocks: function (tokens) {
- var s = 0;
- var t = [];
- var k = false;
- for (var i = 0; i < tokens.length; i++) {
- if (tokens[i][1] == "subs") {
- s++;
- k = true;
- } else if (tokens[i][1] == "sube" && ! --s) {
- tokens = tokens.slice(0, i - t.length).concat([[utils.splitIntoBlocks(t.slice(1)), "block"]]).concat(tokens.slice(i + 1));
- i -= t.length;
- t = [];
- k = false;
- }
- if (k) {
- t.push(tokens[i]);
- }
- }
- return tokens;
- },
- // Split partially tokenized block into statements on EOL (;)
- splitIntoStatements: function (tokens, file) {
- var t = [];
- for (var i = 0; i < tokens.length; i++) {
- t.push([]);
- while (tokens[i][1] != "eol") {
- if (tokens[i][1] == "block") {
- tokens[i][0] = utils.splitIntoStatements(tokens[i][0], file);
- }
- t[t.length - 1].push(tokens[i++]);
- if (i >= tokens.length) {
- throw new NimError("Code block does not end in semicolon.", file, tokens[tokens.length - 1][2]);
- }
- }
- }
- return t;
- }
- };
- // Nim custom errors
- // Stolen from SO
- var NimError = function (msg, file, char) {
- this.message = msg;
- this.name = this.constructor.name;
- this.file = file.replace(/\/+/g, "/");
- this.fileText = filesystem[getPathArray(file)].read();
- var leadingText = this.fileText.slice(0, char);
- this.line = leadingText.match(/\n/g) ? leadingText.match(/\n/g).length + 1 : 1;
- this.char = char - leadingText.lastIndexOf("\n");
- //Error.captureStackTrace(this, this.constructor);
- }
- NimError.prototype = Object.create(Error.prototype);
- NimError.prototype.print = function () {
- return [
- this.file + ":" + this.line + ":" + this.char,
- "\t\t" + this.fileText.split("\n")[this.line - 1],
- "\t\t" + " ".repeat(this.char) + "^",
- this.name + ": " + this.message,
- " at " + this.file + ":" + this.line + ":" + this.char
- ].join("\n");
- };
- // Level -1 Level 0 Level 1 Level 2 Level 3
- // REQUEST -----> Server.process Parser.process Parser.parse -----> CLIENT-READY
- // | ^ | ^
- // |-----> Server.processURI -----| |-----> Tokenizer.tokenize -----|
- // Tokenizer constructor
- var Tokenizer = function () {
- var _this = this;
- // Tokenizes (lexes, lexemizes, lexically analyzes, whatever) Nim-coded HTML, level 2
- this.tokenize = function (text, file) {
- _this.reset();
- _this.raw = text;
- _this.file = file;
- // Array of blocks of pure Nim code
- var re = /<!--{\s*([\W\w]+?)\s*}-->/g;
- var blocks = [],
- indices = [],
- match;
- while (match = re.exec(text)) {
- blocks.push(match[1]);
- for (var i = match.index + 5; /\s/.test(text[i]); i++) {}
- indices.push(i);
- }
- // Plain HTML surrounding Nim blocks, to be interleaved with Nim output
- _this.plain = text.match(/(^|}-->)([\W\w]*?)(<!--{|$)/g).map(function (e) {
- return e.replace(/(<!--{\s*|\s*}-->)/g, "");
- });
- for (var i = 0; i < blocks.length; i++) {
- _this.tokens.push(_this.tokenizeBlock(blocks[i], indices[i]));
- }
- return _this;
- };
- this.tokenizeBlock = function (block, index) {
- var tokens = [];
- for (var i = 0; i < block.length;) {
- var value = block[i];
- var type;
- // Hello, future me.
- // If you're reading this, this tokenizer has probably broken and you probably still remember writing this comment.
- // Don't try to understand the increments. Please.
- // They work. Maybe.
- // Sincerely, past you.
- // https://xkcd.com/1421/
- if (value == "#") {
- type = "comment";
- // While no newline, keep adding to the comment
- while (!/[\r\n]/.test(block[i])) {
- value += block[i++];
- }
- } else if (value == "{") {
- type = "subs";
- } else if (value == "}") {
- type = "sube";
- } else if (value == ";") {
- type = "eol";
- } else if (value == "=") {
- if (block[i + 1] == "=") {
- type = "equality";
- i++;
- } else {
- type = "equals";
- }
- } else if (value == "+") {
- type = "plus";
- } else if (value == "-") {
- type = "minus";
- } else if (value == "*") {
- type = "times";
- } else if (value == "/") {
- type = "div";
- } else if (value == "%") {
- // %/ is intdiv, % is mod, distinguish
- if (block[i + 1] == "/") {
- type = "intdiv";
- i++;
- } else {
- type = "mod";
- }
- } else if (value == ">" || value == "<") {
- type = (value == ">" ? "g" : "l") + "t";
- if (block[i + 1] == "=") {
- type += "e";
- i++;
- }
- } else if (value == "$") {
- // If there is no alphanumeric character in front of the $, error
- if (!/[A-Za-z0-9]/.test(block[++i])) {
- throw new NimError("Expected variable identifier", _this.file, index + i);
- }
- // Keep adding to the identifier until identifier runs out
- while (/[A-Za-z0-9]/.test(block[i])) {
- value += block[i++];
- }
- while (/\s/.test(block[i])) {
- i++;
- }
- if (block[i] == "=" && block[i + 1] != "=") {
- type = "variableSet";
- } else {
- type = "variableGet";
- i--;
- }
- value = value.slice(1);
- } else if (/\d/.test(value)) {
- type = "number";
- var hex = false;
- if (!+value) {
- if (block[++i] != "." && block[i] != "b" && block[i] != "o" && block[i] != "x" && /\D/.test(block[i])) {
- throw new NimError("Expected numerical radix, got " + value + block[i], _this.file, index + i);
- } else {
- if (block[i] == "x") {
- hex = true;
- }
- value += block[i];
- }
- }
- while ((hex ? /[\dA-Fa-f]/ : /[\d\.]/).test(block[i + 1])) {
- value += block[++i];
- }
- value = Number(value);
- } else if (value == "\"" || value == "'") {
- type = "string";
- var delim = value,
- bs = false; // Backslashes indicate BS string endings
- while (block[++i] != delim || bs) {
- if (!block[i]) {
- throw new NimError("String without ending", _this.file, index + i);
- }
- if (block[i] == "\\") {
- bs = true;
- } else {
- if (bs && block[i] == "n") {
- value += "\n";
- bs = false;
- } else {
- value += block[i];
- }
- bs = false;
- }
- }
- value = value.slice(1);
- } else if (/[A-Za-z0-9]/.test(value)) {
- // Identifier time! (ouch)
- while (/[A-Za-z0-9]/.test(block[i + 1])) {
- value += block[++i];
- }
- if (value == "true" || value == "false") {
- type = "bool";
- value = value == "true";
- } else if (block[i + 1] + block[i + 2] == "()") {
- i += 2;
- type = "function";
- } else if (_this.keywords.indexOf(value) > -1) {
- type = value;
- }
- } else {
- throw new NimError("Invalid character: " + value, _this.file, index + i);
- }
- tokens.push([value, type, index + i]);
- while (/\s/.test(block[++i])) {}
- }
- tokens = utils.splitIntoStatements(utils.splitIntoBlocks(tokens), _this.file);
- return tokens;
- };
- // Reset tokenizer, we don't want to create new objects every request
- this.reset = function () {
- _this.tokens = [];
- _this.raw = "";
- _this.plain = [];
- _this.file = "";
- return _this;
- };
- // List of straight identifier keywords, ex. if, elseif, else, def, ...
- this.keywords = ["if", "elseif", "else", "def"];
- // Tokens array, Array of arrays of arrays of tuples
- // Each tuple is a token
- // Each array of tuples is a statement ...;
- // Each array of statements is a Nim block <!--{...}-->
- // The array of Nim blocks is this.tokens
- this.tokens = [];
- this.raw = "";
- this.plain = [];
- this.file = "";
- };
- // Parser constructor
- var Parser = function () {
- var _this = this;
- // Interprets Nim-coded HTML, level 1
- this.process = function (text, file) {
- _this.functions = defaultFunctions;
- _this.variables = {};
- _this.file = file;
- return _this.parse(_this.tokenizer.tokenize(text, file))[0];
- };
- // Parses tokenized Nim, level 3
- this.parse = function (tokenizer) {
- var tokens = tokenizer.tokens;
- var plain = tokenizer.plain;
- // output[0] is text printed to page
- // output[1] is returned value
- var output = [plain[0]];
- for (var i = 0; i < tokens.length; i++) {
- for (var j = 0; j < tokens[i].length; j++) {
- var statement = _this.parseStatement(tokens[i][j]);
- output[0] += statement[0];
- output[1] = statement[1];
- }
- output[0] += plain[i + 1];
- }
- return output;
- };
- // Parse individual statements
- // VALID STATEMENTS:
- // <comment>
- // <function> [arg1] [arg2] [...]
- // <variable> <equals> <data>
- // <data> <operation> <data>
- // <data>
- // <if> <boolean> <block>
- // <elseif> <boolean> <block>
- // <else> <block>
- // <def> <string> <string(type)> <string...> <block>
- this.parseStatement = function (statement) {
- var output = "",
- ret,
- rettype,
- k;
- var f = function (e) {
- // If type is variableGet and statement isn't one that requires variable-type expressions not to be evaluated beforehand, evaluate variable
- if (e[1] == "variableGet" && !(["def"].indexOf(statement[0][1]) > -1)) {
- return [_this.variables[e[0]], "variableGet"];
- } else if (e[1] == "block") {
- return [e[0].map(f), e[1]];
- } else {
- return e;
- }
- };
- statement = statement.map(function (e, i) {
- // If type is block and statement isn't one that requires blocks not to be evaluated beforehand, evaluate block
- if (e[1] == "block" && !(["if", "elseif", "else", "def"].indexOf(statement[0][1]) > -1 && i == statement.length - 1)) {
- var k = e[0].map(_this.parseStatement).reduce(function (a, b) {
- return [a[0] + b[0], b[1], b[2], b[3]];
- });
- output += k[0];
- return [k[1], k[2]];
- } else {
- return e;
- }
- }).map(f);
- var lastLooked = 0;
- switch (statement[0][1]) {
- case "comment":
- break;case "function":
- var name = statement[0][0];
- var func = _this.functions[name];
- if (func) {
- var args = statement.slice(1);
- if (args.length != func.args) {
- throw new NimError("Incorrect number of arguments: Expected " + func.args.length + " arguments for function `" + name + "` but got " + args.length, _this.file, statement[statement.length - 1][2]);
- }
- var res = func.run(args.map(function (e) {
- return e[0];
- }));
- output += res[0];
- ret = res[1];
- rettype = func.type;
- lastLooked = statement.length - 1;
- } else {
- throw new NimError("Undefined function: " + name, _this.file, statement[0][2]);
- }
- break;case "variableSet":
- _this.variables[statement[0][0]] = statement[1][0];
- ret = statement[1][0];
- rettype = statement[1][1];
- lastLooked = 1;
- break;case "number":
- case "string":
- case "bool":
- case "variableGet":
- switch (statement[1][1]) {
- case "plus":
- ret = statement[0][0] + statement[2][0];
- break;case "minus":
- ret = statement[0][0] - statement[2][0];
- break;case "times":
- ret = statement[0][0] * statement[2][0];
- break;case "div":
- ret = statement[0][0] / statement[2][0];
- break;case "mod":
- ret = statement[0][0] % statement[2][0];
- break;case "intdiv":
- ret = Math.floor(statement[0][0] / statement[2][0]);
- break;case "equality":
- ret = statement[0][0] == statement[2][0];
- break;case "gt":
- ret = statement[0][0] > statement[2][0];
- break;case "gte":
- ret = statement[0][0] >= statement[2][0];
- break;case "lt":
- ret = statement[0][0] < statement[2][0];
- break;case "lte":
- ret = statement[0][0] <= statement[2][0];
- break;case undefined:
- ret = statement[0];
- break;default:
- throw new NimError("Expected operation on " + statement[0][1], _this.file, statement[0][2]);
- }
- rettype = "" + ret === ret ? "string" : ret == true || ret == false ? "bool" : "number";
- lastLooked = 2;
- break;case "if":
- _this.conditions = [statement[1][0]];
- if (statement[1][0]) {
- var r = statement[2][0].map(_this.parseStatement).reduce(function (a, b) {
- return [a[0] + b[0], b[1], b[2], b[3]];
- });
- output = r[0];
- ret = r[1];
- rettype = r[2];
- }
- lastLooked = 2;
- break;case "elseif":
- if (!_this.conditions.length) {
- throw new NimError("elseif after no if or elseif", _this.file, statement[0][2]);
- }
- if (!_this.conditions.reduce(function (a, b) {
- return a || b;
- }) && statement[1][0]) {
- var r = statement[2][0].map(_this.parseStatement).reduce(function (a, b) {
- return [a[0] + b[0], b[1], b[2], b[3]];
- });
- output = r[0];
- ret = r[1];
- rettype = r[2];
- }
- _this.conditions.push(statement[1][0]);
- lastLooked = 2;
- break;case "else":
- if (!_this.conditions.length) {
- throw new NimError("else after no if or elseif", _this.file, statement[0][2]);
- }
- if (!_this.conditions.reduce(function (a, b) {
- return a || b;
- })) {
- var r = statement[1][0].map(_this.parseStatement).reduce(function (a, b) {
- return [a[0] + b[0], b[1], b[2], b[3]];
- });
- output = r[0];
- ret = r[1];
- rettype = r[2];
- }
- lastLooked = 1;
- break;case "def":
- var args = statement.slice(3, statement.length - 1);
- var code = statement[statement.length - 1][0];
- _this.functions[statement[1][0]] = {
- args: args.length,
- type: statement[2],
- run: function (a, o) {
- var parser2 = Object.assign(new Parser(), _this);
- for (var i = 0; i < args.length; i++) {
- parser2.variables[args[i][0]] = a[i];
- }
- var k = code.map(parser2.parseStatement).reduce(function (a, b) {
- return [a[0] + b[0], b[1], b[2], b[3]];
- });
- return [k[0], k[1]];
- }
- };
- lastLooked = statement.length - 1;
- break;default:
- throw new NimError("Invalid statement beginning: " + statement[0][0] + " (" + statement[0][1] + ")", _this.file, statement[0][2]);
- }
- if (statement.length - 1 > lastLooked) {
- throw new NimError("Unexpected token: `" + statement[lastLooked + 1] + "`", _this.file, statement[lastLooked + 1][2]);
- }
- return [output, ret, rettype, statement[statement.length - 1][2]];
- };
- // Functions
- this.functions = defaultFunctions;
- // Variables
- this.variables = {};
- // Condition of previous if/elseif/else, if needed
- this.conditions = [];
- // Moving on to level 2...
- this.tokenizer = new Tokenizer();
- };
- }
- // The module system is disallowed per guildelines in PJS, so we use this callback.
- var init = function (modules, p) {
- var parser = new Parser();
- modules.downloadProgram("4548198515802112", "t.nim", function () {
- var res;
- try {
- res = parser.process(filesystem[getPathArray("/t.nim")].read(), "/t.nim");
- } catch (e) {
- if (e instanceof NimError) {
- res = e.print();
- } else {
- throw e;
- }
- }
- p.size(600, 600);
- p.frameRate(30);
- p.background(255);
- p.fill(0);
- p.textSize(12);
- p.text(res, 10, 10, 580, 580);
- });
- };
- var fs = { File: File };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement