<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[aldidana]]></title><description><![CDATA[aldidana]]></description><link>https://aldidana.com/</link><image><url>https://aldidana.com/favicon.png</url><title>aldidana</title><link>https://aldidana.com/</link></image><generator>Ghost 3.42</generator><lastBuildDate>Sat, 25 Apr 2026 21:21:33 GMT</lastBuildDate><atom:link href="https://aldidana.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[A Context-Aware Kubernetes Desktop App + OpenClaw]]></title><description><![CDATA[<p><em>Talk delivered at OpenClaw Jakarta Meetup · 2026</em></p><!--kg-card-begin: html--><video width="100%" controls>
  <source src="https://cdn.aldidana.com/demo/kubemotion-demo.mp4" type="video/mp4">
</video><!--kg-card-end: html--><p>If you've ever managed multiple Kubernetes clusters (staging, production, development or client environments), you know the anxiety of running a command and realizing <em>too late</em> you were in the wrong context. That's not a skill issue. That's a tooling issue. And it's exactly</p>]]></description><link>https://aldidana.com/a-context-aware-kubernetes-desktop-app-openclaw/</link><guid isPermaLink="false">69de710a0bead449b20d2c8d</guid><category><![CDATA[kubernetes]]></category><category><![CDATA[openclaw]]></category><category><![CDATA[llm]]></category><category><![CDATA[ai]]></category><category><![CDATA[ai agent]]></category><category><![CDATA[rust]]></category><category><![CDATA[egui]]></category><dc:creator><![CDATA[Aldi Perdana]]></dc:creator><pubDate>Tue, 14 Apr 2026 17:07:12 GMT</pubDate><content:encoded><![CDATA[<p><em>Talk delivered at OpenClaw Jakarta Meetup · 2026</em></p><!--kg-card-begin: html--><video width="100%" controls>
  <source src="https://cdn.aldidana.com/demo/kubemotion-demo.mp4" type="video/mp4">
