[
  {
    "path": ".github/workflows/main.yml",
    "content": "name: build\n\non:\n  push:\n    branches: [master]\n  pull_request:\n    branches: [master]\n\nenv:\n  CARGO_TERM_COLOR: always\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v2\n      - name: Build\n        run: cargo build --verbose\n      - name: Run tests\n        run: cargo test --verbose\n\n  demo:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v2\n      - name: Build\n        run: ./scripts/build-demo.sh\n      - name: Deploy staging\n        uses: amondnet/vercel-action@v19\n        if: github.event_name == 'pull_request'\n        with:\n          vercel-token: ${{ secrets.VERCEL_TOKEN }}\n          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}\n          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n      - name: check production\n        id: prod_or_not\n        run: |\n          if [ \"$REF\" == 'refs/head/master' ]\n          then\n            echo \"::set-output name=vercel-args::--prod\"\n          else\n            echo \"::set-output name=vercel-args::\"\n          fi\n        env:\n          REF: ${{ github.ref }}\n      - name: Deploy production\n        uses: amondnet/vercel-action@v19\n        if: github.event_name == 'push'\n        with:\n          vercel-token: ${{ secrets.VERCEL_TOKEN }}\n          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}\n          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}\n          vercel-args: ${{ steps.prod_or_not.outputs.vercel-args }}\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n"
  },
  {
    "path": ".gitignore",
    "content": "/target\n**/*.rs.bk\nfixtures/\n.DS_Store\ndemo/static/worker/\ndemo/static/main.js\ndemo/static/main.wasm\n.vercel\n"
  },
  {
    "path": "Cargo.toml",
    "content": "[package]\nname = \"naskah\"\nversion = \"0.1.0\"\nauthors = [\"Fatih Kalifa <fatihkalifa@gmail.com>\"]\nrepository = \"https://github.com/pveyes/naskah\"\ndescription = \"Bahasa pemrograman dengan sintaks Bahasa Indonesia\"\nlicense = \"MIT\"\n\n[workspace]\n\nmembers = [\n  \"demo\",\n  \"parser\",\n  \"printer\"\n]\n\n[dependencies]\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 Fatih Kalifa\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."
  },
  {
    "path": "README.md",
    "content": "# naskah [![Actions Status](https://github.com/pveyes/naskah/workflows/build/badge.svg)](https://github.com/pveyes/naskah/actions)\n\n> Bahasa pemrograman dengan sintaks Bahasa Indonesia\n\nDemo: https://naskah.vercel.app/\n\n## Tipe data\n\nSaat ini hanya 4 tipe data yang didukung oleh naskah:\n\n- angka `123`\n- huruf `\"hello\"`\n- boolean `benar` / `salah`\n- kosong `kosong`\n\n## Operator\n\nOperasi yang didukung oleh `naskah` adalah:\n\n- Penjumlahan `+`\n- Pengurangan `-`\n- Perkalian `*`\n- Pembagian `\\`\n- Sisa pembagian `%`\n- Pangkat `^`\n\nSelain itu ada juga operasi untuk membandingkan dua variabel / tipe data\n\n- Sama dengan `==`\n- Tidak sama dengan `!=`\n- Lebih dari `>`\n- Kurang dari `<`\n\n## Sintaks\n\n### Deklarasi variabel\n\n```\nmisal x = 4;\nmisal y = x;\n```\n\n### Percabangan\n\n```\njika x == 2 {\n\n}\n\njika x == kosong {\n\n}\n```\n\nUntuk kasus-kasus umum, naskah menyediakan sintaks khusus untuk pengecekan terhadap `kosong`, `benar` dan `salah`. Tidak perlu menulis operator `==`, cukup `x kosong`.\n\n```\njika x kosong {\n\n}\n```\n\n### Perulangan\n\nNaskah saat ini hanya mempunyai 1 tipe perulangan yang tidak pernah berhenti\n\n```\nulang {\n\n}\n```\n\nUntuk berhenti di dalam perulangan, dapat menggunakan sintaks `berhenti;`\n\n```\nulang {\n  jika x > 2 {\n    berhenti;\n  }\n}\n```\n\n## Lisensi\n\nBahasa pemrograman Naskah terlisensi dibawah lisensi MIT.\n"
  },
  {
    "path": "demo/Cargo.toml",
    "content": "[package]\nname = \"naskah-demo\"\nversion = \"0.1.0\"\nauthors = [\"Fatih Kalifa <fatihkalifa@gmail.com>\"]\n\n[lib]\ncrate-type = [\"cdylib\", \"rlib\"]\n\n[dependencies]\nserde = \"^1.0.80\"\nserde_derive = \"^1.0.80\"\nyew =  \"0.17\"\nweb-sys = { version = \"0.3.45\", features = ['Window', 'Document', 'Element'] }\nwasm-bindgen = \"0.2.67\"\nprinter = { path = \"../printer\" }"
  },
  {
    "path": "demo/README.md",
    "content": "# naskah-demo\n\nThis demo is built in Rust using [Yew](https://github.com/DenisKolodin/yew) framework.\n\n## Overview\nIt consists of two entry point inside `src/bin/`\n\n - `main.rs` App entry point, component initialization and initial render\n - `worker.rs` Compiler run inside web worker\n\nMost of the time, we only need to watch & rebuild `main.rs`. But for any worker changes, we need to rebuild manually (see setup below).\n\n## Setup\n\nRun this inside `demo` directory\n\n```sh\ncargo install cargo-web\n# build worker script\ncargo web build --bin worker --target=wasm32-unknown-unknown --release\n# run app & watch for changes\ncargo web start --bin main --target=wasm32-unknown-unknown --release\n# visit [::1]:8000\nopen http://localhost:8000\n```\n"
  },
  {
    "path": "demo/src/lib.rs",
    "content": "extern crate printer;\nextern crate wasm_bindgen;\nextern crate web_sys;\nextern crate yew;\n\nuse wasm_bindgen::prelude::*;\nuse yew::prelude::*;\n\nuse printer::to_js;\n\nstruct Model {\n    link: ComponentLink<Self>,\n    code: String,\n    transpiled: String,\n}\n\nenum Msg {\n    ChangeCode(String),\n}\n\nconst EXAMPLE_CODE: &str = \"misal x = 2 + 2;\nmisal y = x > 2;\njika y benar {\n  x = x + 1;\n  menang();\n}\n\";\n\nimpl Component for Model {\n    type Message = Msg;\n    type Properties = ();\n    fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {\n        Self {\n            link,\n            code: EXAMPLE_CODE.into(),\n            transpiled: to_js(EXAMPLE_CODE.into()),\n        }\n    }\n\n    fn update(&mut self, msg: Self::Message) -> ShouldRender {\n        match msg {\n            Msg::ChangeCode(v) => {\n                self.transpiled = to_js(v.clone().into());\n                self.code = v;\n            }\n        }\n        true\n    }\n\n    fn change(&mut self, _props: Self::Properties) -> ShouldRender {\n        // Should only return \"true\" if new properties are different to\n        // previously received properties.\n        // This component has no properties so we will always return \"false\".\n        false\n    }\n\n    fn view(&self) -> Html {\n        html! {\n            <>\n                <div>\n                    <label for=\"input\",>{\"Masukan\"}</label>\n                    <textarea value={&self.code} oninput={self.link.callback(|e: InputData| Msg::ChangeCode(e.value))} />\n                </div>\n                <div>\n                    <label>{\"Keluaran (JavaScript):\"}</label>\n                    <pre id=\"js\",>{&self.transpiled}</pre>\n                </div>\n            </>\n        }\n    }\n}\n\n#[wasm_bindgen(start)]\npub fn run_app() {\n    let win = web_sys::window().unwrap();\n    let doc = win.document().unwrap();\n    if let Some(element) = doc.query_selector(\"#playground\").expect(\"No matching id\") {\n        App::<Model>::new().mount(element);\n    }\n}\n"
  },
  {
    "path": "demo/static/index.html",
    "content": "<html>\n  <head>\n    <title>Naskah - Bahasa pemrogramman dengan sintaks Bahasa Indonesia</title>\n    <link rel=\"stylesheet\" type=\"text/css\" href=\"./style.css\" />\n  </head>\n\n  <body>\n    <main>\n      <div class=\"container\">\n        <header class=\"hero\">\n          <h1 class=\"naskah\">Naskah</h1>\n          <span\n            ><span class=\"naskah\">Naskah</span> adalah bahasa pemrogramman\n            dengan sintaks Bahasa Indonesia</span\n          >\n        </header>\n        <center>\n          <a\n            href=\"https://github.com/pveyes/naskah\"\n            target=\"_blank\"\n            class=\"github-corner\"\n            aria-label=\"View source on Github\"\n            ><svg\n              width=\"80\"\n              height=\"80\"\n              viewBox=\"0 0 250 250\"\n              style=\"\n                fill: #333;\n                color: #fff;\n                position: absolute;\n                top: 0;\n                border: 0;\n                right: 0;\n              \"\n              aria-hidden=\"true\"\n            >\n              <path\n                d=\"M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z\"\n              ></path>\n              <path\n                d=\"M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2\"\n                fill=\"currentColor\"\n                style=\"transform-origin: 130px 106px\"\n                class=\"octo-arm\"\n              ></path>\n              <path\n                d=\"M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z\"\n                fill=\"currentColor\"\n                class=\"octo-body\"\n              ></path></svg\n          ></a>\n        </center>\n        <div class=\"demo\">\n          <h2>Demo</h2>\n          <span\n            >Saat ini <span class=\"naskah\">Naskah</span> masih dalam\n            pengembangan dan hanya bisa menghasilkan sintaks\n            <a href=\"https://developer.mozilla.org/bm/docs/Web/JavaScript\"\n              >JavaScript</a\n            >.</span\n          >\n          <div id=\"playground\" class=\"demo-playground\"></div>\n        </div>\n      </div>\n    </main>\n    <script type=\"module\">\n      import init from \"./assets/wasm.js\";\n      init();\n    </script>\n  </body>\n</html>\n"
  },
  {
    "path": "demo/static/style.css",
    "content": "html, body {\n  margin: 0;\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;\n}\n\nbody {\n  background-color: #fff1ec;\n}\n\nmain {\n  height: 100vh;\n}\n\n.container {\n  width: 700px;\n  margin: 0 auto;\n}\n\nheader {\n  text-align: center;\n}\n\nheader h1 {\n  font-size: 45px;\n  margin: 0;\n  padding: 13px 0;\n}\n\n.hero {\n  margin-bottom: 50px;\n}\n\n.naskah {\n  font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;\n  font-weight: 600;\n}\n\n.demo {\n  text-align: center;\n}\n\n.demo-playground {\n  display: flex;\n  justify-content: space-around;\n  margin-top: 20px;\n  text-align: initial;\n}\n\n.demo-playground > div {\n  position: relative;\n}\n\n.demo-playground label {\n  display: block;\n  margin-bottom: 20px;\n}\n\ntextarea {\n  border: none;\n  outline: none;\n  display: block;\n  width: 100%;\n}\n\ntextarea, pre {\n  margin: 0;\n  font-family: 'Courier New', Courier, monospace;\n  font-size: 14px;\n  border: 1px solid #ddd;\n  background-color: #fff;\n  width: 300px;\n  min-height: 300px;\n  padding: 5px 10px;\n  border-radius: 3px;\n  box-sizing: border-box;\n}\n\n@media (max-width: 768px) {\n  .github-corner {\n    display: none;\n  }\n}\n\n.github-corner:hover .octo-arm {\n  animation: octocat-wave 560ms ease-in-out\n}\n\n@keyframes octocat-wave {\n  0%, 100% { transform: rotate(0) }\n  20%, 60% { transform: rotate(-25deg) }\n  40%, 80% { transform: rotate(10deg) }\n}\n"
  },
  {
    "path": "parser/Cargo.toml",
    "content": "[package]\nname = \"parser\"\nversion = \"0.1.0\"\nauthors = [\"Fatih Kalifa <fatihkalifa@gmail.com>\"]\n\n[dependencies]\nlazy_static = \"1.1.0\"\nnom = { version = \"^4.1.0\", features = [\"regexp\", \"regexp_macros\"] }\nregex = \"1.0.5\""
  },
  {
    "path": "parser/src/ast.rs",
    "content": "#[derive(PartialEq, Debug)]\npub enum Literal {\n    Number(i64),\n    Null,\n    String(String),\n    Boolean(bool),\n}\n\n#[derive(PartialEq, Debug)]\npub struct Identifier {\n    pub name: String,\n}\n\n#[derive(PartialEq, Debug)]\npub struct CallExpression {\n    pub callee: Identifier,\n    pub arguments: Vec<Expression>,\n}\n\n#[derive(PartialEq, Debug)]\npub struct AssignmentExpression {\n    pub id: Identifier,\n    pub value: Box<Expression>,\n}\n\n#[derive(PartialEq, Debug)]\npub enum Expression {\n    Identifier(Identifier),\n    Literal(Literal),\n    BinaryExpression(Box<BinaryExpression>),\n    CallExpression(CallExpression),\n    Assignment(AssignmentExpression),\n}\n\n#[derive(PartialEq, Debug)]\npub enum Operator {\n    Addition,\n    Substraction,\n    Multiplication,\n    Division,\n    Remainder,\n    Exponentiation,\n    Equal,\n    NotEqual,\n    GreaterThan,\n    LessThan,\n    GreaterThanOrEqualTo,\n    LessThanOrEqualTo,\n}\n\n#[derive(PartialEq, Debug)]\npub struct BinaryExpression {\n    pub left: Expression,\n    pub right: Expression,\n    pub operator: Operator,\n}\n\n#[derive(PartialEq, Debug)]\npub struct VariableDeclaration {\n    pub id: Identifier,\n    pub value: Expression,\n}\n\n#[derive(PartialEq, Debug)]\npub enum AlternateStatement {\n    IfStatement(Box<IfStatement>),\n    BlockStatement(BlockStatement),\n}\n\n#[derive(PartialEq, Debug)]\npub struct IfStatement {\n    pub test: Expression,\n    pub consequent: BlockStatement,\n    pub alternate: Option<AlternateStatement>,\n}\n\n#[derive(PartialEq, Debug)]\npub struct BlockStatement {\n    pub body: Option<Vec<Statement>>,\n}\n\n#[derive(PartialEq, Debug)]\npub enum Statement {\n    Break,\n    Continue,\n    Expression(Expression),\n    VariableDeclaration(VariableDeclaration),\n    BlockStatement(BlockStatement),\n    Loop(BlockStatement),\n    IfStatement(IfStatement),\n}\n\n#[derive(PartialEq, Debug)]\npub struct Program {\n    pub body: Vec<Statement>,\n}\n"
  },
  {
    "path": "parser/src/expr.rs",
    "content": "use super::ast::*;\nuse super::identifier::identifier;\nuse super::literal::literal;\n\nnamed!(\n    assignment_expression<Expression>,\n    do_parse!(\n        i: identifier\n            >> ws!(tag!(\"=\"))\n            >> e: expression\n            >> (Expression::Assignment(AssignmentExpression {\n                id: i,\n                value: Box::new(e)\n            }))\n    )\n);\n\nnamed!(\n    fn_arguments<Vec<Expression>>,\n    // this is buggy because we can do fn(a b)\n    // TODO fix this\n    fold_many0!(\n        do_parse!(e: expression >> opt!(tag!(\",\")) >> (e)),\n        Vec::new(),\n        |mut acc: Vec<Expression>, item| {\n            acc.push(item);\n            acc\n        }\n    )\n);\n\nnamed!(\n    call_expression<Expression>,\n    do_parse!(\n        c: identifier\n            >> tag!(\"(\")\n            >> args: fn_arguments\n            >> tag!(\")\")\n            >> (Expression::CallExpression(CallExpression {\n                callee: c,\n                arguments: args\n            }))\n    )\n);\n\nnamed!(\n    simple_expression<Expression>,\n    alt_complete!(\n        map!(literal, |l| Expression::Literal(l)) | map!(identifier, |i| Expression::Identifier(i))\n    )\n);\n\nnamed!(\n  pub expression<Expression>,\n  alt_complete!(\n      assignment_expression |\n      call_expression |\n      binary_expression |\n      simple_expression\n  )\n);\n\nnamed!(\n    operator<Operator>,\n    // we use alt_complete here just to be safe\n    // because operator can contains different length\n    alt_complete!(\n        map!(tag!(\"+\"), |_| Operator::Addition)\n            | map!(tag!(\"-\"), |_| Operator::Substraction)\n            | map!(tag!(\"*\"), |_| Operator::Multiplication)\n            | map!(tag!(\"/\"), |_| Operator::Division)\n            | map!(tag!(\"%\"), |_| Operator::Remainder)\n            | map!(tag!(\"^\"), |_| Operator::Exponentiation)\n            | map!(tag!(\"==\"), |_| Operator::Equal)\n            | map!(tag!(\"!=\"), |_| Operator::NotEqual)\n            | map!(tag!(\">=\"), |_| Operator::GreaterThanOrEqualTo)\n            | map!(tag!(\"<=\"), |_| Operator::LessThanOrEqualTo)\n            | map!(tag!(\">\"), |_| Operator::GreaterThan)\n            | map!(tag!(\"<\"), |_| Operator::LessThan)\n    )\n);\n\nnamed!(\n    pub binary_expression<Expression>,\n    do_parse!(\n        first: simple_expression\n            >> fold: fold_many0!(\n                do_parse!(op: ws!(operator) >> expr: simple_expression >> (op, expr)),\n                first,\n                |left: Expression, (operator, right): (Operator, Expression)| {\n                    Expression::BinaryExpression(Box::new(BinaryExpression {\n                        left,\n                        right,\n                        operator,\n                    }))\n                }\n            )\n            >> (fold)\n    )\n);\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    #[test]\n    fn op() {\n        assert_eq!(operator(&b\"+\"[..]), Ok((&b\"\"[..], Operator::Addition)));\n        assert_eq!(operator(&b\"-\"[..]), Ok((&b\"\"[..], Operator::Substraction)));\n        assert_eq!(\n            operator(&b\"*\"[..]),\n            Ok((&b\"\"[..], Operator::Multiplication))\n        );\n        assert_eq!(operator(&b\"/\"[..]), Ok((&b\"\"[..], Operator::Division)));\n        assert_eq!(operator(&b\"%\"[..]), Ok((&b\"\"[..], Operator::Remainder)));\n        assert_eq!(\n            operator(&b\"^\"[..]),\n            Ok((&b\"\"[..], Operator::Exponentiation))\n        );\n        assert_eq!(operator(&b\"==\"[..]), Ok((&b\"\"[..], Operator::Equal)));\n        assert_eq!(operator(&b\"!=\"[..]), Ok((&b\"\"[..], Operator::NotEqual)));\n        assert_eq!(operator(&b\">\"[..]), Ok((&b\"\"[..], Operator::GreaterThan)));\n        assert_eq!(operator(&b\"<\"[..]), Ok((&b\"\"[..], Operator::LessThan)));\n        assert_eq!(operator(&b\">=\"[..]), Ok((&b\"\"[..], Operator::GreaterThanOrEqualTo)));\n        assert_eq!(operator(&b\"<=\"[..]), Ok((&b\"\"[..], Operator::LessThanOrEqualTo)));\n    }\n\n    #[test]\n\n    fn binary_expression_literals() {\n        assert_eq!(\n            expression(&b\"\\\"kosong\\\" != kosong;\"[..]),\n            Ok((\n                &b\";\"[..],\n                Expression::BinaryExpression(Box::new(BinaryExpression {\n                    left: Expression::Literal(Literal::String(String::from(\"kosong\"))),\n                    right: Expression::Literal(Literal::Null),\n                    operator: Operator::NotEqual\n                }))\n            ))\n        )\n    }\n\n    #[test]\n    fn binary_expression_as_expression() {\n        assert_eq!(\n            expression(&b\"x > 5;\"[..]),\n            Ok((\n                &b\";\"[..],\n                Expression::BinaryExpression(Box::new(BinaryExpression {\n                    left: Expression::Identifier(Identifier {\n                        name: String::from(\"x\")\n                    }),\n                    right: Expression::Literal(Literal::Number(5)),\n                    operator: Operator::GreaterThan\n                }))\n            ))\n        )\n    }\n\n    #[test]\n    fn recursive_binary_expression() {\n        assert_eq!(\n            expression(&b\"1 > 2 + 3;\"[..]),\n            Ok((\n                &b\";\"[..],\n                Expression::BinaryExpression(Box::new(BinaryExpression {\n                    left: Expression::BinaryExpression(Box::new(BinaryExpression {\n                        left: Expression::Literal(Literal::Number(1)),\n                        right: Expression::Literal(Literal::Number(2)),\n                        operator: Operator::GreaterThan,\n                    })),\n                    right: Expression::Literal(Literal::Number(3)),\n                    operator: Operator::Addition,\n                }))\n            ))\n        );\n    }\n\n    #[test]\n    fn basic_call_expression() {\n        assert_eq!(\n            expression(&b\"hello()\"[..]),\n            Ok((\n                &b\"\"[..],\n                Expression::CallExpression(CallExpression {\n                    callee: Identifier {\n                        name: String::from(\"hello\")\n                    },\n                    arguments: vec![]\n                })\n            ))\n        );\n    }\n\n    #[test]\n    fn fn_with_arguments() {\n        assert_eq!(\n            expression(&b\"tulis(\\\"hello, world!\\\")\"[..]),\n            Ok((\n                &b\"\"[..],\n                Expression::CallExpression(CallExpression {\n                    callee: Identifier {\n                        name: String::from(\"tulis\")\n                    },\n                    arguments: vec![Expression::Literal(Literal::String(String::from(\n                        \"hello, world!\"\n                    )))]\n                })\n            ))\n        );\n    }\n}\n"
  },
  {
    "path": "parser/src/identifier.rs",
    "content": "use super::ast::Identifier;\nuse std::str;\n\nnamed!(\n  pub identifier<Identifier>,\n  map!(\n    map_res!(re_bytes_find!(r\"^[a-zA-Z_]\\w*\"), str::from_utf8),\n    |name| Identifier {\n      name: String::from(name)\n    }\n  )\n);\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    #[test]\n    fn alphanumeric() {\n        assert_eq!(\n            identifier(&b\"x;\"[..]),\n            Ok((\n                &b\";\"[..],\n                Identifier {\n                    name: String::from(\"x\")\n                }\n            ))\n        );\n\n        assert_eq!(\n            identifier(&b\"y2\"[..]),\n            Ok((\n                &b\"\"[..],\n                Identifier {\n                    name: String::from(\"y2\")\n                }\n            ))\n        );\n\n        assert_eq!(\n            identifier(&b\"x_y_2\"[..]),\n            Ok((\n                &b\"\"[..],\n                Identifier {\n                    name: String::from(\"x_y_2\")\n                }\n            ))\n        );\n    }\n\n    #[test]\n    fn can_start_with_underscore() {\n        assert_eq!(\n            identifier(&b\"__x\"[..]),\n            Ok((\n                &b\"\"[..],\n                Identifier {\n                    name: String::from(\"__x\")\n                }\n            ))\n        );\n    }\n\n    #[test]\n    fn cannot_start_with_number() {\n        assert_ne!(\n            identifier(&b\"2x\"[..]),\n            Ok((\n                &b\"\"[..],\n                Identifier {\n                    name: String::from(\"2x\")\n                }\n            ))\n        );\n    }\n}\n"
  },
  {
    "path": "parser/src/lib.rs",
    "content": "#[macro_use]\nextern crate nom;\nextern crate regex;\n\npub mod ast;\nmod expr;\nmod identifier;\nmod literal;\nmod number;\nmod statement;\nmod variable;\n\nuse self::ast::*;\nuse self::statement::parse_statement;\n\nnamed!(\n  pub program<Program>,\n  map!(\n    many0!(\n      do_parse!(s: parse_statement >> tag!(\"\\n\") >> (s))\n    ),\n    |body| Program { body }\n  )\n);\n\npub fn parse(input: &str) -> Result<Program, nom::Err<&[u8]>> {\n  let (_res, p) = program(input.as_bytes())?;\n  Ok(p)\n}\n"
  },
  {
    "path": "parser/src/literal.rs",
    "content": "use super::ast::Literal;\nuse super::number::number_literal;\nuse std::str;\n\nnamed!(\n    boolean<bool>,\n    alt!(map!(tag!(\"benar\"), |_| true) | map!(tag!(\"salah\"), |_| false))\n);\n\nnamed!(\n  pub boolean_literal<Literal>,\n  map!(boolean, |b| Literal::Boolean(b))\n);\n\nnamed!(pub null_literal<Literal>, map!(tag!(\"kosong\"), |_| Literal::Null));\n\nnamed!(\n    string_literal<Literal>,\n    map!(\n        delimited!(\n            char!('\"'),\n            map_res!(is_not!(\"\\\"\"), str::from_utf8),\n            char!('\"')\n        ),\n        |s| Literal::String(String::from(s))\n    )\n);\n\nnamed!(\n  pub literal<Literal>,\n  alt_complete!(null_literal | boolean_literal | number_literal | string_literal)\n);\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    #[test]\n    fn boolean() {\n        assert_eq!(\n            literal(&b\"benar\"[..]),\n            Ok((&b\"\"[..], Literal::Boolean(true)))\n        );\n\n        assert_eq!(\n            literal(&b\"salah\"[..]),\n            Ok((&b\"\"[..], Literal::Boolean(false)))\n        );\n    }\n\n    #[test]\n    fn string() {\n        assert_eq!(\n            literal(&b\"\\\"p23u08rfwi\\\"\"[..]),\n            Ok((&b\"\"[..], Literal::String(String::from(\"p23u08rfwi\"))))\n        );\n    }\n\n    #[test]\n    fn number() {\n        assert_eq!(literal(&b\"2 \"[..]), Ok((&b\" \"[..], Literal::Number(2))));\n    }\n\n    #[test]\n    fn null() {\n        assert_eq!(literal(&b\"kosong\"[..]), Ok((&b\"\"[..], Literal::Null)));\n    }\n}\n"
  },
  {
    "path": "parser/src/number.rs",
    "content": "use super::ast::Literal;\nuse nom::{is_digit, is_hex_digit};\nuse std::i64;\nuse std::str;\n\nnamed!(sign, recognize!(opt!(one_of!(\"+-\"))));\n\n#[allow(dead_code)]\nfn is_bin_digit(chr: u8) -> bool {\n    chr == b'0' || chr == b'1'\n}\n\nnamed!(dec_digits, take_while!(is_digit));\nnamed!(hex_digits, take_while!(is_hex_digit));\nnamed!(bin_digits, take_while1!(is_bin_digit));\n\nnamed!(bin_literal, recognize!(do_parse!(sign >> bin_digits >> ())));\n\nnamed!(\n    decimal_literal,\n    recognize!(do_parse!(sign >> dec_digits >> ()))\n);\n\nnamed!(hex_literal, recognize!(do_parse!(sign >> hex_digits >> ())));\n\nnamed!(\n    binary<i64>,\n    map_res!(map_res!(bin_literal, str::from_utf8), |s| {\n        i64::from_str_radix(s, 2)\n    })\n);\n\nnamed!(\n    decimal<i64>,\n    map_res!(map_res!(decimal_literal, str::from_utf8), |s| {\n        i64::from_str_radix(s, 10)\n    })\n);\n\nnamed!(\n    hexadecimal<i64>,\n    map_res!(map_res!(hex_literal, str::from_utf8), |s| {\n        i64::from_str_radix(s, 16)\n    })\n);\n\nnamed!(\n    integer<i64>,\n    alt_complete!(\n        preceded!(tag!(\"0b\"), binary)\n            | preceded!(tag!(\"0x\"), hexadecimal)\n            | preceded!(opt!(tag!(\"0d\")), decimal)\n    )\n);\n\nnamed!(\n  pub number_literal<Literal>,\n  map!(integer, |d| Literal::Number(d))\n);\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    #[test]\n    fn test_bin_literal() {\n        assert_eq!(bin_literal(&b\"0 \"[..]), Ok((&b\" \"[..], &b\"0\"[..])));\n        assert_eq!(bin_literal(&b\"1 \"[..]), Ok((&b\" \"[..], &b\"1\"[..])));\n        assert_eq!(bin_literal(&b\"10 \"[..]), Ok((&b\" \"[..], &b\"10\"[..])));\n    }\n\n    #[test]\n    fn test_decimal_literal() {\n        assert_eq!(decimal_literal(&b\"0 \"[..]), Ok((&b\" \"[..], &b\"0\"[..])));\n        assert_eq!(decimal_literal(&b\"10 \"[..]), Ok((&b\" \"[..], &b\"10\"[..])));\n    }\n\n    #[test]\n    fn test_hex_literal() {\n        assert_eq!(hex_literal(&b\"0 \"[..]), Ok((&b\" \"[..], &b\"0\"[..])));\n        assert_eq!(hex_literal(&b\"f \"[..]), Ok((&b\" \"[..], &b\"f\"[..])));\n        assert_eq!(hex_literal(&b\"10 \"[..]), Ok((&b\" \"[..], &b\"10\"[..])));\n    }\n\n    #[test]\n    fn test_binary() {\n        assert_eq!(binary(&b\"0 \"[..]), Ok((&b\" \"[..], 0)));\n        assert_eq!(binary(&b\"1 \"[..]), Ok((&b\" \"[..], 1)));\n        assert_eq!(binary(&b\"10 \"[..]), Ok((&b\" \"[..], 2)));\n    }\n\n    #[test]\n    fn test_decimal() {\n        assert_eq!(decimal(&b\"0 \"[..]), Ok((&b\" \"[..], 0)));\n        assert_eq!(decimal(&b\"10 \"[..]), Ok((&b\" \"[..], 10)));\n    }\n\n    #[test]\n    fn test_hexadecimal() {\n        assert_eq!(hexadecimal(&b\"0 \"[..]), Ok((&b\" \"[..], 0)));\n        assert_eq!(hexadecimal(&b\"f \"[..]), Ok((&b\" \"[..], 15)));\n        assert_eq!(hexadecimal(&b\"10 \"[..]), Ok((&b\" \"[..], 16)));\n    }\n\n    #[test]\n    fn test_integer() {\n        assert_eq!(integer(&b\"0 \"[..]), Ok((&b\" \"[..], 0)));\n        assert_eq!(integer(&b\"100 \"[..]), Ok((&b\" \"[..], 100)));\n        assert_eq!(integer(&b\"0xf \"[..]), Ok((&b\" \"[..], 15)));\n        assert_eq!(integer(&b\"0b10 \"[..]), Ok((&b\" \"[..], 2)));\n        assert_eq!(integer(&b\"0d10 \"[..]), Ok((&b\" \"[..], 10)));\n    }\n\n    #[test]\n    fn test_number_literal() {\n        assert_eq!(\n            number_literal(&b\"2 \"[..]),\n            Ok((&b\" \"[..], Literal::Number(2)))\n        )\n    }\n}\n"
  },
  {
    "path": "parser/src/statement.rs",
    "content": "use super::ast::*;\nuse super::expr::{binary_expression, expression};\nuse super::identifier::identifier;\nuse super::literal::{boolean_literal, null_literal};\nuse super::variable::variable_declaration;\n\nnamed!(\n    break_statement<Statement>,\n    map!(tag!(\"berhenti;\"), |_| Statement::Break)\n);\n\nnamed!(\n    continue_statement<Statement>,\n    map!(tag!(\"lanjut;\"), |_| Statement::Continue)\n);\n\nnamed!(\n    expression_statement<Statement>,\n    map!(do_parse!(e: expression >> tag!(\";\") >> (e)), |e| {\n        Statement::Expression(e)\n    })\n);\n\nnamed!(\n    pub parse_statement<Statement>,\n    alt_complete!(\n            loop_statement\n            // TODO unsyntactic break/continue\n            | break_statement\n            | continue_statement\n            | map!(if_statement, |b| Statement::IfStatement(b))\n            | map!(block_statement, |b| Statement::BlockStatement(b))\n            | map!(variable_declaration, |v| Statement::VariableDeclaration(v))\n            | expression_statement\n    )\n);\n\nnamed!(\n    block_statement<BlockStatement>,\n    map!(\n        // block statement can contains nothing, that's why we need to\n        // exclude any whitespace\n        delimited!(\n            tag!(\"{\"),\n            ws!(opt!(many1!(ws!(parse_statement)))),\n            tag!(\"}\")\n        ),\n        |body| BlockStatement { body }\n    )\n);\n\nnamed!(\n    loop_statement<Statement>,\n    map!(\n        // either ulang{} or ulang { }\n        preceded!(ws!(tag!(\"ulang\")), block_statement),\n        Statement::Loop\n    )\n);\n\nnamed!(\n    special_if_condition<Expression>,\n    do_parse!(\n        left: identifier\n            >> tag!(\" \")\n            >> right: alt_complete!(null_literal | boolean_literal)\n            >> (Expression::BinaryExpression(Box::new(BinaryExpression {\n                left: Expression::Identifier(left),\n                right: Expression::Literal(right),\n                operator: Operator::Equal\n            })))\n    )\n);\n\nnamed!(\n    else_statement<Option<AlternateStatement>>,\n    opt!(preceded!(\n        tag!(\" atau \"),\n        alt_complete!(\n            map!(if_statement, |s| AlternateStatement::IfStatement(Box::new(\n                s\n            ))) | map!(block_statement, |s| AlternateStatement::BlockStatement(s))\n        )\n    ))\n);\n\nnamed!(\n    single_if_statement<IfStatement>,\n    preceded!(\n        tag!(\"jika \"),\n        do_parse!(\n            expr: alt_complete!(special_if_condition | binary_expression)\n                >> tag!(\" \")\n                >> st: block_statement\n                >> (IfStatement {\n                    test: expr,\n                    consequent: st,\n                    alternate: None,\n                })\n        )\n    )\n);\n\nnamed!(\n    if_else_statement<IfStatement>,\n    preceded!(\n        tag!(\"jika \"),\n        do_parse!(\n            expr: alt_complete!(special_if_condition | binary_expression)\n                >> tag!(\" \")\n                >> st: block_statement\n                >> els: else_statement\n                >> (IfStatement {\n                    test: expr,\n                    consequent: st,\n                    alternate: els,\n                })\n        )\n    )\n);\n\nnamed!(\n    if_statement<IfStatement>,\n    // the reason we put if_else_statement first is because the\n    // similarities of both syntax, considering single_if_statement will\n    // also parse if_else_statement (albeit returning Incomplete), we\n    // have to prioritize if_else and only parse single_if_statement\n    // if the first parser failed. maybe not the most performant, but it works\n    alt_complete!(if_else_statement | single_if_statement)\n);\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    #[test]\n    fn empty_block_statement() {\n        assert_eq!(\n            parse_statement(&b\"{\\n}\"[..]),\n            Ok((\n                &b\"\"[..],\n                Statement::BlockStatement(BlockStatement { body: None })\n            ))\n        );\n    }\n\n    #[test]\n    fn var_decl_inside_block() {\n        assert_eq!(\n            parse_statement(&b\"{\\nmisal x = 5;\\n}\"[..]),\n            Ok((\n                &b\"\"[..],\n                Statement::BlockStatement(BlockStatement {\n                    body: Some(vec![Statement::VariableDeclaration(VariableDeclaration {\n                        id: Identifier {\n                            name: String::from(\"x\")\n                        },\n                        value: Expression::Literal(Literal::Number(5))\n                    })])\n                })\n            ))\n        );\n    }\n\n    #[test]\n    fn recursive_empty_block_statement() {\n        assert_eq!(\n            parse_statement(&b\"{\\n{\\n}\\n}\"[..]),\n            Ok((\n                &b\"\"[..],\n                Statement::BlockStatement(BlockStatement {\n                    body: Some(vec![Statement::BlockStatement(BlockStatement {\n                        body: None\n                    })])\n                })\n            ))\n        );\n    }\n\n    #[test]\n    fn test_loop_statement() {\n        assert_eq!(\n            parse_statement(&b\"ulang {\\nberhenti;\\nlanjut;\\n}\"[..]),\n            Ok((\n                &b\"\"[..],\n                Statement::Loop(BlockStatement {\n                    body: Some(vec![Statement::Break, Statement::Continue])\n                })\n            ))\n        );\n    }\n\n    #[test]\n    fn test_if_statement() {\n        assert_eq!(\n            parse_statement(&b\"jika a == salah {\\nmisal z = benar;\\n}\"[..]),\n            Ok((\n                &b\"\"[..],\n                Statement::IfStatement(IfStatement {\n                    test: Expression::BinaryExpression(Box::new(BinaryExpression {\n                        left: Expression::Identifier(Identifier {\n                            name: String::from(\"a\")\n                        }),\n                        right: Expression::Literal(Literal::Boolean(false)),\n                        operator: Operator::Equal\n                    })),\n                    consequent: BlockStatement {\n                        body: Some(vec![Statement::VariableDeclaration(VariableDeclaration {\n                            id: Identifier {\n                                name: String::from(\"z\")\n                            },\n                            value: Expression::Literal(Literal::Boolean(true))\n                        })])\n                    },\n                    alternate: None\n                })\n            ))\n        );\n    }\n\n    #[test]\n    fn test_if_else_statement() {\n        assert_eq!(\n            parse_statement(&b\"jika a == salah {\\n} atau {\\n}\"[..]),\n            Ok((\n                &b\"\"[..],\n                Statement::IfStatement(IfStatement {\n                    test: Expression::BinaryExpression(Box::new(BinaryExpression {\n                        left: Expression::Identifier(Identifier {\n                            name: String::from(\"a\")\n                        }),\n                        right: Expression::Literal(Literal::Boolean(false)),\n                        operator: Operator::Equal\n                    })),\n                    consequent: BlockStatement { body: None },\n                    alternate: Some(AlternateStatement::BlockStatement(BlockStatement {\n                        body: None\n                    }))\n                })\n            ))\n        );\n    }\n\n    #[test]\n    fn if_special() {\n        assert_eq!(\n            parse_statement(&b\"jika a benar {\\n}\"[..]),\n            parse_statement(&b\"jika a == benar {\\n}\"[..])\n        );\n\n        assert_eq!(\n            parse_statement(&b\"jika a salah {\\n} atau {\\n}\"[..]),\n            parse_statement(&b\"jika a == salah {\\n} atau {\\n}\"[..]),\n        );\n\n        assert_eq!(\n            parse_statement(&b\"jika a kosong {\\n}\"[..]),\n            parse_statement(&b\"jika a == kosong {\\n}\"[..])\n        );\n    }\n\n    #[test]\n    fn recursive_if_else() {\n        assert_eq!(\n            parse_statement(&b\"jika a benar {\\n} atau jika b kosong {\\n}\"[..]),\n            Ok((\n                &b\"\"[..],\n                Statement::IfStatement(IfStatement {\n                    test: Expression::BinaryExpression(Box::new(BinaryExpression {\n                        left: Expression::Identifier(Identifier {\n                            name: String::from(\"a\")\n                        }),\n                        right: Expression::Literal(Literal::Boolean(true)),\n                        operator: Operator::Equal\n                    })),\n                    consequent: BlockStatement { body: None },\n                    alternate: Some(AlternateStatement::IfStatement(Box::new(IfStatement {\n                        test: Expression::BinaryExpression(Box::new(BinaryExpression {\n                            left: Expression::Identifier(Identifier {\n                                name: String::from(\"b\")\n                            }),\n                            right: Expression::Literal(Literal::Null),\n                            operator: Operator::Equal\n                        })),\n                        consequent: BlockStatement { body: None },\n                        alternate: None\n                    })))\n                })\n            ))\n        );\n\n        assert_eq!(\n            parse_statement(&b\"jika c == 2 {\\n} atau jika d benar {\\n} atau {\\n}\"[..]),\n            Ok((\n                &b\"\"[..],\n                Statement::IfStatement(IfStatement {\n                    test: Expression::BinaryExpression(Box::new(BinaryExpression {\n                        left: Expression::Identifier(Identifier {\n                            name: String::from(\"c\")\n                        }),\n                        right: Expression::Literal(Literal::Number(2)),\n                        operator: Operator::Equal\n                    })),\n                    consequent: BlockStatement { body: None },\n                    alternate: Some(AlternateStatement::IfStatement(Box::new(IfStatement {\n                        test: Expression::BinaryExpression(Box::new(BinaryExpression {\n                            left: Expression::Identifier(Identifier {\n                                name: String::from(\"d\")\n                            }),\n                            right: Expression::Literal(Literal::Boolean(true)),\n                            operator: Operator::Equal\n                        })),\n                        consequent: BlockStatement { body: None },\n                        alternate: Some(AlternateStatement::BlockStatement(BlockStatement {\n                            body: None\n                        }))\n                    })))\n                })\n            ))\n        );\n    }\n\n    #[test]\n    fn simple_expression_statement() {\n        assert_eq!(\n            parse_statement(&b\"alert();\"[..]),\n            Ok((\n                &b\"\"[..],\n                Statement::Expression(Expression::CallExpression(CallExpression {\n                    callee: Identifier {\n                        name: String::from(\"alert\")\n                    },\n                    arguments: vec![]\n                }))\n            ))\n        );\n    }\n\n    #[test]\n    fn reassignment() {\n        assert_eq!(\n            parse_statement(&b\"x = x ^ 5;\"[..]),\n            Ok((\n                &b\"\"[..],\n                Statement::Expression(Expression::Assignment(AssignmentExpression {\n                    id: Identifier {\n                        name: String::from(\"x\"),\n                    },\n                    value: Box::new(Expression::BinaryExpression(Box::new(BinaryExpression {\n                        left: Expression::Identifier(Identifier {\n                            name: String::from(\"x\"),\n                        }),\n                        right: Expression::Literal(Literal::Number(5)),\n                        operator: Operator::Exponentiation,\n                    })))\n                }))\n            ))\n        );\n    }\n}\n"
  },
  {
    "path": "parser/src/variable.rs",
    "content": "use super::ast::*;\nuse super::expr::expression;\nuse super::identifier::identifier;\n\nnamed!(\n  pub variable_declaration<VariableDeclaration>,\n  preceded!(tag!(\"misal \"), do_parse!(\n      id: identifier\n      // we can do either x = 2 or x=2\n      // both is fine\n      >> ws!(tag!(\"=\"))\n      >> expr: expression\n      >> tag!(\";\")\n      >> (VariableDeclaration {\n        id: id,\n        value: expr\n      })\n\n  ))\n);\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    #[test]\n    fn boolean_assignment() {\n        assert_eq!(\n            variable_declaration(&b\"misal x = benar;\"[..]),\n            Ok((\n                &b\"\"[..],\n                VariableDeclaration {\n                    id: Identifier {\n                        name: String::from(\"x\")\n                    },\n                    value: Expression::Literal(Literal::Boolean(true)),\n                }\n            ))\n        );\n    }\n\n    #[test]\n    fn string_assignment() {\n        assert_eq!(\n            variable_declaration(&b\"misal x = \\\"str\\\";\"[..]),\n            Ok((\n                &b\"\"[..],\n                VariableDeclaration {\n                    id: Identifier {\n                        name: String::from(\"x\")\n                    },\n                    value: Expression::Literal(Literal::String(String::from(\"str\"))),\n                }\n            ))\n        );\n    }\n\n    #[test]\n    fn number_assignment() {\n        assert_eq!(\n            variable_declaration(&b\"misal x = 5;\"[..]),\n            Ok((\n                &b\"\"[..],\n                VariableDeclaration {\n                    id: Identifier {\n                        name: String::from(\"x\")\n                    },\n                    value: Expression::Literal(Literal::Number(5)),\n                }\n            ))\n        );\n    }\n\n    #[test]\n    fn null_assignment() {\n        assert_eq!(\n            variable_declaration(&b\"misal x = kosong;\"[..]),\n            Ok((\n                &b\"\"[..],\n                VariableDeclaration {\n                    id: Identifier {\n                        name: String::from(\"x\")\n                    },\n                    value: Expression::Literal(Literal::Null),\n                }\n            ))\n        );\n    }\n\n    #[test]\n    fn identifier_assignment() {\n        assert_eq!(\n            variable_declaration(&b\"misal x = a;\"[..]),\n            Ok((\n                &b\"\"[..],\n                VariableDeclaration {\n                    id: Identifier {\n                        name: String::from(\"x\")\n                    },\n                    value: Expression::Identifier(Identifier {\n                        name: String::from(\"a\")\n                    }),\n                }\n            ))\n        );\n    }\n\n    #[test]\n    fn binary_expression_assignment() {\n        assert_eq!(\n            variable_declaration(&b\"misal sum = 2 + 3;\"[..]),\n            Ok((\n                &b\"\"[..],\n                VariableDeclaration {\n                    id: Identifier {\n                        name: String::from(\"sum\")\n                    },\n                    value: Expression::BinaryExpression(Box::new(BinaryExpression {\n                        left: Expression::Literal(Literal::Number(2)),\n                        right: Expression::Literal(Literal::Number(3)),\n                        operator: Operator::Addition,\n                    }))\n                }\n            ))\n        );\n    }\n}\n"
  },
  {
    "path": "printer/Cargo.toml",
    "content": "[package]\nname = \"printer\"\nversion = \"0.1.0\"\nauthors = [\"Fatih Kalifa <fatihkalifa@gmail.com>\"]\n\n[dependencies]\nparser = { path = \"../parser\" }"
  },
  {
    "path": "printer/src/js.rs",
    "content": "use parser::ast::*;\n\nfn insert_indent(depth: u8) -> String {\n    let mut res = String::new();\n    let mut x = 0;\n\n    if depth == 0 {\n        return res;\n    }\n\n    loop {\n        res.push_str(\"  \");\n        x = x + 1;\n        if x >= depth {\n            break;\n        }\n    }\n\n    res\n}\n\nfn print_literal(l: Literal) -> String {\n    match l {\n        Literal::Null => String::from(\"null\"),\n        Literal::Boolean(bool) => match bool {\n            true => String::from(\"true\"),\n            false => String::from(\"false\"),\n        },\n        Literal::Number(n) => n.to_string(),\n        Literal::String(s) => {\n            let mut x = String::new();\n            x.push_str(\"\\\"\");\n            x.push_str(&s);\n            x.push_str(\"\\\"\");\n            x\n        }\n    }\n}\n\nfn print_identifier(i: Identifier) -> String {\n    i.name\n}\n\nfn print_binary_expression(b: Box<BinaryExpression>) -> String {\n    let mut res = String::new();\n\n    let val = *b;\n    let left = print_expression(val.left);\n    let right = print_expression(val.right);\n    let operator = print_operator(val.operator);\n\n    res.push_str(&left);\n    res.push_str(\" \");\n    res.push_str(&operator);\n    res.push_str(\" \");\n    res.push_str(&right);\n    res\n}\n\nfn print_operator(op: Operator) -> String {\n    match op {\n        Operator::Addition => String::from(\"+\"),\n        Operator::Substraction => String::from(\"-\"),\n        Operator::Multiplication => String::from(\"*\"),\n        Operator::Division => String::from(\"/\"),\n        Operator::Remainder => String::from(\"%\"),\n        Operator::Exponentiation => String::from(\"**\"),\n        Operator::Equal => String::from(\"===\"),\n        Operator::NotEqual => String::from(\"!==\"),\n        Operator::GreaterThan => String::from(\">\"),\n        Operator::LessThan => String::from(\"<\"),\n        Operator::GreaterThanOrEqualTo => String::from(\">=\"),\n        Operator::LessThanOrEqualTo => String::from(\"<=\"),\n    }\n}\n\nfn print_call_expression(c: CallExpression) -> String {\n    let mut x = String::new();\n    x.push_str(&c.callee.name);\n    x.push_str(\"(\");\n    for argument in c.arguments {\n        let arg = print_expression(argument);\n        x.push_str(&arg);\n        x.push_str(\",\");\n    }\n    x.push_str(\")\");\n    x\n}\n\nfn print_assignment_expression(s: AssignmentExpression) -> String {\n    let mut res = String::new();\n    res.push_str(&print_identifier(s.id));\n    res.push_str(\" = \");\n    res.push_str(&print_expression(*s.value));\n    res\n}\n\nfn print_expression(e: Expression) -> String {\n    match e {\n        Expression::Assignment(e) => print_assignment_expression(e),\n        Expression::Literal(l) => print_literal(l),\n        Expression::BinaryExpression(b) => print_binary_expression(b),\n        Expression::CallExpression(c) => print_call_expression(c),\n        Expression::Identifier(i) => print_identifier(i),\n    }\n}\n\nfn print_block_statement(b: BlockStatement, depth: u8) -> String {\n    let mut res = String::new();\n    res.push_str(\"{\\n\");\n    let content = match b.body {\n        Some(statements) => {\n            let mut sts = String::new();\n            for statement in statements {\n                sts.push_str(&print_statement(statement, depth + 1));\n            }\n            sts\n        }\n        None => String::from(\"\"),\n    };\n    res.push_str(&content);\n    res.push_str(&insert_indent(depth));\n    res.push_str(\"}\");\n    res\n}\n\nfn print_variable_declaration(v: VariableDeclaration, depth: u8) -> String {\n    let id = print_identifier(v.id);\n    let val = print_expression(v.value);\n    let mut st = String::new();\n    st.push_str(&insert_indent(depth));\n    st.push_str(\"var \");\n    st.push_str(&id);\n    st.push_str(\" = \");\n    st.push_str(&val);\n    st.push_str(\";\");\n    st\n}\n\nfn print_if_statement(i: IfStatement, depth: u8, inside_else: bool) -> String {\n    let mut res = String::new();\n    let else_statement = match i.alternate {\n        Some(st) => {\n            let mut res = String::new();\n            let x = match st {\n                AlternateStatement::IfStatement(i) => print_if_statement(*i, depth, true),\n                AlternateStatement::BlockStatement(b) => print_block_statement(b, depth),\n            };\n\n            res.push_str(\" else \");\n            res.push_str(&x);\n            res\n        }\n        None => String::from(\"\"),\n    };\n\n    if !inside_else {\n        res.push_str(&insert_indent(depth));\n    }\n    res.push_str(\"if (\");\n    res.push_str(&print_expression(i.test));\n    res.push_str(\") \");\n    res.push_str(&print_block_statement(i.consequent, depth));\n    res.push_str(&else_statement);\n    res\n}\n\nfn print_loop_statement(b: BlockStatement, depth: u8) -> String {\n    let mut res = String::new();\n    res.push_str(&insert_indent(depth));\n    res.push_str(\"while(true) \");\n    res.push_str(&print_block_statement(b, depth));\n    res\n}\n\nfn print_statement(s: Statement, depth: u8) -> String {\n    let mut res = String::new();\n    let x: String = match s {\n        Statement::Expression(e) => {\n            let mut res = String::new();\n            res.push_str(&insert_indent(depth));\n            res.push_str(&print_expression(e));\n            res.push_str(\";\");\n            res\n        }\n        Statement::VariableDeclaration(v) => print_variable_declaration(v, depth),\n        Statement::BlockStatement(s) => print_block_statement(s, depth),\n        Statement::IfStatement(s) => print_if_statement(s, depth, false),\n        Statement::Loop(s) => print_loop_statement(s, depth),\n        Statement::Break => insert_indent(depth) + &String::from(\"break;\"),\n        Statement::Continue => insert_indent(depth) + &String::from(\"continue;\"),\n    };\n    res.push_str(&x);\n    res.push_str(\"\\n\");\n    res\n}\n\npub fn print(ast: Program) -> String {\n    let mut js = String::new();\n    for statement in ast.body {\n        js.push_str(&print_statement(statement, 0));\n    }\n\n    js\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n    #[test]\n    fn name() {\n        let s = print(Program {\n            body: vec![Statement::VariableDeclaration(VariableDeclaration {\n                id: Identifier {\n                    name: String::from(\"x\"),\n                },\n                value: Expression::Literal(Literal::Null),\n            })],\n        });\n\n        assert_eq!(&s, &\"var x = null;\\n\")\n    }\n}\n"
  },
  {
    "path": "printer/src/lib.rs",
    "content": "extern crate parser;\n\nmod js;\n\nuse parser::parse;\n\npub fn to_js(s: String) -> String {\n    let naskah_ast = parse(&s);\n    match naskah_ast {\n        Ok(ast) => js::print(ast),\n        Err(_) => String::from(\"salah sintaks\"),\n    }\n}\n\n#[cfg(test)]\nmod test {\n    use super::*;\n\n    #[test]\n    fn ext_single() {\n        let js = to_js(String::from(\"misal x = null;\\n\"));\n        assert_eq!(js, String::from(\"var x = null;\\n\"));\n    }\n\n    #[test]\n    fn ext_multi() {\n        let js = to_js(String::from(\"misal x = null;\\nmisal y = benar;\\n\"));\n        assert_eq!(js, String::from(\"var x = null;\\nvar y = true;\\n\"));\n    }\n}\n"
  },
  {
    "path": "scripts/build-demo.sh",
    "content": "rustup default nightly\ncargo install wasm-pack\ncargo build\ncd demo\nwasm-pack build --target web --out-name wasm --out-dir ./static/assets"
  },
  {
    "path": "src/lib.rs",
    "content": "\n"
  },
  {
    "path": "vercel.json",
    "content": "{\n  \"name\": \"naskah\",\n  \"alias\": [\"naskah\"],\n  \"version\": 2,\n  \"github\": {\n    \"enabled\": false\n  },\n  \"builds\": [{ \"src\": \"./demo/static/**\", \"use\": \"@now/static\" }],\n  \"routes\": [{ \"src\": \"/(.*)\", \"dest\": \"demo/static/$1\" }]\n}\n"
  }
]