Full Code of pveyes/naskah for AI

master a60037c94fcb cached
25 files
48.3 KB
12.4k tokens
80 symbols
1 requests
Download .txt
Repository: pveyes/naskah
Branch: master
Commit: a60037c94fcb
Files: 25
Total size: 48.3 KB

Directory structure:
gitextract_c3b19t87/

├── .github/
│   └── workflows/
│       └── main.yml
├── .gitignore
├── Cargo.toml
├── LICENSE
├── README.md
├── demo/
│   ├── Cargo.toml
│   ├── README.md
│   ├── src/
│   │   └── lib.rs
│   └── static/
│       ├── index.html
│       └── style.css
├── parser/
│   ├── Cargo.toml
│   └── src/
│       ├── ast.rs
│       ├── expr.rs
│       ├── identifier.rs
│       ├── lib.rs
│       ├── literal.rs
│       ├── number.rs
│       ├── statement.rs
│       └── variable.rs
├── printer/
│   ├── Cargo.toml
│   └── src/
│       ├── js.rs
│       └── lib.rs
├── scripts/
│   └── build-demo.sh
├── src/
│   └── lib.rs
└── vercel.json

================================================
FILE CONTENTS
================================================

================================================
FILE: .github/workflows/main.yml
================================================
name: build

on:
  push:
    branches: [master]
  pull_request:
    branches: [master]

env:
  CARGO_TERM_COLOR: always

jobs:
  test:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
      - name: Build
        run: cargo build --verbose
      - name: Run tests
        run: cargo test --verbose

  demo:
    runs-on: ubuntu-latest

    steps:
      - uses: actions/checkout@v2
      - name: Build
        run: ./scripts/build-demo.sh
      - name: Deploy staging
        uses: amondnet/vercel-action@v19
        if: github.event_name == 'pull_request'
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
          github-token: ${{ secrets.GITHUB_TOKEN }}
      - name: check production
        id: prod_or_not
        run: |
          if [ "$REF" == 'refs/head/master' ]
          then
            echo "::set-output name=vercel-args::--prod"
          else
            echo "::set-output name=vercel-args::"
          fi
        env:
          REF: ${{ github.ref }}
      - name: Deploy production
        uses: amondnet/vercel-action@v19
        if: github.event_name == 'push'
        with:
          vercel-token: ${{ secrets.VERCEL_TOKEN }}
          vercel-org-id: ${{ secrets.VERCEL_ORG_ID }}
          vercel-project-id: ${{ secrets.VERCEL_PROJECT_ID }}
          vercel-args: ${{ steps.prod_or_not.outputs.vercel-args }}
          github-token: ${{ secrets.GITHUB_TOKEN }}


