i32s, variables, and addition

This commit is contained in:
mars 2022-08-26 21:53:56 -06:00
commit ef6b222db2
8 changed files with 727 additions and 0 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
/target

366
Cargo.lock generated Normal file
View File

@ -0,0 +1,366 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "anyhow"
version = "1.0.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1485d4d2cc45e7b201ee3767015c96faa5904387c9d87c6efdd0fb511f12d305"
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "block-buffer"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
dependencies = [
"generic-array",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cpufeatures"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc948ebb96241bb40ab73effeb80d9f93afaad49359d159a5e61be51619fe813"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
version = "0.1.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
dependencies = [
"generic-array",
"typenum",
]
[[package]]
name = "digest"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2fb860ca6fafa5552fb6d0e816a69c8e49f0908bf524e30a90d97c85892d506"
dependencies = [
"block-buffer",
"crypto-common",
]
[[package]]
name = "downcast-rs"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
[[package]]
name = "dust-bunny"
version = "0.1.0"
dependencies = [
"pest",
"pest_derive",
"wasm-encoder",
"wasmi",
"wasmprinter",
]
[[package]]
name = "generic-array"
version = "0.14.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "indexmap"
version = "1.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10a35a97730320ffe8e2d410b5d3b69279b98d2c14bdb8b70ea89ecf7888d41e"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "leb128"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.132"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8371e4e5341c3a96db127eb2465ac681ced4c433e01dd0e938adbef26ba93ba5"
[[package]]
name = "libm"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "292a948cd991e376cf75541fe5b97a1081d713c618b4f1b9500f8844e49eb565"
[[package]]
name = "memory_units"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8452105ba047068f40ff7093dd1d9da90898e63dd61736462e9cdda6a90ad3c3"
[[package]]
name = "num-bigint"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
dependencies = [
"autocfg",
"num-integer",
"num-traits",
]
[[package]]
name = "num-integer"
version = "0.1.45"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
dependencies = [
"autocfg",
"num-traits",
]
[[package]]
name = "num-rational"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
dependencies = [
"autocfg",
"num-bigint",
"num-integer",
"num-traits",
]
[[package]]
name = "num-traits"
version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
dependencies = [
"autocfg",
]
[[package]]
name = "once_cell"
version = "1.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "074864da206b4973b84eb91683020dbefd6a8c3f0f38e054d93954e891935e4e"
[[package]]
name = "pest"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4b0560d531d1febc25a3c9398a62a71256c0178f2e3443baedd9ad4bb8c9deb4"
dependencies = [
"thiserror",
"ucd-trie",
]
[[package]]
name = "pest_derive"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "905708f7f674518498c1f8d644481440f476d39ca6ecae83319bba7c6c12da91"
dependencies = [
"pest",
"pest_generator",
]
[[package]]
name = "pest_generator"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5803d8284a629cc999094ecd630f55e91b561a1d1ba75e233b00ae13b91a69ad"
dependencies = [
"pest",
"pest_meta",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "pest_meta"
version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1538eb784f07615c6d9a8ab061089c6c54a344c5b4301db51990ca1c241e8c04"
dependencies = [
"once_cell",
"pest",
"sha-1",
]
[[package]]
name = "proc-macro2"
version = "1.0.43"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
dependencies = [
"proc-macro2",
]
[[package]]
name = "sha-1"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "028f48d513f9678cda28f6e4064755b3fbb2af6acd672f2c209b62323f7aea0f"
dependencies = [
"cfg-if",
"cpufeatures",
"digest",
]
[[package]]
name = "spin"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f6002a767bff9e83f8eeecf883ecb8011875a21ae8da43bffb817a57e78cc09"
[[package]]
name = "syn"
version = "1.0.99"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "thiserror"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994"
dependencies = [
"thiserror-impl",
]
[[package]]
name = "thiserror-impl"
version = "1.0.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "typenum"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "ucd-trie"
version = "0.1.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89570599c4fe5585de2b388aab47e99f7fa4e9238a1399f707a02e356058141c"
[[package]]
name = "unicode-ident"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
[[package]]
name = "version_check"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasm-encoder"
version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d443c5a7daae71697d97ec12ad70b4fe8766d3a0f4db16158ac8b781365892f7"
dependencies = [
"leb128",
]
[[package]]
name = "wasmi"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81a9c47f1785842298b082431a60d6c27bbceda84ff14558598ba44f5c609ccd"
dependencies = [
"spin",
"wasmi_core",
"wasmparser-nostd",
]
[[package]]
name = "wasmi_core"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a53379aecfee3604c2423dddbbecf1d65e0327b0249812dcc2737e5a1db43b5"
dependencies = [
"downcast-rs",
"libm",
"memory_units",
"num-rational",
"num-traits",
]
[[package]]
name = "wasmparser"
version = "0.89.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab5d3e08b13876f96dd55608d03cd4883a0545884932d5adf11925876c96daef"
dependencies = [
"indexmap",
]
[[package]]
name = "wasmparser-nostd"
version = "0.83.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "58a45f1058fed5ce7ff3da64153d0537a1ae664d09855fbb1402c6472f09571b"
[[package]]
name = "wasmprinter"
version = "0.2.39"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa9e5ee2f56cc8a5da489558114e8c118e5a8416d96aefe63dcf1b5b05b858c6"
dependencies = [
"anyhow",
"wasmparser",
]

11
Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
name = "dust-bunny"
version = "0.1.0"
edition = "2021"
[dependencies]
pest = "2.3"
pest_derive = "2"
wasm-encoder = "0.16"
wasmi = "0.15"
wasmprinter = "0.2"

84
src/ast.rs Normal file
View File

@ -0,0 +1,84 @@
use crate::parse::{PestPair, Rule as PestRule};
use std::str::FromStr;
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Span<'a> {
pub slice: &'a str,
pub line: usize,
pub column: usize,
}
impl<'a> From<pest::Span<'a>> for Span<'a> {
fn from(other: pest::Span<'a>) -> Self {
let slice = other.as_str();
let (line, column) = other.start_pos().line_col();
Self {
slice,
line,
column,
}
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct BranchBody<'a> {
pub statements: Vec<Statement<'a>>,
pub tail_expr: Option<Box<Expr<'a>>>,
}
pub type ExprPair<'a> = Box<(Expr<'a>, Expr<'a>)>;
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Expr<'a> {
BinaryOp(BinaryOp, ExprPair<'a>),
Literal(Literal<'a>),
Local(Span<'a>),
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum BinaryOp {
Add,
}
impl FromStr for BinaryOp {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
use BinaryOp::*;
match s {
"+" => Ok(Add),
_ => Err(()),
}
}
}
impl<'a> From<PestPair<'a>> for BinaryOp {
fn from(other: PestPair<'a>) -> Self {
assert_eq!(other.as_rule(), PestRule::binary_op);
let span = other.as_span().as_str();
Self::from_str(span).unwrap()
}
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Literal<'a> {
DecimalInteger(Span<'a>),
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub enum Statement<'a> {
Let { ident: Ident<'a>, expr: Expr<'a> },
}
#[derive(Clone, Debug, Eq, Hash, PartialEq)]
pub struct Ident<'a> {
pub span: Span<'a>,
}
impl<'a> From<PestPair<'a>> for Ident<'a> {
fn from(other: PestPair<'a>) -> Self {
assert_eq!(other.as_rule(), PestRule::identifier);
let span = other.as_span().into();
Self { span }
}
}

89
src/cg.rs Normal file
View File

@ -0,0 +1,89 @@
use crate::ast::*;
use std::collections::HashMap;
use wasm_encoder::{Function, Instruction, ValType};
pub struct FunctionBuilder {
locals: Vec<ValType>,
local_names: HashMap<String, u32>,
instructions: Vec<Instruction<'static>>,
}
impl FunctionBuilder {
pub fn new() -> Self {
Self {
locals: Vec::new(),
local_names: HashMap::new(),
instructions: Vec::new(),
}
}
pub fn push_instr(&mut self, instr: Instruction<'static>) {
self.instructions.push(instr);
}
pub fn add_local(&mut self, name: &str, ty: ValType) -> u32 {
let index = self.locals.len() as u32;
self.locals.push(ty);
self.local_names.insert(name.to_string(), index);
index
}
pub fn get_local(&self, name: &str) -> u32 {
*self.local_names.get(name).unwrap()
}
pub fn finish(self) -> Function {
let mut func = Function::new_with_locals_types(self.locals.into_iter());
for instr in self.instructions.into_iter() {
func.instruction(&instr);
}
func.instruction(&Instruction::End);
func
}
}
pub fn codegen_branch(ctx: &mut FunctionBuilder, ast: &BranchBody) {
for stmt in ast.statements.iter() {
match stmt {
Statement::Let { ident, expr } => {
codegen_expr(ctx, expr);
let idx = ctx.add_local(ident.span.slice, ValType::I32);
ctx.push_instr(Instruction::LocalSet(idx));
}
}
}
if let Some(expr) = ast.tail_expr.as_ref() {
codegen_expr(ctx, expr);
}
}
pub fn codegen_expr(ctx: &mut FunctionBuilder, ast: &Expr) {
match ast {
Expr::BinaryOp(op, sides) => {
codegen_expr(ctx, &sides.0);
codegen_expr(ctx, &sides.1);
let instr = match op {
BinaryOp::Add => Instruction::I32Add,
};
ctx.push_instr(instr);
}
Expr::Literal(lit) => match lit {
Literal::DecimalInteger(span) => {
let int: i32 = span.slice.parse().unwrap();
let instr = Instruction::I32Const(int);
ctx.push_instr(instr);
}
},
Expr::Local(span) => {
let idx = ctx.get_local(span.slice);
let instr = Instruction::LocalGet(idx);
ctx.push_instr(instr);
}
}
}

12
src/dust_bunny.pest Normal file
View File

@ -0,0 +1,12 @@
branch = _{ "{" ~ stmt* ~ expr? ~ "}" }
stmt = _{ let_stmt }
let_stmt = { "let" ~ identifier ~ "=" ~ expr ~ ";" }
expr = { term ~ (binary_op ~ term)* }
term = { val }
val = _{ identifier | literal | "(" ~ expr ~ ")" }
literal = _{ int }
binary_op = { "+" }
int = @{ "-"? ~ ASCII_DIGIT+ }
identifier = @{ ("_" | ASCII_ALPHA) ~ ("_" | ASCII_ALPHANUMERIC)* }
WHITESPACE = _{ " " | "\t" | NEWLINE }
COMMENT = _{ "//" ~ (!NEWLINE ~ ANY)* }

92
src/main.rs Normal file
View File

@ -0,0 +1,92 @@
mod ast;
mod cg;
mod parse;
fn main() {
println!("Hello world! Please run cargo test.");
}
#[cfg(test)]
mod tests {
use super::*;
fn branch_test_inner(expected: i32, body: &str) {
let ast = parse::parse_branch(body);
println!("{:#?}", ast);
use wasm_encoder::{
CodeSection, ExportKind, ExportSection, FunctionSection, TypeSection, ValType,
};
let mut builder = cg::FunctionBuilder::new();
cg::codegen_branch(&mut builder, &ast);
let f = builder.finish();
let mut module = wasm_encoder::Module::new();
let mut types = TypeSection::new();
let params = vec![];
let results = vec![ValType::I32];
types.function(params, results);
module.section(&types);
let mut functions = FunctionSection::new();
functions.function(0);
module.section(&functions);
let mut exports = ExportSection::new();
exports.export("f", ExportKind::Func, 0);
module.section(&exports);
let mut codes = CodeSection::new();
codes.function(&f);
module.section(&codes);
let wasm_bytes = module.finish();
let wasm_text = wasmprinter::print_bytes(&wasm_bytes).unwrap();
println!("{}", wasm_text);
use wasmi::{Engine, Extern, Linker, Module, Store};
let engine = Engine::default();
let module = Module::new(&engine, &wasm_bytes[..]).unwrap();
let mut store = Store::new(&engine, ());
let mut linker = <Linker<()>>::new();
let instance_pre = linker.instantiate(&mut store, &module).unwrap();
let instance = instance_pre.start(&mut store).unwrap();
let f = instance
.get_export(&store, "f")
.and_then(Extern::into_func)
.unwrap()
.typed::<(), i32, _>(&mut store)
.unwrap();
let result = f.call(&mut store, ()).unwrap();
assert_eq!(result, expected);
}
macro_rules! branch_test {
($name: ident, $expected:expr, $body:expr) => {
#[test]
fn $name() {
let body = concat!("{", $body, "}");
branch_test_inner($expected, body);
}
};
}
branch_test!(add, 3, "1 + 2");
branch_test!(chained_add, 5, "1 + 1 + 1 + 1 + 1");
branch_test!(
vars,
5,
r#"
let a = 2;
let b = 3;
let c = a + b;
c
"#
);
}

72
src/parse.rs Normal file
View File

@ -0,0 +1,72 @@
use crate::ast::*;
use pest::iterators::*;
use pest::Parser;
use pest_derive::Parser as DeriveParser;
#[derive(DeriveParser)]
#[grammar = "dust_bunny.pest"]
pub struct PestParser;
pub type PestPairs<'a> = Pairs<'a, Rule>;
pub type PestPair<'a> = Pair<'a, Rule>;
pub fn parse_branch(body: &str) -> BranchBody<'_> {
let rule = Rule::branch;
let pairs = PestParser::parse(rule, body).unwrap();
println!("{:#?}", pairs);
let mut statements = Vec::new();
let mut tail_expr = None;
for pair in pairs {
match pair.as_rule() {
Rule::let_stmt => {
let mut tok = pair.into_inner();
let ident = tok.next().unwrap().into();
let expr = parse_expr(tok.next().unwrap());
statements.push(Statement::Let { ident, expr });
}
Rule::expr => {
tail_expr = Some(Box::new(parse_expr(pair)));
break;
}
_ => unimplemented!(),
};
}
BranchBody {
statements,
tail_expr,
}
}
pub fn parse_expr<'a>(mut pair: PestPair<'a>) -> Expr<'a> {
assert_eq!(pair.as_rule(), Rule::expr);
let mut pairs = pair.into_inner();
let first_term = pairs.next().unwrap();
let mut expr = parse_term(first_term);
while pairs.peek().is_some() {
let op = pairs.next().unwrap().into();
let lhs = expr.clone();
let rhs = parse_term(pairs.next().unwrap());
let sides = Box::new((lhs, rhs));
expr = Expr::BinaryOp(op, sides);
}
expr
}
pub fn parse_term<'a>(mut pair: PestPair<'a>) -> Expr<'a> {
assert_eq!(pair.as_rule(), Rule::term);
let term = pair.into_inner().next().unwrap();
match term.as_rule() {
Rule::identifier => Expr::Local(term.as_span().into()),
Rule::int => Expr::Literal(Literal::DecimalInteger(term.as_span().into())),
_ => unimplemented!(),
}
}