diff --git a/Cargo.toml b/Cargo.toml index cf6b6d7..d65444a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,12 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +backend_c = [] +backend_js = [] + +default = ["backend_js"] + [dependencies] structopt = "0.3.21" rust-embed = "5.7.0" \ No newline at end of file diff --git a/builtin/builtin.c b/builtin/builtin.c new file mode 100644 index 0000000..14e60e7 --- /dev/null +++ b/builtin/builtin.c @@ -0,0 +1,9 @@ +/* START builtins */ +#include "stdio.h" + +void _printf(msg) +{ + printf(msg); +} + +/* END builtins */ \ No newline at end of file diff --git a/examples/playground.sb b/examples/playground.sb index 78cfac5..18ec2f4 100644 --- a/examples/playground.sb +++ b/examples/playground.sb @@ -1,5 +1,2 @@ fn main(n: int) { - if n { - return n - } } \ No newline at end of file diff --git a/lib/stdio.sb b/lib/stdio.sb index b86fec7..cf46fd5 100644 --- a/lib/stdio.sb +++ b/lib/stdio.sb @@ -10,8 +10,8 @@ fn println(msg: string) { } // Prints the size of an array -fn len(arr: int[]) { - let c = 0 +fn len(arr: int[]): int { + let c: int = 0 while arr[c] { c = c + 1 } @@ -21,13 +21,13 @@ fn len(arr: int[]) { // Reverses an array // TODO: fix me! -fn rev(arr: int[]) { +fn rev(arr: int[]): int[] { - let l = len(arr) - let new_arr = [] + let l: int = len(arr) + let new_arr: int[] = [] - let i = 0 - let j = l + let i: int = 0 + let j: int = l while i < l { new_arr[i] = arr[j] i = i - 1 diff --git a/src/generator/c.rs b/src/generator/c.rs new file mode 100644 index 0000000..04ea85c --- /dev/null +++ b/src/generator/c.rs @@ -0,0 +1,256 @@ +use crate::generator::Generator; +use crate::parser::node_type::*; +/** + * Copyright 2020 Garrit Franke + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +use crate::util::Either; + +pub struct CGenerator; + +impl Generator for CGenerator { + fn generate(prog: Program) -> String { + let mut code = String::new(); + + let raw_builtins = + crate::Builtins::get("builtin.c").expect("Could not locate builtin functions"); + code += std::str::from_utf8(raw_builtins.as_ref()) + .expect("Unable to interpret builtin functions"); + let funcs: String = prog + .func + .into_iter() + .map(|f| generate_function(f)) + .collect(); + + code += &funcs; + + return code; + } +} + +pub(super) fn generate_type(t: Either>) -> String { + let (ty, name) = match t { + Either::Left(var) => (var.ty, Some(var.name)), + Either::Right(ty) => (ty, None), + }; + match ty { + Some(t) => match t { + Type::Int => "int".into(), + Type::Str => "char *".into(), + Type::Bool => "bool".into(), + Type::Array(_) => match name { + Some(n) => format!( + "{T} {N}[]", + T = generate_type(Either::Right(Some(t))), + N = n + ), + None => format!("{}[]", generate_type(Either::Right(Some(t)))), + }, + }, + None => "void".into(), + } +} + +fn generate_function(func: Function) -> String { + let arguments: String = func + .arguments + .into_iter() + .map(|var| format!("{} {}", generate_type(Either::Left(var.clone())), var.name)) + .collect::>() + .join(", "); + let t = generate_type(Either::Right(func.ret_type)); + let mut raw = format!("{T} {N}({A})", T = t, N = func.name, A = arguments); + + raw += &generate_block(func.body); + + raw +} + +fn generate_block(block: Statement) -> String { + let mut generated = String::from("{\n"); + + let statements = match block { + Statement::Block(blk) => blk, + _ => panic!("Block body should be of type Statement::Block"), + }; + + for statement in statements { + generated += &generate_statement(statement); + } + + generated += "}\n"; + + generated +} + +fn generate_statement(statement: Statement) -> String { + let state = match statement { + Statement::Return(ret) => generate_return(ret), + Statement::Declare(var, val) => generate_declare(var, val), + Statement::Exp(val) => generate_expression(val) + ";\n", + Statement::If(expr, if_state, else_state) => { + generate_conditional(expr, *if_state, else_state.map(|x| *x)) + } + Statement::Assign(name, state) => generate_assign(*name, *state), + Statement::Block(_) => generate_block(statement), + Statement::While(expr, body) => generate_while_loop(expr, *body), + }; + + format!("{}\n", state) +} + +fn generate_expression(expr: Expression) -> String { + let st = match expr { + Expression::Int(val) => val.to_string(), + Expression::Variable(val) | Expression::Str(val) => val, + Expression::Bool(b) => b.to_string(), + Expression::FunctionCall(name, e) => generate_function_call(name, e), + Expression::Array(els) => generate_array(els), + Expression::ArrayAccess(name, expr) => generate_array_access(name, *expr), + Expression::BinOp(left, op, right) => generate_bin_op(*left, op, *right), + }; + + format!("{}", st) +} + +fn generate_while_loop(expr: Expression, body: Statement) -> String { + let mut out_str = String::from("while ("); + + out_str += &generate_expression(expr); + out_str += ") "; + out_str += &generate_block(body); + out_str +} + +fn generate_array(elements: Vec) -> String { + let mut out_str = String::from("["); + + out_str += &elements + .iter() + .map(|el| match el { + Expression::Int(x) => x.to_string(), + Expression::Str(x) => x.to_string(), + _ => todo!("Not yet implemented"), + }) + .collect::>() + .join(", "); + + out_str += "]"; + out_str +} + +fn generate_array_access(name: String, expr: Expression) -> String { + format!("{n}[{e}]", n = name, e = generate_expression(expr)) +} + +fn generate_conditional( + expr: Expression, + if_state: Statement, + else_state: Option, +) -> String { + let expr_str = generate_expression(expr); + + let body = match if_state { + Statement::Block(blk) => blk, + _ => panic!("Conditional body should be of type block"), + }; + + let mut outcome = format!("if ({})", expr_str); + + outcome += "{\n"; + for statement in body { + outcome += &generate_statement(statement); + } + outcome += "}"; + + if let Some(else_state) = else_state { + outcome += "else "; + outcome += &generate_statement(else_state); + } + outcome +} + +fn generate_declare(var: Variable, val: Option) -> String { + // var is used here to not collide with scopes. + // TODO: Can let be used instead? + match val { + Some(expr) => format!( + "{} {} = {};", + generate_type(Either::Left(var.to_owned())), + var.to_owned().name, + generate_expression(expr) + ), + None => format!( + "{} {};", + generate_type(Either::Left(var.to_owned())), + var.name + ), + } +} + +fn generate_function_call(func: String, args: Vec) -> String { + let formatted_args = args + .into_iter() + .map(|arg| match arg { + Expression::Int(i) => i.to_string(), + Expression::Bool(v) => v.to_string(), + Expression::ArrayAccess(name, expr) => generate_array_access(name, *expr), + Expression::FunctionCall(n, a) => generate_function_call(n, a), + Expression::Str(s) | Expression::Variable(s) => s, + Expression::Array(_) => todo!(), + Expression::BinOp(left, op, right) => generate_bin_op(*left, op, *right), + }) + .collect::>() + .join(","); + format!("{N}({A})", N = func, A = formatted_args) +} + +fn generate_return(ret: Option) -> String { + match ret { + Some(expr) => format!("return {};", generate_expression(expr)), + None => "return;".to_string(), + } +} + +fn generate_bin_op(left: Expression, op: BinOp, right: Expression) -> String { + let op_str = match op { + BinOp::Addition => "+", + BinOp::And => "&&", + BinOp::Division => "/", + BinOp::Equal => "===", + BinOp::GreaterThan => ">", + BinOp::GreaterThanOrEqual => ">=", + BinOp::LessThan => "<", + BinOp::LessThanOrEqual => "<=", + BinOp::Modulus => "%", + BinOp::Multiplication => "*", + BinOp::NotEqual => "!==", + BinOp::Or => "||", + BinOp::Subtraction => "-", + }; + format!( + "{l} {op} {r}", + l = generate_expression(left), + op = op_str, + r = generate_expression(right) + ) +} + +fn generate_assign(name: Expression, expr: Expression) -> String { + format!( + "{} = {};", + generate_expression(name), + generate_expression(expr) + ) +} diff --git a/src/generator/mod.rs b/src/generator/mod.rs index ce8dc16..137834a 100644 --- a/src/generator/mod.rs +++ b/src/generator/mod.rs @@ -15,9 +15,24 @@ */ use crate::parser::node_type::*; +pub mod c; pub mod js; +#[cfg(test)] +mod tests; pub mod x86; pub trait Generator { fn generate(prog: Program) -> String; } + +pub fn generate(prog: Program) -> String { + #[cfg(feature = "backend_c")] + { + c::CGenerator::generate(prog) + } + + #[cfg(feature = "backend_js")] + { + js::JsGenerator::generate(prog) + } +} diff --git a/src/generator/tests/c_tests.rs b/src/generator/tests/c_tests.rs new file mode 100644 index 0000000..d4b7b5b --- /dev/null +++ b/src/generator/tests/c_tests.rs @@ -0,0 +1,9 @@ +use crate::generator::c::generate_type; +use crate::parser::node_type::Type; +use crate::util::Either; + +#[test] +fn test_generate_type_regular_type() { + let t = generate_type(Either::Right(Some(Type::Int))); + assert_eq!(t, "int") +} diff --git a/src/generator/tests/mod.rs b/src/generator/tests/mod.rs new file mode 100644 index 0000000..8999cd3 --- /dev/null +++ b/src/generator/tests/mod.rs @@ -0,0 +1 @@ +mod c_tests; diff --git a/src/main.rs b/src/main.rs index 03f0b03..f42c866 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,7 +16,6 @@ extern crate rust_embed; extern crate structopt; -use crate::generator::Generator; use crate::Opt::Build; use std::fs::File; use std::io::Read; @@ -68,7 +67,7 @@ fn main() -> Result<(), String> { let tokens = lexer::tokenize(&contents); let program = parser::parse(tokens, Some(contents))?; - let output = generator::js::JsGenerator::generate(program); + let output = generator::generate(program); let mut file = std::fs::File::create(out_file).expect("create failed"); file.write_all(output.as_bytes()).expect("write failed"); file.flush().expect("Could not flush file"); diff --git a/src/parser/node_type.rs b/src/parser/node_type.rs index 14e07ec..8f81c1a 100644 --- a/src/parser/node_type.rs +++ b/src/parser/node_type.rs @@ -30,13 +30,13 @@ pub struct Function { pub ret_type: Option, } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Clone)] pub struct Variable { pub name: String, pub ty: Option, } -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Clone)] pub enum Type { Int, Str, diff --git a/src/util/mod.rs b/src/util/mod.rs index 71f2d57..9a7561a 100644 --- a/src/util/mod.rs +++ b/src/util/mod.rs @@ -14,3 +14,9 @@ * limitations under the License. */ pub mod string_util; + +/// Datatype that holds one of two types +pub enum Either { + Left(L), + Right(R), +}