================================================
FILE: .gitignore
================================================
/target
**/*.rs.bk
fixtures/
.DS_Store
demo/static/worker/
demo/static/main.js
demo/static/main.wasm
.vercel


================================================
FILE: Cargo.toml
================================================
[package]
name = "naskah"
version = "0.1.0"
authors = ["Fatih Kalifa <fatihkalifa@gmail.com>"]
repository = "https://github.com/pveyes/naskah"
description = "Bahasa pemrograman dengan sintaks Bahasa Indonesia"
license = "MIT"

[workspace]

members = [
  "demo",
  "parser",
  "printer"
]

[dependencies]


================================================
FILE: LICENSE
================================================
MIT License

Copyright (c) 2018 Fatih Kalifa

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

================================================
FILE: README.md
================================================
# naskah [![Actions Status](https://github.com/pveyes/naskah/workflows/build/badge.svg)](https://github.com/pveyes/naskah/actions)

> Bahasa pemrograman dengan sintaks Bahasa Indonesia

Demo: https://naskah.vercel.app/

## Tipe data

Saat ini hanya 4 tipe data yang didukung oleh naskah:

- angka `123`
- huruf `"hello"`
- boolean `benar` / `salah`
- kosong `kosong`

## Operator

Operasi yang didukung oleh `naskah` adalah:

- Penjumlahan `+`
- Pengurangan `-`
- Perkalian `*`
- Pembagian `\`
- Sisa pembagian `%`
- Pangkat `^`

Selain itu ada juga operasi untuk membandingkan dua variabel / tipe data

- Sama dengan `==`
- Tidak sama dengan `!=`
- Lebih dari `>`
- Kurang dari `<`

## Sintaks

### Deklarasi variabel

```
misal x = 4;
misal y = x;
```

### Percabangan

```
jika x == 2 {

}

jika x == kosong {

}
```

Untuk kasus-kasus umum, naskah menyediakan sintaks khusus untuk pengecekan terhadap `kosong`, `benar` dan `salah`. Tidak perlu menulis operator `==`, cukup `x kosong`.

```
jika x kosong {

}
```

### Perulangan

Naskah saat ini hanya mempunyai 1 tipe perulangan yang tidak pernah berhenti

```
ulang {

}
```

Untuk berhenti di dalam perulangan, dapat menggunakan sintaks `berhenti;`

```
ulang {
  jika x > 2 {
    berhenti;
  }
}
```

## Lisensi

Bahasa pemrograman Naskah terlisensi dibawah lisensi MIT.


================================================
FILE: demo/Cargo.toml
================================================
[package]
name = "naskah-demo"
version = "0.1.0"
authors = ["Fatih Kalifa <fatihkalifa@gmail.com>"]

[lib]
crate-type = ["cdylib", "rlib"]

[dependencies]
serde = "^1.0.80"
serde_derive = "^1.0.80"
yew =  "0.17"
web-sys = { version = "0.3.45", features = ['Window', 'Document', 'Element'] }
wasm-bindgen = "0.2.67"
printer = { path = "../printer" }

================================================
FILE: demo/README.md
================================================
# naskah-demo

This demo is built in Rust using [Yew](https://github.com/DenisKolodin/yew) framework.

## Overview
It consists of two entry point inside `src/bin/`

 - `main.rs` App entry point, component initialization and initial render
 - `worker.rs` Compiler run inside web worker

Most of the time, we only need to watch & rebuild `main.rs`. But for any worker changes, we need to rebuild manually (see setup below).

## Setup

Run this inside `demo` directory

```sh
cargo install cargo-web
# build worker script
cargo web build --bin worker --target=wasm32-unknown-unknown --release
# run app & watch for changes
cargo web start --bin main --target=wasm32-unknown-unknown --release
# visit [::1]:8000
open http://localhost:8000
```


================================================
FILE: demo/src/lib.rs
================================================
extern crate printer;
extern crate wasm_bindgen;
extern crate web_sys;
extern crate yew;

use wasm_bindgen::prelude::*;
use yew::prelude::*;

use printer::to_js;

struct Model {
    link: ComponentLink<Self>,
    code: String,
    transpiled: String,
}

enum Msg {
    ChangeCode(String),
}

const EXAMPLE_CODE: &str = "misal x = 2 + 2;
misal y = x > 2;
jika y benar {
  x = x + 1;
  menang();
}
";

impl Component for Model {
    type Message = Msg;
    type Properties = ();
    fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
        Self {
            link,
            code: EXAMPLE_CODE.into(),
            transpiled: to_js(EXAMPLE_CODE.into()),
        }
    }

    fn update(&mut self, msg: Self::Message) -> ShouldRender {
        match msg {
            Msg::ChangeCode(v) => {
                self.transpiled = to_js(v.clone().into());
                self.code = v;
            }
        }
        true
    }

    fn change(&mut self, _props: Self::Properties) -> ShouldRender {
        // Should only return "true" if new properties are different to
        // previously received properties.
        // This component has no properties so we will always return "false".
        false
    }

    fn view(&self) -> Html {
        html! {
            <>
                <div>
                    <label for="input",>{"Masukan"}</label>
                    <textarea value={&self.code} oninput={self.link.callback(|e: InputData| Msg::ChangeCode(e.value))} />
                </div>
                <div>
                    <label>{"Keluaran (JavaScript):"}</label>
                    <pre id="js",>{&self.transpiled}</pre>
                </div>
            </>
        }
    }
}

#[wasm_bindgen(start)]
pub fn run_app() {
    let win = web_sys::window().unwrap();
    let doc = win.document().unwrap();
    if let Some(element) = doc.query_selector("#playground").expect("No matching id") {
        App::<Model>::new().mount(element);
    }
}


================================================
FILE: demo/static/index.html
================================================
<html>
  <head>
    <title>Naskah - Bahasa pemrogramman dengan sintaks Bahasa Indonesia</title>
    <link rel="stylesheet" type="text/css" href="./style.css" />
  </head>

  <body>
    <main>
      <div class="container">
        <header class="hero">
          <h1 class="naskah">Naskah</h1>
          <span
            ><span class="naskah">Naskah</span> adalah bahasa pemrogramman
            dengan sintaks Bahasa Indonesia</span
          >
        </header>
        <center>
          <a
            href="https://github.com/pveyes/naskah"
            target="_blank"
            class="github-corner"
            aria-label="View source on Github"
            ><svg
              width="80"
              height="80"
              viewBox="0 0 250 250"
              style="
                fill: #333;
                color: #fff;
                position: absolute;
                top: 0;
                border: 0;
                right: 0;
              "
              aria-hidden="true"
            >
              <path
                d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"
              ></path>
              <path
                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"
                fill="currentColor"
                style="transform-origin: 130px 106px"
                class="octo-arm"
              ></path>
              <path
                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"
                fill="currentColor"
                class="octo-body"
              ></path></svg
          ></a>
        </center>
        <div class="demo">
          <h2>Demo</h2>
          <span
            >Saat ini <span class="naskah">Naskah</span> masih dalam
            pengembangan dan hanya bisa menghasilkan sintaks
            <a href="https://developer.mozilla.org/bm/docs/Web/JavaScript"
              >JavaScript</a
            >.</span
          >
          <div id="playground" class="demo-playground"></div>
        </div>
      </div>
    </main>
    <script type="module">
      import init from "./assets/wasm.js";
      init();
    </script>
  </body>
</html>


================================================
FILE: demo/static/style.css
================================================
html, body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
}

body {
  background-color: #fff1ec;
}

main {
  height: 100vh;
}

.container {
  width: 700px;
  margin: 0 auto;
}

header {
  text-align: center;
}

header h1 {
  font-size: 45px;
  margin: 0;
  padding: 13px 0;
}

.hero {
  margin-bottom: 50px;
}

.naskah {
  font-family: 'Trebuchet MS', 'Lucida Sans Unicode', 'Lucida Grande', 'Lucida Sans', Arial, sans-serif;
  font-weight: 600;
}

.demo {
  text-align: center;
}

.demo-playground {
  display: flex;
  justify-content: space-around;
  margin-top: 20px;
  text-align: initial;
}

.demo-playground > div {
  position: relative;
}

.demo-playground label {
  display: block;
  margin-bottom: 20px;
}

textarea {
  border: none;
  outline: none;
  display: block;
  width: 100%;
}

textarea, pre {
  margin: 0;
  font-family: 'Courier New', Courier, monospace;
  font-size: 14px;
  border: 1px solid #ddd;
  background-color: #fff;
  width: 300px;
  min-height: 300px;
  padding: 5px 10px;
  border-radius: 3px;
  box-sizing: border-box;
}

@media (max-width: 768px) {
  .github-corner {
    display: none;
  }
}

.github-corner:hover .octo-arm {
  animation: octocat-wave 560ms ease-in-out
}

@keyframes octocat-wave {
  0%, 100% { transform: rotate(0) }
  20%, 60% { transform: rotate(-25deg) }
  40%, 80% { transform: rotate(10deg) }
}


================================================
FILE: parser/Cargo.toml
================================================
[package]
name = "parser"
version = "0.1.0"
authors = ["Fatih Kalifa <fatihkalifa@gmail.com>"]

[dependencies]
lazy_static = "1.1.0"
nom = { version = "^4.1.0", features = ["regexp", "regexp_macros"] }
regex = "1.0.5"

================================================
FILE: parser/src/ast.rs
================================================
#[derive(PartialEq, Debug)]
pub enum Literal {
    Number(i64),
    Null,
    String(String),
    Boolean(bool),
}

#[derive(PartialEq, Debug)]
pub struct Identifier {
    pub name: String,
}

#[derive(PartialEq, Debug)]
pub struct CallExpression {
    pub callee: Identifier,
    pub arguments: Vec<Expression>,
}

#[derive(PartialEq, Debug)]
pub struct AssignmentExpression {
    pub id: Identifier,
    pub value: Box<Expression>,
}

#[derive(PartialEq, Debug)]
pub enum Expression {
    Identifier(Identifier),
    Literal(Literal),
    BinaryExpression(Box<BinaryExpression>),
    CallExpression(CallExpression),
    Assignment(AssignmentExpression),
}

#[derive(PartialEq, Debug)]
pub enum Operator {
    Addition,
    Substraction,
    Multiplication,
    Division,
    Remainder,
    Exponentiation,
    Equal,
    NotEqual,
    GreaterThan,
    LessThan,
    GreaterThanOrEqualTo,
    LessThanOrEqualTo,
}

#[derive(PartialEq, Debug)]
pub struct BinaryExpression {
    pub left: Expression,
    pub right: Expression,
    pub operator: Operator,
}

#[derive(PartialEq, Debug)]
pub struct VariableDeclaration {
    pub id: Identifier,
    pub value: Expression,
}

#[derive(PartialEq, Debug)]
pub enum AlternateStatement {
    IfStatement(Box<IfStatement>),
    BlockStatement(BlockStatement),
}

#[derive(PartialEq, Debug)]
pub struct IfStatement {
    pub test: Expression,
    pub consequent: BlockStatement,
    pub alternate: Option<AlternateStatement>,
}

#[derive(PartialEq, Debug)]
pub struct BlockStatement {
    pub body: Option<Vec<Statement>>,
}

#[derive(PartialEq, Debug)]
pub enum Statement {
    Break,
    Continue,
    Expression(Expression),
    VariableDeclaration(VariableDeclaration),
    BlockStatement(BlockStatement),
    Loop(BlockStatement),
    IfStatement(IfStatement),
}

#[derive(PartialEq, Debug)]
pub struct Program {
    pub body: Vec<Statement>,
}


================================================
FILE: parser/src/expr.rs
================================================
use super::ast::*;
use super::identifier::identifier;
use super::literal::literal;

named!(
    assignment_expression<Expression>,
    do_parse!(
        i: identifier
            >> ws!(tag!("="))
            >> e: expression
            >> (Expression::Assignment(AssignmentExpression {
                id: i,
                value: Box::new(e)
            }))
    )
);

named!(
    fn_arguments<Vec<Expression>>,
    // this is buggy because we can do fn(a b)
    // TODO fix this
    fold_many0!(
        do_parse!(e: expression >> opt!(tag!(",")) >> (e)),
        Vec::new(),
        |mut acc: Vec<Expression>, item| {
            acc.push(item);
            acc
        }
    )
);

named!(
    call_expression<Expression>,
    do_parse!(
        c: identifier
            >> tag!("(")
            >> args: fn_arguments
            >> tag!(")")
            >> (Expression::CallExpression(CallExpression {
                callee: c,
                arguments: args
            }))
    )
);

named!(
    simple_expression<Expression>,
    alt_complete!(
        map!(literal, |l| Expression::Literal(l)) | map!(identifier, |i| Expression::Identifier(i))
    )
);

named!(
  pub expression<Expression>,
  alt_complete!(
      assignment_expression |
      call_expression |
      binary_expression |
      simple_expression
  )
);

named!(
    operator<Operator>,
    // we use alt_complete here just to be safe
    // because operator can contains different length
    alt_complete!(
        map!(tag!("+"), |_| Operator::Addition)
            | map!(tag!("-"), |_| Operator::Substraction)
            | map!(tag!("*"), |_| Operator::Multiplication)
            | map!(tag!("/"), |_| Operator::Division)
            | map!(tag!("%"), |_| Operator::Remainder)
            | map!(tag!("^"), |_| Operator::Exponentiation)
            | map!(tag!("=="), |_| Operator::Equal)
            | map!(tag!("!="), |_| Operator::NotEqual)
            | map!(tag!(">="), |_| Operator::GreaterThanOrEqualTo)
            | map!(tag!("<="), |_| Operator::LessThanOrEqualTo)
            | map!(tag!(">"), |_| Operator::GreaterThan)
            | map!(tag!("<"), |_| Operator::LessThan)
    )
);

named!(
    pub binary_expression<Expression>,
    do_parse!(
        first: simple_expression
            >> fold: fold_many0!(
                do_parse!(op: ws!(operator) >> expr: simple_expression >> (op, expr)),
                first,
                |left: Expression, (operator, right): (Operator, Expression)| {
                    Expression::BinaryExpression(Box::new(BinaryExpression {
                        left,
                        right,
                        operator,
                    }))
                }
            )
            >> (fold)
    )
);

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn op() {
        assert_eq!(operator(&b"+"[..]), Ok((&b""[..], Operator::Addition)));
        assert_eq!(operator(&b"-"[..]), Ok((&b""[..], Operator::Substraction)));
        assert_eq!(
            operator(&b"*"[..]),
            Ok((&b""[..], Operator::Multiplication))
        );
        assert_eq!(operator(&b"/"[..]), Ok((&b""[..], Operator::Division)));
        assert_eq!(operator(&b"%"[..]), Ok((&b""[..], Operator::Remainder)));
        assert_eq!(
            operator(&b"^"[..]),
            Ok((&b""[..], Operator::Exponentiation))
        );
        assert_eq!(operator(&b"=="[..]), Ok((&b""[..], Operator::Equal)));
        assert_eq!(operator(&b"!="[..]), Ok((&b""[..], Operator::NotEqual)));
        assert_eq!(operator(&b">"[..]), Ok((&b""[..], Operator::GreaterThan)));
        assert_eq!(operator(&b"<"[..]), Ok((&b""[..], Operator::LessThan)));
        assert_eq!(operator(&b">="[..]), Ok((&b""[..], Operator::GreaterThanOrEqualTo)));
        assert_eq!(operator(&b"<="[..]), Ok((&b""[..], Operator::LessThanOrEqualTo)));
    }

    #[test]

    fn binary_expression_literals() {
        assert_eq!(
            expression(&b"\"kosong\" != kosong;"[..]),
            Ok((
                &b";"[..],
                Expression::BinaryExpression(Box::new(BinaryExpression {
                    left: Expression::Literal(Literal::String(String::from("kosong"))),
                    right: Expression::Literal(Literal::Null),
                    operator: Operator::NotEqual
                }))
            ))
        )
    }

    #[test]
    fn binary_expression_as_expression() {
        assert_eq!(
            expression(&b"x > 5;"[..]),
            Ok((
                &b";"[..],
                Expression::BinaryExpression(Box::new(BinaryExpression {
                    left: Expression::Identifier(Identifier {
                        name: String::from("x")
                    }),
                    right: Expression::Literal(Literal::Number(5)),
                    operator: Operator::GreaterThan
                }))
            ))
        )
    }

    #[test]
    fn recursive_binary_expression() {
        assert_eq!(
            expression(&b"1 > 2 + 3;"[..]),
            Ok((
                &b";"[..],
                Expression::BinaryExpression(Box::new(BinaryExpression {
                    left: Expression::BinaryExpression(Box::new(BinaryExpression {
                        left: Expression::Literal(Literal::Number(1)),
                        right: Expression::Literal(Literal::Number(2)),
                        operator: Operator::GreaterThan,
                    })),
                    right: Expression::Literal(Literal::Number(3)),
                    operator: Operator::Addition,
                }))
            ))
        );
    }

    #[test]
    fn basic_call_expression() {
        assert_eq!(
            expression(&b"hello()"[..]),
            Ok((
                &b""[..],
                Expression::CallExpression(CallExpression {
                    callee: Identifier {
                        name: String::from("hello")
                    },
                    arguments: vec![]
                })
            ))
        );
    }

    #[test]
    fn fn_with_arguments() {
        assert_eq!(
            expression(&b"tulis(\"hello, world!\")"[..]),
            Ok((
                &b""[..],
                Expression::CallExpression(CallExpression {
                    callee: Identifier {
                        name: String::from("tulis")
                    },
                    arguments: vec![Expression::Literal(Literal::String(String::from(
                        "hello, world!"
                    )))]
                })
            ))
        );
    }
}


================================================
FILE: parser/src/identifier.rs
================================================
use super::ast::Identifier;
use std::str;

named!(
  pub identifier<Identifier>,
  map!(
    map_res!(re_bytes_find!(r"^[a-zA-Z_]\w*"), str::from_utf8),
    |name| Identifier {
      name: String::from(name)
    }
  )
);

#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn alphanumeric() {
        assert_eq!(
            identifier(&b"x;"[..]),
            Ok((
                &b";"[..],
                Identifier {
                    name: String::from("x")
                }
            ))
        );

        assert_eq!(
            identifier(&b"y2"[..]),
            Ok((
                &b""[..],
                Identifier {
                    name: String::from("y2")
                }
            ))
        );

        assert_eq!(
            identifier(&b"x_y_2"[..]),
            Ok((
                &b""[..],
                Identifier {
                    name: String::from("x_y_2")
                }
            ))
        );
    }

    #[test]
    fn can_start_with_underscore() {
        assert_eq!(
            identifier(&b"__x"[..]),
            Ok((
                &b""[..],
                Identifier {
                    name: String::from("__x")
                }
            ))
        );
    }

    #[test]
    fn cannot_start_with_number() {
        assert_ne!(
            identifier(&b"2x"[..]),
            Ok((
                &b""[..],
                Identifier {
                    name: String::from("2x")
                }
            ))
        );
    }
}


================================================
FILE: parser/src/lib.rs
================================================
#[macro_use]
extern crate nom;
extern crate regex;

pub mod ast;
mod expr;
mod identifier;
mod literal;
mod number;
mod statement;
mod variable;

use self::ast::*;
use self::statement::parse_statement;

named!(
  pub program<Program>,
  map!(
    many0!(
      do_parse!(s: parse_statement >> tag!("\n") >> (s))
    ),
    |body| Program { body }
  )
);

pub fn parse(input: &str) -> Result<Program, nom::Err<&[u8]>> {
  let (_res, p) = program(input.as_bytes())?;
  Ok(p)
}


================================================
FILE: parser/src/literal.rs
================================================
use super::ast::Literal;
use super::number::number_literal;
use std::str;

named!(
    boolean<bool>,
    alt!(map!(tag!("benar"), |_| true) | map!(tag!("salah"), |_| false))
);

named!(
  pub boolean_literal<Literal>,
  map!(boolean, |b| Literal::Boolean(b))
);

named!(pub null_literal<Literal>, map!(tag!("kosong"), |_| Literal::Null));

named!(
    string_literal<Literal>,
    map!(
        delimited!(
            char!('"'),
            map_res!(is_not!("\""), str::from_utf8),
            char!('"')
        ),
        |s| Literal::String(String::from(s))
    )
);

named!(
  pub literal<Literal>,
  alt_complete!(null_literal | boolean_literal | number_literal | string_literal)
);

#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn boolean() {
        assert_eq!(
            literal(&b"benar"[..]),
            Ok((&b""[..], Literal::Boolean(true)))
        );

        assert_eq!(
            literal(&b"salah"[..]),
            Ok((&b""[..], Literal::Boolean(false)))
        );
    }

    #[test]
    fn string() {
        assert_eq!(
            literal(&b"\"p23u08rfwi\""[..]),
            Ok((&b""[..], Literal::String(String::from("p23u08rfwi"))))
        );
    }

    #[test]
    fn number() {
        assert_eq!(literal(&b"2 "[..]), Ok((&b" "[..], Literal::Number(2))));
    }

    #[test]
    fn null() {
        assert_eq!(literal(&b"kosong"[..]), Ok((&b""[..], Literal::Null)));
    }
}


================================================
FILE: parser/src/number.rs
================================================
use super::ast::Literal;
use nom::{is_digit, is_hex_digit};
use std::i64;
use std::str;

named!(sign, recognize!(opt!(one_of!("+-"))));

#[allow(dead_code)]
fn is_bin_digit(chr: u8) -> bool {
    chr == b'0' || chr == b'1'
}

named!(dec_digits, take_while!(is_digit));
named!(hex_digits, take_while!(is_hex_digit));
named!(bin_digits, take_while1!(is_bin_digit));

named!(bin_literal, recognize!(do_parse!(sign >> bin_digits >> ())));

named!(
    decimal_literal,
    recognize!(do_parse!(sign >> dec_digits >> ()))
);

named!(hex_literal, recognize!(do_parse!(sign >> hex_digits >> ())));

named!(
    binary<i64>,
    map_res!(map_res!(bin_literal, str::from_utf8), |s| {
        i64::from_str_radix(s, 2)
    })
);

named!(
    decimal<i64>,
    map_res!(map_res!(decimal_literal, str::from_utf8), |s| {
        i64::from_str_radix(s, 10)
    })
);

named!(
    hexadecimal<i64>,
    map_res!(map_res!(hex_literal, str::from_utf8), |s| {
        i64::from_str_radix(s, 16)
    })
);

named!(
    integer<i64>,
    alt_complete!(
        preceded!(tag!("0b"), binary)
            | preceded!(tag!("0x"), hexadecimal)
            | preceded!(opt!(tag!("0d")), decimal)
    )
);

named!(
  pub number_literal<Literal>,
  map!(integer, |d| Literal::Number(d))
);

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn test_bin_literal() {
        assert_eq!(bin_literal(&b"0 "[..]), Ok((&b" "[..], &b"0"[..])));
        assert_eq!(bin_literal(&b"1 "[..]), Ok((&b" "[..], &b"1"[..])));
        assert_eq!(bin_literal(&b"10 "[..]), Ok((&b" "[..], &b"10"[..])));
    }

    #[test]
    fn test_decimal_literal() {
        assert_eq!(decimal_literal(&b"0 "[..]), Ok((&b" "[..], &b"0"[..])));
        assert_eq!(decimal_literal(&b"10 "[..]), Ok((&b" "[..], &b"10"[..])));
    }

    #[test]
    fn test_hex_literal() {
        assert_eq!(hex_literal(&b"0 "[..]), Ok((&b" "[..], &b"0"[..])));
        assert_eq!(hex_literal(&b"f "[..]), Ok((&b" "[..], &b"f"[..])));
        assert_eq!(hex_literal(&b"10 "[..]), Ok((&b" "[..], &b"10"[..])));
    }

    #[test]
    fn test_binary() {
        assert_eq!(binary(&b"0 "[..]), Ok((&b" "[..], 0)));
        assert_eq!(binary(&b"1 "[..]), Ok((&b" "[..], 1)));
        assert_eq!(binary(&b"10 "[..]), Ok((&b" "[..], 2)));
    }

    #[test]
    fn test_decimal() {
        assert_eq!(decimal(&b"0 "[..]), Ok((&b" "[..], 0)));
        assert_eq!(decimal(&b"10 "[..]), Ok((&b" "[..], 10)));
    }

    #[test]
    fn test_hexadecimal() {
        assert_eq!(hexadecimal(&b"0 "[..]), Ok((&b" "[..], 0)));
        assert_eq!(hexadecimal(&b"f "[..]), Ok((&b" "[..], 15)));
        assert_eq!(hexadecimal(&b"10 "[..]), Ok((&b" "[..], 16)));
    }

    #[test]
    fn test_integer() {
        assert_eq!(integer(&b"0 "[..]), Ok((&b" "[..], 0)));
        assert_eq!(integer(&b"100 "[..]), Ok((&b" "[..], 100)));
        assert_eq!(integer(&b"0xf "[..]), Ok((&b" "[..], 15)));
        assert_eq!(integer(&b"0b10 "[..]), Ok((&b" "[..], 2)));
        assert_eq!(integer(&b"0d10 "[..]), Ok((&b" "[..], 10)));
    }

    #[test]
    fn test_number_literal() {
        assert_eq!(
            number_literal(&b"2 "[..]),
            Ok((&b" "[..], Literal::Number(2)))
        )
    }
}


================================================
FILE: parser/src/statement.rs
================================================
use super::ast::*;
use super::expr::{binary_expression, expression};
use super::identifier::identifier;
use super::literal::{boolean_literal, null_literal};
use super::variable::variable_declaration;

named!(
    break_statement<Statement>,
    map!(tag!("berhenti;"), |_| Statement::Break)
);

named!(
    continue_statement<Statement>,
    map!(tag!("lanjut;"), |_| Statement::Continue)
);

named!(
    expression_statement<Statement>,
    map!(do_parse!(e: expression >> tag!(";") >> (e)), |e| {
        Statement::Expression(e)
    })
);

named!(
    pub parse_statement<Statement>,
    alt_complete!(
            loop_statement
            // TODO unsyntactic break/continue
            | break_statement
            | continue_statement
            | map!(if_statement, |b| Statement::IfStatement(b))
            | map!(block_statement, |b| Statement::BlockStatement(b))
            | map!(variable_declaration, |v| Statement::VariableDeclaration(v))
            | expression_statement
    )
);

named!(
    block_statement<BlockStatement>,
    map!(
        // block statement can contains nothing, that's why we need to
        // exclude any whitespace
        delimited!(
            tag!("{"),
            ws!(opt!(many1!(ws!(parse_statement)))),
            tag!("}")
        ),
        |body| BlockStatement { body }
    )
);

named!(
    loop_statement<Statement>,
    map!(
        // either ulang{} or ulang { }
        preceded!(ws!(tag!("ulang")), block_statement),
        Statement::Loop
    )
);

named!(
    special_if_condition<Expression>,
    do_parse!(
        left: identifier
            >> tag!(" ")
            >> right: alt_complete!(null_literal | boolean_literal)
            >> (Expression::BinaryExpression(Box::new(BinaryExpression {
                left: Expression::Identifier(left),
                right: Expression::Literal(right),
                operator: Operator::Equal
            })))
    )
);

named!(
    else_statement<Option<AlternateStatement>>,
    opt!(preceded!(
        tag!(" atau "),
        alt_complete!(
            map!(if_statement, |s| AlternateStatement::IfStatement(Box::new(
                s
            ))) | map!(block_statement, |s| AlternateStatement::BlockStatement(s))
        )
    ))
);

named!(
    single_if_statement<IfStatement>,
    preceded!(
        tag!("jika "),
        do_parse!(
            expr: alt_complete!(special_if_condition | binary_expression)
                >> tag!(" ")
                >> st: block_statement
                >> (IfStatement {
                    test: expr,
                    consequent: st,
                    alternate: None,
                })
        )
    )
);

named!(
    if_else_statement<IfStatement>,
    preceded!(
        tag!("jika "),
        do_parse!(
            expr: alt_complete!(special_if_condition | binary_expression)
                >> tag!(" ")
                >> st: block_statement
                >> els: else_statement
                >> (IfStatement {
                    test: expr,
                    consequent: st,
                    alternate: els,
                })
        )
    )
);

named!(
    if_statement<IfStatement>,
    // the reason we put if_else_statement first is because the
    // similarities of both syntax, considering single_if_statement will
    // also parse if_else_statement (albeit returning Incomplete), we
    // have to prioritize if_else and only parse single_if_statement
    // if the first parser failed. maybe not the most performant, but it works
    alt_complete!(if_else_statement | single_if_statement)
);

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn empty_block_statement() {
        assert_eq!(
            parse_statement(&b"{\n}"[..]),
            Ok((
                &b""[..],
                Statement::BlockStatement(BlockStatement { body: None })
            ))
        );
    }

    #[test]
    fn var_decl_inside_block() {
        assert_eq!(
            parse_statement(&b"{\nmisal x = 5;\n}"[..]),
            Ok((
                &b""[..],
                Statement::BlockStatement(BlockStatement {
                    body: Some(vec![Statement::VariableDeclaration(VariableDeclaration {
                        id: Identifier {
                            name: String::from("x")
                        },
                        value: Expression::Literal(Literal::Number(5))
                    })])
                })
            ))
        );
    }

    #[test]
    fn recursive_empty_block_statement() {
        assert_eq!(
            parse_statement(&b"{\n{\n}\n}"[..]),
            Ok((
                &b""[..],
                Statement::BlockStatement(BlockStatement {
                    body: Some(vec![Statement::BlockStatement(BlockStatement {
                        body: None
                    })])
                })
            ))
        );
    }

    #[test]
    fn test_loop_statement() {
        assert_eq!(
            parse_statement(&b"ulang {\nberhenti;\nlanjut;\n}"[..]),
            Ok((
                &b""[..],
                Statement::Loop(BlockStatement {
                    body: Some(vec![Statement::Break, Statement::Continue])
                })
            ))
        );
    }

    #[test]
    fn test_if_statement() {
        assert_eq!(
            parse_statement(&b"jika a == salah {\nmisal z = benar;\n}"[..]),
            Ok((
                &b""[..],
                Statement::IfStatement(IfStatement {
                    test: Expression::BinaryExpression(Box::new(BinaryExpression {
                        left: Expression::Identifier(Identifier {
                            name: String::from("a")
                        }),
                        right: Expression::Literal(Literal::Boolean(false)),
                        operator: Operator::Equal
                    })),
                    consequent: BlockStatement {
                        body: Some(vec![Statement::VariableDeclaration(VariableDeclaration {
                            id: Identifier {
                                name: String::from("z")
                            },
                            value: Expression::Literal(Literal::Boolean(true))
                        })])
                    },
                    alternate: None
                })
            ))
        );
    }

    #[test]
    fn test_if_else_statement() {
        assert_eq!(
            parse_statement(&b"jika a == salah {\n} atau {\n}"[..]),
            Ok((
                &b""[..],
                Statement::IfStatement(IfStatement {
                    test: Expression::BinaryExpression(Box::new(BinaryExpression {
                        left: Expression::Identifier(Identifier {
                            name: String::from("a")
                        }),
                        right: Expression::Literal(Literal::Boolean(false)),
                        operator: Operator::Equal
                    })),
                    consequent: BlockStatement { body: None },
                    alternate: Some(AlternateStatement::BlockStatement(BlockStatement {
                        body: None
                    }))
                })
            ))
        );
    }

    #[test]
    fn if_special() {
        assert_eq!(
            parse_statement(&b"jika a benar {\n}"[..]),
            parse_statement(&b"jika a == benar {\n}"[..])
        );

        assert_eq!(
            parse_statement(&b"jika a salah {\n} atau {\n}"[..]),
            parse_statement(&b"jika a == salah {\n} atau {\n}"[..]),
        );

        assert_eq!(
            parse_statement(&b"jika a kosong {\n}"[..]),
            parse_statement(&b"jika a == kosong {\n}"[..])
        );
    }

    #[test]
    fn recursive_if_else() {
        assert_eq!(
            parse_statement(&b"jika a benar {\n} atau jika b kosong {\n}"[..]),
            Ok((
                &b""[..],
                Statement::IfStatement(IfStatement {
                    test: Expression::BinaryExpression(Box::new(BinaryExpression {
                        left: Expression::Identifier(Identifier {
                            name: String::from("a")
                        }),
                        right: Expression::Literal(Literal::Boolean(true)),
                        operator: Operator::Equal
                    })),
                    consequent: BlockStatement { body: None },
                    alternate: Some(AlternateStatement::IfStatement(Box::new(IfStatement {
                        test: Expression::BinaryExpression(Box::new(BinaryExpression {
                            left: Expression::Identifier(Identifier {
                                name: String::from("b")
                            }),
                            right: Expression::Literal(Literal::Null),
                            operator: Operator::Equal
                        })),
                        consequent: BlockStatement { body: None },
                        alternate: None
                    })))
                })
            ))
        );

        assert_eq!(
            parse_statement(&b"jika c == 2 {\n} atau jika d benar {\n} atau {\n}"[..]),
            Ok((
                &b""[..],
                Statement::IfStatement(IfStatement {
                    test: Expression::BinaryExpression(Box::new(BinaryExpression {
                        left: Expression::Identifier(Identifier {
                            name: String::from("c")
                        }),
                        right: Expression::Literal(Literal::Number(2)),
                        operator: Operator::Equal
                    })),
                    consequent: BlockStatement { body: None },
                    alternate: Some(AlternateStatement::IfStatement(Box::new(IfStatement {
                        test: Expression::BinaryExpression(Box::new(BinaryExpression {
                            left: Expression::Identifier(Identifier {
                                name: String::from("d")
                            }),
                            right: Expression::Literal(Literal::Boolean(true)),
                            operator: Operator::Equal
                        })),
                        consequent: BlockStatement { body: None },
                        alternate: Some(AlternateStatement::BlockStatement(BlockStatement {
                            body: None
                        }))
                    })))
                })
            ))
        );
    }

    #[test]
    fn simple_expression_statement() {
        assert_eq!(
            parse_statement(&b"alert();"[..]),
            Ok((
                &b""[..],
                Statement::Expression(Expression::CallExpression(CallExpression {
                    callee: Identifier {
                        name: String::from("alert")
                    },
                    arguments: vec![]
                }))
            ))
        );
    }

    #[test]
    fn reassignment() {
        assert_eq!(
            parse_statement(&b"x = x ^ 5;"[..]),
            Ok((
                &b""[..],
                Statement::Expression(Expression::Assignment(AssignmentExpression {
                    id: Identifier {
                        name: String::from("x"),
                    },
                    value: Box::new(Expression::BinaryExpression(Box::new(BinaryExpression {
                        left: Expression::Identifier(Identifier {
                            name: String::from("x"),
                        }),
                        right: Expression::Literal(Literal::Number(5)),
                        operator: Operator::Exponentiation,
                    })))
                }))
            ))
        );
    }
}


================================================
FILE: parser/src/variable.rs
================================================
use super::ast::*;
use super::expr::expression;
use super::identifier::identifier;

named!(
  pub variable_declaration<VariableDeclaration>,
  preceded!(tag!("misal "), do_parse!(
      id: identifier
      // we can do either x = 2 or x=2
      // both is fine
      >> ws!(tag!("="))
      >> expr: expression
      >> tag!(";")
      >> (VariableDeclaration {
        id: id,
        value: expr
      })

  ))
);

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn boolean_assignment() {
        assert_eq!(
            variable_declaration(&b"misal x = benar;"[..]),
            Ok((
                &b""[..],
                VariableDeclaration {
                    id: Identifier {
                        name: String::from("x")
                    },
                    value: Expression::Literal(Literal::Boolean(true)),
                }
            ))
        );
    }

    #[test]
    fn string_assignment() {
        assert_eq!(
            variable_declaration(&b"misal x = \"str\";"[..]),
            Ok((
                &b""[..],
                VariableDeclaration {
                    id: Identifier {
                        name: String::from("x")
                    },
                    value: Expression::Literal(Literal::String(String::from("str"))),
                }
            ))
        );
    }

    #[test]
    fn number_assignment() {
        assert_eq!(
            variable_declaration(&b"misal x = 5;"[..]),
            Ok((
                &b""[..],
                VariableDeclaration {
                    id: Identifier {
                        name: String::from("x")
                    },
                    value: Expression::Literal(Literal::Number(5)),
                }
            ))
        );
    }

    #[test]
    fn null_assignment() {
        assert_eq!(
            variable_declaration(&b"misal x = kosong;"[..]),
            Ok((
                &b""[..],
                VariableDeclaration {
                    id: Identifier {
                        name: String::from("x")
                    },
                    value: Expression::Literal(Literal::Null),
                }
            ))
        );
    }

    #[test]
    fn identifier_assignment() {
        assert_eq!(
            variable_declaration(&b"misal x = a;"[..]),
            Ok((
                &b""[..],
                VariableDeclaration {
                    id: Identifier {
                        name: String::from("x")
                    },
                    value: Expression::Identifier(Identifier {
                        name: String::from("a")
                    }),
                }
            ))
        );
    }

    #[test]
    fn binary_expression_assignment() {
        assert_eq!(
            variable_declaration(&b"misal sum = 2 + 3;"[..]),
            Ok((
                &b""[..],
                VariableDeclaration {
                    id: Identifier {
                        name: String::from("sum")
                    },
                    value: Expression::BinaryExpression(Box::new(BinaryExpression {
                        left: Expression::Literal(Literal::Number(2)),
                        right: Expression::Literal(Literal::Number(3)),
                        operator: Operator::Addition,
                    }))
                }
            ))
        );
    }
}


================================================
FILE: printer/Cargo.toml
================================================
[package]
name = "printer"
version = "0.1.0"
authors = ["Fatih Kalifa <fatihkalifa@gmail.com>"]

[dependencies]
parser = { path = "../parser" }

================================================
FILE: printer/src/js.rs
================================================
use parser::ast::*;

fn insert_indent(depth: u8) -> String {
    let mut res = String::new();
    let mut x = 0;

    if depth == 0 {
        return res;
    }

    loop {
        res.push_str("  ");
        x = x + 1;
        if x >= depth {
            break;
        }
    }

    res
}

fn print_literal(l: Literal) -> String {
    match l {
        Literal::Null => String::from("null"),
        Literal::Boolean(bool) => match bool {
            true => String::from("true"),
            false => String::from("false"),
        },
        Literal::Number(n) => n.to_string(),
        Literal::String(s) => {
            let mut x = String::new();
            x.push_str("\"");
            x.push_str(&s);
            x.push_str("\"");
            x
        }
    }
}

fn print_identifier(i: Identifier) -> String {
    i.name
}

fn print_binary_expression(b: Box<BinaryExpression>) -> String {
    let mut res = String::new();

    let val = *b;
    let left = print_expression(val.left);
    let right = print_expression(val.right);
    let operator = print_operator(val.operator);

    res.push_str(&left);
    res.push_str(" ");
    res.push_str(&operator);
    res.push_str(" ");
    res.push_str(&right);
    res
}

fn print_operator(op: Operator) -> String {
    match op {
        Operator::Addition => String::from("+"),
        Operator::Substraction => String::from("-"),
        Operator::Multiplication => String::from("*"),
        Operator::Division => String::from("/"),
        Operator::Remainder => String::from("%"),
        Operator::Exponentiation => String::from("**"),
        Operator::Equal => String::from("==="),
        Operator::NotEqual => String::from("!=="),
        Operator::GreaterThan => String::from(">"),
        Operator::LessThan => String::from("<"),
        Operator::GreaterThanOrEqualTo => String::from(">="),
        Operator::LessThanOrEqualTo => String::from("<="),
    }
}

fn print_call_expression(c: CallExpression) -> String {
    let mut x = String::new();
    x.push_str(&c.callee.name);
    x.push_str("(");
    for argument in c.arguments {
        let arg = print_expression(argument);
        x.push_str(&arg);
        x.push_str(",");
    }
    x.push_str(")");
    x
}

fn print_assignment_expression(s: AssignmentExpression) -> String {
    let mut res = String::new();
    res.push_str(&print_identifier(s.id));
    res.push_str(" = ");
    res.push_str(&print_expression(*s.value));
    res
}

fn print_expression(e: Expression) -> String {
    match e {
        Expression::Assignment(e) => print_assignment_expression(e),
        Expression::Literal(l) => print_literal(l),
        Expression::BinaryExpression(b) => print_binary_expression(b),
        Expression::CallExpression(c) => print_call_expression(c),
        Expression::Identifier(i) => print_identifier(i),
    }
}

fn print_block_statement(b: BlockStatement, depth: u8) -> String {
    let mut res = String::new();
    res.push_str("{\n");
    let content = match b.body {
        Some(statements) => {
            let mut sts = String::new();
            for statement in statements {
                sts.push_str(&print_statement(statement, depth + 1));
            }
            sts
        }
        None => String::from(""),
    };
    res.push_str(&content);
    res.push_str(&insert_indent(depth));
    res.push_str("}");
    res
}

fn print_variable_declaration(v: VariableDeclaration, depth: u8) -> String {
    let id = print_identifier(v.id);
    let val = print_expression(v.value);
    let mut st = String::new();
    st.push_str(&insert_indent(depth));
    st.push_str("var ");
    st.push_str(&id);
    st.push_str(" = ");
    st.push_str(&val);
    st.push_str(";");
    st
}

fn print_if_statement(i: IfStatement, depth: u8, inside_else: bool) -> String {
    let mut res = String::new();
    let else_statement = match i.alternate {
        Some(st) => {
            let mut res = String::new();
            let x = match st {
                AlternateStatement::IfStatement(i) => print_if_statement(*i, depth, true),
                AlternateStatement::BlockStatement(b) => print_block_statement(b, depth),
            };

            res.push_str(" else ");
            res.push_str(&x);
            res
        }
        None => String::from(""),
    };

    if !inside_else {
        res.push_str(&insert_indent(depth));
    }
    res.push_str("if (");
    res.push_str(&print_expression(i.test));
    res.push_str(") ");
    res.push_str(&print_block_statement(i.consequent, depth));
    res.push_str(&else_statement);
    res
}

fn print_loop_statement(b: BlockStatement, depth: u8) -> String {
    let mut res = String::new();
    res.push_str(&insert_indent(depth));
    res.push_str("while(true) ");
    res.push_str(&print_block_statement(b, depth));
    res
}

fn print_statement(s: Statement, depth: u8) -> String {
    let mut res = String::new();
    let x: String = match s {
        Statement::Expression(e) => {
            let mut res = String::new();
            res.push_str(&insert_indent(depth));
            res.push_str(&print_expression(e));
            res.push_str(";");
            res
        }
        Statement::VariableDeclaration(v) => print_variable_declaration(v, depth),
        Statement::BlockStatement(s) => print_block_statement(s, depth),
        Statement::IfStatement(s) => print_if_statement(s, depth, false),
        Statement::Loop(s) => print_loop_statement(s, depth),
        Statement::Break => insert_indent(depth) + &String::from("break;"),
        Statement::Continue => insert_indent(depth) + &String::from("continue;"),
    };
    res.push_str(&x);
    res.push_str("\n");
    res
}

pub fn print(ast: Program) -> String {
    let mut js = String::new();
    for statement in ast.body {
        js.push_str(&print_statement(statement, 0));
    }

    js
}

#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn name() {
        let s = print(Program {
            body: vec![Statement::VariableDeclaration(VariableDeclaration {
                id: Identifier {
                    name: String::from("x"),
                },
                value: Expression::Literal(Literal::Null),
            })],
        });

        assert_eq!(&s, &"var x = null;\n")
    }
}


================================================
FILE: printer/src/lib.rs
================================================
extern crate parser;

mod js;

use parser::parse;

pub fn to_js(s: String) -> String {
    let naskah_ast = parse(&s);
    match naskah_ast {
        Ok(ast) => js::print(ast),
        Err(_) => String::from("salah sintaks"),
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn ext_single() {
        let js = to_js(String::from("misal x = null;\n"));
        assert_eq!(js, String::from("var x = null;\n"));
    }

    #[test]
    fn ext_multi() {
        let js = to_js(String::from("misal x = null;\nmisal y = benar;\n"));
        assert_eq!(js, String::from("var x = null;\nvar y = true;\n"));
    }
}


================================================
FILE: scripts/build-demo.sh
================================================
rustup default nightly
cargo install wasm-pack
cargo build
cd demo
wasm-pack build --target web --out-name wasm --out-dir ./static/assets

================================================
FILE: src/lib.rs
================================================



================================================
FILE: vercel.json
================================================
{
  "name": "naskah",
  "alias": ["naskah"],
  "version": 2,
  "github": {
    "enabled": false
  },
  "builds": [{ "src": "./demo/static/**", "use": "@now/static" }],
  "routes": [{ "src": "/(.*)", "dest": "demo/static/$1" }]
}
Download .txt
gitextract_c3b19t87/

├── .github/
│   └── workflows/
│       └── main.yml
├── .gitignore
├── Cargo.toml
├── LICENSE
├── README.md
├── demo/
│   ├── Cargo.toml
│   ├── README.md
│   ├── src/
│   │   └── lib.rs
│   └── static/
│       ├── index.html
│       └── style.css
├── parser/
│   ├── Cargo.toml
│   └── src/
│       ├── ast.rs
│       ├── expr.rs
│       ├── identifier.rs
│       ├── lib.rs
│       ├── literal.rs
│       ├── number.rs
│       ├── statement.rs
│       └── variable.rs
├── printer/
│   ├── Cargo.toml
│   └── src/
│       ├── js.rs
│       └── lib.rs
├── scripts/
│   └── build-demo.sh
├── src/
│   └── lib.rs
└── vercel.json
Download .txt
SYMBOL INDEX (80 symbols across 11 files)

FILE: demo/src/lib.rs
  type Model (line 11) | struct Model {
  type Msg (line 17) | enum Msg {
  constant EXAMPLE_CODE (line 21) | const EXAMPLE_CODE: &str = "misal x = 2 + 2;
  type Message (line 30) | type Message = Msg;
  type Properties (line 31) | type Properties = ();
  method create (line 32) | fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
  method update (line 40) | fn update(&mut self, msg: Self::Message) -> ShouldRender {
  method change (line 50) | fn change(&mut self, _props: Self::Properties) -> ShouldRender {
  method view (line 57) | fn view(&self) -> Html {
  function run_app (line 74) | pub fn run_app() {

FILE: parser/src/ast.rs
  type Literal (line 2) | pub enum Literal {
  type Identifier (line 10) | pub struct Identifier {
  type CallExpression (line 15) | pub struct CallExpression {
  type AssignmentExpression (line 21) | pub struct AssignmentExpression {
  type Expression (line 27) | pub enum Expression {
  type Operator (line 36) | pub enum Operator {
  type BinaryExpression (line 52) | pub struct BinaryExpression {
  type VariableDeclaration (line 59) | pub struct VariableDeclaration {
  type AlternateStatement (line 65) | pub enum AlternateStatement {
  type IfStatement (line 71) | pub struct IfStatement {
  type BlockStatement (line 78) | pub struct BlockStatement {
  type Statement (line 83) | pub enum Statement {
  type Program (line 94) | pub struct Program {

FILE: parser/src/expr.rs
  function op (line 107) | fn op() {
  function binary_expression_literals (line 130) | fn binary_expression_literals() {
  function binary_expression_as_expression (line 145) | fn binary_expression_as_expression() {
  function recursive_binary_expression (line 162) | fn recursive_binary_expression() {
  function basic_call_expression (line 181) | fn basic_call_expression() {
  function fn_with_arguments (line 197) | fn fn_with_arguments() {

FILE: parser/src/identifier.rs
  function alphanumeric (line 18) | fn alphanumeric() {
  function can_start_with_underscore (line 51) | fn can_start_with_underscore() {
  function cannot_start_with_number (line 64) | fn cannot_start_with_number() {

FILE: parser/src/lib.rs
  function parse (line 26) | pub fn parse(input: &str) -> Result<Program, nom::Err<&[u8]>> {

FILE: parser/src/literal.rs
  function boolean (line 38) | fn boolean() {
  function string (line 51) | fn string() {
  function number (line 59) | fn number() {
  function null (line 64) | fn null() {

FILE: parser/src/number.rs
  function is_bin_digit (line 9) | fn is_bin_digit(chr: u8) -> bool {
  function test_bin_literal (line 66) | fn test_bin_literal() {
  function test_decimal_literal (line 73) | fn test_decimal_literal() {
  function test_hex_literal (line 79) | fn test_hex_literal() {
  function test_binary (line 86) | fn test_binary() {
  function test_decimal (line 93) | fn test_decimal() {
  function test_hexadecimal (line 99) | fn test_hexadecimal() {
  function test_integer (line 106) | fn test_integer() {
  function test_number_literal (line 115) | fn test_number_literal() {

FILE: parser/src/statement.rs
  function empty_block_statement (line 137) | fn empty_block_statement() {
  function var_decl_inside_block (line 148) | fn var_decl_inside_block() {
  function recursive_empty_block_statement (line 166) | fn recursive_empty_block_statement() {
  function test_loop_statement (line 181) | fn test_loop_statement() {
  function test_if_statement (line 194) | fn test_if_statement() {
  function test_if_else_statement (line 222) | fn test_if_else_statement() {
  function if_special (line 245) | fn if_special() {
  function recursive_if_else (line 263) | fn recursive_if_else() {
  function simple_expression_statement (line 324) | fn simple_expression_statement() {
  function reassignment (line 340) | fn reassignment() {

FILE: parser/src/variable.rs
  function boolean_assignment (line 27) | fn boolean_assignment() {
  function string_assignment (line 43) | fn string_assignment() {
  function number_assignment (line 59) | fn number_assignment() {
  function null_assignment (line 75) | fn null_assignment() {
  function identifier_assignment (line 91) | fn identifier_assignment() {
  function binary_expression_assignment (line 109) | fn binary_expression_assignment() {

FILE: printer/src/js.rs
  function insert_indent (line 3) | fn insert_indent(depth: u8) -> String {
  function print_literal (line 22) | fn print_literal(l: Literal) -> String {
  function print_identifier (line 40) | fn print_identifier(i: Identifier) -> String {
  function print_binary_expression (line 44) | fn print_binary_expression(b: Box<BinaryExpression>) -> String {
  function print_operator (line 60) | fn print_operator(op: Operator) -> String {
  function print_call_expression (line 77) | fn print_call_expression(c: CallExpression) -> String {
  function print_assignment_expression (line 90) | fn print_assignment_expression(s: AssignmentExpression) -> String {
  function print_expression (line 98) | fn print_expression(e: Expression) -> String {
  function print_block_statement (line 108) | fn print_block_statement(b: BlockStatement, depth: u8) -> String {
  function print_variable_declaration (line 127) | fn print_variable_declaration(v: VariableDeclaration, depth: u8) -> Stri...
  function print_if_statement (line 140) | fn print_if_statement(i: IfStatement, depth: u8, inside_else: bool) -> S...
  function print_loop_statement (line 168) | fn print_loop_statement(b: BlockStatement, depth: u8) -> String {
  function print_statement (line 176) | fn print_statement(s: Statement, depth: u8) -> String {
  function print (line 198) | pub fn print(ast: Program) -> String {
  function name (line 211) | fn name() {

FILE: printer/src/lib.rs
  function to_js (line 7) | pub fn to_js(s: String) -> String {
  function ext_single (line 20) | fn ext_single() {
  function ext_multi (line 26) | fn ext_multi() {
Condensed preview — 25 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (53K chars).
[
  {
    "path": ".github/workflows/main.yml",
    "chars": 1540,
    "preview": "name: build\n\non:\n  push:\n    branches: [master]\n  pull_request:\n    branches: [master]\n\nenv:\n  CARGO_TERM_COLOR: always\n"
  },
  {
    "path": ".gitignore",
    "chars": 109,
    "preview": "/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",
    "chars": 304,
    "preview": "[package]\nname = \"naskah\"\nversion = \"0.1.0\"\nauthors = [\"Fatih Kalifa <fatihkalifa@gmail.com>\"]\nrepository = \"https://git"
  },
  {
    "path": "LICENSE",
    "chars": 1068,
    "preview": "MIT License\n\nCopyright (c) 2018 Fatih Kalifa\n\nPermission is hereby granted, free of charge, to any person obtaining a co"
  },
  {
    "path": "README.md",
    "chars": 1329,
    "preview": "# naskah [![Actions Status](https://github.com/pveyes/naskah/workflows/build/badge.svg)](https://github.com/pveyes/naska"
  },
  {
    "path": "demo/Cargo.toml",
    "chars": 348,
    "preview": "[package]\nname = \"naskah-demo\"\nversion = \"0.1.0\"\nauthors = [\"Fatih Kalifa <fatihkalifa@gmail.com>\"]\n\n[lib]\ncrate-type = "
  },
  {
    "path": "demo/README.md",
    "chars": 739,
    "preview": "# naskah-demo\n\nThis demo is built in Rust using [Yew](https://github.com/DenisKolodin/yew) framework.\n\n## Overview\nIt co"
  },
  {
    "path": "demo/src/lib.rs",
    "chars": 1977,
    "preview": "extern crate printer;\nextern crate wasm_bindgen;\nextern crate web_sys;\nextern crate yew;\n\nuse wasm_bindgen::prelude::*;\n"
  },
  {
    "path": "demo/static/index.html",
    "chars": 2755,
    "preview": "<html>\n  <head>\n    <title>Naskah - Bahasa pemrogramman dengan sintaks Bahasa Indonesia</title>\n    <link rel=\"styleshee"
  },
  {
    "path": "demo/static/style.css",
    "chars": 1461,
    "preview": "html, body {\n  margin: 0;\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarel"
  },
  {
    "path": "parser/Cargo.toml",
    "chars": 217,
    "preview": "[package]\nname = \"parser\"\nversion = \"0.1.0\"\nauthors = [\"Fatih Kalifa <fatihkalifa@gmail.com>\"]\n\n[dependencies]\nlazy_stat"
  },
  {
    "path": "parser/src/ast.rs",
    "chars": 1891,
    "preview": "#[derive(PartialEq, Debug)]\npub enum Literal {\n    Number(i64),\n    Null,\n    String(String),\n    Boolean(bool),\n}\n\n#[de"
  },
  {
    "path": "parser/src/expr.rs",
    "chars": 6571,
    "preview": "use super::ast::*;\nuse super::identifier::identifier;\nuse super::literal::literal;\n\nnamed!(\n    assignment_expression<Ex"
  },
  {
    "path": "parser/src/identifier.rs",
    "chars": 1514,
    "preview": "use super::ast::Identifier;\nuse std::str;\n\nnamed!(\n  pub identifier<Identifier>,\n  map!(\n    map_res!(re_bytes_find!(r\"^"
  },
  {
    "path": "parser/src/lib.rs",
    "chars": 475,
    "preview": "#[macro_use]\nextern crate nom;\nextern crate regex;\n\npub mod ast;\nmod expr;\nmod identifier;\nmod literal;\nmod number;\nmod "
  },
  {
    "path": "parser/src/literal.rs",
    "chars": 1422,
    "preview": "use super::ast::Literal;\nuse super::number::number_literal;\nuse std::str;\n\nnamed!(\n    boolean<bool>,\n    alt!(map!(tag!"
  },
  {
    "path": "parser/src/number.rs",
    "chars": 3216,
    "preview": "use super::ast::Literal;\nuse nom::{is_digit, is_hex_digit};\nuse std::i64;\nuse std::str;\n\nnamed!(sign, recognize!(opt!(on"
  },
  {
    "path": "parser/src/statement.rs",
    "chars": 11771,
    "preview": "use super::ast::*;\nuse super::expr::{binary_expression, expression};\nuse super::identifier::identifier;\nuse super::liter"
  },
  {
    "path": "parser/src/variable.rs",
    "chars": 3370,
    "preview": "use super::ast::*;\nuse super::expr::expression;\nuse super::identifier::identifier;\n\nnamed!(\n  pub variable_declaration<V"
  },
  {
    "path": "printer/Cargo.toml",
    "chars": 143,
    "preview": "[package]\nname = \"printer\"\nversion = \"0.1.0\"\nauthors = [\"Fatih Kalifa <fatihkalifa@gmail.com>\"]\n\n[dependencies]\nparser ="
  },
  {
    "path": "printer/src/js.rs",
    "chars": 6277,
    "preview": "use parser::ast::*;\n\nfn insert_indent(depth: u8) -> String {\n    let mut res = String::new();\n    let mut x = 0;\n\n    if"
  },
  {
    "path": "printer/src/lib.rs",
    "chars": 625,
    "preview": "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 "
  },
  {
    "path": "scripts/build-demo.sh",
    "chars": 137,
    "preview": "rustup default nightly\ncargo install wasm-pack\ncargo build\ncd demo\nwasm-pack build --target web --out-name wasm --out-di"
  },
  {
    "path": "src/lib.rs",
    "chars": 1,
    "preview": "\n"
  },
  {
    "path": "vercel.json",
    "chars": 229,
    "preview": "{\n  \"name\": \"naskah\",\n  \"alias\": [\"naskah\"],\n  \"version\": 2,\n  \"github\": {\n    \"enabled\": false\n  },\n  \"builds\": [{ \"src"
  }
]

About this extraction

This page contains the full source code of the pveyes/naskah GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 25 files (48.3 KB), approximately 12.4k tokens, and a symbol index with 80 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!