</video><!--kg-card-end: html--><p>If you've ever managed multiple Kubernetes clusters (staging, production, development or client environments), you know the anxiety of running a command and realizing <em>too late</em> you were in the wrong context. That's not a skill issue. That's a tooling issue. And it's exactly the problem I set out to solve.</p><p>At the OpenClaw Jakarta Meetup, I walked through how I'm building <strong>Kubemotion</strong>, a cross-platform Kubernetes desktop client built with Rust and egui, with OpenClaw embedded at its core.</p><h2 id="the-problem">The Problem</h2><p>Most of us have internalized the Kubernetes friction so deeply that we stop noticing it. But look at the workflow honestly:</p><ul><li><strong>Remembering kubectl flags</strong>: resource names, flags, and context switching all live in your head</li><li><strong>Hunting across screens</strong>: logs, events, and rollout state are scattered across tabs and commands</li><li><strong>Sharing context in incidents</strong>: explaining "what I'm seeing" to a teammate is slower than it should be</li></ul><p>When something breaks in production, you don't want to juggle five terminal tabs and a Slack thread. What engineers actually want is simple: <em>"Why is THIS failing?"</em> with context already attached, no copy-pasting. One place to see -&gt; ask -&gt; act. And when writes are involved, safe actions with explicit confirmation.</p><h2 id="the-idea-chat-that-drives-the-interface">The Idea: Chat That Drives the Interface</h2><p>The core insight behind Kubemotion is this: <strong>put OpenClaw inside the K8s desktop app</strong>. Not "chat in Telegram." Chat that <em>drives</em> the interface.</p><p>The interaction model is a three-step loop:</p><ol><li><strong>Click a resource</strong> - a Pod, Deployment, or Namespace automatically becomes the chat context</li><li><strong>Ask in plain language</strong> - "why is this crashing?", "show previous logs", "what changed?"</li><li><strong>The UI reacts and answers</strong> - it opens logs/events, highlights the problem, and offers next actions right there in the interface</li></ol><p>This is what makes it different from bolting a chatbot onto a dashboard. The selected resource <em>is</em> the context. No prompt engineering required - the app already knows what you're looking at.</p><h2 id="architecture">Architecture</h2><p>The system has three layers that communicate cleanly:</p><!--kg-card-begin: html--><table>
<thead>
<tr>
<th>Layer</th>
<th>Responsibilities</th>
</tr>
</thead>
<tbody>
<tr>
<td><strong>Desktop App</strong></td>
<td>Explorer + chat panel, resource context, action buttons</td>
</tr>
<tr>
<td><strong>OpenClaw Gateway</strong></td>
<td>Auth + policy, tool allowlist, audit trail, kubeconfig via HTTP or WebSocket</td>
</tr>
<tr>
<td><strong>Kubernetes</strong></td>
<td>Read: logs / events / describe; Write: optional + confirmed; RBAC</td>
</tr>
</tbody>
</table><!--kg-card-end: html--><p>The Gateway sits in the middle as a controlled bridge. It doesn't give the agent unrestricted access to your cluster. It enforces which tools are available, routes kubeconfig, and keeps a full audit trail of every interaction.</p><h2 id="mvp-scope-v1-">MVP Scope (v1)</h2><p>The first version is deliberately focused. The must-have capabilities are:</p><ul><li><strong>Auto context</strong>: cluster / namespace / selected resource passed automatically with every message</li><li><strong>Read tools</strong>: logs, events, describe, rollout status</li><li><strong>UI actions</strong>: open logs, jump to deployment, highlight failures</li><li><strong>Action cards</strong>: quick-tap shortcuts - "Open logs" · "Show events" · "Explain cause"</li></ul><p>The goal for v1 is to make the <em>read</em> side feel completely native. You click a pod, ask a question, and the app responds and navigates itself. No terminal switching. No copying resource names by hand.</p><h2 id="security-first-guardrails-by-design">Security First: Guardrails by Design</h2><p>Giving an AI agent access to a Kubernetes cluster isn't something to do carelessly. The security model is intentional and conservative.</p><p><strong>Policy:</strong></p><ul><li>Tool allowlist: start read-only and expand deliberately</li><li>RBAC maps to <em>the user</em>, not a god agent with blanket permissions</li><li>All write operations require explicit confirmation before execution</li></ul><p><strong>Operations:</strong></p><ul><li>Full audit trail: who asked and what was executed</li><li>Scopes enforced at cluster / namespace / resource level</li><li>Rate limits and safe defaults throughout</li></ul><p>For the OpenClaw Gateway, the baseline hardening config looks like this:</p><pre><code class="language-yaml">gateway:
  bind: loopback
  auth:
    mode: token
    token: ${OPENCLAW_GATEWAY_TOKEN}
    rateLimit:
      maxAttempts: 10
      windowMs: 60000
      lockoutMs: 300000
      exemptLoopback: true</code></pre><p>Three non-negotiable rules: always authenticate (token or password, never skip it), bind to loopback unless a proxy is explicitly in front, and enable rate limiting with a lockout window.</p>]]></content:encoded></item><item><title><![CDATA[Membuat Bahasa Pemrograman Sederhana dengan Rust dan LLVM - Bagian 3 JIT Compiler]]></title><description><![CDATA[<p>Setelah kita membuat <em>Lexer</em> dan <em>Parser </em>kita memerlukan 1 hal lagi yaitu fungsi untuk mengevaluasi <em>expression</em> yang sudah ada.</p><p>Untuk kode yang sudah lengkap bisa dilihat di github: <a href="https://github.com/aldidana/hitung">https://github.com/aldidana/hitung</a></p><blockquote>Kita bisa saja mengevaluasi expression yang sudah ada langsung menggunakan Rust tanpa llvm atau kita juga bisa</blockquote>]]></description><link>https://aldidana.com/membuat-bahasa-pemrograman-rust-llvm-jit-compiler/</link><guid isPermaLink="false">60016ec972f9c00e191748f4</guid><category><![CDATA[rust]]></category><category><![CDATA[llvm]]></category><dc:creator><![CDATA[Aldi Perdana]]></dc:creator><pubDate>Tue, 19 Jan 2021 04:00:00 GMT</pubDate><content:encoded><![CDATA[<p>Setelah kita membuat <em>Lexer</em> dan <em>Parser </em>kita memerlukan 1 hal lagi yaitu fungsi untuk mengevaluasi <em>expression</em> yang sudah ada.</p><p>Untuk kode yang sudah lengkap bisa dilihat di github: <a href="https://github.com/aldidana/hitung">https://github.com/aldidana/hitung</a></p><blockquote>Kita bisa saja mengevaluasi expression yang sudah ada langsung menggunakan Rust tanpa llvm atau kita juga bisa mentranslate expression yang ada tadi menjadi sebuah Bytecode yang akan dijalankan di VM</blockquote><p>Namun untuk tutorial kali ini kita akan menjalankannya dengan menggunakan J<em>IT (Just In Time) compiler </em>dari <em>llvm</em>.</p><p>LLVM adalah sebuah <em>compiler infrastructure </em>dimana kita dapat membuat sebuah instruksi berupa<em> intermediate representation</em> atau <em>IR</em>, kemudian llvm juga dapat meng-<em>compile IR </em>menjadi binary yang berdiri sendiri (<em>standalone binary</em>) atau melakukan <em>Just In Time compilation</em> pada saat <em>runtime</em>.</p><p>Dibawah ini adalah contoh <em>IR</em> dari kode <code><strong>if 1 &lt; 2 then 1 else 0</strong></code>. </p><figure class="kg-card kg-code-card"><pre><code class="language-llvm">define double @@berhitung() {
entry:
  br i64 1, label %entry1, label %entry2

entry1:                                           ; preds = %entry
  br label %entry3

entry2:                                           ; preds = %entry
  br label %entry3

entry3:                                           ; preds = %entry2, %entry1
  %entry4 = phi double [ 1.000000e+00, %entry1 ], [ 0.000000e+00, %entry2 ]
  ret double %entry4
}</code></pre><figcaption>if.ll</figcaption></figure><p>Kita akan menggunakan <em>library</em> <a href="https://github.com/TheDan64/inkwell"><strong>Inkwell</strong></a>. <strong>Inkwell</strong> merupakan sebuah <em>high-level wrapper</em> dari <em>library</em> <a href="https://crates.io/crates/llvm-sys"><strong>llvm-sys</strong></a>.</p><h2 id="llvm">LLVM</h2><p>Untuk mengikutin ini pastikan kia sudah meng-<em>install llvm </em><strong>versi 10</strong>.</p><p>Untuk mengecek versi llvm yang sudah terinstall bisa menggunakan perintah ini:</p><pre><code class="language-bash">llvm-config --version</code></pre><h2 id="struktur">Struktur</h2><pre><code>hitung
│   Cargo.toml   
│
└───src
	| main.rs
	| lexer.rs
   	| token.rs
	| expression.rs
	| parser.rs
	| jit.rs //file baru</code></pre><h2 id="cargo-toml">Cargo.toml</h2><p>Buka file cargo.toml dan tambahkan <em>library</em> <em>inkwell</em>.</p><pre><code class="language-toml">[package]
name = "hitung"
version = "0.1.0"
authors = ["Nama Author"]
edition = "2018"

[dependencies]
inkwell = { git = "https://github.com/TheDan64/inkwell", branch = "master", features = ["llvm10-0"] }</code></pre><h2 id="jit-just-in-time-">JIT (Just In Time)</h2><p>Kita akan membutuhkan 3 objek yang ada di <em>llvm</em>.</p><ol><li>Context: Bagian inti/<em>core</em> dari <em>llvm</em></li><li>Module: Tempat dimana fungsi dan <em>IR</em> di simpan</li><li>Builder: Yang akan melakukan instruksi kemudian memasukannya ke dalam <em>BasicBlock</em></li></ol><p>Buka file <strong>jit.rs </strong>dan tambahkan barisan kode berikut ini:</p><pre><code class="language-rust">use std::collections::HashMap;
use std::path::Path;

use inkwell;
use inkwell::builder::Builder;
use inkwell::context::Context;
use inkwell::module::Module;
use inkwell::values::PointerValue;
use inkwell::FloatPredicate;
use inkwell::OptimizationLevel;

use crate::expression::Expression;
use crate::lexer::Lexer;
use crate::parser::Parser;
use crate::token::Token;

pub type FuncSign = unsafe extern "C" fn() -&gt; f64;</code></pre><p>Kita menambahkan beberapa <em>module</em> yang berkaitan dengan<em> inkwell</em> dan membuat<em> </em>tipe data yang dibutuhkan untuk hasil dari fungsi yang dipanggil oleh <em>llvm</em>.</p><p>Untuk bagian <code>pub type FuncSign = unsafe extern "C" fn() -&gt; f64;</code> disini adalah kita akan memanggil eksternal kode agar dapat menggunakan suatu fungsi dari bahasa pemrograman lain dalam hal ini bahasa pemrograman C, bagian "C" diatas disebut ABI <em>(Application Binary Interface)</em> sedangkan<em> keyword extern</em> yang memfasilitasi FFI <em>(Foreign Function Interface)</em>. Penggunaan <em>keyword extern </em>sendiri tidak membutuhkan <em>keyword</em> <em>unsafe</em> tetapi untuk memanggilnya dibutuhkan <em>keyword</em> <em>unsafe, </em>dikarenakan Rust tidak mengetahui aturan dan jaminan<em> </em>dari bahasa lain tersebut.</p><p>Tambahkan kode berikut setelah kode diatas:</p><pre><code class="language-rust">pub struct Compiler&lt;'ctx&gt; {
    context: &amp;'ctx Context,
    module: Module&lt;'ctx&gt;,
    builder: Builder&lt;'ctx&gt;,

    variables: HashMap&lt;String, PointerValue&lt;'ctx&gt;&gt;,
    debug: bool,
}</code></pre><p><em>Struct</em> <em>Compiler</em> terdiri dari <strong>Context</strong> untuk menyimpan konteks <em>llvm</em> selama program berjalan, <strong>Module</strong>, <strong>Builder</strong>, <strong>Variables</strong> untuk menyimpan <strong><em>Global Variable </em></strong>berupa <em>HashMap</em> yang isinya adalah sebuah <em><strong>pointer</strong></em>, <strong>Debug</strong> untuk menampilkan <em>IR </em>yang telah dibuat.</p><p>Kemudian lanjutkan dengan membuat <em>associate function</em> dan <em>method</em> untuk <em>Compiler</em> diatas.</p><figure class="kg-card kg-code-card"><pre><code class="language-rust">impl&lt;'ctx&gt; Compiler&lt;'ctx&gt; {
    pub fn new(context: &amp;'ctx Context, debug: bool) -&gt; Self {
        let module = context.create_module("hitung");
        let builder = context.create_builder();

        Compiler {
            context,
            module,
            builder,
            variables: HashMap::new(),
            debug,
        }
    }
    pub fn compile_source(&amp;mut self, source: &amp;str) -&gt; Result&lt;f64, String&gt; {
        let lexer = Lexer::new(source);
        let tokens = lexer.lex();
        let mut parser = Parser::new(tokens);
        match parser.expr(0) {
            Ok(expression) =&gt; self.jit_compile(expression),
            Err(e) =&gt; Err(e),
        }
    }
    .... // fungsi berikutnya
}</code></pre><figcaption>jit.rs</figcaption></figure><p>Fungsi <em>new</em> diatas menerima sebuah <em>reference</em> <em>Context</em> dari fungsi yang memanggilnya karena kita membutuhkannya selama program berjalan, fungsi ini juga akan membuat sebuah module yang dinamai "hitung".</p><p>Fungsi <em>compile_source </em>akan memanggil <em>Lexer</em> dan <em>Parser</em> untuk menghasilkan <em>expression</em> dari kode sumber yang sudah dimasukan, fungsi ini akan memanggil fungsi <em>jit_compile </em>jika dapat menghasilkan <em>expression</em> yang benar<em>.</em></p><p>Tambahkan fungsi <em>jit_compile</em> berikut setelah fungsi <em>compile_source</em>:</p><figure class="kg-card kg-code-card"><pre><code class="language-rust">... // fungsi sebelumnya
pub fn jit_compile(&amp;mut self, expr: Expression) -&gt; Result&lt;f64, String&gt; {
    let float = self.context.f64_type();
    let fn_type = float.fn_type(&amp;[], false);
    let function = self.module.add_function("berhitung", fn_type, None);
    let basic_block = self.context.append_basic_block(function, "entry");
    self.builder.position_at_end(basic_block);

    let return_val = self.eval(expr)?;
    self.builder.build_return(Some(&amp;return_val));

    let execution_engine = self
        .module
        .create_jit_execution_engine(OptimizationLevel::None)
        .map_err(|e| e.to_string())
        .unwrap();

    let last_func = self.module.get_last_function().unwrap();
    let last_func_name = last_func.get_name().to_str().unwrap();

    let function_calc = unsafe { execution_engine.get_function::&lt;FuncSign&gt;(last_func_name) };

    if self.debug {
        println!("LLVM IR:");
        function.print_to_stderr();
        self.module
            .print_to_file(Path::new("hitung.ll"))
            .expect("Error print to file");
    }

    match execution_engine.remove_module(&amp;self.module) {
        Ok(_ok) =&gt; match function_calc {
            Ok(f) =&gt; Ok(unsafe { f.call() }),
            Err(err) =&gt; {
                println!("!&gt; Error during execution: {:?}", err);
                Err(err.to_string())
            }
        },
        Err(err) =&gt; Err(err.to_string()),
    }
}</code></pre><figcaption>jit.rs</figcaption></figure><p>Fungsi <em>jit_compile </em>akan membuat sebuah fungsi "berhitung" di dalam <em>module "hitung"</em>  dengan tipe data <em>f64 </em>dan fungsi ini tidak mempunyai parameter. Selanjutnya kita membuat sebuah <em>basic block</em> yang dinamai "entry", <em>basic block</em> adalah urutan untuk instruksi, <em>basic block</em> disini mengacu pada fungsi yang sudah dibuat yaitu fungsi "berhitung". Lalu kita menginformasikan <em>builder</em> untuk mulai dari/berpindah ke <em>basic block </em>ini. <em>Basic block</em> dari fungsi ini juga membutuhkan tipe data yang akan dikembalikan, oleh karena itu tipe data yang akan dikembalikan diambil setelah mengevaluasi expression yang ada dari fungsi <em>eval</em>.</p><p>Berikut ini contoh fungsi dan <em>basic block llvm</em>:</p><pre><code class="language-llvm">define double @berhitung() {
entry:
	ret double 1.000000e+00
}</code></pre><p>Selanjutnya membuat <em>execution_engine</em> dari <em>module</em> yang sudah kita buat sebelumnya, <em>execution_engine</em> akan menjalankan fungsi yang sudah diisi <em>basic block</em>. Disini juga kita akan mencetak setiap hasil dari module tersebut ke file hitung.ll jika <em>Debug</em> diset menjadi <em>true</em>. Kemudian <em>execution_engine</em> melepas<em> module </em>yang kita buat sebelumnya agar kita bisa membuat kembali<em> execution_engine </em>baru dengan module yang sama dari setiap kode sumber yang dimasukan.</p><h3 id="aritmatika">Aritmatika</h3><p>Sebelum ke tahap selanjutnya, disini akan membahas fungsi aritmatika yang ada di <em>llvm.</em></p><p>Misalnya kita mempunyai sebuah fungsi penjumlahan seperti ini:</p><pre><code class="language-rust">fn add(lhs: f64, rhs: f64) -&gt; f64 {
	lhs + rhs
}</code></pre><p>Dalam <em>library</em> <em>Inkwell</em> kita bisa memanggilnya dengan fungsi yang mirip seperti fungsi diatas:</p><pre><code class="language-rust">builder.build_float_add(lhs, rhs, "add") // nama bisa diisi apa saja</code></pre><p>Maka fungsi diatas akan langsung mengembalikan jawaban yang sesuai.</p><h2 id="eval">Eval</h2><p>Kembali ke file <strong>jit.rs</strong>,<strong> </strong>setelah fungsi<strong> </strong><em>jit_compile</em><strong> </strong>tambahkan kode berikut:</p><figure class="kg-card kg-code-card"><pre><code class="language-rust">fn eval(
    &amp;mut self,
    expression: Expression,
) -&gt; Result&lt;inkwell::values::FloatValue&lt;'ctx&gt;, String&gt; {
    match expression {
        Expression::Variable(name) =&gt; match self.variables.get(&amp;name) {
            Some(value) =&gt; {
                let val = self.builder.build_load(*value, name.as_str());
                Ok(val.into_float_value())
            }
            None =&gt; Err("Variable not declared".to_string()),
        },
        Expression::Num(n) =&gt; {
            let float = self.context.f64_type();
            Ok(float.const_float(n as f64))
        }
        Expression::Unary(operator, expr) =&gt; match operator {
            Token::Add =&gt; {
                let num = self.eval(*expr)?;

                Ok(num)
            }
            Token::Sub =&gt; {
                let float = self.context.f64_type();
                let num = self.eval(*expr)?;
                let rhs = float.const_float_from_string("-1");
                let sum = self.builder.build_float_mul(num, rhs, "mul");
                Ok(sum)
            }
            _ =&gt; Err("Expression for Unary must be + or -".to_string()),
        },
        ... // berikutnya
	}
}</code></pre><figcaption>jit.rs</figcaption></figure><p><strong>catatan: fungsi <em>eval</em> dipanggil dengan cara <em>recursive</em> untuk mengevaluasi <em>expression</em> lainnya.</strong></p><p>Jika <em>expression</em> itu adalah <em>expression</em> untuk <em>variable </em>maka kita akan mencari apakah <em>variable</em> tersebut ada atau tidak, <em>variable</em> saat ini disimpan di <em>global variable</em>, jika <em>variable</em> tersebut ada maka kita mengambil <em>pointer</em> dari isi <em>variable</em> tersebut.</p><p>Jika expression berupa angka atau <em>num </em>maka kita hanya perlu mengembalikan angka tersebut dengan tipe data yang sudah ditentukan (dalam hal ini <em>f64</em>)</p><p>Jika expression tersebut berupa <em>Unary</em> operator maka kita perlu menentukan bagian kiri dan bagian kanan yang ada dari expression tersebut, misalnya jika +3 maka akan menjadi 3 tetapi jika -3 tetap menjadi -3, disini kita hanya mengecek <em>unary</em> untuk +angka dan -angka.</p><p>Masih didalam fungsi <em>eval</em>, tambahkan kode berikutnya:</p><figure class="kg-card kg-code-card"><pre><code class="language-rust">... // sebelumnya
Expression::Binary(left, operator, right) =&gt; match operator {
    Token::LT | Token::GT =&gt; {
        let lhs = self.eval(*left)?;
        let rhs = self.eval(*right)?;

        let predicate = match operator {
            Token::LT =&gt; FloatPredicate::OLT,
            Token::GT =&gt; FloatPredicate::OGT,
            _ =&gt; return Err("Operator not supported".to_string()),
        };

        let conditional = self.builder.build_float_compare(predicate, lhs, rhs, "if");

        Ok(self.builder.build_unsigned_int_to_float(
            conditional,
            self.context.f64_type(),
            "bool",
        ))
    },
    Token::ASSIGN =&gt; match *left {
        Expression::Variable(var) =&gt; {
            let f64_type = self.context.f64_type();
            let global = self.module.add_global(f64_type, None, var.as_str());
            global.set_initializer(&amp;f64_type.const_float(0 as f64));
            let rhs = self.eval(*right)?;
            global.set_initializer(&amp;rhs);
            self.builder.build_store(global.as_pointer_value(), rhs);

            let value = self
                .builder
                .build_load(global.as_pointer_value(), var.as_str());
            self.variables.insert(var, global.as_pointer_value());

            Ok(value.into_float_value())
        }
        _ =&gt; Err("Assignment must be a variable".to_string()),
    },
    _ =&gt; {
        let lhs = self.eval(*left)?;
        let rhs = self.eval(*right)?;

        match operator {
            Token::Add =&gt; Ok(self.builder.build_float_add(lhs, rhs, "add")),
            Token::Sub =&gt; Ok(self.builder.build_float_sub(lhs, rhs, "sub")),
            Token::Mul =&gt; Ok(self.builder.build_float_mul(lhs, rhs, "mul")),
            Token::Div =&gt; Ok(self.builder.build_float_div(lhs, rhs, "div")),
            _ =&gt; Err("Operator not supported".to_string()),
        }
    }
},
Expression::Paren(expr) =&gt; {
    let result = &amp;self.eval(*expr)?;
    Ok(*result)
}
... // berikutnya</code></pre><figcaption>jit.rs</figcaption></figure><p>Untuk <em>binary</em> operator:</p><ul><li>Jika <em>expression</em> berupa sebuah komparasi lebih besar (GT) dari atau lebih kecil dari (LT) <em>llvm</em> memiliki sebuah <em>predicate</em> sesuai dengan dua hal tersebut, komparasi akan mengembalikan angka 1 jika <em>true</em> dan 0 jika <em>false</em>.</li><li>Jika <em>expression</em> berupa <em>Variable Assignment </em>maka kita akan mengambil angka dari <em>variable</em> tersebut lalu menyimpannya di<em> global variable, </em>disini juga kita langsung mengembalikan angka dari <em>variable</em> tersebut.</li><li>Jika <em>Binary operator</em> bukan dari kedua diatas, mak<em>a binary operator</em> tersebut adalah sebuah aritmatika.</li></ul><p>Kemudian jika <em>expression</em> yang ditemukan berupa tanda kurung maka kita akan mengevaluasi <em>expression</em> yang ada didalamnya.</p><p>Terakhir masih dengan fungsi yang sama yaitu fungsi <em>eval, </em>tambahkan kode dibawah ini:</p><figure class="kg-card kg-code-card"><pre><code class="language-rust">... // sebelumnya
Expression::Conditional(cond, then, els) =&gt; {
    let if_cond = self.eval(*cond)?;

    let function = self.module.get_last_function().unwrap();

    let then_block = self.context.append_basic_block(function, "entry");
    let else_block = self.context.append_basic_block(function, "entry");
    let cont_block = self.context.append_basic_block(function, "entry");

    let i64_type = self.context.i64_type();

    self.builder.build_conditional_branch(
        if_cond.const_to_unsigned_int(i64_type),
        then_block,
        else_block,
    );
    self.builder.position_at_end(then_block);

    let then_value = self.eval(*then)?;
    self.builder.build_unconditional_branch(cont_block);
    let then_block = self.builder.get_insert_block().unwrap();

    // build else block
    self.builder.position_at_end(else_block);
    let else_value = self.eval(*els)?;
    self.builder.build_unconditional_branch(cont_block);

    let else_block = self.builder.get_insert_block().unwrap();
    self.builder.position_at_end(cont_block);

    let phi = self.builder.build_phi(self.context.f64_type(), "entry");
    phi.add_incoming(&amp;[(&amp;then_value, then_block), (&amp;else_value, else_block)]);

    Ok(phi.as_basic_value().into_float_value())
}</code></pre><figcaption>jit.rs</figcaption></figure><p>Untuk <em>expression</em> terakhir adalah <em>branching/conditional</em> atau pencabangan. Kita menambahkan 3 <em>basic block</em> dari fungsi yang sudah dibuat, yaitu <em>basic block</em> untuk <em>then</em>, <em>else</em> dan <em>cont</em> yang dinamai "entry", jika memiliki nama <em>basic block</em> yang sama makan nama tersebut akan ditambahkan angka, misalnya "entry" -&gt; "entry1" -&gt; "entry2". Untuk menentukan cabang mana yang akan dijalankan adalah dengan <em><strong>phi node </strong></em>dengan memberikan daftar instruksi dari sebuah <em>basic block</em> dan nilai dari <em>basic block</em> itu sendiri.</p><h3 id="test-jit">Test JIT</h3><p>Tambahkan kode ini di bagian paling bawah untuk mengetest fungsi yang ada pada file <strong>jit.rs</strong> ini:</p><pre><code class="language-rust">impl&lt;'ctx&gt; Compiler&lt;'ctx&gt; {
 ....
}

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

    #[test]
    fn test_eval_from_expression() {
        let expression = Expression::Binary(
            Box::new(Expression::Binary(
                Box::new(Expression::from(3)),
                Token::Add,
                Box::new(Expression::from(2)),
            )),
            Token::Sub,
            Box::new(Expression::from(2)),
        );

        let context = Context::create();
        let module = context.create_module("test_hitung");
        let builder = context.create_builder();

        let mut compiler = Compiler {
            context: &amp;context,
            module,
            builder,
            variables: Default::default(),
            debug: false,
        };

        let actual = compiler.jit_compile(expression).unwrap();
        assert_eq!(3.0, actual);
    }

    #[test]
    fn test_eval_from_source() {
        let context = Context::create();
        let mut compiler = Compiler::new(&amp;context, false);

        let actual = compiler.compile_source(r"2 + 2 * 3 / 2").unwrap();

        assert_eq!(5.0, actual);
    }

    #[test]
    fn test_eval_from_source_if_then_else() {
        let context = Context::create();
        let mut compiler = Compiler::new(&amp;context, false);

        let actual = compiler.compile_source(r"if 1 &lt; 2 then 123 else 456").unwrap();

        assert_eq!(123.0, actual);
    }
}</code></pre><p>jalankan test dan pastikan semuanya sukses</p><pre><code class="language-rust">cargo test</code></pre><h2 id="main">Main</h2><p>Saatnya kembali ke file <strong>main.rs </strong>untuk menggunakan <em>jit compiler</em> yang telah dibuat, pastikan file <strong>main.rs</strong> menjadi seperti dibawah ini:</p><pre><code class="language-rust">use std::io;
use std::io::Write;

