diff --git a/packager.sh b/packager.sh index 1fcd38b..61d4d4e 100755 --- a/packager.sh +++ b/packager.sh @@ -2,7 +2,7 @@ #! nix-shell -i bash #! nix-shell -p bash nodejs jq -node itty/utils/thread.js spool spinny.yarn spinny.js +node itty/utils/thread.js spool spinny.yarn spinny.js s.spin r.spin node itty/utils/thread.js metadata-from-json spinny.yarn yarnball-meta.json #cat spinny.yarn mergefile.json | jq -s '.[0] as $old | .[1] as $merge | .[0].content | fromjson * $merge | tojson as $new | $old + {"content":$new} | tojson' | jq 'fromjson' > spinny.yarn.tmp diff --git a/r.spin b/r.spin new file mode 100644 index 0000000..ac708e9 --- /dev/null +++ b/r.spin @@ -0,0 +1,107 @@ +out `Small ``Game\'\'\n' +use term + +set `score' `0' +set `win.maxw' { + # ^ win.maxw is just the variable name. The dot is just a character. + # the dot doesn't mean anything special here. + + # We doin RPN :O + sub `1' \ + # `term:rows' is the name a command being run. \ + # since variable names are quite permissive, this is just the name of a \ + # command defined within the file `term' (imported at the top) \ + # \ + # Also note that a \ at the end of a line, even after a comment \ + # continues a line. meaning, term:rows will be the seconc argument \ + # of the subtraction above. \ + term:rows +} +set `win.maxh' {sub `1' term:cols} + +set `guy.char' `\U0001FBC5' +set `guy.x' `1' +set `guy.y' `1' + +# Unicode is supported inside quotes: +set `star.char' `⭐' + +# also non-ascii is allowed in identifiers: +set `\x1b' `hello' +# and escapes are allowed in the middle of identifiers: +out \x1b # << outputs hello +# and interpolation can be done on its own: +out <`\x1b'> # << also outputs hello +# and ofc, you could use ^[ in the raw code: +out  # << bad idea, but yes, it still writes hello. +# and then we'll undefine it for your sanity: +del `\x1b' + +# `def' defines a command +def randstarpos { + set `star.x' { + # Get the real part of the rounded number after adding 1. ( + # rounding removes decimal parts but leaves the immaginary + # part): + rea {rnd {add `1' { + mul win.maxw rnd + # ^ rnd returns a random number + # from 0 to 1 + }}} + } + set `star.y' {rea {rnd + add `1' {mul rnd win.maxh} + }} +} +def w {set `guy.y' {rea {rnd { + max `1' {sub `1' guy.y} +}}}} +def a {set `guy.x' {rea {rnd {max `1' {sub `1' guy.x}}}}} +def s {set `guy.y' {rea {rnd {min win.maxh {add `1' guy.y}}}}} +def d {set `guy.x' {rea {rnd {min win.maxw {add `1' guy.x}}}}} + +!randstarpos +inf !{ +#^the infinite loop command. +#Note that the bang is nessicary as otherwise, the lazy would always +#inherit the same state. + out `\x1b[;H' + # ^writes to stdout + out `\x1b[;H' + map inp \ + `w' !w \ + `k' !w \ + `a' !a \ + `j' !a \ + `s' !s \ + `h' !s \ + `d' !d \ + `l' !d \ + defa {} + # ^map -- maps a value to a result + # The arguments alternate between + # things to match against and lazies + # to run as a result of those matches. + # The result is always a block that + # will be ccl'd if the match is found. + # The last thing to match against is + # always assumed to be the default. + # It is convention to make the last + # matching argument `defa'. + # `inp' gets a single char from stdin. + + ifs { + eql `' `' + # ^ eql will return `f' if they're + # not equal and `' if they're equal. + eql `' `' + # Note that since output is combined, this block is effectively + # an and operator. The actual and operator is better tho. It + # provides short-circuting. + } then !{ + !randstarpos + set score {add `1' score} + out `\x1b[2J\x1b[HScore: ' + } +} + diff --git a/s.spin b/s.spin new file mode 100644 index 0000000..83043d1 --- /dev/null +++ b/s.spin @@ -0,0 +1,136 @@ +out `Small ``Game\'\'\n' +use term + +# Before we begin, a few notes +# - All objects are strings +# - There are a few different kinds of objects. +# - variables which have a static value they always evaluate to +# - lazies (contained by curlies (`{' and `}')) lazilly evaluate the lines +# within themselves only when nessicary. They may take arguments the same +# way commands do +# - commands, which are essentially lazies behind the mask of a variable name +# - strings, contained by a backtick to open (``') and a single quote to close +# (`\''). +# - Each line begins with any object +# - builtin commands are always 3 characters long +# - the starting object is followed by a space, then the arguments, which are +# space seperated objects +# - the arguments are passed to the first object +# - variables and strings ignore these arguments +# - commands and lazies may access these arguments with the `arg' command +# - interpolation, either within a variable name, command name, or string, is +# done with the `<...>' syntax. +# - The arrows, as they're called, evaluate the line inside them, then insert +# that value into the variable name/command name/string. +# - thus a lone `' on a line evaluates to the value of the variable +# that is named by varname. +# - To say that backwards, varname contains the name of a variable, and that +# variable has a value, and that value is what we're getting. +# - When a object begins with a bang (`!'), if object being evaluated sets any +# variables, it sets them within the current scope. +# - Variable and function names are stripped of bangs and spaces. +# - Booleans don't exist. Instead, any string that is empty (`') is truthy and +# all other (non-empty) strings are falsey. +# - Numbers are strings in the formats: +# - `[+\-]?real(\.dec)?[+\-]imag(\.dec)?i' +# - `[+\-]?real(\.dec)?' +# - `[+\-]?imag(\.dec)?i' +# - where, imag is the immaginary part, real is the real part, and the .dec is +# the decimal part of the one it appears to the right of. +# - These strings are in any base from 3 to 19. +# - bases 20+ are reserved since they would involve the letter i (which +# denotes the immaginary part) +# - the current base is defined by the 'base' variable, which is always in +# base 2. +# - base 2 is reserved for no good reason. +# - all bases lower than two are reserved because they're inconveinent to +# implement. + +set `score' `0' +set `win.maxw' { + # ^ win.maxw is just the variable name. The dot is just a character. + # the dot doesn't mean anything special here. + + # We doin RPN :O + sub `1' term:rows + # `term:rows' is the name a command being run. + # since variable names are quite permissive, this is just the name of a + # command defined within the file `term' (imported at the top) +} +set `win.maxh' {sub `1' term:cols} + +set `guy.char' `\U0001FBC5' +set `guy.x' `1' +set `guy.y' `1' + +# Unicode is supported inside quotes: +set `star.char' `⭐' + +# `def' defines a command +def randstarpos { + set `star.x' { + # Get the real part of the rounded number after adding 1. ( + # rounding removes decimal parts but leaves the immaginary + # part): + rea {rnd {add `1' { + mul win.maxw rnd + # ^ rnd returns a random number + # from 0 to 1 + }}} + } + set `star.y' {rea {rnd + add `1' {mul rnd win.maxh} + }} +} +def w {set `guy.y' {rea {rnd { + max `1' {sub `1' guy.y} +}}}} +def a {set `guy.x' {rea {rnd {max `1' {sub `1' guy.x}}}}} +def s {set `guy.y' {rea {rnd {min win.maxh {add `1' guy.y}}}}} +def d {set `guy.x' {rea {rnd {min win.maxw {add `1' guy.x}}}}} + +!randstarpos +inf !{ +#^the infinite loop command. +#Note that the bang is nessicary as otherwise, the lazy would always +#inherit the same state. + out `\x1b[;H' + # ^writes to stdout + out `\x1b[;H' + map inp \ + `w' !w \ + `k' !w \ + `a' !a \ + `j' !a \ + `s' !s \ + `h' !s \ + `d' !d \ + `l' !d \ + defa {} + # ^map -- maps a value to a result + # The arguments alternate between + # things to match against and lazies + # to run as a result of those matches. + # The result is always a block that + # will be ccl'd if the match is found. + # The last thing to match against is + # always assumed to be the default. + # It is convention to make the last + # matching argument `defa'. + # `inp' gets a single char from stdin. + + ifs { + eql `' `' + # ^ eql will return `f' if they're + # not equal and `' if they're equal. + eql `' `' + # Note that since output is combined, this block is effectively + # an and operator. The actual and operator is better tho. It + # provides short-circuting. + } then !{ + !randstarpos + set score {add `1' score} + out `\x1b[2J\x1b[HScore: ' + } +} + diff --git a/spinny.js b/spinny.js index fb76327..5687710 100644 --- a/spinny.js +++ b/spinny.js @@ -8,35 +8,48 @@ // - Spinny source files (.spin) // - Spinny compiled json files (.spic) -function onRun() { +try { +async function onRun() { let cliArgs = parseCliArgs(); + let fileobj + let file - try { const fileobj = io.open(fs.resolve(cliArgs.filename), "r") + // File open: + try { + fileobj = io.open(fs.resolve(cliArgs.filename), "r") } catch (e) { display.print(`Spinny: Error opening file or resolving path \`${cliArgs.filename}'.`); + display.print(`${e.name}@${e.lineNumber}-${e.columnNumber}: ${e.message}`); io.error([22, "file open failed"]); } - try { const file = fileobj.read(); + try { + file = fileobj.read(); } catch (e) { display.print(`Spinny: Error reading file \`${cliArgs.filename}'.`); + display.print(`${e.name}@${e.lineNumber}-${e.columnNumber}: ${e.message}`); io.error([23, "file read failed"]); } - const fileSpincVersion = file.split("\n")[1].split("@"); + + // Spinc Checker: let isSpincFile = false; - if (fileSpincVersion.slice(0,2).join("\n") === "SPINC\nspinny:itty") { - // This is a spinny source file - if (fileSpincVersion[2] !== "noversion") { - let niceversion = spileSpincVersion[2].replace( - /[\t %]/g , "") - display.print("Spinny: Incorrect or unrecognized compiled spinc file"); - display.print(`Spinny: version \`${niceversion}'.`); - display.print("Spinny: This version of the spinny interpreter exclusively"); - display.print("Spinny: accepts compiled spinc files of version"); - display.print("Spinny: `noversion'."); - display.print("Spinny: Use `—version' or `—help' for more information."); - io.error([31, "invalid spinc version"]); + if (file.split("\n").length > 1) { + const fileSpincVersion = file.split("\n")[1].split("@"); + if (fileSpincVersion.length > 2 && + fileSpincVersion.slice(0,2).join("\n") === "SPINC\nspinny:itty") { + // This is a spinny source file + if (fileSpincVersion[2] !== "noversion") { + let niceversion = spileSpincVersion[2].replace( + /[\t %]/g , "") + display.print("Spinny: Incorrect or unrecognized compiled spinc file"); + display.print(`Spinny: version \`${niceversion}'.`); + display.print("Spinny: This version of the spinny interpreter exclusively"); + display.print("Spinny: accepts compiled spinc files of version"); + display.print("Spinny: `noversion'."); + display.print("Spinny: Use `—version' or `—help' for more information."); + io.error([31, "invalid spinc version"]); + } + isSpincFile = true; } - isSpincFile = true; } if (cliArgs.compileOnly && isSpincFile) { display.print("Spinny: Cannot compile a spinc file."); @@ -44,18 +57,32 @@ function onRun() { io.error([32, "spinc already compiled"]); } + let compiled if (!isSpincFile) { // We must compile the file - const parser = new spinnyParser; - const compiled = parser.compile(file) + const parser = new spinnyParser({ + parsestep:cliArgs.debugStep + }); + let compiled; + try { + compiled = await parser.compile(file) + } catch (e) { + if (!(e instanceof spinnyCompileError)) + throw e + let debug = e.debugStack + let last = e.debugStack.slice(-1)[0] + display.print("Spinny: Error compiling file..."); + display.print(`Spinny: Unfinished ${last[0]} at ${last[1]}`); + io.error([45, "compilation failed"]); + } + display.print(JSON.stringify(compiled)) + quit(); } display.print("Spinny: Running and/or saving files is not currently supported."); display.print("Spinny: Dumping contents instead."); if (isSpincFile) display.print(file.split("\n").slice(2).join('\n')) - else - display.print(compiled) } function showHelp(){ @@ -70,25 +97,25 @@ function showHelp(){ "Syntax:\n" + `\t${program.name} [flags...] [—] \n` + "Where:\n" + - "\t[flags...] are any combination of the flags\n" + - "\t available in the `Flags' section\n" + - "\t below.\n" + - "\t[—] Is an emdash (—) or a double dash (--)\n" + - "\t to mark where the filename is supposed\n" + - "\t to go. (Note that any time you see an\n" + - "\t em dash in this help page, you may\n" + - "\t choose to use a double dash in its\n" + - "\t place if that is your preference.)\n" + - "\t is the `.spin' or `.spinc' file to\n" + - "\t compile/run\n" + + "\t[flags...] are any combination of the flags\n" + + "\t available in the `Flags' section\n" + + "\t below.\n" + + "\t[—] Is an emdash (—) or a double dash (--)\n" + + "\t to mark where the filename is supposed\n" + + "\t to go. (Note that any time you see an\n" + + "\t em dash in this help page, you may\n" + + "\t choose to use a double dash in its\n" + + "\t place if that is your preference.)\n" + + "\t is the `.spin' or `.spinc' file to\n" + + "\t compile/run\n" + "Flags:\n" + - "\t—help Print this help page and exit.\n" + - "\t—version Print version and exit.\n" + - "\t—compile Compile the code to a .spinc file.\n" + - "\t The .spinc file must be specified\n" + - "\t with `—output'.\n" + - "\t—output Specifies the output file for the\n" + - "\t `—compile' flag.\n" + "\t—help Print this help page and exit.\n" + + "\t—version Print version and exit.\n" + + "\t—compile Compile the code to a .spinc file.\n" + + "\t The .spinc file must be specified\n" + + "\t with `—output'.\n" + + "\t—output Specifies the output file for the\n" + + "\t `—compile' flag.\n" ); }; @@ -105,18 +132,19 @@ function showVersion(){ function parseCliArgs() { let compileOnly = false; - let filename = ""; + let filename = null; let noMoreFlags = false; let nextIsOutput = false let output = ""; let letterFlag = false let flag = false + let debugStep = false for (let i=0;i": - if (inside === INSIDE.INTER) + if (inside === this.INSIDE.INTER) break wordLoop break case "\n": - if (inside === INSIDE.FILE - || inside == INSIDE.LAZY) + if (inside === this.INSIDE.FILE + || inside == this.INSIDE.LAZY) break wordLoop break case "\\": @@ -238,14 +329,14 @@ class spinnyParser { } break } - words.push(this.parseWord(inside)); + words.push(await this.parseWord(inside)); } - this.debugStack.pop() + await this.untag() return words } - parseComment() { - this.debugStack.push(["Comment", this.index]) + async parseComment() { + await this.tag("Comment") commentLoop: while (1) { switch (this.now()) { @@ -258,11 +349,11 @@ class spinnyParser { this.next(); } } - this.debugStack.pop() + await this.untag() } - - parseWord(inside) { - this.debugStack.push(["Word", this.index]) + + async parseWord(inside) { + await this.tag("Word") let bang = false; let word = {} word[CMPVER.KEYS.BANG] = false; @@ -273,41 +364,47 @@ class spinnyParser { switch (this.now()) { case "{": word[CMPVER.KEYS.TYPE] = CMPVER.TYPE.LAZY; - word[CMPVER.KEYS.LINES] = this.parseLazy(); + word[CMPVER.KEYS.LINES] = await this.parseLazy(); + break; case "`": - word[CMPVER.KEYS.TYPE] = CMPVER.TYPE.LIT; - word[CMPVER.KEYS.PARTS] = this.parseLiteral(); + word[CMPVER.KEYS.TYPE] = CMPVER.TYPE.LIT; + word[CMPVER.KEYS.PARTS] = await this.parseLiteral(); + break; default: word[CMPVER.KEYS.TYPE] = CMPVER.TYPE.ID; - word[CMPVER.KEYS.PARTS] = this.parseIdentifier(inside); + word[CMPVER.KEYS.PARTS] = await this.parseIdentifier(inside); } - this.debugStack.pop() + await this.untag() return word } - - parseLazy() { - this.debugStack.push(["Lazy", this.index]) + + async parseLazy() { + await this.tag("Lazy") this.next(); // Skip `{' let lines = [] while (this.now() !== "}") { - lines.push(this.parseLine(INSIDE.LAZY)) + lines.push(await this.parseLine(this.INSIDE.LAZY)) + if (this.now() === "\n") + // Continue to next line like a file would + this.next(); } - this.debugStack.pop() + this.next(); // Skip `}' + await this.untag() return lines } - - parseLiteral() { - this.debugStack.push(["Literal", this.index]) + + async parseLiteral() { + await this.tag("Literal") this.next(); // Skip `\`' let parts = [""] while (this.now() !== "\'") { switch (this.now()) { case "\\": - parts[parts.length-1]+=this.parseEscape(); + parts[parts.length-1]+=await this.parseEscape(); break case "<": parts.push( - this.parseInterpolation() + await this.parseInterpolation() ); parts.push("") break @@ -317,12 +414,13 @@ class spinnyParser { break } } - this.debugStack.pop(); + this.next(); // Skip `\'' + await this.untag(); return parts } - parseEscape() { - this.debugStack.push(["Literal", this.index]) + async parseEscape() { + await this.tag("Escape") this.next(); // skip `\\' let out = ""; switch (this.now()) { @@ -366,54 +464,57 @@ class spinnyParser { out = this.now(); // record the thing after the `\\' this.next(); // skip over it break - + } - this.debugStack.pop(); + await this.untag(); return out; } - parseInterpolation() { - this.debugStack.push(["Interpolation", this.index]) + async parseInterpolation() { + await this.tag("Interpolation") this.next(); // skip `\<' let line while (this.now() !== ">") { - line = this.parseLine(INSIDE.INTER) + line = await this.parseLine(this.INSIDE.INTER) } this.next(); // skip `>' - this.debugStack.pop() + await this.untag() return line } - - parseIdentifier(inside) { - this.debugStack.push(["Identifier", this.index]) + + async parseIdentifier(inside) { + await this.tag("Identifier") let parts = [""] idLoop: while (1) { switch (this.now()) { case "\\": - parts[parts.length-1]+=this.parseEscape(); + parts[parts.length-1]+=await this.parseEscape(); continue case "<": parts.push( - this.parseInterpolation() + await this.parseInterpolation() ); parts.push("") continue case "}": - if (inside === INSIDE.LAZY) + if (inside === this.INSIDE.LAZY) break idLoop break case ">": - if (inside === INSIDE.INTER) + if (inside === this.INSIDE.INTER) break idLoop break case "#": + case " ": + case "\t": + case "\n": break idLoop } parts[parts.length-1]+=this.now(); this.next(); } - this.debugStack.pop(); + await this.untag(); return parts } } @@ -437,5 +538,10 @@ const SPIDNEY = {//spinny identifiers const CMPVER = SPIDNEY["noversion"] //compileversion // When the program starts: -onRun(); -quit(); + await onRun(); + quit(); +} catch (e) { + display.print(`Spinny: Something has gone horribly wrong, commander.`); + display.print(`${e.name}@${e.lineNumber}-${e.columnNumber}: ${e.message}`); + io.error([9, "unknown error"]); +} diff --git a/spinny.yarn b/spinny.yarn index 9a3177f..8ece8e8 100644 --- a/spinny.yarn +++ b/spinny.yarn @@ -1 +1 @@ -{"protected":false,"mode":"f","metadata":{"name":"spinny","author":"Anya Merry :3","version":[0,0,3],"dependencies":[],"paths":{"spinny.js":"%B/spinny.js"}},"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\\nfunction onRun() {\\n\\tlet cliArgs = parseCliArgs();\\n\\n\\ttry { const fileobj = 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\\tio.error([22, \\\"file open failed\\\"]);\\n\\t}\\n\\ttry { const file = fileobj.read();\\n\\t} catch (e) {\\n\\t\\tdisplay.print(`Spinny: Error reading file \\\\`${cliArgs.filename}'.`);\\n\\t\\tio.error([23, \\\"file read failed\\\"]);\\n\\t}\\n\\tconst fileSpincVersion = file.split(\\\"\\\\n\\\")[1].split(\\\"@\\\");\\n\\tlet isSpincFile = false;\\n\\tif (fileSpincVersion.slice(0,2).join(\\\"\\\\n\\\") === \\\"SPINC\\\\nspinny:itty\\\") {\\n\\t\\t// This is a spinny source file\\n\\t\\tif (fileSpincVersion[2] !== \\\"noversion\\\") {\\n\\t\\t\\tlet niceversion = spileSpincVersion[2].replace(\\n\\t\\t\\t\\t/[\\\\t %]/g , \\\"\\\")\\n\\t\\t\\tdisplay.print(\\\"Spinny: Incorrect or unrecognized compiled spinc file\\\");\\n\\t\\t\\tdisplay.print(`Spinny: version \\\\`${niceversion}'.`);\\n\\t\\t\\tdisplay.print(\\\"Spinny: This version of the spinny interpreter exclusively\\\");\\n\\t\\t\\tdisplay.print(\\\"Spinny: accepts compiled spinc files of version\\\");\\n\\t\\t\\tdisplay.print(\\\"Spinny: `noversion'.\\\");\\n\\t\\t\\tdisplay.print(\\\"Spinny: Use `—version' or `—help' for more information.\\\");\\n\\t\\t\\tio.error([31, \\\"invalid spinc version\\\"]);\\n\\t\\t}\\n\\t\\tisSpincFile = true;\\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\\tif (!isSpincFile) {\\n\\t\\t// We must compile the file\\n\\t\\tconst parser = new spinnyParser;\\n\\t\\tconst compiled = parser.compile(file)\\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\\telse\\n\\t\\tdisplay.print(compiled)\\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...] [—] \\\\n` +\\n\\t\\t\\\"Where:\\\\n\\\" +\\n\\t\\t\\t\\\"\\\\t[flags...] are any combination of the flags\\\\n\\\" +\\n\\t\\t\\t\\\"\\\\t available in the `Flags' section\\\\n\\\" +\\n\\t\\t\\t\\\"\\\\t below.\\\\n\\\" +\\n\\t\\t\\t\\\"\\\\t[—] Is an emdash (—) or a double dash (--)\\\\n\\\" +\\n\\t\\t\\t\\\"\\\\t to mark where the filename is supposed\\\\n\\\" +\\n\\t\\t\\t\\\"\\\\t to go. (Note that any time you see an\\\\n\\\" +\\n\\t\\t\\t\\\"\\\\t em dash in this help page, you may\\\\n\\\" +\\n\\t\\t\\t\\\"\\\\t choose to use a double dash in its\\\\n\\\" +\\n\\t\\t\\t\\\"\\\\t place if that is your preference.)\\\\n\\\" +\\n\\t\\t\\t\\\"\\\\t is the `.spin' or `.spinc' file to\\\\n\\\" +\\n\\t\\t\\t\\\"\\\\t compile/run\\\\n\\\" +\\n\\t\\t\\\"Flags:\\\\n\\\" +\\n\\t\\t\\t\\\"\\\\t—help Print this help page and exit.\\\\n\\\" +\\n\\t\\t\\t\\\"\\\\t—version Print version and exit.\\\\n\\\" +\\n\\t\\t\\t\\\"\\\\t—compile Compile the code to a .spinc file.\\\\n\\\" +\\n\\t\\t\\t\\\"\\\\t The .spinc file must be specified\\\\n\\\" +\\n\\t\\t\\t\\\"\\\\t with `—output'.\\\\n\\\" +\\n\\t\\t\\t\\\"\\\\t—output Specifies the output file for the\\\\n\\\" +\\n\\t\\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 = \\\"\\\";\\n\\tlet noMoreFlags = false;\\n\\tlet nextIsOutput = false\\n\\tlet output = \\\"\\\";\\n\\tlet letterFlag = false\\n\\tlet flag = false\\n\\tfor (let i=0;i\\\":\\n\\t\\t\\t\\t\\tif (inside === 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 === INSIDE.FILE\\n\\t\\t\\t\\t\\t\\t|| inside == 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(this.parseWord(inside));\\n\\t\\t}\\n\\t\\tthis.debugStack.pop()\\n\\t\\treturn words\\n\\t}\\n\\n\\tparseComment() {\\n\\t\\tthis.debugStack.push([\\\"Comment\\\", this.index])\\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\\tthis.debugStack.pop()\\n\\t}\\n\\t\\n\\tparseWord(inside) {\\n\\t\\tthis.debugStack.push([\\\"Word\\\", this.index])\\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] = this.parseLazy();\\n\\t\\t\\tcase \\\"`\\\":\\n\\t\\t\\t\\t\\tword[CMPVER.KEYS.TYPE] = CMPVER.TYPE.LIT;\\n\\t\\t\\t\\t\\tword[CMPVER.KEYS.PARTS] = this.parseLiteral();\\n\\t\\t\\tdefault:\\n\\t\\t\\t\\tword[CMPVER.KEYS.TYPE] = CMPVER.TYPE.ID;\\n\\t\\t\\t\\tword[CMPVER.KEYS.PARTS] = this.parseIdentifier(inside);\\n\\t\\t}\\n\\t\\tthis.debugStack.pop()\\n\\t\\treturn word\\n\\t}\\n\\t\\n\\tparseLazy() {\\n\\t\\tthis.debugStack.push([\\\"Lazy\\\", this.index])\\n\\t\\tthis.next(); // Skip `{'\\n\\t\\tlet lines = []\\n\\t\\twhile (this.now() !== \\\"}\\\") {\\n\\t\\t\\tlines.push(this.parseLine(INSIDE.LAZY))\\n\\t\\t}\\n\\t\\tthis.debugStack.pop()\\n\\t\\treturn lines\\n\\t}\\n\\t\\n\\tparseLiteral() {\\n\\t\\tthis.debugStack.push([\\\"Literal\\\", this.index])\\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]+=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\\tthis.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.debugStack.pop();\\n\\t\\treturn parts\\n\\t}\\n\\n\\tparseEscape() {\\n\\t\\tthis.debugStack.push([\\\"Literal\\\", this.index])\\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\\t\\t\\t\\n\\t\\t}\\n\\t\\tthis.debugStack.pop();\\n\\t\\treturn out;\\n\\t}\\n\\n\\tparseInterpolation() {\\n\\t\\tthis.debugStack.push([\\\"Interpolation\\\", this.index])\\n\\t\\tthis.next(); // skip `\\\\<'\\n\\t\\tlet line\\n\\t\\twhile (this.now() !== \\\">\\\") {\\n\\t\\t\\tline = this.parseLine(INSIDE.INTER)\\n\\t\\t}\\n\\t\\tthis.next(); // skip `>'\\n\\t\\tthis.debugStack.pop()\\n\\t\\treturn line\\n\\t}\\n\\t\\n\\tparseIdentifier(inside) {\\n\\t\\tthis.debugStack.push([\\\"Identifier\\\", this.index])\\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]+=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\\tthis.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 === 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 === 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\\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\\tthis.debugStack.pop();\\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:\\nonRun();\\nquit();\\n\"}}"} \ No newline at end of file +{"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...] [—] \\\\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 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 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\\\":\\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 `' 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[;H'\\n\\t# ^writes to stdout\\n\\tout `\\\\x1b[;H'\\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 `' `'\\n\\t\\t# ^ eql will return `f' if they're\\n\\t\\t# not equal and `' if they're equal.\\n\\t\\teql `' `'\\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: '\\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[;H'\\n\\t# ^writes to stdout\\n\\tout `\\\\x1b[;H'\\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 `' `'\\n\\t\\t# ^ eql will return `f' if they're\\n\\t\\t# not equal and `' if they're equal.\\n\\t\\teql `' `'\\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: '\\n\\t}\\n}\\n\\n\"}}"} \ No newline at end of file diff --git a/yarnball-meta.json b/yarnball-meta.json index 0d49af0..36558db 100644 --- a/yarnball-meta.json +++ b/yarnball-meta.json @@ -4,6 +4,8 @@ "version": [0,0,3], "dependencies": [], "paths": { - "spinny.js": "%B/spinny.js" + "spinny.js": "%B/spinny.js", + "s.spin": "/s.spin", + "r.spin": "/r.spin" } }