spinny-itty/spinny.yarn

1 line
No EOL
27 KiB
Text

{"protected":false,"mode":"f","metadata":{"name":"spinny","author":"Anya Merry :3","version":[0,0,3],"dependencies":[],"paths":{"spinny.js":"%B/spinny.js","s.spin":"/s.spin","r.spin":"/r.spin"}},"content":"{\"spinny.js\":{\"protected\":false,\"metadata\":{\"runner\":\"js\"},\"mode\":\"f\",\"content\":\"////////////////////////////////////\\n// SPINNY : A Programming Language /\\n////////////////////////////////////\\n\\n// This is the Spinny compiler and\\n// interpreter for itty.\\n// It accepts\\n// - Spinny source files (.spin)\\n// - Spinny compiled json files (.spic)\\n\\ntry {\\nasync function onRun() {\\n\\tlet cliArgs = parseCliArgs();\\n\\tlet fileobj\\n\\tlet file\\n\\n\\t// File open:\\n\\ttry {\\n\\t\\tfileobj = io.open(fs.resolve(cliArgs.filename), \\\"r\\\")\\n\\t} catch (e) {\\n\\t\\tdisplay.print(`Spinny: Error opening file or resolving path \\\\`${cliArgs.filename}'.`);\\n\\t\\tdisplay.print(`${e.name}@${e.lineNumber}-${e.columnNumber}: ${e.message}`);\\n\\t\\tio.error([22, \\\"file open failed\\\"]);\\n\\t}\\n\\ttry {\\n\\t\\tfile = fileobj.read();\\n\\t} catch (e) {\\n\\t\\tdisplay.print(`Spinny: Error reading file \\\\`${cliArgs.filename}'.`);\\n\\t\\tdisplay.print(`${e.name}@${e.lineNumber}-${e.columnNumber}: ${e.message}`);\\n\\t\\tio.error([23, \\\"file read failed\\\"]);\\n\\t}\\n\\n\\t// Spinc Checker:\\n\\tlet isSpincFile = false;\\n\\tif (file.split(\\\"\\\\n\\\").length > 1) {\\n\\t\\tconst fileSpincVersion = file.split(\\\"\\\\n\\\")[1].split(\\\"@\\\");\\n\\t\\tif (fileSpincVersion.length > 2 &&\\n\\t\\t\\tfileSpincVersion.slice(0,2).join(\\\"\\\\n\\\") === \\\"SPINC\\\\nspinny:itty\\\") {\\n\\t\\t\\t// This is a spinny source file\\n\\t\\t\\tif (fileSpincVersion[2] !== \\\"noversion\\\") {\\n\\t\\t\\t\\tlet niceversion = spileSpincVersion[2].replace(\\n\\t\\t\\t\\t\\t/[\\\\t %]/g , \\\"\\\")\\n\\t\\t\\t\\tdisplay.print(\\\"Spinny: Incorrect or unrecognized compiled spinc file\\\");\\n\\t\\t\\t\\tdisplay.print(`Spinny: version \\\\`${niceversion}'.`);\\n\\t\\t\\t\\tdisplay.print(\\\"Spinny: This version of the spinny interpreter exclusively\\\");\\n\\t\\t\\t\\tdisplay.print(\\\"Spinny: accepts compiled spinc files of version\\\");\\n\\t\\t\\t\\tdisplay.print(\\\"Spinny: `noversion'.\\\");\\n\\t\\t\\t\\tdisplay.print(\\\"Spinny: Use `—version' or `—help' for more information.\\\");\\n\\t\\t\\t\\tio.error([31, \\\"invalid spinc version\\\"]);\\n\\t\\t\\t}\\n\\t\\t\\tisSpincFile = true;\\n\\t\\t}\\n\\t}\\n\\tif (cliArgs.compileOnly && isSpincFile) {\\n\\t\\tdisplay.print(\\\"Spinny: Cannot compile a spinc file.\\\");\\n\\t\\tdisplay.print(\\\"Spinny: The file is already compiled.\\\");\\n\\t\\tio.error([32, \\\"spinc already compiled\\\"]);\\n\\t}\\n\\n\\tlet compiled\\n\\tif (!isSpincFile) {\\n\\t\\t// We must compile the file\\n\\t\\tconst parser = new spinnyParser({\\n\\t\\t\\tparsestep:cliArgs.debugStep\\n\\t\\t});\\n\\t\\tlet compiled;\\n\\t\\ttry {\\n\\t\\t\\tcompiled = await parser.compile(file)\\n\\t\\t} catch (e) {\\n\\t\\t\\tif (!(e instanceof spinnyCompileError))\\n\\t\\t\\t\\tthrow e\\n\\t\\t\\tlet debug = e.debugStack\\n\\t\\t\\tlet last = e.debugStack.slice(-1)[0]\\n\\t\\t\\tdisplay.print(\\\"Spinny: Error compiling file...\\\");\\n\\t\\t\\tdisplay.print(`Spinny: Unfinished ${last[0]} at ${last[1]}`);\\n\\t\\t\\tio.error([45, \\\"compilation failed\\\"]);\\n\\t\\t}\\n\\t\\tdisplay.print(JSON.stringify(compiled))\\n\\t\\tquit();\\n\\t}\\n\\n\\tdisplay.print(\\\"Spinny: Running and/or saving files is not currently supported.\\\");\\n\\tdisplay.print(\\\"Spinny: Dumping contents instead.\\\");\\n\\tif (isSpincFile)\\n\\t\\tdisplay.print(file.split(\\\"\\\\n\\\").slice(2).join('\\\\n'))\\n}\\n\\nfunction showHelp(){\\n\\tdisplay.print(\\n\\t\\t// . 1 . 2 . 3 . 4 . 5 . 6\\n\\t\\t// ^\\n\\t\\t\\\"This is the Spinny compiler and interpreter for itty.\\\\n\\\" +\\n\\t\\t\\\"It accepts...\\\\n\\\" +\\n\\t\\t\\\" • Spinny source files (.spin)\\\\n\\\" +\\n\\t\\t\\\" • Spinny compiled json files (.spinc)\\\\n\\\" +\\n\\t\\t\\\"\\\\n\\\" +\\n\\t\\t\\\"Syntax:\\\\n\\\" +\\n\\t\\t`\\\\t${program.name} [flags...] [—] <filename>\\\\n` +\\n\\t\\t\\\"Where:\\\\n\\\" +\\n\\t\\t\\\"\\\\t[flags...] are any combination of the flags\\\\n\\\" +\\n\\t\\t\\\"\\\\t available in the `Flags' section\\\\n\\\" +\\n\\t\\t\\\"\\\\t below.\\\\n\\\" +\\n\\t\\t\\\"\\\\t[—] Is an emdash (—) or a double dash (--)\\\\n\\\" +\\n\\t\\t\\\"\\\\t to mark where the filename is supposed\\\\n\\\" +\\n\\t\\t\\\"\\\\t to go. (Note that any time you see an\\\\n\\\" +\\n\\t\\t\\\"\\\\t em dash in this help page, you may\\\\n\\\" +\\n\\t\\t\\\"\\\\t choose to use a double dash in its\\\\n\\\" +\\n\\t\\t\\\"\\\\t place if that is your preference.)\\\\n\\\" +\\n\\t\\t\\\"\\\\t<filename> is the `.spin' or `.spinc' file to\\\\n\\\" +\\n\\t\\t\\\"\\\\t compile/run\\\\n\\\" +\\n\\t\\t\\\"Flags:\\\\n\\\" +\\n\\t\\t\\\"\\\\t—help Print this help page and exit.\\\\n\\\" +\\n\\t\\t\\\"\\\\t—version Print version and exit.\\\\n\\\" +\\n\\t\\t\\\"\\\\t—compile Compile the code to a .spinc file.\\\\n\\\" +\\n\\t\\t\\\"\\\\t The .spinc file must be specified\\\\n\\\" +\\n\\t\\t\\\"\\\\t with `—output'.\\\\n\\\" +\\n\\t\\t\\\"\\\\t—output <file> Specifies the output file for the\\\\n\\\" +\\n\\t\\t\\\"\\\\t `—compile' flag.\\\\n\\\"\\n\\t);\\n};\\n\\nfunction showVersion(){\\n\\tdisplay.print(\\n\\t\\t// . 1 . 2 . 3 . 4 . 5 . 6\\n\\t\\t// ^\\n\\t\\t\\\"Spinny for itty\\\\n\\\" +\\n\\t\\t\\\"Version 0.0.3\\\\n\\\" +\\n\\t\\t\\\"Known spinc versions:\\\\n\\\" +\\n\\t\\t\\\" • `noversion'\\\\n\\\"\\n\\t)\\n};\\n\\nfunction parseCliArgs() {\\n\\tlet compileOnly = false;\\n\\tlet filename = null;\\n\\tlet noMoreFlags = false;\\n\\tlet nextIsOutput = false\\n\\tlet output = \\\"\\\";\\n\\tlet letterFlag = false\\n\\tlet flag = false\\n\\tlet debugStep = false\\n\\tfor (let i=0;i<args.length;i++) {\\n\\t\\tlet arg = args[i];\\n\\t\\tif (nextIsOutput) {\\n\\t\\t\\toutput = arg;\\n\\t\\t\\tnextIsOutput = false;\\n\\t\\t\\tcontinue;\\n\\t\\t}\\n\\t\\tif (!noMoreFlags) {\\n\\t\\t\\t// Flags\\n\\t\\t\\tflag = false;\\n\\t\\t\\tletterFlag = false;\\n\\t\\t\\tif (arg.slice(0,1) === \\\"—\\\") {\\n\\t\\t\\t\\tflag = true;\\n\\t\\t\\t\\targ = arg.slice(1);\\n\\t\\t\\t} else if (arg.slice(0,2) === \\\"--\\\") {\\n\\t\\t\\t\\tflag = true;\\n\\t\\t\\t\\targ = arg.slice(2);\\n\\t\\t\\t} else if (arg.slice(0,1) === \\\"-\\\") {\\n\\t\\t\\t\\tflag = true;\\n\\t\\t\\t\\tletterFlag = true;\\n\\t\\t\\t\\targ = arg.slice(1);\\n\\t\\t\\t}\\n\\t\\t\\tif (flag) { switch (arg) {\\n\\t\\t\\t\\tcase \\\"c\\\":\\n\\t\\t\\t\\tcase \\\"compile\\\":\\n\\t\\t\\t\\t\\tcompileOnly = true;\\n\\t\\t\\t\\t\\tbreak;\\n\\t\\t\\t\\tcase \\\"h\\\":\\n\\t\\t\\t\\tcase \\\"help\\\":\\n\\t\\t\\t\\t\\tshowHelp();\\n\\t\\t\\t\\t\\tquit();\\n\\t\\t\\t\\tcase \\\"v\\\":\\n\\t\\t\\t\\tcase \\\"version\\\":\\n\\t\\t\\t\\t\\tshowVersion();\\n\\t\\t\\t\\t\\tquit();\\n\\t\\t\\t\\tcase \\\"o\\\":\\n\\t\\t\\t\\tcase \\\"output\\\":\\n\\t\\t\\t\\t\\tnextIsOutput = true;\\n\\t\\t\\t\\t\\tbreak\\n\\t\\t\\t\\tcase \\\"debugstep\\\":\\n\\t\\t\\t\\t\\tdebugStep = true;\\n\\t\\t\\t\\tcase \\\"\\\":\\n\\t\\t\\t\\t\\tif (!letterFlag) {\\n\\t\\t\\t\\t\\t\\tnoMoreFlags = true;\\n\\t\\t\\t\\t\\t\\tbreak\\n\\t\\t\\t\\t\\t}\\n\\t\\t\\t\\tdefault:\\n\\t\\t\\t\\t\\tif (letterFlag){\\n\\t\\t\\t\\t\\t\\tdisplay.print(`Spinny: Unrecognized flag \\\\`-${arg}'.`);\\n\\t\\t\\t\\t\\t} else {\\n\\t\\t\\t\\t\\t\\tdisplay.print(`Spinny: Unrecognized flag \\\\`—${arg}'.`);\\n\\t\\t\\t\\t\\t}\\n\\t\\t\\t\\t\\tdisplay.print(\\\"Spinny: For allowed flags, use `—help'.\\\");\\n\\t\\t\\t\\t\\tio.error([11, \\\"unknown flag\\\"]);\\n\\t\\t\\t}; continue; }\\n\\t\\t}\\n\\t\\tfilename = arg;\\n\\t}\\n\\tif (filename === null) {\\n\\t\\tdisplay.print(\\\"Spinny: No filename provided :c\\\");\\n\\t\\tdisplay.print(\\\"Spinny: For help, use `—help'.\\\");\\n\\t\\tio.error([13, \\\"am i alone in this world?\\\"]);\\n\\t}\\n\\treturn {\\n\\t\\tfilename: filename,\\n\\t\\tcompileOnly: compileOnly,\\n\\t\\toutput: output,\\n\\t\\tdebugStep: debugStep\\n\\t};\\n}\\n\\nclass spinnyCompileError extends Error {\\n\\tconstructor(message, debugStack) {\\n\\t\\tsuper(message);\\n\\t\\tthis.name = \\\"spinnyCompileError\\\";\\n\\t\\tthis.debugStack = debugStack;\\n\\t}\\n}\\n\\nclass spinnyParser {\\n\\tINSIDE = {\\n\\t\\tFILE: 0,\\n\\t\\tINTER: 1,\\n\\t\\tLAZY: 2\\n\\t}\\n\\tconstructor(modifiers=null) {\\n\\t\\tthis.mods = modifiers\\n\\t}\\n\\tasync compile(sourceFileContents) {\\n\\t\\tthis.file = sourceFileContents;\\n\\t\\tthis.index = 0;\\n\\t\\tthis.debugStack = [];\\n\\t\\treturn await this.parseFile()\\n\\t}\\n\\tasync parseFile() {\\n\\t\\tawait this.tag(\\\"File\\\")\\n\\t\\tlet lines = [];\\n\\n\\t\\t// A bunch of lines\\n\\t\\twhile (this.index+1 < this.file.length) {\\n\\t\\t\\tif (this.now() == \\\"\\\\n\\\") {\\n\\t\\t\\t\\tthis.next();\\n\\t\\t\\t\\tcontinue\\n\\t\\t\\t}\\n\\t\\t\\tlines.push(await this.parseLine(this.INSIDE.FILE))\\n\\t\\t}\\n\\t\\tawait this.untag();\\n\\t\\treturn lines\\n\\t}\\n\\n\\t// Utility Functions\\n\\tnow() { return this.file[this.index] }\\n\\tnext() {\\n\\t\\tif (this.index++ === this.file.length)\\n\\t\\t\\tthrow new spinnyCompileError(\\\"Compilation failed at EOF\\\", this.debugStack)\\n\\t\\treturn this.file[this.index]\\n\\t}\\n\\tpeek() { return this.file[this.index+1] }\\n\\tasync tag(tagtype) {\\n\\t\\tlet stackupd = {\\n\\t\\t\\ttype: tagtype,\\n\\t\\t\\tfpos: this.getFpos(this.index)\\n\\t\\t};\\n\\t\\tif (this.mods.parsestep) {\\n\\t\\t\\tconst regex = /\\\\t/g\\n\\t\\t\\tdisplay.print(`Begin ${stackupd.type} @ ${stackupd.fpos[0]}:${stackupd.fpos[1]}`);\\n\\t\\t\\tdisplay.print(` └ ${this.file.split('\\\\n').slice(stackupd.fpos[0]-1)[0].replace(regex,\\\"\\\\u21a3\\\")}`);\\n\\t\\t\\tdisplay.print(\\\" \\\" + \\\"^\\\".padStart(stackupd.fpos[1]));\\n\\t\\t\\tif (await io.read() === \\\"exit\\\")\\n\\t\\t\\t\\tthrow new spinnyCompileError(\\\"Compilation canceled by user.\\\", this.debugStack)\\n\\t\\t}\\n\\t\\tthis.debugStack.push(stackupd);\\n\\t}\\n\\tasync untag() {\\n\\t\\tif (this.mods.parsestep) {\\n\\t\\t\\tconst regex = /\\\\t/g\\n\\t\\t\\tlet stackupd = this.debugStack.slice(-1)[0];\\n\\t\\t\\tlet endFpos = this.getFpos(this.index)\\n\\t\\t\\tdisplay.print(`End ${stackupd.type} @ ${endFpos[0]}:${endFpos[1]}`);\\n\\t\\t\\tdisplay.print(` └ ${this.file.split('\\\\n').slice(endFpos[0]-1)[0].replace(regex,\\\"\\\\u21a3\\\")}`);\\n\\t\\t\\tdisplay.print(\\\" \\\" + \\\"^\\\".padStart(endFpos[1]));\\n\\t\\t\\tif (await io.read() === \\\"exit\\\")\\n\\t\\t\\t\\tthrow new spinnyCompileError(\\\"Compilation canceled by user.\\\", this.debugStack)\\n\\t\\t}\\n\\t\\tthis.debugStack.pop();\\n\\t}\\n\\tgetFpos(index) {\\n\\t\\tlet upto = this.file.substring(0, index).split('\\\\n');\\n\\t\\treturn [upto.length, upto.slice(-1)[0].length + 1];\\n\\t}\\n\\n\\t// Parsing Functions\\n\\tasync parseLine(inside) {\\n\\t\\tawait this.tag(\\\"Line\\\")\\n\\t\\tlet words = []\\n\\t\\twordLoop:\\n\\t\\twhile (1) {\\n\\t\\t\\tswitch (this.now()) {\\n\\t\\t\\t\\tcase \\\" \\\":\\n\\t\\t\\t\\tcase \\\"\\\\t\\\":\\n\\t\\t\\t\\t\\t// advance past whitespace till\\n\\t\\t\\t\\t\\t// next word\\n\\t\\t\\t\\t\\tthis.next();\\n\\t\\t\\t\\t\\tcontinue\\n\\t\\t\\t\\tcase \\\"#\\\":\\n\\t\\t\\t\\t\\tawait this.parseComment();\\n\\t\\t\\t\\t\\tcontinue\\n\\t\\t\\t\\tcase \\\"}\\\":\\n\\t\\t\\t\\t\\tif (inside === this.INSIDE.LAZY)\\n\\t\\t\\t\\t\\t\\tbreak wordLoop\\n\\t\\t\\t\\t\\tbreak\\n\\t\\t\\t\\tcase \\\">\\\":\\n\\t\\t\\t\\t\\tif (inside === this.INSIDE.INTER)\\n\\t\\t\\t\\t\\t\\tbreak wordLoop\\n\\t\\t\\t\\t\\tbreak\\n\\t\\t\\t\\tcase \\\"\\\\n\\\":\\n\\t\\t\\t\\t\\tif (inside === this.INSIDE.FILE\\n\\t\\t\\t\\t\\t\\t|| inside == this.INSIDE.LAZY)\\n\\t\\t\\t\\t\\t\\tbreak wordLoop\\n\\t\\t\\t\\t\\tbreak\\n\\t\\t\\t\\tcase \\\"\\\\\\\\\\\":\\n\\t\\t\\t\\t\\tif (this.now() == \\\"\\\\\\\\\\\" && this.peek() == \\\"\\\\n\\\") {\\n\\t\\t\\t\\t\\t\\t// Line continuation\\n\\t\\t\\t\\t\\t\\t// Go to next line\\n\\t\\t\\t\\t\\t\\tthis.next(); // skip `\\\\\\\\'\\n\\t\\t\\t\\t\\t\\tthis.next(); // skip `\\\\n'\\n\\t\\t\\t\\t\\t\\tcontinue\\n\\t\\t\\t\\t\\t}\\n\\t\\t\\t\\t\\tbreak\\n\\t\\t\\t}\\n\\t\\t\\twords.push(await this.parseWord(inside));\\n\\t\\t}\\n\\t\\tawait this.untag()\\n\\t\\treturn words\\n\\t}\\n\\n\\tasync parseComment() {\\n\\t\\tawait this.tag(\\\"Comment\\\")\\n\\t\\tcommentLoop:\\n\\t\\twhile (1) {\\n\\t\\t\\tswitch (this.now()) {\\n\\t\\t\\t\\tcase \\\"\\\\n\\\":\\n\\t\\t\\t\\t\\tbreak commentLoop\\n\\t\\t\\t\\tcase \\\"\\\\\\\\\\\":\\n\\t\\t\\t\\t\\tif (this.peek() == \\\"\\\\n\\\")\\n\\t\\t\\t\\t\\t\\tbreak commentLoop\\n\\t\\t\\t\\tdefault:\\n\\t\\t\\t\\t\\tthis.next();\\n\\t\\t\\t}\\n\\t\\t}\\n\\t\\tawait this.untag()\\n\\t}\\n\\n\\tasync parseWord(inside) {\\n\\t\\tawait this.tag(\\\"Word\\\")\\n\\t\\tlet bang = false;\\n\\t\\tlet word = {}\\n\\t\\tword[CMPVER.KEYS.BANG] = false;\\n\\t\\tif (this.now() === \\\"!\\\") {\\n\\t\\t\\tword[CMPVER.KEYS.BANG] = true;\\n\\t\\t\\tthis.next();\\n\\t\\t}\\n\\t\\tswitch (this.now()) {\\n\\t\\t\\tcase \\\"{\\\":\\n\\t\\t\\t\\tword[CMPVER.KEYS.TYPE] = CMPVER.TYPE.LAZY;\\n\\t\\t\\t\\tword[CMPVER.KEYS.LINES] = await this.parseLazy();\\n\\t\\t\\t\\tbreak;\\n\\t\\t\\tcase \\\"`\\\":\\n\\t\\t\\t\\tword[CMPVER.KEYS.TYPE] = CMPVER.TYPE.LIT;\\n\\t\\t\\t\\tword[CMPVER.KEYS.PARTS] = await this.parseLiteral();\\n\\t\\t\\t\\tbreak;\\n\\t\\t\\tdefault:\\n\\t\\t\\t\\tword[CMPVER.KEYS.TYPE] = CMPVER.TYPE.ID;\\n\\t\\t\\t\\tword[CMPVER.KEYS.PARTS] = await this.parseIdentifier(inside);\\n\\t\\t}\\n\\t\\tawait this.untag()\\n\\t\\treturn word\\n\\t}\\n\\n\\tasync parseLazy() {\\n\\t\\tawait this.tag(\\\"Lazy\\\")\\n\\t\\tthis.next(); // Skip `{'\\n\\t\\tlet lines = []\\n\\t\\twhile (this.now() !== \\\"}\\\") {\\n\\t\\t\\tlines.push(await this.parseLine(this.INSIDE.LAZY))\\n\\t\\t\\tif (this.now() === \\\"\\\\n\\\")\\n\\t\\t\\t\\t// Continue to next line like a file would\\n\\t\\t\\t\\tthis.next();\\n\\t\\t}\\n\\t\\tthis.next(); // Skip `}'\\n\\t\\tawait this.untag()\\n\\t\\treturn lines\\n\\t}\\n\\n\\tasync parseLiteral() {\\n\\t\\tawait this.tag(\\\"Literal\\\")\\n\\t\\tthis.next(); // Skip `\\\\`'\\n\\t\\tlet parts = [\\\"\\\"]\\n\\t\\twhile (this.now() !== \\\"\\\\'\\\") {\\n\\t\\t\\tswitch (this.now()) {\\n\\t\\t\\t\\tcase \\\"\\\\\\\\\\\":\\n\\t\\t\\t\\t\\tparts[parts.length-1]+=await this.parseEscape();\\n\\t\\t\\t\\t\\tbreak\\n\\t\\t\\t\\tcase \\\"<\\\":\\n\\t\\t\\t\\t\\tparts.push(\\n\\t\\t\\t\\t\\t\\tawait this.parseInterpolation()\\n\\t\\t\\t\\t\\t);\\n\\t\\t\\t\\t\\tparts.push(\\\"\\\")\\n\\t\\t\\t\\t\\tbreak\\n\\t\\t\\t\\tdefault:\\n\\t\\t\\t\\t\\tparts[parts.length-1]+=this.now();\\n\\t\\t\\t\\t\\tthis.next();\\n\\t\\t\\t\\t\\tbreak\\n\\t\\t\\t}\\n\\t\\t}\\n\\t\\tthis.next(); // Skip `\\\\''\\n\\t\\tawait this.untag();\\n\\t\\treturn parts\\n\\t}\\n\\n\\tasync parseEscape() {\\n\\t\\tawait this.tag(\\\"Escape\\\")\\n\\t\\tthis.next(); // skip `\\\\\\\\'\\n\\t\\tlet out = \\\"\\\";\\n\\t\\tswitch (this.now()) {\\n\\t\\t\\tcase \\\"<\\\":\\n\\t\\t\\t\\tthis.next(); // skip `\\\\<'\\n\\t\\t\\t\\tout = \\\"<\\\";\\n\\t\\t\\t\\tbreak\\n\\t\\t\\tcase \\\"x\\\":\\n\\t\\t\\t\\tthis.next(); // skip `x'\\n\\t\\t\\t\\tout = String.fromCharCode( parseInt(\\n\\t\\t\\t\\t\\tthis.next()+this.next()\\n\\t\\t\\t\\t\\t,16));\\n\\t\\t\\t\\tbreak\\n\\t\\t\\tcase \\\"u\\\":\\n\\t\\t\\t\\tthis.next(); // skip `u'\\n\\t\\t\\t\\tout = String.fromCharCode( parseInt(\\n\\t\\t\\t\\t\\tthis.next()+this.next()+this.next()+this.next()\\n\\t\\t\\t\\t\\t,16));\\n\\t\\t\\t\\tbreak\\n\\t\\t\\tcase \\\"U\\\":\\n\\t\\t\\t\\tthis.next(); // skip `U'\\n\\t\\t\\t\\tout = String.fromCharCode( parseInt(\\n\\t\\t\\t\\t\\tthis.next()+this.next()+this.next()+this.next()+\\n\\t\\t\\t\\t\\tthis.next()+this.next()+this.next()+this.next()\\n\\t\\t\\t\\t\\t,16));\\n\\t\\t\\t\\tbreak\\n\\t\\t\\tcase \\\"s\\\":\\n\\t\\t\\t\\tthis.next(); // skip `s'\\n\\t\\t\\t\\tout = \\\" \\\";\\n\\t\\t\\t\\tbreak\\n\\t\\t\\tcase \\\"t\\\":\\n\\t\\t\\t\\tthis.next(); // skip `t'\\n\\t\\t\\t\\tout = \\\"\\\\t\\\";\\n\\t\\t\\t\\tbreak\\n\\t\\t\\tcase \\\"n\\\":\\n\\t\\t\\t\\tthis.next(); // skip `n'\\n\\t\\t\\t\\tout = \\\"\\\\n\\\";\\n\\t\\t\\t\\tbreak\\n\\t\\t\\tdefault:\\n\\t\\t\\t\\t// Just send the character:\\n\\t\\t\\t\\tout = this.now(); // record the thing after the `\\\\\\\\'\\n\\t\\t\\t\\tthis.next(); // skip over it\\n\\t\\t\\t\\tbreak\\n\\n\\t\\t}\\n\\t\\tawait this.untag();\\n\\t\\treturn out;\\n\\t}\\n\\n\\tasync parseInterpolation() {\\n\\t\\tawait this.tag(\\\"Interpolation\\\")\\n\\t\\tthis.next(); // skip `\\\\<'\\n\\t\\tlet line\\n\\t\\twhile (this.now() !== \\\">\\\") {\\n\\t\\t\\tline = await this.parseLine(this.INSIDE.INTER)\\n\\t\\t}\\n\\t\\tthis.next(); // skip `>'\\n\\t\\tawait this.untag()\\n\\t\\treturn line\\n\\t}\\n\\n\\tasync parseIdentifier(inside) {\\n\\t\\tawait this.tag(\\\"Identifier\\\")\\n\\t\\tlet parts = [\\\"\\\"]\\n\\t\\tidLoop:\\n\\t\\twhile (1) {\\n\\t\\t\\tswitch (this.now()) {\\n\\t\\t\\t\\tcase \\\"\\\\\\\\\\\":\\n\\t\\t\\t\\t\\tparts[parts.length-1]+=await this.parseEscape();\\n\\t\\t\\t\\t\\tcontinue\\n\\t\\t\\t\\tcase \\\"<\\\":\\n\\t\\t\\t\\t\\tparts.push(\\n\\t\\t\\t\\t\\t\\tawait this.parseInterpolation()\\n\\t\\t\\t\\t\\t);\\n\\t\\t\\t\\t\\tparts.push(\\\"\\\")\\n\\t\\t\\t\\t\\tcontinue\\n\\t\\t\\t\\tcase \\\"}\\\":\\n\\t\\t\\t\\t\\tif (inside === this.INSIDE.LAZY)\\n\\t\\t\\t\\t\\t\\tbreak idLoop\\n\\t\\t\\t\\t\\tbreak\\n\\t\\t\\t\\tcase \\\">\\\":\\n\\t\\t\\t\\t\\tif (inside === this.INSIDE.INTER)\\n\\t\\t\\t\\t\\t\\tbreak idLoop\\n\\t\\t\\t\\t\\tbreak\\n\\t\\t\\t\\tcase \\\"#\\\":\\n\\t\\t\\t\\tcase \\\" \\\":\\n\\t\\t\\t\\tcase \\\"\\\\t\\\":\\n\\t\\t\\t\\tcase \\\"\\\\n\\\":\\n\\t\\t\\t\\t\\tbreak idLoop\\n\\t\\t\\t}\\n\\t\\t\\tparts[parts.length-1]+=this.now();\\n\\t\\t\\tthis.next();\\n\\t\\t}\\n\\t\\tawait this.untag();\\n\\t\\treturn parts\\n\\t}\\n}\\n\\nconst SPIDNEY = {//spinny identifiers\\n\\t\\\"noversion\\\": {\\n\\t\\tNAME: \\\"noversion\\\",\\n\\t\\tKEYS: {\\n\\t\\t\\tTYPE: \\\"t\\\",\\n\\t\\t\\tBANG: \\\"b\\\",\\n\\t\\t\\tPARTS: \\\"p\\\",\\n\\t\\t\\tLINES: \\\"l\\\"\\n\\t\\t},\\n\\t\\tTYPE: {\\n\\t\\t\\tLIT: 0,\\n\\t\\t\\tID: 1,\\n\\t\\t\\tLAZY: 2\\n\\t\\t}\\n\\t}\\n}\\nconst CMPVER = SPIDNEY[\\\"noversion\\\"] //compileversion\\n\\n// When the program starts:\\n\\tawait onRun();\\n\\tquit();\\n} catch (e) {\\n\\tdisplay.print(`Spinny: Something has gone horribly wrong, commander.`);\\n\\tdisplay.print(`${e.name}@${e.lineNumber}-${e.columnNumber}: ${e.message}`);\\n\\tio.error([9, \\\"unknown error\\\"]);\\n}\\n\"},\"s.spin\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"out `Small ``Game\\\\'\\\\'\\\\n'\\nuse term\\n\\n# Before we begin, a few notes\\n# - All objects are strings\\n# - There are a few different kinds of objects.\\n# - variables which have a static value they always evaluate to\\n# - lazies (contained by curlies (`{' and `}')) lazilly evaluate the lines\\n# within themselves only when nessicary. They may take arguments the same\\n# way commands do\\n# - commands, which are essentially lazies behind the mask of a variable name\\n# - strings, contained by a backtick to open (``') and a single quote to close\\n# (`\\\\'').\\n# - Each line begins with any object\\n# - builtin commands are always 3 characters long\\n# - the starting object is followed by a space, then the arguments, which are\\n# space seperated objects\\n# - the arguments are passed to the first object\\n# - variables and strings ignore these arguments\\n# - commands and lazies may access these arguments with the `arg' command\\n# - interpolation, either within a variable name, command name, or string, is \\n# done with the `<...>' syntax.\\n# - The arrows, as they're called, evaluate the line inside them, then insert\\n# that value into the variable name/command name/string.\\n# - thus a lone `<varname>' on a line evaluates to the value of the variable\\n# that is named by varname.\\n# - To say that backwards, varname contains the name of a variable, and that\\n# variable has a value, and that value is what we're getting.\\n# - When a object begins with a bang (`!'), if object being evaluated sets any\\n# variables, it sets them within the current scope.\\n# - Variable and function names are stripped of bangs and spaces.\\n# - Booleans don't exist. Instead, any string that is empty (`') is truthy and\\n# all other (non-empty) strings are falsey.\\n# - Numbers are strings in the formats:\\n# - `[+\\\\-]?real(\\\\.dec)?[+\\\\-]imag(\\\\.dec)?i'\\n# - `[+\\\\-]?real(\\\\.dec)?'\\n# - `[+\\\\-]?imag(\\\\.dec)?i'\\n# - where, imag is the immaginary part, real is the real part, and the .dec is\\n# the decimal part of the one it appears to the right of.\\n# - These strings are in any base from 3 to 19.\\n# - bases 20+ are reserved since they would involve the letter i (which\\n# denotes the immaginary part)\\n# - the current base is defined by the 'base' variable, which is always in\\n# base 2.\\n# - base 2 is reserved for no good reason.\\n# - all bases lower than two are reserved because they're inconveinent to\\n# implement.\\n\\nset `score' `0'\\nset `win.maxw' {\\n\\t# ^ win.maxw is just the variable name. The dot is just a character.\\n\\t# the dot doesn't mean anything special here.\\n\\n\\t# We doin RPN :O\\n\\tsub `1' term:rows\\n\\t# `term:rows' is the name a command being run.\\n\\t# since variable names are quite permissive, this is just the name of a\\n\\t# command defined within the file `term' (imported at the top)\\n}\\nset `win.maxh' {sub `1' term:cols}\\n\\nset `guy.char' `\\\\U0001FBC5'\\nset `guy.x' `1'\\nset `guy.y' `1'\\n\\n# Unicode is supported inside quotes:\\nset `star.char' `⭐'\\n\\n# `def' defines a command\\ndef randstarpos {\\n\\tset `star.x' {\\n\\t\\t# Get the real part of the rounded number after adding 1. (\\n\\t\\t# rounding removes decimal parts but leaves the immaginary\\n\\t\\t# part):\\n\\t\\trea {rnd {add `1' {\\n\\t\\t\\tmul win.maxw rnd \\n\\t\\t\\t# ^ rnd returns a random number\\n\\t\\t\\t# from 0 to 1\\n\\t\\t}}}\\n\\t}\\n\\tset `star.y' {rea {rnd\\n\\t\\tadd `1' {mul rnd win.maxh}\\n\\t}}\\n}\\ndef w {set `guy.y' {rea {rnd {\\n\\t\\tmax `1' {sub `1' guy.y}\\n}}}}\\ndef a {set `guy.x' {rea {rnd {max `1' {sub `1' guy.x}}}}}\\ndef s {set `guy.y' {rea {rnd {min win.maxh {add `1' guy.y}}}}}\\ndef d {set `guy.x' {rea {rnd {min win.maxw {add `1' guy.x}}}}}\\n\\n!randstarpos\\ninf !{\\n#^the infinite loop command.\\n#Note that the bang is nessicary as otherwise, the lazy would always\\n#inherit the same state.\\n\\tout `\\\\x1b[<rea {rnd guy.y}>;<rea {rnd guy.x}>H<guy.char>'\\n\\t# ^writes to stdout\\n\\tout `\\\\x1b[<rea {rnd star.y}>;<rea {rnd star.x}>H<star.char>'\\n\\tmap inp \\\\\\n\\t\\t`w' !w \\\\\\n\\t\\t`k' !w \\\\\\n\\t\\t`a' !a \\\\\\n\\t\\t`j' !a \\\\\\n\\t\\t`s' !s \\\\\\n\\t\\t`h' !s \\\\\\n\\t\\t`d' !d \\\\\\n\\t\\t`l' !d \\\\\\n\\t\\tdefa {}\\n\\t# ^map -- maps a value to a result\\n\\t# The arguments alternate between\\n\\t# things to match against and lazies\\n\\t# to run as a result of those matches.\\n\\t# The result is always a block that\\n\\t# will be ccl'd if the match is found.\\n\\t# The last thing to match against is\\n\\t# always assumed to be the default.\\n\\t# It is convention to make the last\\n\\t# matching argument `defa'.\\n\\t# `inp' gets a single char from stdin.\\n\\n\\tifs {\\n\\t\\teql `<guy.x>' `<star.x>'\\n\\t\\t# ^ eql will return `f' if they're\\n\\t\\t# not equal and `' if they're equal.\\n\\t\\teql `<guy.y>' `<star.y>'\\n\\t\\t# Note that since output is combined, this block is effectively\\n\\t\\t# an and operator. The actual and operator is better tho. It\\n\\t\\t# provides short-circuting.\\n\\t} then !{\\n\\t\\t!randstarpos\\n\\t\\tset score {add `1' score}\\n\\t\\tout `\\\\x1b[2J\\\\x1b[HScore: <rea {rnd score}>'\\n\\t}\\n}\\n\\n\"},\"r.spin\":{\"protected\":false,\"metadata\":{},\"mode\":\"f\",\"content\":\"out `Small ``Game\\\\'\\\\'\\\\n'\\nuse term\\n\\nset `score' `0'\\nset `win.maxw' {\\n\\t# ^ win.maxw is just the variable name. The dot is just a character.\\n\\t# the dot doesn't mean anything special here.\\n\\n\\t# We doin RPN :O\\n\\tsub `1' \\\\\\n\\t# `term:rows' is the name a command being run. \\\\\\n\\t# since variable names are quite permissive, this is just the name of a \\\\\\n\\t# command defined within the file `term' (imported at the top) \\\\\\n\\t# \\\\\\n\\t# Also note that a \\\\ at the end of a line, even after a comment \\\\\\n\\t# continues a line. meaning, term:rows will be the seconc argument \\\\\\n\\t# of the subtraction above. \\\\\\n\\tterm:rows \\n}\\nset `win.maxh' {sub `1' term:cols}\\n\\nset `guy.char' `\\\\U0001FBC5'\\nset `guy.x' `1'\\nset `guy.y' `1'\\n\\n# Unicode is supported inside quotes:\\nset `star.char' `⭐'\\n\\n# also non-ascii is allowed in identifiers:\\nset `\\\\x1b' `hello'\\n# and escapes are allowed in the middle of identifiers:\\nout \\\\x1b # << outputs hello\\n# and interpolation can be done on its own:\\nout <`\\\\x1b'> # << also outputs hello\\n# and ofc, you could use ^[ in the raw code:\\nout \\u001b # << bad idea, but yes, it still writes hello.\\n# and then we'll undefine it for your sanity:\\ndel `\\\\x1b'\\n\\n# `def' defines a command\\ndef randstarpos {\\n\\tset `star.x' {\\n\\t\\t# Get the real part of the rounded number after adding 1. (\\n\\t\\t# rounding removes decimal parts but leaves the immaginary\\n\\t\\t# part):\\n\\t\\trea {rnd {add `1' {\\n\\t\\t\\tmul win.maxw rnd \\n\\t\\t\\t# ^ rnd returns a random number\\n\\t\\t\\t# from 0 to 1\\n\\t\\t}}}\\n\\t}\\n\\tset `star.y' {rea {rnd\\n\\t\\tadd `1' {mul rnd win.maxh}\\n\\t}}\\n}\\ndef w {set `guy.y' {rea {rnd {\\n\\t\\tmax `1' {sub `1' guy.y}\\n}}}}\\ndef a {set `guy.x' {rea {rnd {max `1' {sub `1' guy.x}}}}}\\ndef s {set `guy.y' {rea {rnd {min win.maxh {add `1' guy.y}}}}}\\ndef d {set `guy.x' {rea {rnd {min win.maxw {add `1' guy.x}}}}}\\n\\n!randstarpos\\ninf !{\\n#^the infinite loop command.\\n#Note that the bang is nessicary as otherwise, the lazy would always\\n#inherit the same state.\\n\\tout `\\\\x1b[<rea {rnd guy.y}>;<rea {rnd guy.x}>H<guy.char>'\\n\\t# ^writes to stdout\\n\\tout `\\\\x1b[<rea {rnd star.y}>;<rea {rnd star.x}>H<star.char>'\\n\\tmap inp \\\\\\n\\t\\t`w' !w \\\\\\n\\t\\t`k' !w \\\\\\n\\t\\t`a' !a \\\\\\n\\t\\t`j' !a \\\\\\n\\t\\t`s' !s \\\\\\n\\t\\t`h' !s \\\\\\n\\t\\t`d' !d \\\\\\n\\t\\t`l' !d \\\\\\n\\t\\tdefa {}\\n\\t# ^map -- maps a value to a result\\n\\t# The arguments alternate between\\n\\t# things to match against and lazies\\n\\t# to run as a result of those matches.\\n\\t# The result is always a block that\\n\\t# will be ccl'd if the match is found.\\n\\t# The last thing to match against is\\n\\t# always assumed to be the default.\\n\\t# It is convention to make the last\\n\\t# matching argument `defa'.\\n\\t# `inp' gets a single char from stdin.\\n\\n\\tifs {\\n\\t\\teql `<guy.x>' `<star.x>'\\n\\t\\t# ^ eql will return `f' if they're\\n\\t\\t# not equal and `' if they're equal.\\n\\t\\teql `<guy.y>' `<star.y>'\\n\\t\\t# Note that since output is combined, this block is effectively\\n\\t\\t# an and operator. The actual and operator is better tho. It\\n\\t\\t# provides short-circuting.\\n\\t} then !{\\n\\t\\t!randstarpos\\n\\t\\tset score {add `1' score}\\n\\t\\tout `\\\\x1b[2J\\\\x1b[HScore: <rea {rnd score}>'\\n\\t}\\n}\\n\\n\"}}"}