Browse Source

Add C backend

github-actions
Garrit Franke 1 year ago
parent
commit
9459c11afa
  1. 6
      Cargo.toml
  2. 9
      builtin/builtin.c
  3. 3
      examples/playground.sb
  4. 14
      lib/stdio.sb
  5. 256
      src/generator/c.rs
  6. 15
      src/generator/mod.rs
  7. 9
      src/generator/tests/c_tests.rs
  8. 1
      src/generator/tests/mod.rs
  9. 3
      src/main.rs
  10. 4
      src/parser/node_type.rs
  11. 6
      src/util/mod.rs

6
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"

9
builtin/builtin.c

@ -0,0 +1,9 @@
/* START builtins */
#include "stdio.h"
void _printf(msg)
{
printf(msg);
}
/* END builtins */

3
examples/playground.sb

@ -1,5 +1,2 @@
fn main(n: int) {
if n {
return n
}
}

14
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

256
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<Variable, Option<Type>>) -> 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::<Vec<String>>()
.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<Expression>) -> 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::<Vec<String>>()
.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<Statement>,
) -> 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<Expression>) -> 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<Expression>) -> 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::<Vec<String>>()
.join(",");
format!("{N}({A})", N = func, A = formatted_args)
}
fn generate_return(ret: Option<Expression>) -> 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)
)
}

15
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)
}
}

9
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")
}

1
src/generator/tests/mod.rs

@ -0,0 +1 @@
mod c_tests;

3
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");

4
src/parser/node_type.rs

@ -30,13 +30,13 @@ pub struct Function {
pub ret_type: Option<Type>,
}
#[derive(Debug, Eq, PartialEq)]
#[derive(Debug, Eq, PartialEq, Clone)]
pub struct Variable {
pub name: String,
pub ty: Option<Type>,
}
#[derive(Debug, Eq, PartialEq)]
#[derive(Debug, Eq, PartialEq, Clone)]
pub enum Type {
Int,
Str,

6
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<L, R> {
Left(L),
Right(R),
}

Loading…
Cancel
Save