use inkwell::context::Context;

mod expression;
mod jit;
mod lexer;
mod parser;
mod token;

use jit::Compiler;

fn main() {
    let mut debug = false;

    for arg in std::env::args() {
        match arg.as_str() {
            "debug" =&gt; debug = true,
            _ =&gt; (),
        }
    }

    let context = Context::create();
    let mut compiler = Compiler::new(&amp;context, debug);

    loop {
        // repl
        println!();
        print!("&gt; ");

        io::stdout().flush().expect("Error when flush stdout.");

        let mut input = String::new();

        io::stdin()
            .read_line(&amp;mut input)
            .expect("Could not read from standard input.");

        match compiler.compile_source(input.as_str()) {
            Ok(result) =&gt; println!("{}", result),
            Err(err) =&gt; {
                eprintln!("Error {:?}", err);
                break
            },
        }
    }
}
</code></pre><p>Jalankan program dan aktifkan <em>debug</em> mode</p><pre><code class="language-rust">cargo run debug</code></pre><p>Silahkan ketikan input yang diinginkan, contoh:</p><pre><code class="language-rust">a = 2 // tekan enter
a + 2 * 3 tekan enter</code></pre><p>Maka hasilnya adalah <code><strong>8</strong></code></p><p>contoh menghitung:</p><pre><code class="language-rust">2 + ( 4 + 5) * 7 / 2 * 4</code></pre><p>Hasilnya <code><strong>128</strong></code></p><p>contoh <em>if then else</em>:</p><pre><code class="language-rust">if 20 &gt; 15 then 100 else 99</code></pre><p>Hasilnya <code><strong>100</strong></code></p><p>Jika <em>debug</em> aktif pastikan anda dapat melihat file <strong>hitung.ll</strong></p><p>Masih banyak sekali hal yang dapat ditingkatkan di bahasa "hitung" ini, misalnya menambahkan fungsi dan memanggil fungsi tersebut, <em>for loop, if then else </em>yang tidak hanya 1 level.</p><p>Sekian.</p><ol><li>Bagian 1 - <a href="https://aldidana.com/membuat-bahasa-pemrograman-rust-llvm-lexer/">https://aldidana.com/membuat-bahasa-pemrograman-rust-llvm-lexer/</a></li><li>Bagian 2 - <a href="https://aldidana.com/membuat-bahasa-pemrograman-rust-llvm-parser/">https://aldidana.com/membuat-bahasa-pemrograman-rust-llvm-parser/</a></li><li>(Disini) Bagian 3 - <a href="https://aldidana.com/membuat-bahasa-pemrograman-rust-llvm-jit-compiler/">https://aldidana.com/membuat-bahasa-pemrograman-rust-llvm-jit-compiler/</a></li></ol>]]></content:encoded></item><item><title><![CDATA[Membuat Bahasa Pemrograman Sederhana dengan Rust dan LLVM - Bagian 2 Parser]]></title><description><![CDATA[<p>Tahap berikutnya setelah melakukan <em>lexical analysis</em> adalah <em><strong>Parsing</strong></em>. </p><p>Untuk kode yang sudah lengkap bisa dilihat di github: <a href="https://github.com/aldidana/hitung">https://github.com/aldidana/hitung</a></p><figure class="kg-card kg-image-card kg-width-full"><img src="https://aldidana.com/content/images/2021/01/parser.png" class="kg-image" alt="Ast"></figure><p>Proses <strong><em>parsing </em></strong>akan memvalidasi <em>token</em> yang sudah ada menjadi sebuah <strong>AST (Abstract Syntax Tree)</strong><em>,</em><strong> AST </strong>yaitu<strong> </strong>sebuah pohon yang berisikan <em>Expression</em> dari <em>token</em> yang ada seperti pada gambar</p>]]></description><link>https://aldidana.com/membuat-bahasa-pemrograman-rust-llvm-parser/</link><guid isPermaLink="false">600005e372f9c00e191746bf</guid><category><![CDATA[rust]]></category><category><![CDATA[llvm]]></category><dc:creator><![CDATA[Aldi Perdana]]></dc:creator><pubDate>Tue, 19 Jan 2021 03:00:00 GMT</pubDate><content:encoded><![CDATA[<p>Tahap berikutnya setelah melakukan <em>lexical analysis</em> adalah <em><strong>Parsing</strong></em>. </p><p>Untuk kode yang sudah lengkap bisa dilihat di github: <a href="https://github.com/aldidana/hitung">https://github.com/aldidana/hitung</a></p><figure class="kg-card kg-image-card kg-width-full"><img src="https://aldidana.com/content/images/2021/01/parser.png" class="kg-image" alt="Ast"></figure><p>Proses <strong><em>parsing </em></strong>akan memvalidasi <em>token</em> yang sudah ada menjadi sebuah <strong>AST (Abstract Syntax Tree)</strong><em>,</em><strong> AST </strong>yaitu<strong> </strong>sebuah pohon yang berisikan <em>Expression</em> dari <em>token</em> yang ada seperti pada gambar diatas.</p><p>Untuk parser kita akan menggunakan teknik <em><strong><a href="https://en.wikipedia.org/wiki/Operator-precedence_parser#Pratt_parsing">Pratt Parser</a></strong></em> yang berdasarkan <strong><em>Recursive Descent</em></strong>. <strong><em>Pratt Parser </em></strong>dan<strong><em> Recursive Descent </em></strong>mengambil paradigma dari <strong><em>Top Down Precedence Parsing </em></strong>menerapkan <em>leftmost derivation </em>yaitu akan memproses dari <em>root </em>atau akarnya kemudian dari kiri ke kanan.</p><p>Disini kita akan menggunakan nilai<em> left binding power</em> dari sebuah token seperti yang ada di kode <strong>token.rs</strong></p><figure class="kg-card kg-code-card"><pre><code class="language-rust">....
pub fn lbp(&amp;self) -&gt; usize {
	match *self {
        Token::Add =&gt; 10,
        Token::Sub =&gt; 10,
        Token::Mul =&gt; 20,
        Token::Div =&gt; 20,
        Token::LParen =&gt; 99,
        Token::ASSIGN =&gt; 100,
        Token::RParen =&gt; 0,
        _ =&gt; 0,
    }
}</code></pre><figcaption>token.rs</figcaption></figure><h2 id="struktur">Struktur</h2><pre><code>hitung
│   Cargo.toml   
│
└───src
	| main.rs
	| lexer.rs
   	| token.rs
	| expression.rs // file baru
	| parser.rs // file baru</code></pre><h2 id="expression">Expression</h2><p>Pertama kita akan membuat sebuah file <strong>expression.rs </strong>yaitu representasi dari AST.</p><p>Tambahkan kode berikut ini:</p><figure class="kg-card kg-code-card"><pre><code class="language-rust">use crate::token::Token;

