From 1c3d7c6bd11ec494dae8f41c759f485fda2718c7 Mon Sep 17 00:00:00 2001 From: Garen Tyler Date: Thu, 10 Dec 2020 19:53:38 -0700 Subject: [PATCH] Code generation --- src/ast.rs | 124 +++++++++++++++++++++++++++++++++++++---------- src/main.rs | 16 ++++-- src/parse/mod.rs | 54 +++++++++++++-------- test.js | 2 +- 4 files changed, 144 insertions(+), 52 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index aae87a8..258ec8c 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,4 +1,6 @@ use serde::{Deserialize, Serialize}; +use ron::from_str; + #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum AstNodeKind { // Primitives @@ -23,6 +25,7 @@ pub enum AstNodeKind { FunctionReturn, FunctionDefinition, VariableDefinition, + VariableDeclaration, Assign, // Blank node Null, @@ -42,48 +45,112 @@ impl AstNode { subnodes, } } - pub fn emit(&self, f: &mut dyn std::fmt::Write) -> std::fmt::Result { + pub fn emit(&self) -> String { use AstNodeKind::*; match self.kind { - Integer => write!(f, "i32.const {}\n", self.value), - Identifier => write!(f, "get_local ${}\n", self.value), - Add => { - self.subnodes[0].emit(f)?; - self.subnodes[1].emit(f)?; - write!(f, "i32.add\n") + // Primitives + Integer => format!("(i32.const {})", self.value), + Identifier => format!("(get_local ${})", self.value), + // Unary operators + Not => format!("(i32.eq (i32.const 0) {})", self.subnodes[1].emit()), + // Infix operators + NotEqual => format!("(i32.ne {} {})", self.subnodes[0].emit(), self.subnodes[1].emit()), + Equal => format!("(i32.eq {} {})", self.subnodes[0].emit(), self.subnodes[1].emit()), + Add => format!("(i32.add {} {})", self.subnodes[0].emit(), self.subnodes[1].emit()), + Subtract => format!("(i32.sub {} {})", self.subnodes[0].emit(), self.subnodes[1].emit()), + Multiply => format!("(i32.mul {} {})", self.subnodes[0].emit(), self.subnodes[1].emit()), + Divide => format!("(i32.div_s {} {})", self.subnodes[0].emit(), self.subnodes[1].emit()), + // Control flow + Block => { + let mut out = String::new(); + for node in &self.subnodes { + out += ""; + out += &node.emit(); + } + out + } + IfStatement => { + let mut out = String::new(); + out += &format!("(if {} (then {})", self.subnodes[0].emit(), self.subnodes[1].emit()); // Emit the conditional and consequence. + if let Some(alternative) = self.subnodes.get(2) { + out += &format!(" (else {})", alternative.emit()); // Emit the alternative. + } + out += ")"; + out + } + WhileLoop => { + let loop_symbol = "while_loop"; // TODO: Make generate unique symbol for nested loops. + let mut out = String::new(); + out += &format!("(block ${}_wrapper", loop_symbol); + out += &format!(" (loop ${}_loop", loop_symbol); + out += &format!(" {}", self.subnodes[1].emit()); + out += &format!(" (br_if ${}_wrapper (i32.eq (i32.const 0) {}))", loop_symbol, self.subnodes[0].emit()); + out += &format!(" (br ${}_loop)", loop_symbol); + out += "))"; + out } Program => { - write!(f, "(module\n")?; + let mut out = String::new(); + out += "(module"; let mut exported = vec![]; for node in &self.subnodes { - node.emit(f)?; + out += " "; + out += &node.emit(); if node.kind == FunctionDefinition { exported.push(node.value.clone()); } } for export in exported { - write!(f, "(export \"{0}\" (func ${0}))\n", export)?; + out += &format!(" (export \"{0}\" (func ${0}))", export); } - write!(f, ")") + out += ")"; + out } + // Functions and variables + FunctionCall => { + let mut out = String::new(); + out += &format!("(call ${}", from_str::(&self.value).unwrap().value); + for n in &self.subnodes { + out += " "; + out += &n.emit(); + } + out += ")"; + out + }, + FunctionReturn => format!("{} (return)", self.subnodes[0].emit()), FunctionDefinition => { - write!(f, "(func ${}", self.value)?; - // let body = self.subnodes[0]; - for a in &self.subnodes[1..] { - write!(f, " (param ${} i32)", a.value)?; + let mut out = String::new(); + out += &format!("(func ${}", self.value); + let body = self.subnodes[0].clone(); + for n in &self.subnodes[1..] { + out += &format!(" (param ${} i32)", n.value); } - write!(f, " (result i32)\n")?; - self.subnodes[0].emit(f)?; - write!(f, ")\n") - } - Block => { - for node in &self.subnodes { - node.emit(f)?; + let mut func_returns_value = false; + let mut index = 0; + loop { + if index >= body.subnodes.len() { + break; + } + match body.subnodes[index].kind { + AstNodeKind::FunctionReturn => func_returns_value = true, + _ => {} + } + index += 1; } - write!(f, "") + if func_returns_value { + out += " (result i32)"; + } + for n in &body.subnodes { + out += " "; + out += &n.emit(); + } + out += ")"; + out } - FunctionReturn => self.subnodes[0].emit(f), - _ => Ok(()), + VariableDeclaration => format!("(local ${} i32)", self.value), + Assign => format!("(set_local ${} {})", self.value, self.subnodes[0].emit()), + // Blank node / other + Null | _ => "".into(), } } @@ -219,6 +286,13 @@ impl AstNode { subnodes: vec![value], } } + pub fn variable_declaration(name: String) -> AstNode { + AstNode { + kind: AstNodeKind::VariableDeclaration, + value: name, + subnodes: vec![], + } + } pub fn assign(name: String, value: AstNode) -> AstNode { AstNode { kind: AstNodeKind::Assign, diff --git a/src/main.rs b/src/main.rs index ad6dd90..cc55b7c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,13 +4,19 @@ use std::fs::File; use std::io::prelude::*; fn main() -> std::io::Result<()> { - let src = r#"function add(left, right) { return left + right; }"#; + let src = r#" + function a(num) { + return num; + } + function main(num) { + var amt = a(2); + return num + amt; + }"#; let ast = pivot::parse(src); - // println!("{}", ast); - let mut code = String::new(); - ast.emit(&mut code); + println!("{}", ast); + let code = ast.emit(); println!("{}", code); let binary = wat::parse_str(code).unwrap(); - let mut file = File::create("out.wasm")?.write_all(&binary)?; + File::create("out.wasm")?.write_all(&binary)?; Ok(()) } diff --git a/src/parse/mod.rs b/src/parse/mod.rs index 3c96e34..567866a 100644 --- a/src/parse/mod.rs +++ b/src/parse/mod.rs @@ -1,15 +1,22 @@ mod combinators; -use crate::ast::AstNode; +use crate::ast::{AstNode, AstNodeKind}; use combinators::Parser; use ron::{from_str, to_string}; pub fn parse>(src: T) -> AstNode { let src: String = src.into(); - let parse_program = Parser::custom(parse_statement) + let whitespace = Parser::regex(r"[ \n\r\t]+"); + let comments = Parser::regex(r"[/][/].*").or(Parser::regex(r"[/][*].*[*][/]")); + let ignored = whitespace.or(comments).repeat_range(0..usize::MAX); + let statement = ignored.optional().ignore().and(Parser::custom(parse_statement)); + let parse_program = statement.clone() .repeat_range(0..usize::MAX) .map(|matched| { let data = from_str::>(&matched)?; + for d in &data { + println!("{}", d); + } let mut statements = vec![]; for d in data { statements.push(from_str::(&d)?); @@ -159,29 +166,31 @@ fn parse_statement(src: String) -> Result<(String, String), String> { .and(block_statement.clone()) .map(|matched| { let data = from_str::>(&matched)?; - for d in &data { - println!("{}", d); - } - let body = from_str::(&data[1])?; - println!("body: {}", body); + let mut body = from_str::(&data[1])?; let data = from_str::>(&data[0])?; - for d in &data { - println!("{}", d); - } let name = from_str::(&data[0])?.value; - println!("name: {}", name); let params = from_str::>(&data[1])?; let mut parameters = vec![]; - for p in from_str::>(¶ms[0])? { - parameters.push(from_str::(&p)?); + if params.len() != 0 { + for p in from_str::>(¶ms[0])? { + parameters.push(from_str::(&p)?); + } } - println!("{:?}", parameters); + // Hoist variable definitions. + let mut vars = vec![]; + let mut others = vec![]; + for node in &body.subnodes { + match node.kind { + AstNodeKind::VariableDefinition => { + vars.push(AstNode::variable_declaration(node.value.clone())); + others.push(AstNode::assign(node.value.clone(), node.subnodes[0].clone())) + }, + _ => others.push(node.clone()), + } + } + vars.append(&mut others); + body.subnodes = vars; Ok(to_string(&AstNode::function_definition(name, parameters, body))?) - }) - .map(|matched| { - let function_definition = from_str::(&matched)?; - println!("{}", function_definition); - Ok(matched) }); return_statement.clone() .or(if_statement.clone()) @@ -283,8 +292,11 @@ fn parse_expression(src: String) -> Result<(String, String), String> { let callee = data[0].clone(); let args = from_str::>(&data[1])?; let mut ast_args = vec![]; - for arg in &args { - ast_args.push(from_str::(arg)?); + if let Some(args) = args.get(0) { + let args = from_str::>(&args)?; + for arg in &args { + ast_args.push(from_str::(arg)?); + } } Ok(to_string( &AstNode::function_call(callee, ast_args), diff --git a/test.js b/test.js index 8d19271..b6d620e 100644 --- a/test.js +++ b/test.js @@ -1,5 +1,5 @@ require("webassembly") .load("out.wasm") .then(module => { - console.log("2 + 2 is " + module.exports.add(2, 2)); + console.log("3 + 2 is " + module.exports.main(3)); });