[
  {
    "path": ".gitignore",
    "content": "node_modules/\nexamples/*.js\nrockstar-parser.js\npackage-lock.json"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 Wolfgang Faust\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n\n"
  },
  {
    "path": "README.md",
    "content": "This is a work-in-progress implementation of the [Rockstar](https://github.com/dylanbeattie/rockstar) language.\n\nIt transpiles Rockstar code to JavaScript.\n\nUsage:\n```\nyarn install\n./rockstar program.rock\nnode program.js\n```\n\n**Note:** Due to the extremely fast speed of updates to Rockstar, this implementation may not always match the current spec. [wolfgang42/rockstar](https://github.com/wolfgang42/rockstar) has the version of this spec targeted by this implementation. View differences between the two here: [wolfgang42/master...dylanbeattie/master](https://github.com/wolfgang42/rockstar/compare/master...dylanbeattie:master)\n\nAlso, since this is a WIP not all of Rockstar works properly yet. See the [Spec Complete milestone](https://github.com/wolfgang42/rockstar-js/milestone/1) for the list of unimplemented features.\n\nContributions welcome!\n\n# Design\nTranspilation is broken up into three stages: parsing, block grouping, and code generation.\n\n## Parsing\nFirst, the text of the program is parsed into statements and expressions. The resulting tokens are objects which have a `t` property containing the type of the token, plus other properties (generally a single mnemonic letter) with additional information about the token.\n\nParsing is currently implemented using [PEG.js](https://pegjs.org/), a JS Parser Generator. I'm not entirely convinced that this was the best choice but it seems to work OK so far.\n\nFor example, the poetic string literal:\n```\nBilly says hello world!\n```\nis parsed by this expression:\n```\nPoeticString = v:Variable _ 'says' ' ' t:$[^\\n]*\n\t{ return {t: 'Set', v: v, e: {t: 'Literal', v: t}} }\n```\ninto this token:\n```javascript\n{t: \"Set\", v: {t: \"Variable\", n: \"Billy\"}, e: {t: \"Literal\", v: \"hello world!\"}}\n```\n(Notice that this token contains two other tokens, `v` and `e`.)\n\n**Note for developers:** After changing `rockstar-parser.peg`, make sure you run `yarn build` to regenerate the parser code.\n\n## Block grouping\nThe parsing step returns a series of statements, but does not know about blocks. This step (implemented by the `groupBlocks` function) finds statements which begin blocks (`If`, `While`, and so on) and groups the statements together inside a `Block` token, removing `BlankLine` tokens.\n\n## Code generation\nThis stage takes the tokens and emits JavaScript code. Each token has a function in the `generators` object which takes a token and returns a string. Many of these operate recursively, calling `expr()` on a token to generate code to be included.\n\nFor example, the `Set` token generator:\n```javascript\nSet: s => `${expr(s.v)}=${expr(s.e)};`,\n```\ntakes a Token like this:\n```javascript\n{t: \"Set\", v: {t: \"Variable\", n: \"Billy\"}, e: {t: \"Literal\", v: \"hello world!\"}}\n```\nand returns this code:\n```javascript\nBilly=\"hello world!\";\n```\n"
  },
  {
    "path": "examples/fizzbuzz-idiomatic.out.txt",
    "content": "1\n2\nFizz!\n4\nBuzz!\nFizz!\n7\n8\nFizz!\nBuzz!\n11\nFizz!\n13\n14\nFizzBuzz!\n16\n17\nFizz!\n19\nBuzz!\nFizz!\n22\n23\nFizz!\nBuzz!\n26\nFizz!\n28\n29\nFizzBuzz!\n31\n32\nFizz!\n34\nBuzz!\nFizz!\n37\n38\nFizz!\nBuzz!\n41\nFizz!\n43\n44\nFizzBuzz!\n46\n47\nFizz!\n49\nBuzz!\nFizz!\n52\n53\nFizz!\nBuzz!\n56\nFizz!\n58\n59\nFizzBuzz!\n61\n62\nFizz!\n64\nBuzz!\nFizz!\n67\n68\nFizz!\nBuzz!\n71\nFizz!\n73\n74\nFizzBuzz!\n76\n77\nFizz!\n79\nBuzz!\nFizz!\n82\n83\nFizz!\nBuzz!\n86\nFizz!\n88\n89\nFizzBuzz!\n91\n92\nFizz!\n94\nBuzz!\nFizz!\n97\n98\nFizz!\nBuzz!\n"
  },
  {
    "path": "examples/fizzbuzz-idiomatic.rock",
    "content": "Midnight takes your heart and your soul\nWhile your heart is as high as your soul\nPut your heart without your soul into your heart\n\nGive back your heart\n\n\nDesire is a lovestruck ladykiller\nMy world is nothing \nFire is ice\nHate is water\nUntil my world is Desire,\nBuild my world up\nIf Midnight taking my world, Fire is nothing and Midnight taking my world, Hate is nothing\nShout \"FizzBuzz!\"\nTake it to the top\n\nIf Midnight taking my world, Fire is nothing\nShout \"Fizz!\"\nTake it to the top\n\nIf Midnight taking my world, Hate is nothing\nSay \"Buzz!\"\nTake it to the top\n\nWhisper my world\n"
  },
  {
    "path": "examples/fizzbuzz-minimalist.out.txt",
    "content": "1\n2\nFizz!\n4\nBuzz!\nFizz!\n7\n8\nFizz!\nBuzz!\n11\nFizz!\n13\n14\nFizzBuzz!\n16\n17\nFizz!\n19\nBuzz!\nFizz!\n22\n23\nFizz!\nBuzz!\n26\nFizz!\n28\n29\nFizzBuzz!\n31\n32\nFizz!\n34\nBuzz!\nFizz!\n37\n38\nFizz!\nBuzz!\n41\nFizz!\n43\n44\nFizzBuzz!\n46\n47\nFizz!\n49\nBuzz!\nFizz!\n52\n53\nFizz!\nBuzz!\n56\nFizz!\n58\n59\nFizzBuzz!\n61\n62\nFizz!\n64\nBuzz!\nFizz!\n67\n68\nFizz!\nBuzz!\n71\nFizz!\n73\n74\nFizzBuzz!\n76\n77\nFizz!\n79\nBuzz!\nFizz!\n82\n83\nFizz!\nBuzz!\n86\nFizz!\n88\n89\nFizzBuzz!\n91\n92\nFizz!\n94\nBuzz!\nFizz!\n97\n98\nFizz!\nBuzz!\n"
  },
  {
    "path": "examples/fizzbuzz-minimalist.rock",
    "content": "Modulus takes Number and Divisor\nWhile Number is as high as Divisor\nPut Number minus Divisor into Number\n    (blank line ending While block)\nGive back Number\n    (blank line ending function declaration)\nLimit is 100\nCounter is 0\nFizz is 3\nBuzz is 5\nUntil Counter is Limit\nBuild Counter up\nIf Modulus taking Counter, Fizz is 0 and Modulus taking Counter, Buzz is 0\nSay \"FizzBuzz!\"\nContinue\n    (blank line ending 'If' Block)\nIf Modulus taking Counter and Fizz is 0\nSay \"Fizz!\"\nContinue\n    (blank line ending 'If' Block)\nIf Modulus taking Counter and Buzz is 0\nSay \"Buzz!\"\nContinue\n    (blank line ending 'If' Block)\nSay Counter\n    (EOL ending Until block)\n"
  },
  {
    "path": "examples/listen.rock",
    "content": "Listen to my voice\nBuild my voice up\nScream my voice\n"
  },
  {
    "path": "examples/treesort.out.txt",
    "content": "822\n4805\n9117\n10097\n10402\n13586\n20636\n24037\n24805\n32533\n34673\n37542\n39292\n54876\n64894\n74296\n74945\n76520\n80959\n91665\n"
  },
  {
    "path": "examples/treesort.rock",
    "content": "Nodeoperationread is 0\nNodeoperationwrite is 1\nNodevalueignored is 0\nNodevariablevalue is 0\nNodevariableleft is 1\nNodevariableright is 2\nMakenode takes Value, Left, Right\n    Node takes Operation, Variable, Newvalue\n        If Operation is Nodeoperationread\n            If Variable is Nodevariablevalue\n                Give back Value\n            (end if)\n            If Variable is Nodevariableleft\n                Give back Left\n            (end if)\n            If Variable is Nodevariableright\n                Give back Right\n            (end if)\n        (end if)\n        If Operation is Nodeoperationwrite\n            If Variable is Nodevariablevalue\n                Put Newvalue into Value\n            (end if)\n            If Variable is Nodevariableleft\n                Put Newvalue into Left\n            (end if)\n            If Variable is Nodevariableright\n                Put Newvalue into Right\n            (end if)\n        (end if)\n    (end function)\n    Give back Node\n(end function)\nInsertnode takes Node, Newvalue\n    Put Makenode taking Newvalue, nothing, nothing into Newnode\n    If Node is nothing\n        Give back Newnode\n    (end if)\n    Put Node into Originalroot\n    Put nothing into Parentnode\n    Put Nodevariableright into Direction\n    While Node is not nothing\n        Put Node taking Nodeoperationread, Nodevariablevalue, Nodevalueignored into Value\n        Put Nodevariableright into Direction\n        If Newvalue is less than Value\n            Put Nodevariableleft into Direction\n        (end if)\n        Put Node into Parentnode\n        Put Node taking Nodeoperationread, Direction, Nodevalueignored into Node\n    (end while)\n    Put Parentnode taking Nodeoperationwrite, Direction, Newnode into Unusedreturnvalue\n    Give back Originalroot\n(end function)\nInorder takes Node\n    If Node is nothing\n        Give back nothing\n    (end if)\n    Put Node taking Nodeoperationread, Nodevariableleft, Nodevalueignored into Left\n    Put Inorder taking Left into Unusedreturnvalue\n    Put Node taking Nodeoperationread, Nodevariablevalue, Nodevalueignored into Value\n    Say Value\n    Put Node taking Nodeoperationread, Nodevariableright, Nodevalueignored into Right\n    Put Inorder taking Right into Unusedreturnvalue\n    Give back nothing\n(end function)\nMain takes Root\n    Put Insertnode taking Root, 10097 into Root\n    Put Insertnode taking Root, 32533 into Root\n    Put Insertnode taking Root, 76520 into Root\n    Put Insertnode taking Root, 13586 into Root\n    Put Insertnode taking Root, 34673 into Root\n    Put Insertnode taking Root, 54876 into Root\n    Put Insertnode taking Root, 80959 into Root\n    Put Insertnode taking Root, 9117 into Root\n    Put Insertnode taking Root, 39292 into Root\n    Put Insertnode taking Root, 74945 into Root\n    Put Insertnode taking Root, 37542 into Root\n    Put Insertnode taking Root, 4805 into Root\n    Put Insertnode taking Root, 64894 into Root\n    Put Insertnode taking Root, 74296 into Root\n    Put Insertnode taking Root, 24805 into Root\n    Put Insertnode taking Root, 24037 into Root\n    Put Insertnode taking Root, 20636 into Root\n    Put Insertnode taking Root, 10402 into Root\n    Put Insertnode taking Root, 822 into Root\n    Put Insertnode taking Root, 91665 into Root\n    Put Inorder taking Root into Unusedreturnvalue\n(end function)\nPut Main taking nothing into Unusedreturnvalue\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"rockstar-js\",\n  \"version\": \"0.0.1\",\n  \"description\": \"JavaScript transpiler for the esoteric language 'Rockstar'\",\n  \"homepage\": \"https://github.com/wolfgang42/rockstar-js\",\n  \"bugs\": \"https://github.com/wolfgang42/rockstar-js/issues\",\n  \"repository\": \"github:wolfgang42/rockstar-js\",\n  \"license\": \"MIT\",\n  \"author\": \"Wolfgang Faust\",\n  \"main\": \"src/rockstar.js\",\n  \"bin\": \"./rockstar\",\n  \"directories\": {\n    \"lib\": \"src/\",\n    \"example\": \"examples/\"\n  },\n  \"dependencies\": {},\n  \"devDependencies\": {\n    \"mocha\": \"^5.2.0\",\n    \"pegjs\": \"^0.10.0\"\n  },\n  \"scripts\": {\n    \"build\": \"pegjs src/rockstar-parser.peg\",\n    \"test\": \"mocha\",\n    \"prepublish\": \"npm run build\"\n  }\n}\n"
  },
  {
    "path": "rockstar",
    "content": "#!/usr/bin/env node\nconst rockstar = require('./src/rockstar')\nconst fs = require('fs')\n\nfunction promisify(fn, ...args) {\n\treturn new Promise((resolve, reject) => fn(...args, (err, result) => {\n\t\tif (err) {\n\t\t\treject(err)\n\t\t} else {\n\t\t\tresolve(result)\n\t\t}\n\t}))\n}\n\nconst filename = process.argv[2]\n\npromisify(fs.readFile, filename, 'utf-8')\n.then(rockstar.compile)\n.then(code => promisify(fs.writeFile, filename.replace('.rock', '.js'), code))\n.then(null, e => {\n\tconsole.error(e)\n\tprocess.exit(1)\n})\n"
  },
  {
    "path": "src/rockstar-parser.peg",
    "content": "Program = Statement *\nStatement = _? s:(FunctionDeclaration/Operation/BlankLine) ','? _* '\\n' {return s}\nOperation = Loop / If / ArithmeticStatement / GiveBack / Set / Put / Listen / Say / Continue / Break / PoeticString\n_ = ((' ' / '\\t')+) / Comment\nComment = '(' [^)]* ')'\n\nVariable =\n\t(n:(CommonVariable / ProperVariable) {return {t: 'Variable', n}})\n\t/ Pronoun {return {t: 'Pronoun'}}\nCommonVariable = p:('the'i/'my'i/'your'i) _ v:$([a-z]+) {return p+v}\nProperWord = $([A-Z][A-Za-z]+)\nProperVariable = $(ProperWord _ ProperVariable) / $ProperWord\nPronoun = (\n\t'it'i\n\t/'he'i/'she'i/'him'i/'her'i\n\t/'them'i/'they'i\n\t/'ze'i/'hir'i/'zie'i/'zir'i/'xe'i/'xem'i/'ve'i/'ver'i\n)\n\nTypeLiteral = v:TypeLiteralValue { return {t: 'Literal', v} }\nTypeLiteralValue =\n\t('nothing'/'nobody'/'nowhere'/'empty'/'gone') {return 0} // TODO null?\n\t/ ('true'/'right'/'yes'/'ok') {return true}\n    / ('false'/'wrong'/'no'/'lies') {return false}\n    / 'mysterious' { return undefined}\n\nString = '\"' v:$[^\"]+ '\"'\n\t{ return {t: 'Literal', v}}\n\nNumber = n:$([0-9]+ ('.' [0-9]+)?)\n\t{ return {t: 'Literal', v: parseFloat(n)} }\n\nArithmeticStatement = BuildUp / KnockDown\nBuildUp = 'Build' _ v:Variable _ 'up'\n\t{ return {t: 'Rement', v: v, o: '++'} }\nKnockDown = 'Knock' _ v:Variable _ 'down'\n\t{ return {t: 'Rement', v: v, o: '--'} }\n\nPoeticString = v:Variable _ 'says' ' ' t:$[^\\n]*\n\t{ return {t: 'Set', v: v, e: {t: 'Literal', v: t}} }\n\nPoeticNumber = n:PoeticDigits d:PoeticDecimal? {return {t: 'Literal', v: parseFloat(d?n+'.'+d:n)}}\nPoeticDecimal = '.' _ d:PoeticDigits {return d}\nPoeticDigits =\n\tl:PoeticDigit ( _ / [\\',;:?!] )+ r:PoeticDigits { return l+r }\n\t/ d: PoeticDigit { return d }\nPoeticDigit = t:[A-Za-z]+ {return (t.length%10).toString()}\n\nArithmeticExpression =\n\tl:SimpleExpression\n\t_ o:ArithmeticOperator _\n\tr:SimpleExpression\n\t{ return {t: 'Arithmetic', l, o, r} }\nArithmeticOperator =\n\t('minus'/'without') {return '-'}\n\t/ ('plus'/'with') {return '+'}\n\t/ ('times'/'of') {return '*'}\n\t/ ('over') {return '/'}\n\nComparison = l:SimpleExpression _ b:BoolCheck c:Comparator? r:SimpleExpression\n\t{ return {t: 'Comparison', l, r, b, c} }\nBoolCheck =\n\t(('is'_'not'_)/\"ain't\"_) { return false }\n\t/ 'is'_ { return true }\nComparator =\n\t('higher'/'greater'/'bigger'/'stronger')_'than'_ { return 'gt' }\n    / ('lower'/'less'/'smaller'/'weaker')_'than'_ { return 'lt' }\n    / 'as'_('high'/'great'/'big'/'strong')_'as'_ {return 'ge'}\n    / 'as'_('low'/'little'/'small'/'weak')_'as'_ {return 'le'}\n\nListen = 'Listen' _ 'to' _ v:Variable\n\t{return {t:'Listen', v}}\nSay = ('Say'/'Shout'/'Whisper'/'Scream') _ e:Expression\n\t{return {t:'Say', e}}\n\nIf = 'If' _ e:Expression\n\t{ return {t: 'If', e} }\nElse = 'Else' {return {t: 'Else'}}\n\nLoop = c:('While'/'Until') _ e:Expression\n\t{ return {t: 'Loop', c, e} }\nContinue = ('Continue' / ('Take'_'it'_'to'_'the'_'top')) {return {t: 'Continue'}}\nBreak = ('Break' / ('Break'_'it'_'down')) {return {t: 'Break'}}\n\nFunctionDeclaration = n:Variable _ 'takes' _ a:FunctionDeclarationArguments\n\t{ return {t: 'FunctionDeclaration', n, a: a.map(a => a.n)} }\nFunctionDeclarationArguments = \n\ta:Variable (_'and'_ / _?','_?) b:FunctionDeclarationArguments { return [a].concat(b) }\n    / a:Variable { return [a] }\nGiveBack = 'Give back' _ e:Expression\n\t{ return {t: 'GiveBack', e} }\nBlankLine = '' {return {t: 'BlankLine'}}\n\n\nFunctionCall = f:Variable _ 'taking' _ a:FunctionCallArguments\n\t{ return {t: 'FunctionCall', f, a} }\n// TODO 'and' is overloaded, so can't use full Expression syntax\nFunctionCallArguments = \n\ta:SimpleExpression (_'and'_ / _?','_?) b:FunctionCallArguments { return [a].concat(b) }\n    / a:SimpleExpression { return [a] }\n\n\n///////////////////////\n// TODO Everything below here is never explicitly defined in the spec\nExpression = ArithmeticExpression / BooleanOperation\nSimpleExpression = FunctionCall / TypeLiteral / Variable / Number / String / PoeticNumber\nBooleanOperation =\n\t(\n    \tl:(Comparison / SimpleExpression)\n        _ b:('and'/'or') _\n        r:Expression\n        { return {t: 'BooleanOperation', l, b, r} }\n    )\n    / l: (Comparison / SimpleExpression) { return l }\n\nSet = v:Variable _ ('is'/'was'/'were') _ e:Expression\n\t{ return {t: 'Set', v: v, e} }\nPut = 'Put' _ e:Expression _ 'into' _ v:Variable\n\t{ return {t: 'Set', v: v, e} }\n"
  },
  {
    "path": "src/rockstar.js",
    "content": "const parser = require('./rockstar-parser')\n\nlet it\nconst generators = {\n\tBlock: b => `{${b.s.map(expr).join('')}}`,\n\tFunctionDeclaration: f => `function ${expr(f.n)}(${f.a.map(varname).join(',')})`,\n\tFunctionCall: f => `${expr(f.f)}(${f.a.map(expr).join(',')})`,\n\tLoop: w => {\n\t\tlet cond = expr(w.e)\n\t\tif (w.c === 'Until') cond = `!(${cond})`\n\t\treturn `while(${cond})`\n\t},\n\tContinue: _ => 'continue;',\n\tBreak: _ => 'break;',\n\tIf: i => `if(${expr(i.e)})`,\n\tElse: _ => 'else',\n\tComparison: c => {\n\t\tlet ret = expr(c.l)\n\t\tif (c.c) {\n\t\t\tconst comp = {\n\t\t\t\tgt: '>',\n\t\t\t\tlt: '<',\n\t\t\t\tge: '>=',\n\t\t\t\tle: '<=',\n\t\t\t}\n\t\t\tret += comp[c.c]\n\t\t} else {\n\t\t\tif (c.b) {\n\t\t\t\tret += '==='\n\t\t\t} else {\n\t\t\t\tret += '!=='\n\t\t\t}\n\t\t}\n\t\tret += expr(c.r)\n\t\tif (!c && !b) ret = `!(${ret})`\n\t\treturn ret\n\t},\n\tBooleanOperation: b => `${expr(b.l)}${b.b=='and'?'&&':'||'}${expr(b.r)}`,\n\tVariable: v => {\n\t\tit = v\n\t\treturn varname(v.n)\n\t},\n\tPronoun: p => expr(it),\n\tRement: r => `${expr(r.v)}${r.o};`,\n\tArithmetic: a => `${expr(a.l)}${a.o}${expr(a.r)}`,\n\tSet: s => `${expr(s.v)}=${expr(s.e)};`,\n\tLiteral: l => JSON.stringify(l.v),\n\tGiveBack: g => `return ${expr(g.e)};`,\n\tSay: s=>`console.log(${expr(s.e)});`,\n\tListen: ({ v }) => `${varname(v.n)} = $readLineSync();`\n}\n\nconst dependencies = {\n\taddReadLine: `\n\t\tfunction $readLineSync() {\n\t\t\tconst line = [];\n\t\t\tconst buffer = Buffer.alloc(1);\n\t\t\twhile (true) {\n\t\t\t\tconst bytes = $fs.readSync(1, buffer, 0, 1, null);\n\t\t\t\tif (!bytes) break;\n\t\t\t\tif (buffer[0] === 10 || buffer[0] === 13) break;\n\t\t\t\tline.push(buffer[0]);\n\t\t\t}\n\t\t\treturn Buffer.from(line).toString('utf-8');\n\t\t}\n\t\t`,\n\tfs: `const $fs = require('fs');`,\n}\n\nfunction varname(v) {\n\treturn v.replace(/ /g, '')\n}\n\nfunction expr(e) {\n\tif (!(e.t in generators)) {\n\t\tconsole.log(e)\n\t\tthrow new Error('Unknown statement type: '+e.t)\n\t}\n\treturn generators[e.t](e)\n}\n\nfunction _groupBlocks(statements) {\n\tlet ret = []\n\tlet stmt\n\twhile (stmt = statements.shift()) {\n\t\tif (stmt.t == 'BlankLine') return ret\n\t\tret.push(stmt)\n\t\tif (stmt.t == 'If' || stmt.t == 'Else' || stmt.t == 'Loop' || stmt.t == 'FunctionDeclaration') {\n\t\t\tret.push({\n\t\t\t\tt: 'Block',\n\t\t\t\ts: _groupBlocks(statements),\n\t\t\t})\n\t\t}\n\t}\n\treturn ret\n}\nfunction groupBlocks(statements) {\n\tconst ret = []\n\twhile (statements.length !== 0) {\n\t\t_groupBlocks(statements).forEach(s => ret.push(s))\n\t}\n\treturn ret\n}\n\nfunction computeDependencies(statements) {\n\tconst deps = [];\n\tif (statements.some(s => s.t === 'Listen')) {\n\t\tdeps.push('fs', 'addReadLine');\n\t}\n\t// TODO: (eventually) remove dup `deps`\n\treturn deps;\n}\n\nfunction generateDependencies(deps) {\n\treturn deps.map(d => dependencies[d]);\n}\n\nfunction parse(programText) {\n\t// Parser requires newline before EOF, add it in case there wasn't one already.\n\treturn parser.parse(programText+'\\n')\n}\n\nfunction compile(programText) {\n\tconst statements = parse(programText)\n\tconst dependencies = generateDependencies(computeDependencies(statements));\n\tconst program = groupBlocks(statements)\n\n\treturn [\n\t\t...dependencies,\n\t\t...program.map(expr)\n\t].join('')\n}\n\nmodule.exports = {\n\tvarname,\n\texpr,\n\tgroupBlocks,\n\tparse,\n\tcompile,\n\tcomputeDependencies,\n\tgenerateDependencies,\n}\n"
  },
  {
    "path": "test/integration.js",
    "content": "const test_examples = [\n\t'fizzbuzz-idiomatic',\n\t'fizzbuzz-minimalist',\n\t'listen',\n\t'treesort',\n]\n\nconst rockstar = require('../src/rockstar')\nconst fs = require('fs')\nconst child_process = require('child_process')\n\nfunction promisify(fn, ...args) {\n\treturn new Promise((resolve, reject) => fn(...args, (...result) => resolve(result)))\n}\nasync function promisify_one(fn, ...args) {\n\tconst r = await promisify(fn, ...args)\n\tif (r.length !== 1) throw new Error('promisify_one called with returning '+r.length+' args')\n\treturn r[0]\n}\nasync function promisify_err(fn, ...args) {\n\tconst r = await promisify(fn, ...args)\n\tif (r.length !== 2) throw new Error('promisify_one called with returning '+r.length+' args')\n\tif (r[0]) throw r[0]\n\treturn r[1]\n}\nconst readFile = filename => promisify_err(fs.readFile, filename, 'utf-8')\nconst fileExists = filename => promisify_one(fs.exists, filename)\n\nfunction runJs(code) {\n\treturn new Promise((resolve, reject) => {\n\t\tlet output = ''\n\t\tconst node = child_process.spawn('node')\n\t\tnode.stdout.on('data', data => output += data)\n\t\tnode.on('close', () => resolve(output))\n\t\tnode.stdin.end(code)\n\t})\n}\n\ndescribe('Examples', () => {\n\ttest_examples.forEach(example => it(example, async () => {\n\t\texample = `${__dirname}/../examples/${example}`\n\t\tconst rsCode = await readFile(example+'.rock')\n\t\tconst jsCode = rockstar.compile(rsCode)\n\t\tif (await fileExists(example+'.out.txt')) {\n\t\t\tconst expectOutput = await readFile(example+'.out.txt')\n\t\t\tconst realOutput = await runJs(jsCode)\n\t\t\tif (expectOutput != realOutput) throw new Error('Actual output does not match expected output')\n\t\t}\n\t}))\n})\n"
  }
]