#[derive(Debug, PartialEq)]
pub enum Expression {
    Num(f64),
    Unary(Token, Box&lt;Expression&gt;),
    Binary(Box&lt;Expression&gt;, Token, Box&lt;Expression&gt;),
    Paren(Box&lt;Expression&gt;),
    Variable(String),
    Conditional(Box&lt;Expression&gt;, Box&lt;Expression&gt;, Box&lt;Expression&gt;),
}

impl From&lt;isize&gt; for Expression {
    fn from(n: isize) -&gt; Self {
        Expression::Num(n as f64)
    }
}</code></pre><figcaption>expression.rs</figcaption></figure><p>Untuk <em>enum</em> <em><strong>Expression </strong></em>terutama dibagian yang menggunakan <strong><em>Box </em></strong>dimana<strong> </strong>berisikan<strong> </strong><em>enum</em><strong> <em>Expression</em> </strong>itu sendiri (<em>Recursive structure</em>) kita membutuhkan <em><strong>Bo</strong></em><strong><em>x&lt;T&gt;</em></strong>. Untuk <em>Recursive structure </em>dibutuhkan sebanyak apa alokasi ukuran/<em>size</em> yang dibutuhkan di memory, contoh dibawah ini tidak akan bisa <em>dicompile:</em></p><pre><code class="language-rust">pub enum Expression {
	...
    Unary(Token, Expression),
    // disini compiler tidak tahu seberapa banyak alokasi yang dibutuhkan
    // dengan menggunakan Box yang dimana sudah ditentukan ukurannya
    // maka untuk `Unary` kita akan tahu berapa ukuran yang dibutuhkan
}</code></pre><p><strong><em>Box</em></strong> adalah <strong><em>smart pointer </em></strong>yang mengalokasikan data ke dalam <em>heap</em> dengan nilai bertipe data <strong>T, <em>Box </em></strong>termasuk<strong><em> smart pointer</em> </strong>dikarenakan ketika<strong> <em>Box</em> </strong>sudah tidak digunakan lagi maka otomatis<strong> <em>destructor</em> </strong>untuk<strong> <em>Box</em> </strong>tersebut akan dipanggil dan object yang ada didalamnya akan dihapus serta memory di <strong><em>heap</em> </strong>akan dihapus/<em>freed</em>. </p><h2 id="parser">Parser</h2><p>Untuk selanjutnya buat file <strong>parser.rs </strong>dan tambahkan kode berikut:</p><figure class="kg-card kg-code-card"><pre><code class="language-rust">use std::iter::Iterator;
use std::iter::Peekable;
use std::vec::IntoIter;

use crate::expression::Expression;
use crate::token::Token;

pub struct Parser {
    tokens: Peekable&lt;IntoIter&lt;Token&gt;&gt;,
}

impl Parser {
    pub fn new(tokens: Vec&lt;Token&gt;) -&gt; Self {
        Parser {
            tokens: tokens.into_iter().peekable(),
        }
    }

    pub fn handle_next(&amp;mut self) -&gt; Result&lt;Token, String&gt; {
        self.tokens.next().ok_or("Error get next token".to_string())
    }
}</code></pre><figcaption>parser.rs</figcaption></figure><p><em>Struct</em> <em>Parser</em> berisikan <em>tokens</em> yang akan diproses untuk menjadi sebuah <em>Expression</em>. Disini kita membuat <em>Iterator</em> dari <em>tokens</em> tersebut sama seperti <em>input</em> di bagian 1 Lexer.</p><p>Tambahkan kode berikut ini setelah fungsi <em>handle_next()</em></p><figure class="kg-card kg-code-card"><pre><code class="language-rust">//Null Denotation
pub fn nud(&amp;mut self, token: Token) -&gt; Result&lt;Expression, String&gt; {
    match token {
        Token::ILLEGAL =&gt; Err("Input not supported".to_string()),
        Token::IDENTIFIER(i) =&gt; Ok(Expression::Variable(i)),
        Token::Num(n) =&gt; Ok(Expression::Num(n)),
        Token::Sub | Token::Add =&gt; {
            let tok = self.tokens.next().expect("eee");
            match tok {
                Token::Num(n) =&gt; {
                    Ok(Expression::Unary(
                        token.clone(),
                        Box::new(Expression::Num(n)),
                    ))
                }
                _ =&gt; Err("Input not supported".to_string()),
            }
        }
        Token::LParen =&gt; {
            let mut parenthesis = Vec::new();
            let mut counter: usize = 1;

            while let Some(token) = self.tokens.next() {
                match &amp;token {
                    Token::LParen =&gt; counter += 1,
                    Token::RParen =&gt; {
                        match counter {
                            1 =&gt; {
                                return Parser::new(parenthesis)
                                    .expr(0)
                                    .map(|t| Expression::Paren(Box::new(t)));
                            }
                            0 =&gt; return Err("Unmatched closing paren".to_string()),
                            _ =&gt; {}
                        };
                        counter -= 1;
                    }
                    _ =&gt; {}
                };
                parenthesis.push(token);
            }

            Err("Unmatched closing paren".to_string())
        }
        Token::RParen =&gt; Err("Unmatched closing paren".to_string()),
        Token::If =&gt; {
            let lhs = self.handle_next()?;
            let cmp = self.handle_next()?;
            let rhs = self.handle_next()?;

            let lhs = self.nud(lhs)?;
            let rhs = self.nud(rhs)?;

            let left = Box::new(
                Expression::Binary(
                    Box::new(lhs),
                    cmp,
                    Box::new(rhs),
                )
            );

            let then = self.handle_next()?;
            let then = self.handle_next()?;
            let then_expression = self.nud(then)?;
            let else_if = self.handle_next()?;
            let else_if = self.handle_next()?;
            let else_expression = self.nud(else_if)?;

            Ok(Expression::Conditional(
                left,
                Box::new(then_expression),
                Box::new(else_expression),
            ))
        }
        _ =&gt; Err(format!("Token {:?} error", token.clone())),
    }
}</code></pre><figcaption>parser.rs</figcaption></figure><p><em>Nud</em> atau <em>null denotation </em>adalah fungsi yang akan memproses sebuah <em>token prefix </em>disinilah yang hanya memproses sebuah <em>literal </em>dalam contoh ini sebuah <em>number</em> dan <em>variable </em>sedangkan <em>Led</em> atau <em>Left Denotation </em>yang akan memproses sebuah <em>token infix </em>dalam contoh ini adalah operator<em> Binary</em>.</p><p>Fungsi <em>nud </em>akan menentukan setiap token akan menjadi <em>Expression</em> yang sudah ditentukan, untuk hal <em>branching</em> atau <em>if </em>saat ini kita akan membatasi hanya 1 level jadi kita tidak akan bisa melakukan <em>if</em> didalam <em>if</em>.</p><p>selanjutnya untuk fungsi <em>led</em> tambahkan kode berikut setelah fungsi <em>nud</em> diatas:</p><figure class="kg-card kg-code-card"><pre><code class="language-rust">...
//Left Denotation
pub fn led(&amp;mut self, bp: usize, left: Expression, token: Token) -&gt; Result&lt;Expression, String&gt; {
    match token {
        Token::Add | Token::Sub | Token::Mul | Token::Div | Token::ASSIGN =&gt; {
            let rhs = self.expr(bp)?;
            Ok(Expression::Binary(
                Box::new(left),
                token,
                Box::new(rhs),
            ))
        }
        _ =&gt; Err(format!("Token {:?} error", token)),
    }
}</code></pre><figcaption>parser.rs</figcaption></figure><p>Selanjutnya tambahkan fungsi <em>expr </em>setelah fungsi <em>led</em>, fungsi <em>expr </em>inilah yang akan mengevaluasi setiap <em>token</em> dan memanggil fungsi sebelumnya yaitu fungsi <em>nud</em> dan <em>led</em>. Berikut kodenya:</p><figure class="kg-card kg-code-card"><pre><code class="language-rust">pub fn expr(&amp;mut self, rbp: usize) -&gt; Result&lt;Expression, String&gt; {
    let first_token = self.handle_next()?;
    let mut left = self.nud(first_token)?;

    while let Some(peeked) = self.tokens.peek() {
        if *peeked == Token::ILLEGAL {
            return Err("Input not supported".to_string());
        }

        if rbp &gt;= peeked.lbp() {
            break;
        }

        let op = self.handle_next()?;
        left = self.led(op.lbp(), left, op)?;
    }

    Ok(left)
}</code></pre><figcaption>parser.rs</figcaption></figure><h3 id="test-parser">Test Parser</h3><p>Jangan lupa untuk meambahkan test untuk <strong>parser.rs</strong>, tambahkan kode berikut dibagian paling bawah:</p><figure class="kg-card kg-code-card"><pre><code class="language-rust">#[cfg(test)]
mod test {
	use super::*;

	#[test]
	fn test_nud() {
		let tokens = vec![
			Token::Sub,
			Token::from(3),
			Token::Mul,
			Token::from(2),
			Token::EOF,
		];
		let expression = Parser::new(tokens).expr(0).unwrap();

		let expected = Expression::Binary(
			Box::new(Expression::Unary(
				Token::Sub,
				Box::new(Expression::from(3))
			)),
			Token::Mul,
			Box::new(Expression::from(2)),
		);

		assert_eq!(expected, expression);
	}

	#[test]
	fn test_error() {
		let tokens = vec![Token::Mul, Token::from(2), Token::EOF];
		let expression = Parser::new(tokens).expr(0).map_err(|e| e);

		let expected = Err(String::from("Token Mul error"));

		assert_eq!(expected, expression);
	}

	#[test]
	fn test_binary() {
		let tokens = vec![Token::from(3), Token::Div, Token::from(2), Token::EOF];
		let expression = Parser::new(tokens).expr(0).unwrap();

		let expected = Expression::Binary(
			Box::new(Expression::from(3)),
			Token::Div,
			Box::new(Expression::from(2)),
		);

		assert_eq!(expected, expression);
	}

	#[test]
	fn test_unary() {
		let tokens = vec![Token::Sub, Token::from(2), Token::EOF];
		let expression = Parser::new(tokens).expr(0).unwrap();

		let expected = Expression::Unary(Token::Sub, Box::new(Expression::from(2)));

		assert_eq!(expected, expression);
	}

	#[test]
	fn test_precedence_add_and_mul() {
		let tokens = vec![
			Token::from(3),
			Token::Add,
			Token::from(2),
			Token::Mul,
			Token::from(2),
			Token::EOF,
		];
		let expression = Parser::new(tokens).expr(0).unwrap();

		let expected = Expression::Binary(
			Box::new(Expression::from(3)),
			Token::Add,
			Box::new(Expression::Binary(
				Box::new(Expression::from(2)),
				Token::Mul,
				Box::new(Expression::from(2)),
			)),
		);

		assert_eq!(expected, expression);
	}

	#[test]
	fn test_precedence_add_and_sub() {
		let tokens = vec![
			Token::from(3),
			Token::Add,
			Token::from(2),
			Token::Sub,
			Token::from(2),
			Token::EOF,
		];
		let expression = Parser::new(tokens).expr(0).unwrap();

		let expected = Expression::Binary(
			Box::new(Expression::Binary(
				Box::new(Expression::from(3)),
				Token::Add,
				Box::new(Expression::from(2)),
			)),
			Token::Sub,
			Box::new(Expression::from(2)),
		);

		assert_eq!(expected, expression);
	}

	#[test]
	fn test_assignment() {
		let tokens = vec![
			Token::IDENTIFIER("a".to_string()),
			Token::ASSIGN,
			Token::from(2),
		];

		let expression = Parser::new(tokens).expr(0).unwrap();

		let expected = Expression::Binary(
			Box::new(Expression::Variable("a".to_string())),
			Token::ASSIGN,
			Box::new(Expression::from(2)),
		);

		assert_eq!(expected, expression);
	}

	#[test]
	fn test_if_then_else() {
		let tokens = vec![
			Token::If,
			Token::from(1),
			Token::LT,
			Token::from(9),
			Token::Then,
			Token::from(1),
			Token::Else,
			Token::from(0),
		];

		let expression = Parser::new(tokens).expr(0).unwrap();

		let expected = Expression::Conditional(
			Box::new(Expression::Binary(
				Box::new(Expression::from(1)),
				Token::LT,
				Box::new(Expression::from(9)),
			)),
			Box::new(Expression::from(1)),
			Box::new(Expression::from(0)),
		);

		assert_eq!(expected, expression);
	}
}</code></pre><figcaption>parser.rs</figcaption></figure><h2 id="main">Main</h2><p>Setelah menambahkan beberapa kode diatas, saatnya untuk mencoba menjalankan program dengan menggunakan <em>parser</em> yang telah kita buat.</p><p>Tambahkan kode berikut ini di file <strong>main.rs</strong></p><figure class="kg-card kg-code-card"><pre><code class="language-rust">use std::io;
use std::io::Write;

mod lexer;
mod token;
mod expression;
mod parser;

use lexer::Lexer;
use parser::Parser;

fn main() {
    loop {
        println!();
        print!("&gt; ");

        io::stdout()
            .flush()
            .expect("Error when flush stdout.");

        let mut input = String::new();

        io::stdin()
            .read_line(&amp;mut input)
            .expect("Could not read from standard input.");

        let lexer = Lexer::new(input.as_str());
        let result = lexer.lex();

        let mut parser = Parser::new(result);
        match parser.expr(0) {
            Ok(expression) =&gt; println!("Result {:?}", expression),
            Err(err) =&gt; eprintln!("Error {:?}", err),
        }
    }
}

</code></pre><figcaption>main.rs</figcaption></figure><p>Disini kita menambahkan 2 file yaitu <strong>expression.rs</strong> dan <strong>parser.rs</strong></p><p>Untuk melihat hasilnya jalankan program dengan perintah sebagai berikut:</p><pre><code class="language-bash">cargo run</code></pre><p>Input kode berikut ini</p><pre><code class="language-rust">a = 1 * 2 + 3 * 4</code></pre><p>Maka kode diatas akan menghasilkan <em>AST</em> berupa <em>expression</em> sebagai berikut</p><pre><code class="language-rust">Binary(Binary(Binary(Variable("a"), ASSIGN, Num(1.0)), Mul, Num(2.0)), Add, Binary(Num(3.0), Mul, Num(4.0)))</code></pre><p>contoh lainnya:</p><pre><code class="language-rust">if 1 &gt; 2 then 1 else 0</code></pre><p>hasilnya sebagai berikut:</p><pre><code class="language-rust">Conditional(Binary(Num(1.0), GT, Num(2.0)), Num(1.0), Num(0.0))</code></pre><ol><li>Bagian 1 - <a href="https://aldidana.com/membuat-bahasa-pemrograman-rust-llvm-lexer/">https://aldidana.com/membuat-bahasa-pemrograman-rust-llvm-lexer/</a></li><li>(Disini) Bagian 2 - <a href="https://aldidana.com/membuat-bahasa-pemrograman-rust-llvm-parser/">https://aldidana.com/membuat-bahasa-pemrograman-rust-llvm-parser/</a></li><li>Bagian 3 - <a href="https://aldidana.com/membuat-bahasa-pemrograman-rust-llvm-jit-compiler/">https://aldidana.com/membuat-bahasa-pemrograman-rust-llvm-jit-compiler/</a></li></ol>]]></content:encoded></item><item><title><![CDATA[Membuat Bahasa Pemrograman Sederhana dengan Rust dan LLVM - Bagian 1 Lexer]]></title><description><![CDATA[<p>Tahap pertama sebuah <em>compiler</em> adalah mengubah kode sumber (source code) menjadi sebuah <em>token, token </em>ini adalah sebuah kata yang dapat dimengerti oleh bahasa pemrograman tersebut, tahap ini disebut <strong><em>Lexical Analysis </em></strong>atau juga<strong><em> Scanner</em></strong><em>.</em></p><p>Untuk kode yang sudah lengkap bisa dilihat di github: <a href="https://github.com/aldidana/hitung">https://github.com/aldidana/hitung</a></p><figure class="kg-card kg-image-card"><img src="https://aldidana.com/content/images/2021/01/token-4.png" class="kg-image"></figure><p>Pada gambar diatas</p>]]></description><link>https://aldidana.com/membuat-bahasa-pemrograman-rust-llvm-lexer/</link><guid isPermaLink="false">5ff6e01872f9c00e191742c8</guid><category><![CDATA[rust]]></category><category><![CDATA[llvm]]></category><dc:creator><![CDATA[Aldi Perdana]]></dc:creator><pubDate>Mon, 18 Jan 2021 03:00:00 GMT</pubDate><content:encoded><![CDATA[<p>Tahap pertama sebuah <em>compiler</em> adalah mengubah kode sumber (source code) menjadi sebuah <em>token, token </em>ini adalah sebuah kata yang dapat dimengerti oleh bahasa pemrograman tersebut, tahap ini disebut <strong><em>Lexical Analysis </em></strong>atau juga<strong><em> Scanner</em></strong><em>.</em></p><p>Untuk kode yang sudah lengkap bisa dilihat di github: <a href="https://github.com/aldidana/hitung">https://github.com/aldidana/hitung</a></p><figure class="kg-card kg-image-card"><img src="https://aldidana.com/content/images/2021/01/token-4.png" class="kg-image"></figure><p>Pada gambar diatas setiap <em>character </em>dalam kode sumber akan ditentukan masuk ke bagian <em>token </em>yang sesuai.</p><h2 id="membuat-program-baru">Membuat Program Baru</h2><p>Untuk mengikuti ini pastikan Rust sudah ter-<em>install</em>, kemudian ikuti perintah dibawah ini untuk membuat program Rust baru:</p><pre><code class="language-command">cargo new hitung</code></pre><h2 id="struktur-file">Struktur File</h2><p>Struktur yang akan dibuat untuk saat ini:</p><pre><code>hitung
│   Cargo.toml   
│
└───src
	| main.rs
	| lexer.rs
   	| token.rs</code></pre><h2 id="token"><strong>Token</strong></h2><p>Kita akan memulai pada file <strong>token.rs, </strong>tambahkan kode berikut:</p><figure class="kg-card kg-code-card"><pre><code class="language-rust">#[derive(Debug, PartialEq, Clone)]
pub enum Token {
	LParen,
	RParen,
	Add,
	Sub,
	Mul,
	Div,
	Num(f64),
	EOF,
	ILLEGAL,
	ASSIGN,
	IDENTIFIER(String),
	If,
	Then,
	Else,
	EQ,
	LT,
	GT,
}

impl From&lt;i32&gt; for Token {
	fn from(n: i32) -&gt; Self {
		Token::Num(n as f64)
	}
}

impl Token {
	//Left Binding Power
	pub fn lbp(&amp;self) -&gt; usize {
		match *self {
			Token::Add =&gt; 10,
			Token::Sub =&gt; 10,
			Token::Mul =&gt; 20,
			Token::Div =&gt; 20,
			Token::LParen =&gt; 99,
			Token::ASSIGN =&gt; 100,
			Token::RParen =&gt; 0,
			_ =&gt; 0,
		}
	}
}</code></pre><figcaption>token.rs</figcaption></figure><p>Dibagian ini kita membuat sebuah <strong><em>enum Token</em></strong> dengan nama yang sudah ditentukan, <em>enum </em>juga dapat diberikan sebuah tipe data didalamnya seperti Token::Num(f64) diatas.</p><p>Selanjutnya <strong><em>Token</em></strong> juga dapat melakukan konversi dari tipe data i32 menjadi f64, dengan implementasi <em><strong>trait From</strong></em>, <em><strong>trait From</strong></em> ini memiliki metode atau fungsi <em><strong>from</strong> </em>yang mengambil tipe data <em><strong>generic</strong>, </em>contoh: </p><pre><code class="language-rust">trait From&lt;T&gt; {
  pub fn from(_: T) -&gt; Self
}

// Token mempunyai metode atau fungsi from

Token::from(2) // akan menjadi tipe data f64
Token::Num(2.0)</code></pre><p>Pada kode terakhir <em>token </em>disini untuk menentukan seberapa beban atau prioritas sebuah <em>token</em> dibanding <em>token</em> lainnya misalnya perkalian lebih dulu di evaluasi dibandingkan dengan penjumlahan, sehingga 3+2*2 adalah 7 bukan 10, tetapi jika hasilnya ingin 10 bisa menggunakan tanda kurung (3+2)*2.</p><h3 id="test-token"><strong>Test Token</strong></h3><p>Jangan lupa menambahkan test untuk <strong>token.rs, </strong>tambahkan kode berikut ini setelah baris terakhir dari kode yang ada di dalam <strong>token.rs</strong>:</p><figure class="kg-card kg-code-card"><pre><code class="language-rust">#[cfg(test)]
mod test {
	use super::*;

	#[test]
	fn test_token_add() {
		let actual = Token::Add.lbp();
		let expected: usize = 10;

		assert_eq!(expected, actual);
	}

	#[test]
	fn test_token_sub() {
		let actual = Token::Sub.lbp();
		let expected: usize = 10;

		assert_eq!(expected, actual);
	}

	#[test]
	fn test_token_mul() {
		let actual = Token::Mul.lbp();
		let expected: usize = 20;

		assert_eq!(expected, actual);
	}

	#[test]
	fn test_token_div() {
		let actual = Token::Div.lbp();
		let expected: usize = 20;

		assert_eq!(expected, actual);
	}

	#[test]
	fn test_token_open_paren() {
		let actual = Token::LParen.lbp();
		let expected: usize = 99;

		assert_eq!(expected, actual);
	}

	#[test]
	fn test_token_close_paren() {
		let actual = Token::RParen.lbp();
		let expected: usize = 0;

		assert_eq!(expected, actual);
	}

	#[test]
	fn test_token_assignment() {
		let actual = Token::ASSIGN.lbp();
		let expected: usize = 100;

		assert_eq!(expected, actual);
	}
}</code></pre><figcaption>token.rs</figcaption></figure><p>Disini kita membuat test untuk mengecek<em> left binding power </em>dari sebuah token.</p><h2 id="lexer">Lexer</h2><p>Lanjut ke file <strong>lexer.rs, </strong>pada bagian awal baris tambahkan kode berikut ini:</p><figure class="kg-card kg-code-card"><pre><code class="language-rust">use std::iter::Peekable;
use std:str:Chars;

use crate::token::Token;</code></pre><figcaption>lexer.rs</figcaption></figure><p>Jangan khawatir akan dijelaskan mengenai barisan kode tersebut. Disini kita akan menggunakan <strong><em>Peekable </em></strong>dan <strong><em>Chars</em></strong><em>.</em><strong><em> Peekable</em></strong> yaitu sebuah iterator yang mempunyai metode atau fungsi <em><strong>peek() </strong></em>yang akan mengembalikan sebuah <em>reference</em> dari elemen berikutnya, sehingga posisi sebuah <em>char </em>tidak akan maju ke elemen berikutnya, contohnya seperti ini:</p><pre><code class="language-rust">let array = [1, 2, 3]; //sebuah array

let mut iter = array.iter().peekable(); // maka iter akan mempunyai metode atau fungsi peek

iter.peek() // akan mengembalikan tipe optional sehinnga isinya Some(1)
iter.peek() // Some(1)
iter.peek() // Some(1)

// Tidak peduli seberapa banyak kita memanggil fungsi peek() iterator tidak akan maju ke elemen berikutnya

iter.next() // maju ke elemen berikutnya

iter.peek() // Some(2)</code></pre><p>Kemudian menggunakan <em><strong>Chars </strong></em>karena akan menyimpan input dari kode sumber menjadi sebuah <em><strong>Peekable</strong> </em>yang didalamnya mempunyai tipe<em> <strong>Chars</strong></em>. Selanjutnya untuk token adalah meng-<em>import</em> agar dapat mengakses <strong><em>Enum</em></strong> <strong><em>Token</em></strong> yang sudah dibuat sebelumnya.</p><p>Tambahkan kode berikut ini setelah kode <strong>lexer.rs </strong>terakhir:</p><figure class="kg-card kg-code-card"><pre><code class="language-rust">#[derive(Debug)]
pub struct Lexer&lt;'a&gt; {
	input: Peekable&lt;Chars&lt;'a&gt;&gt;,
}

impl &lt;'a&gt; Lexer&lt;'a&gt; {
	pub fn new(input: &amp;str) -&gt; Self {
    	Lexer {
        	input: input.chars().peekable(),
        }
        
	pub fn lex(mut self) -&gt; Vec&lt;Token&gt; {
		let mut tokens = vec![];
        loop {
            let current_token = self.next_token();
            if current_token == Token::EOF || current_token == Token::ILLEGAL {
                tokens.push(current_token);
                break;
            } else {
                tokens.push(current_token);
            }
        }

        tokens
    }
}</code></pre><figcaption>lexer.rs</figcaption></figure><p>Pada bagian ini kita membuat <strong><em>Struct Lexer </em></strong>dengan <strong><em>reference </em></strong>yang ditandai oleh <strong><em>lifetime</em></strong> <strong><em>'a</em></strong>, <em><strong>Struct Lexer</strong></em> mempunyai<strong> <em>field </em></strong>input berupa <strong><em>Peekable</em></strong> yang mempunyai tipe data <strong><em>Chars</em></strong>, <strong><em>Chars</em></strong> juga membutuhkan <strong><em>lifetime parameter</em></strong>.</p><p>Berikut in mengenai penjelasan sederhana mengenai <strong><em>lifetime</em></strong> diatas:</p><pre><code class="language-rust">struct DB {
	.....
}

struct App {
	database: &amp;DB, // borrow atau meminjam struct DB diatas
}

// kode diatas tidak akan bisa dicompile
// dikarenakan struct DB tidak diketahui lifetimenya

struct App&lt;'a&gt; {
	database: &amp;'a DB,
}

// Kode diatas berhasil dicompile
// karena field database yang mempunyai tipe DB mempunyai lifetime yang sama seperti App
// Struct App tidak boleh outlive dari reference yaitu struct DB</code></pre><p>fungsi <strong><em>new() </em></strong>dari Lexer adalah <strong><em>associated functions </em></strong>untuk membuat sebuah <strong><em>instance</em></strong> dari tipe data itu sendiri.</p><pre><code class="language-rust">.... {
	fn new() -&gt; Self // ini adalah associated function karena tidak mengambil self pada parameter pertama

	fn move(&amp;mut self) // methods
    fn attack(self) // methods
}</code></pre><p>Sedangkan fungsi <strong><em>lex() </em></strong>adalah sebuah <strong><em>methods </em></strong>dari<strong><em> Lexer, </em></strong>fungsi ini akan melakukan perulangan hingga <strong><em>input</em></strong> berupa kode sumber selesai atau berakhir.</p><p>Masih di dalam file<strong> lexer.rs, </strong>tambahkan kode dibawah ini tepat setelah fungsi <strong><em>lex </em></strong>dan masih di dalam<strong><em> block impl </em></strong>dari<strong><em> Lexer</em></strong></p><figure class="kg-card kg-code-card"><pre><code class="language-rust">// masih di dalam impl Lexer
.... {
	pub fn next_token(&amp;mut self) -&gt; Token {
		match self.input.peek() {
			Some(ch) =&gt; match ch {
				' ' | '\t' =&gt; {
					self.input.next();
					self.next_token()
				}
				'\n' =&gt; {
					self.input.next();
					self.next_token()
				}
				ch if ch.is_numeric() =&gt; self.read_numeric(),
				'+' =&gt; {
					self.input.next();
					Token::Add
				}
				'-' =&gt; {
					self.input.next();
					Token::Sub
				}
				'*' =&gt; {
					self.input.next();
					Token::Mul
				}
				'/' =&gt; {
					self.input.next();
					Token::Div
				}
				'(' =&gt; {
					self.input.next();
					Token::LParen
				}
				')' =&gt; {
					self.input.next();
					Token::RParen
				}
				ch if ch.is_alphabetic() =&gt; {
					self.read_identifier()
				}
				'=' =&gt; {
					self.input.next();
					match self.input.peek() {
						Some(c) =&gt; {
							if *c == '=' {
								self.input.next();
								Token::EQ
							} else {
								self.input.next();
								Token::ASSIGN
							}
						}
						_ =&gt; {
							self.next_token()
						}
					}
				}
				'&lt;' =&gt; {
					self.input.next();
					Token::LT
				}
				'&gt;' =&gt; {
					self.input.next();
					Token::GT
				}
				_ =&gt; {
					self.input.next();
					Token::ILLEGAL
				}
			},
			None =&gt; Token::EOF,
		}
	}

	fn read_numeric(&amp;mut self) -&gt; Token {
		let mut literal = String::new();

		loop {
			match self.input.peek() {
				Some(&amp;ch) =&gt; {
					if ch.is_numeric() || ch == '.' {
						literal.push(ch);
						self.input.next();
					} else {
						break;
					}
				}
				_ =&gt; break,
			}
		}

		match literal.parse() {
			Ok(l) =&gt; Token::Num(l),
			Err(_e) =&gt; Token::ILLEGAL,
		}
	}

	fn read_identifier(&amp;mut self) -&gt; Token {
		let mut literal = String::new();

		loop {
			match self.input.peek() {
				Some(&amp;ch) =&gt; {
					if !ch.is_alphabetic() {
						break;
					}
					if ch.is_ascii_whitespace() {
						// self.input.next();
						break;
					};
					literal.push(ch);
					self.input.next();
				},
				_ =&gt; break
			}
		};

		match literal.as_str() {
			"if" =&gt; Token::If,
			"then" =&gt; Token::Then,
			"else" =&gt; Token::Else,
			_ =&gt; Token::IDENTIFIER(literal),
		}
	}
}</code></pre><figcaption>lexer.rs</figcaption></figure><p>fungsi berikutnya ini cukup sederhana, megecek <strong><em>input</em></strong> dari kode sumber  yang akan menjadi token yang sudah kita tentukan.</p><p><em><strong>&amp;mut self </strong></em>pada fungsi adalah agar kita dapat mengubah data dari object itu sendiri, tanpa mengambil atau memindahkan <strong><em>ownership</em></strong>.</p><blockquote>&amp;mut self: mutable reference tanpa mengambil/memindahkan ownership</blockquote><!--kg-card-begin: html--><ul>
    <li>&mut self: mutable reference tanpa mengambil/memindahkan ownership</li>
    <li>mut self: mutable dengan mengambil/memindahkan ownership</li>
    <li>self: immutable dengan mengambil/memindahkan ownership</li>
    <li>&self: immutable tanpa mengambil/memindahkan ownership</li>
</ul><br><!--kg-card-end: html--><h3 id="test-lexer"><strong>Test Lexer</strong></h3><p>Terakhir kita tambahkan test untuk <strong>lexer.rs</strong> pada baris terakhir</p><figure class="kg-card kg-code-card"><pre><code class="language-rust">impl&lt;'a&gt; Lexer&lt;'a&gt; {
	......
}

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

	#[test]
	fn test_num() {
		let lexer = Lexer::new(r#"32"#);
		let tokens = lexer.lex();

		let expected = vec![Token::from(32), Token::EOF];
		assert_eq!(expected, tokens);
	}

	#[test]
	fn test_num_float() {
		let lexer = Lexer::new(r#"32.5"#);
		let tokens = lexer.lex();

		let expected = vec![Token::Num(32.5), Token::EOF];
		assert_eq!(expected, tokens);
	}

	#[test]
	fn test_num_whitespace() {
		let lexer = Lexer::new(r#"32 2"#);
		let tokens = lexer.lex();

		let expected = vec![Token::from(32), Token::from(2), Token::EOF];

		assert_eq!(expected, tokens);
	}

	#[test]
	fn test_num_operator() {
		let lexer = Lexer::new(r#"-+/*"#);
		let tokens = lexer.lex();

		let expected = vec![
			Token::Sub,
			Token::Add,
			Token::Div,
			Token::Mul,
			Token::EOF,
		];

		assert_eq!(expected, tokens);
	}

	#[test]
	fn test_assignment() {
		let lexer = Lexer::new(r#"a = 123"#);
		let tokens = lexer.lex();

		let expected = vec![
			Token::IDENTIFIER("a".to_string()),
			Token::ASSIGN,
			Token::from(123),
			Token::EOF,
		];

		assert_eq!(expected, tokens);
	}

	#[test]
	fn test_conditional() {
		let lexer = Lexer::new(r#"if 1 &lt; 2 then 1 else 0"#);
		let tokens = lexer.lex();

		let expected = vec![
			Token::If,
			Token::from(1),
			Token::LT,
			Token::from(2),
			Token::Then,
			Token::from(1),
			Token::Else,
			Token::from(0),
			Token::EOF,
		];

		assert_eq!(expected, tokens);
	}
}</code></pre><figcaption>lexer.rs</figcaption></figure><h2 id="main">Main</h2><p>Kembali ke fungsi utama untuk menjalankan program ini yaitu <strong>main.rs</strong><em>, </em>tambahkan<em> </em>kode berikut:</p><figure class="kg-card kg-code-card"><pre><code class="language-rust">use std::io;
use std::io::Write;

mod lexer;
mod token;

use lexer::Lexer;

fn main() {
    loop {
        println!();
        print!("&gt; ");

        io::stdout()
            .flush()
            .expect("Could not read from standard input.");

        let mut input = String::new();

        io::stdin()
            .read_line(&amp;mut input)
            .expect("Could not read from standard input.");

        let lexer = Lexer::new(input.as_str());
        let result = lexer.lex();

        println!("{:?}", result);
    }
}</code></pre><figcaption>main.rs</figcaption></figure><p>Pada bagian ini kita menggunakan<strong><em> standard library</em></strong> untuk <strong><em>io</em></strong> (input/output) dan juga <strong><em>Trait Write </em></strong>agar <strong><em>io::stdout()</em></strong> dapat menjalankan fungsi <strong><em>flush()</em></strong>. Kemudian kita membaca setiap input yang masuk ke dalam program dan memanggil fungsi yang ada pada <strong><em>Lexer</em></strong> untuk menghasilkan sebuah <strong><em>token</em></strong>.</p><p>Jalankan program dengan perintah berikut pada terminal:</p><pre><code class="language-command">cargo run</code></pre><p>Kemudian ketik input yang diinginkan lalu tekan enter pada keyboard, misalnya:</p><pre><code class="language-rust">if 1 &gt; 2 then 1 else 0</code></pre><p>maka akan menghasilkan token sebagai berikut:</p><pre><code class="language-command">[If, Num(1.0), GT, Num(2.0), Then, Num(1.0), Else, Num(0.0), EOF]</code></pre><p>contoh lain:</p><pre><code class="language-rust">a = 123</code></pre><p>maka hasilnya:</p><pre><code class="language-command">[IDENTIFIER("a"), ASSIGN, Num(123), EOF]</code></pre><p>Sekian untuk bagian pertama dari Membuat Bahasa Pemrograman Sederhana dengan Rust dan LLVM.</p><ol><li>(Disini) Bagian 1 - <a href="https://aldidana.com/membuat-bahasa-pemrograman-rust-llvm-lexer/">https://aldidana.com/membuat-bahasa-pemrograman-rust-llvm-lexer/</a></li><li>Bagian 2 - <a href="https://aldidana.com/membuat-bahasa-pemrograman-rust-llvm-parser/">https://aldidana.com/membuat-bahasa-pemrograman-rust-llvm-parser/</a></li><li>Bagian 3 - <a href="https://aldidana.com/membuat-bahasa-pemrograman-rust-llvm-jit-compiler/">https://aldidana.com/membuat-bahasa-pemrograman-rust-llvm-jit-compiler/</a></li></ol>]]></content:encoded></item><item><title><![CDATA[Pattern Matching Pada Javascript]]></title><description><![CDATA[<p>Bagi kalian yang sudah pernah atau sering menggunakan bahasa pemrograman <em>functional </em>pasti sudah tidak asing lagi dengan<em> pattern matching, </em>tetapi<em> pattern matching </em>juga ada di bahasa pemrograman imperative yang mendukung <em>pattern matching</em> misalnya <em>Rust.</em></p><p>Contoh pattern matching di <em>Rust</em> adalah sebagai berikut:</p><pre><code class="language-rust">let angka = 1;

match angka {
	1 =&gt; println!</code></pre>]]></description><link>https://aldidana.com/pattern-matching-pada-javascript/</link><guid isPermaLink="false">5f98f12372f9c00e191741ae</guid><category><![CDATA[pattern matching]]></category><category><![CDATA[javascript]]></category><category><![CDATA[rust]]></category><category><![CDATA[elixir]]></category><dc:creator><![CDATA[Aldi Perdana]]></dc:creator><pubDate>Wed, 28 Oct 2020 05:48:18 GMT</pubDate><content:encoded><![CDATA[<p>Bagi kalian yang sudah pernah atau sering menggunakan bahasa pemrograman <em>functional </em>pasti sudah tidak asing lagi dengan<em> pattern matching, </em>tetapi<em> pattern matching </em>juga ada di bahasa pemrograman imperative yang mendukung <em>pattern matching</em> misalnya <em>Rust.</em></p><p>Contoh pattern matching di <em>Rust</em> adalah sebagai berikut:</p><pre><code class="language-rust">let angka = 1;

match angka {
	1 =&gt; println!("Satu"),
    2..=5 =&gt; println!("Dua sampai Lima"),
    _ =&gt; println!("Berapa saja selain diatas")
}</code></pre><p>Sedangkan berikut ini salah satu contoh <em>pattern matching</em> untuk bahasa pemrograman <em>elixir:</em></p><pre><code class="language-elixir">angka = 7

case angka do
  1 -&gt; IO.puts("Satu")
  x when x &lt; 6 -&gt; IO.puts("Dua sampai Lima")
  _x -&gt; IO.puts("Berapa saja selain diatas")
end

</code></pre><p>Untuk membuat <em>pattern matching</em> seperti contoh diatas kita dapat menerapkannya dengan fungsi berikut ini.</p><pre><code class="language-javascript">function matcher(pattern) {
  return (input) =&gt; {
    const patternFound = pattern[input];
    if (!patternFound) {
      return pattern['x'](input)
    };
    return patternFound(input);
  };
}

const match = matcher({
  1: () =&gt; console.log('Satu'),
  2: () =&gt; console.log('Dua sampai Lima'),
  3: () =&gt; console.log('Dua sampai Lima'),
  4: () =&gt; console.log('Dua sampai Lima'),
  5: () =&gt; console.log('Dua sampai Lima'),
  x: (n) =&gt; {
    console.log('x', n);
  }
})

match(12);</code></pre><p>Tentu saja fungsi yang dibuat tersebut masih banyak memiliki kekurangan seperti memilih <em>range</em> tertentu untuk angka.</p><p>Jika menginginkan <em>feature</em> yang lebih banyak untuk <em>pattern matching </em>di Javascript kita bisa menggunakan <a href="https://github.com/z-pattern-matching/z">https://github.com/z-pattern-matching/z</a> atau menunggu proposal berikut <a href="https://github.com/tc39/proposal-pattern-matching">https://github.com/tc39/proposal-pattern-matching</a></p>]]></content:encoded></item><item><title><![CDATA[Membuat VM Sederhana Dengan Rust - Bagian 2]]></title><description><![CDATA[<p>Pada bagian 1, kita sudah membuat file <em>bytecode.rs </em>yang<em> men-generate code </em>dan mencetak hasilnya.</p><!--kg-card-begin: html--><br><!--kg-card-end: html--><p>Buat file<strong> vm.rs </strong>dan tambahkan code berikut:</p><pre><code class="language-rust">use crate::bytecode;

pub struct VM {
  code: Vec&lt;isize&gt;,
  stack: Vec&lt;isize&gt;,
  
  pc: usize, //program counter or ip (instruction pointer)
  sp: isize, //stack</code></pre>]]></description><link>https://aldidana.com/membuat-vm-sederhana-dengan-rust-2/</link><guid isPermaLink="false">5ea861f072f9c00e19173ede</guid><category><![CDATA[rust]]></category><category><![CDATA[virtual-machine]]></category><category><![CDATA[vm]]></category><dc:creator><![CDATA[Aldi Perdana]]></dc:creator><pubDate>Fri, 01 May 2020 02:00:00 GMT</pubDate><content:encoded><![CDATA[<p>Pada bagian 1, kita sudah membuat file <em>bytecode.rs </em>yang<em> men-generate code </em>dan mencetak hasilnya.</p><!--kg-card-begin: html--><br><!--kg-card-end: html--><p>Buat file<strong> vm.rs </strong>dan tambahkan code berikut:</p><pre><code class="language-rust">use crate::bytecode;

pub struct VM {
  code: Vec&lt;isize&gt;,
  stack: Vec&lt;isize&gt;,
  
  pc: usize, //program counter or ip (instruction pointer)
  sp: isize, //stack pointer
  
  debug: bool,
}</code></pre><p>pada bagian <strong>use crate::bytecode; </strong>kita akan menggunakan <em>module </em><strong>bytecode.rs </strong>yang telah kita buat dan gunakan di<strong> main.rs</strong>,<strong> </strong>kegunaan<strong> mod bytecode; </strong>pada file <strong>main.rs </strong>di bagian 1 adalah agar kita dapat menggunakannya di file lain.</p><!--kg-card-begin: html--><br><!--kg-card-end: html--><p>Biasanya sebuah VM terdiri dari <em>frame, call stack, globals, locals </em>dan lainnya, dikarenakan kita hanya membuat VM yang sangat sederhana maka kita membuat <em>struct</em> VM yang berisikan:</p><ul><li><strong>code</strong><em> </em>berupa<em> instructions </em>yang telah kita <em>generate </em>menjadi<em> code</em>.</li><li><strong>stack</strong> dimana kita akan menyimpan data kita.</li><li><strong>pc</strong> <em>(program counter)</em> atau biasanya disebut ip<em> (instruction pointer), </em>adalah petunjuk sebuah instruksi yang menyimpan sebuah alamat <em>(memory address) </em>untuk di eksekusi berikutnya.</li><li><strong>sp </strong>(stack pointer) fungsinya mirip sepeti <em>pc</em>/<em>ip </em>tetapi menunjukkan lokasi dimana alamat <em>(memory address)</em> pada sebuah stack.</li><li><strong>debug </strong>hanya digunakan untuk <em>men-debug.</em></li></ul><!--kg-card-begin: html--><br><!--kg-card-end: html--><p>untuk lebih jelasnya mengenai Rust <em>numeric type</em>, seperti <em>isize</em> dan <em>usize</em> dapat dilihat disini <a href="https://doc.rust-lang.org/reference/types/numeric.html">https://doc.rust-lang.org/reference/types/numeric.html</a>.</p><!--kg-card-begin: html--><br><!--kg-card-end: html--><p>tambahkan code berikut di<strong> vm.rs</strong> setelah code diatas</p><pre><code class="language-rust">impl VM {
  pub fn new(code: Vec&lt;isize&gt;, capacity: usize, debug: bool) -&gt; Self {
    VM {
      code: code,
      stack: Vec::with_capacity(capacity),
      pc: 0,
      sp: -1,
      debug: debug,
    }
  }
}</code></pre><p>Kita menambahkan <em><em>associated functions</em> <strong>new </strong></em>pada VM. Disini stack merupakan <em>Vector</em> dengan kapasitas yang telah kita berikan pada parameter <em>capacity </em>serta <em>stack pointer</em> yang dimulai dari -1.</p><!--kg-card-begin: html--><br><!--kg-card-end: html--><p>Mengenai <em>Array</em> dan <em>Vector</em> lebih jelasnya bisa dilihat disini <a href="https://www.cs.brandeis.edu/~cs146a/rust/doc-02-21-2015/book/arrays-vectors-and-slices.html">https://www.cs.brandeis.edu/~cs146a/rust/doc-02-21-2015/book/arrays-vectors-and-slices.html</a></p><!--kg-card-begin: html--><br><!--kg-card-end: html--><p>Sebelum melanjutkan dibagian <strong>vm.rs</strong> ini, kembali ke file <strong>bytecode.rs</strong> dan tambahkan code berikut di bagian paling bawah<strong> (bukan didalam Impl Instruction)</strong>:</p><pre><code class="language-rust">.....

pub fn disassemble(code: isize, pc: usize) -&gt; (Opcode, isize) {
  match code {
    code if code == Opcode::CONST(code).code() =&gt; (Opcode::CONST(pc as isize), 1),
    code if code == Opcode::ADD.code() =&gt; (Opcode::ADD, 0),
    code if code == Opcode::SUB.code() =&gt; (Opcode::SUB, 0),
    code if code == Opcode::PRINT.code() =&gt; (Opcode::PRINT, 0),
    code if code == Opcode::HALT.code() =&gt; (Opcode::HALT, 0),
    _ =&gt; (Opcode::HALT, 0),
  }
}</code></pre><p>fungsi <em>disassemble </em>untuk<em> </em>mengembalikan<em> tuple </em>berupa nama<em> Opcode </em>dan jumlah <em>Operand</em> pada<em> Opcode </em>tersebut<em>.</em></p><!--kg-card-begin: html--><br><!--kg-card-end: html--><p>Kembali ke file <strong>vm.rs</strong> tambahkan code berikut setelah <em>new</em></p><pre><code class="language-rust">impl VM {
  pub fn new ...
  
  // tambahkan disini
  pub fn run(mut self, pc: usize) {
    self.pc = pc;
  
    while self.pc &lt; self.code.len()  {
      let opcode = self.code[self.pc];
      if self.debug {
        let instructions = bytecode::disassemble(opcode, self.pc);
        if instructions.1 &lt; 1 {
          println!("[{:04X}] {:?}", self.pc, instructions.0);
        }
        if instructions.1 == 1 {
          println!("[{:04X}] {:?} {:?}", self.pc, instructions.0, self.code[self.pc+1]);
        }
      }
      
      self.pc += 1;
      match opcode {
        opcode if opcode == bytecode::Opcode::CONST(opcode).code() =&gt; {
          let value = self.code[self.pc];
          self.pc += 1;
          self.sp += 1;
          let current_sp = self.sp as usize;
          self.stack.insert(current_sp, value as isize);
        },
        opcode if opcode == bytecode::Opcode::ADD.code() =&gt; {
          let rhs = self.stack.pop().expect("invalid value");
          let lhs = self.stack.pop().expect("invalid value");		
          let value = lhs + rhs;
          self.stack.push(value);
          
          self.sp -= 1;
        },
        opcode if opcode == bytecode::Opcode::SUB.code() =&gt; {
          let rhs = self.stack.pop().expect("invalid value");
          let lhs = self.stack.pop().expect("invalid value");
          let value = lhs - rhs;
          self.stack.push(value);
          
          self.sp -= 1;
        },
        opcode if opcode == bytecode::Opcode::PRINT.code() =&gt; {
          let current_sp = self.sp as usize;
          let value = self.stack[current_sp];
          self.sp -= 1;
          println!("{}", value)
        },
        opcode if opcode == bytecode::Opcode::HALT.code() =&gt; {
          break
        },
        _ =&gt; panic!("Opcode {} is not supported", opcode),
      }
    }
  }
}</code></pre><p>Fungsi <em>run</em> adalah untuk mengeksekusi sebuah <em>code</em> yang telah kita masukan. <em>opcode </em>disini diambil melalui <em>pc/ip </em>pada<em> code, </em>kemudian kita mengecek apakah kita akan melakukan <em>debug</em> pada program, jika kita melakukan <em>debug</em> pada program maka akan memanggil fungsi <em>disassemble</em> yang telah kita buat di file<em> <strong>bytecode.rs</strong>,<strong> </strong>debug</em> akan mencetak <em>instruction</em> yang diberikan.</p><!--kg-card-begin: html--><br><!--kg-card-end: html--><p>selama program berjalan maka <em>pc (program counter) </em>akan berubah sesuai dengan instruksi yang diberikan.</p><!--kg-card-begin: html--><br><!--kg-card-end: html--><p>Ketika melakukan perintah <em>CONST, sp (stack poiner) </em>akan bergerak (dengan menambahkan) dan mengisi data ke <em>stack </em>sesuai dengan alamat <em>stack pointer </em>tersebut. Untuk perintah <em>ADD</em> dan <em>SUB</em> sama-sama mengambil nilai <em><strong>(pop)</strong></em> dari <em>stack</em> (isi <em>stack</em> berkurang) yang kemudian akan dilakukan operasi <em>ADD </em>(penjumlahan) atau <em>SUB</em> (pengurangan) dimana hasil tersebut akan dimasukan ke dalam <em>stack. </em>Kemudian perintah <em>PRINT </em>akan mengambil data dari<em> stack.</em></p><!--kg-card-begin: html--><br><!--kg-card-end: html--><p>Setelah itu kembali ke file <strong>main.rs </strong>dan tambahkan code berikut:</p><pre><code class="language-rust">mod bytecode;
mod vm;

fn main() {
  //code sebelumnya di bagian-1
  ...
  let pc_start = 0;
  let stack_size = 100;
  let debug = true;
  
  let vm = vm::VM::new(code, stack_size, debug);
  vm.run(pc_start)
}</code></pre><p>jalankan program yang telah dibuat </p><pre><code class="language-bash">cargo run</code></pre><p>hasilnya berupa nilai dari penjumlahan dan penguran:</p><pre><code class="language-bash">[0000] CONST(0) 10
[0002] CONST(2) 20
[0004] ADD
[0005] CONST(5) 9
[0007] SUB
[0008] PRINT
21</code></pre>]]></content:encoded></item><item><title><![CDATA[Membuat VM Sederhana Dengan Rust - Bagian 1]]></title><description><![CDATA[Virtual Machine adalah sebuah program yang menyerupai sebuah komputer dengan mensimulasikan CPU untuk melakukan berbagai macam perintah layaknya komputer]]></description><link>https://aldidana.com/membuat-vm-sederhana-dengan-rust/</link><guid isPermaLink="false">5ea7e33c72f9c00e19173bd7</guid><category><![CDATA[vm]]></category><category><![CDATA[rust]]></category><category><![CDATA[virtual-machine]]></category><dc:creator><![CDATA[Aldi Perdana]]></dc:creator><pubDate>Thu, 30 Apr 2020 03:16:00 GMT</pubDate><content:encoded><![CDATA[<p><em>untuk full code bisa dilihat disini <a href="https://github.com/aldidana/simple-vm">https://github.com/aldidana/simple-vm</a></em></p><p>VM<em> (Virtual Machine) </em>adalah sebuah program yang menyerupai sebuah komputer dengan mensimulasikan CPU untuk melakukan berbagai macam perintah layaknya sebuah komputer fisik berdasarkan tujuan VM tersebut dibuat.</p><p>VM terdiri dari 2 arsitektur:</p><ol><li><em>Stack based</em></li><li><em>Register based</em></li></ol><p>Pada <em>post </em>kali ini kita akan membuat <em>Stack based</em> VM.<em> </em>Salah satu contoh dari <em>Stack based</em> VM adalah JVM (Java Virtual Machine), stack based VM juga lebih mudah diimplementasikan dibandingkan <em>Register based</em>.</p><h3 id="cara-kerja-vm">Cara Kerja VM</h3><p>Cara kerja VM ini cukup sederhana:</p><ol><li>Mengambil instruksi pada daftar instruksi atau <em>code.</em></li><li><em>Decode </em>instruksi tersebut.</li><li>Eksekusi hasil dari <em>decode</em>.</li></ol><h3 id="instructions">Instructions</h3><p><em>Instructions</em> adalah serangkaian perintah untuk melakukan suatu tugas/<em>task, </em>seperti mendeklarasikan suatu variabel dan menambahkannya dengan variabel lainnya jika kedua variabel tersebut bertipe data <em>numeric. Instructions</em> ini terdiri dari <strong><em>Operation Code</em> <em>(opcode)</em></strong> dan <em><strong>Operands</strong></em>.</p><p>Untuk mencetak<strong> 8 + 10</strong>, berikut ini adalah contoh perintahnya:</p><!--kg-card-begin: markdown--><blockquote>
<p>CONST 8 // mendeklarasikan sebuah variabel dengan nilai 8<br>
CONST 10 // mendeklarasikan sebuah variabel dengan nilai 10<br>
ADD // menambahkan kedua variabel diatas<br>
PRINT // mencetak</p>
</blockquote>
<!--kg-card-end: markdown--><h3 id="stack">Stack</h3><p>Sesuai dengan VM yang akan diimplementasikan yaitu <em>Stack based,</em> sama seperti tumpukan ketika mencuci piring, piring yang telah dicuci disimpan di paling atas maka stack ini LIFO <em><strong>(Last In First Out)</strong></em> untuk menambahkan <em><strong>(push)</strong></em> maupun mengurangi<em><strong> (pop)</strong></em> hanya melalui posisi paling atas.</p><hr><h3 id="struktur-code">Struktur Code</h3><p>Untuk mengikuti ini pastikan download dan install Rust pada link berikut ini <a href="https://www.rust-lang.org/learn/get-started"><strong>https://www.rust-lang.org/learn/get-started</strong></a></p><p>setelah menginstall Rust, jalankan perintah berikut:</p><pre><code class="language-bash">cargo new simple-vm</code></pre><p>Kemudian kita akan membuat struktur seperti dibawah ini:</p><pre><code>simple-vm
│   Cargo.toml   
│
└───src
	| main.rs
	| vm.rs
	| bytecode.rs</code></pre><p>tambahkan code berikut ini pada <em>file</em> <strong>bytecode.rs</strong></p><figure class="kg-card kg-code-card"><pre><code class="language-rust">#[derive(Debug)]
pub enum Opcode {
  CONST(isize), // 0
  ADD, // 1
  SUB, // 2
  PRINT, // 3 
  HALT, // 4
}

impl Opcode {
  pub fn code(&amp;self) -&gt; isize {
    match *self {
      Opcode::CONST(_v) =&gt; 0,
      Opcode::ADD =&gt; 1,
      Opcode::SUB =&gt; 2,
      Opcode::PRINT =&gt; 3,
      Opcode::HALT =&gt; 4,
	}
  }
}</code></pre><figcaption>bytecode.rs</figcaption></figure><p><strong>#[derive(Debug)] </strong>adalah <em><strong>attribute</strong></em> dengan mengimplementasikan <em><strong>Debug</strong></em> pada <strong>enum Opcode </strong>sehingga dengan otomatis kita bisa memformatnya menggunakan <strong>{:?}</strong></p><pre><code class="language-rust">pub enum Opcode {
  CONST(isize), // 0
  ADD, // 1
  SUB, // 2
  PRINT, // 3 
  HALT, // 4
}</code></pre><p>disini kita membuat <em>enum </em>Opcode dengan data diatas yang merepresentasikan nomor/code sesuai dengan urutannya, misalnya untuk <strong>CONST(isize)</strong> adalah <em>enum constructor </em>yang berisikan tipe data<em> isize </em>yang merepresentasikan<em> code</em> 0. Tanda <strong>//</strong> hanyalah sebuah <em>comment </em>untuk menjelaskan urutannya<em>.</em></p><pre><code class="language-rust">impl Opcode {
  pub fn code(&amp;self) -&gt; isize {
    match *self {
      Opcode::CONST(_v) =&gt; 0,
      Opcode::ADD =&gt; 1,
      Opcode::SUB =&gt; 2,
      Opcode::PRINT =&gt; 3,
      Opcode::HALT =&gt; 4,
	}
  }
}</code></pre><p>kemudian disini <em>Opcode</em> mempunyai fungsi <em>code </em>untuk mengembalikan nomor/code yang sudah kita sesuaikan.</p><p>masih pada bagian <em>file</em> <strong>bytecode.rs </strong>tambahkan code berikut ini setelahnya.</p><pre><code class="language-rust">#[derive(Debug)]
pub struct Instruction {
  insructions: Vec&lt;Opcode&gt;,
}</code></pre><p>kita membuat <em>struct</em> Instruction yang berisikan <em><strong>instructions</strong> </em>dengan<em> </em>tipe data <strong><em>Vector</em></strong> dari <em>Opcode</em></p><pre><code class="language-rust">impl Instruction {
  pub fn new(opcodes: Vec&lt;Opcode&gt;) -&gt; Self {
    Instruction {
      insructions: opcodes,
    }
  }

  pub fn generate_code(&amp;self) -&gt; Vec&lt;isize&gt; {
    let mut code: Vec&lt;isize&gt; = Vec::new();

    for insruction in self.insructions.iter() {
      match insruction {
        Opcode::CONST(value) =&gt; {
          code.push(insruction.code());
          code.push(*value);
        },
        Opcode::ADD =&gt; code.push(insruction.code()),
        Opcode::SUB =&gt; code.push(insruction.code()),
        Opcode::PRINT =&gt; code.push(insruction.code()),
        Opcode::HALT =&gt; code.push(insruction.code()),
      };
    }

    code
  }
}</code></pre><p>dari <em>struct</em> Instruction diatas kita akan mengimplementasikan beberapa fungsi. Fungsi <em>new </em>adalah<em> associated functions </em>yang tidak mengambil <em>self </em>untuk<em> parameter </em>pertamanya, fungsi ini akan membuat <em>Instruction</em> baru yang berisikan <em>instructions</em> dari <em>opcode</em>. fungsi <em>generate_code </em>adalah fungsi yang mengambil <em>Instruction</em> sendiri, kemudian kita mendeklarasikan variabel <em>code</em> dengan tipe data <em>Vector</em> dari <em>isize, </em>lalu kita akan melakukan pengecekan <em>(match) </em>sesuai<em> enum </em>pada <em>instructions</em> tersebut.</p><hr><p>pada <em>file</em> <strong>main.rs</strong>, tambahkan code berikut:</p><pre><code class="language-rust">mod bytecode;

fn main() {
  let instructions: Vec&lt;bytecode::Opcode&gt; = vec!(
        bytecode::Opcode::CONST(10),
        bytecode::Opcode::CONST(20),
        bytecode::Opcode::ADD,
        bytecode::Opcode::CONST(9),
        bytecode::Opcode::SUB,
        bytecode::Opcode::PRINT,
    );

    let instruction = bytecode::Instruction::new(instructions);
    let code = instruction.generate_code();

    println!("{:?}", code);
}</code></pre><p>disini kita menggunakan <em>module</em> <strong>bytecode.rs</strong> yang telah kita buat sebelumnya. Untuk jelasnya mengenai module bisa dilihat disini <a href="https://doc.rust-lang.org/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html">https://doc.rust-lang.org/book/ch07-00-managing-growing-projects-with-packages-crates-and-modules.html</a>. Lalu kita memasukan beberapa <em>Opcode</em> pada <em>instructions </em>yang kemudian digunakan untuk <em>men-generate </em>sebuah <em>code</em> untuk di eksekusi oleh<em> </em>VM<em>. </em>Kemudian jalankan program yang telah kita buat dengan perintah berikut</p><pre><code class="language-bash">cargo run</code></pre><p>maka akan keluar hasil sebagai berikut</p><pre><code class="language-bash">[0, 10, 0, 20, 1, 0, 9, 2, 3]</code></pre><p>angka tersebut berupa <em>opcode</em> dan <em>operands</em> dari <em>instructions</em> yang telah kita masukan.</p><!--kg-card-begin: html--><br><!--kg-card-end: html--><p><a href="https://aldidana.com/membuat-vm-sederhana-dengan-rust-2/">bagian 2</a></p>]]></content:encoded></item><item><title><![CDATA[Hello World]]></title><description><![CDATA[<p>Sebenernya ini bukan post pertama, dikarenakan blog sebelumnya hilang tidak di backup maka mulai dari awal.</p>]]></description><link>https://aldidana.com/hello-world/</link><guid isPermaLink="false">5ea860d472f9c00e19173ec8</guid><dc:creator><![CDATA[Aldi Perdana]]></dc:creator><pubDate>Tue, 28 Apr 2020 17:00:31 GMT</pubDate><content:encoded><![CDATA[<p>Sebenernya ini bukan post pertama, dikarenakan blog sebelumnya hilang tidak di backup maka mulai dari awal.</p>]]></content:encoded></item></channel></rss>