1
1
Fork 0
mirror of https://github.com/azur1s/bobbylisp.git synced 2024-10-16 02:37:40 -05:00

refactor to have IR in compliation

This commit is contained in:
Natapat Samutpong 2022-02-25 02:58:50 +07:00
parent 5809a4949f
commit a98cc8ca27
6 changed files with 241 additions and 109 deletions

View file

@ -1,9 +1,9 @@
let foo = 1;
let bar = true;
let baz = "qux";
let foo: int = 1;
let bar: string = "str";
let baz: bool = true;
if (bar == true) then
print(baz);
else
print("quux");
fun qux (lhs: int rhs: int) -> int = lhs + rhs;
fun main () -> int = do
puts("Hello, World");
0;
end;

View file

@ -1,14 +1,16 @@
use std::fmt::Display;
use crate::front::parse::Expr;
use crate::middle::ir::{IR, Value};
#[derive(Debug, Clone)]
pub struct Codegen {
pub emitted: String,
}
const HEADER_INCLUDES: [&str; 2] = [
"#include <stdio.h>",
"#include <hycron/stdbool.h>",
const HEADER_INCLUDES: [&str; 3] = [
"<stdio.h>",
"<string.h>",
"<hycron/stdbool.h>",
];
impl Codegen {
@ -22,63 +24,95 @@ impl Codegen {
self.emitted.push_str(&s.to_string());
}
pub fn gen(&mut self, exprs: &[Expr]) {
pub fn gen(&mut self, irs: &[IR]) {
for header in HEADER_INCLUDES.iter() {
self.emit("#include ");
self.emit(header);
self.emit("\n");
}
self.emit("int main() {");
for expr in exprs {
self.gen_expr(expr);
for ir in irs {
self.gen_ir(ir);
}
self.emit("return 0;");
self.emit("}");
}
fn gen_expr(&mut self, expr: &Expr) {
match expr {
Expr::Let { name, value } => {
match &**value {
Expr::Int(i) => self.emit(format!("int {} = {};", name, i)),
Expr::Float(f) => self.emit(format!("double {} = {};", name, f)),
Expr::Boolean(b) => self.emit(format!("bool {} = {};", name, b)),
Expr::String(s) => self.emit(format!("char *{} = \"{}\";", name, s)),
_ => todo!(),
}
fn gen_ir(&mut self, ir: &IR) {
match ir {
IR::Define { name, type_hint, value } => {
self.emit(format!("{} {} = ", type_hint, name));
self.gen_ir(value);
self.emit(";\n");
},
Expr::Call { name, args } => {
match &**name {
Expr::Ident(func) => {
match func.as_str() {
"print" => {
self.emit(format!("printf({});", match &args[0] {
Expr::String(s) => format!("\"{}\"", s),
Expr::Ident(s) => format!("\"%s\", {}", s),
_ => todo!(),
}));
},
_ => todo!(),
IR::Fun { name, return_type_hint, args, body } => {
let args = args.iter().map(|(name, type_hint)| {
format!("{} {}", type_hint, name)
}).collect::<Vec<_>>().join(", ");
self.emit(format!("{} {}({}) {{", return_type_hint, name, args));
match &**body {
IR::Value { value } => {
self.emit("return ");
self.gen_value(&value);
self.emit(";");
},
IR::Do { body } => {
for (i, node) in body.iter().enumerate() {
if i == body.len() - 1 {
self.emit("return ");
};
self.gen_ir(node);
self.emit(";");
}
},
IR::Binary { op, left, right } => {
self.emit("return ");
self.gen_ir(left);
self.emit(op);
self.gen_ir(right);
self.emit(";");
},
_ => todo!(),
}
self.emit("}\n");
},
Expr::If { cond, then, else_ } => {
self.emit("if (".to_string());
self.gen_expr(&cond);
self.emit(") {".to_string());
self.gen_expr(&then);
self.emit("} else {".to_string());
self.gen_expr(&else_);
self.emit("}".to_string());
IR::Call { name, args } => {
match name.as_str() {
"puts" => {
self.emit("printf(");
self.gen_ir(&args[0]);
self.emit(")");
},
_ => {
self.emit(format!("{}(", name));
for (i, arg) in args.iter().enumerate() {
if i != 0 {
self.emit(", ");
}
self.gen_ir(arg);
}
self.emit(")");
}
}
},
Expr::Binary { left, op, right } => {
self.gen_expr(&left);
self.emit(format!(" {} ", op.to_string()));
self.gen_expr(&right);
IR::Value { value } => {
self.gen_value(value);
},
Expr::Ident(s) => self.emit(s.to_string()),
Expr::Boolean(b) => self.emit(format!("{}", b)),
_ => { println!("{:?}", expr); todo!() },
IR::Binary { op, left, right } => {
self.gen_ir(left);
self.emit(op);
self.gen_ir(right);
self.emit(";");
},
_ => todo!()
}
}
fn gen_value(&mut self, value: &Value) {
match value {
Value::Int(i) => self.emit(format!("{}", i)),
Value::Float(f) => self.emit(format!("{}", f)),
Value::Double(d) => self.emit(format!("{}", d)),
Value::Bool(b) => self.emit(format!("{}", b)),
Value::String(s) => self.emit(format!("\"{}\"", s)),
Value::Ident(s) => self.emit(format!("{}", s)),
}
}

View file

@ -4,7 +4,7 @@ use chumsky::prelude::*;
pub enum Token {
// Types
Int(i64), Float(String),
Boolean(bool), String(String),
Bool(bool), String(String),
Ident(String),
// Symbols
@ -13,6 +13,7 @@ pub enum Token {
Semicolon,
Assign, Colon,
Comma,
ReturnHint,
// Keywords
Import,
@ -38,6 +39,14 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>> {
.then_ignore(just('"'))
.collect::<String>()
.map(|s: String| Token::String(s));
let symbol = choice((
just(';').to(Token::Semicolon),
just('=').to(Token::Assign),
just(':').to(Token::Colon),
just(',').to(Token::Comma),
just("->").to(Token::ReturnHint),
));
let operator = choice((
just("+"),
@ -62,16 +71,9 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>> {
just('}'),
)).map(|c| Token::Delimiter(c));
let symbol = choice((
just(';').to(Token::Semicolon),
just('=').to(Token::Assign),
just(':').to(Token::Colon),
just(',').to(Token::Comma),
));
let keyword = text::ident().map(|s: String| match s.as_str() {
"true" => Token::Boolean(true),
"false" => Token::Boolean(false),
"true" => Token::Bool(true),
"false" => Token::Bool(false),
"import" => Token::Import,
"let" => Token::Let,
@ -87,9 +89,9 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>> {
let token = int
.or(float)
.or(string)
.or(symbol)
.or(operator)
.or(delimiter)
.or(symbol)
.or(keyword)
.recover_with(skip_then_retry_until([]));
@ -107,7 +109,7 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>> {
#[derive(Clone, Debug)]
pub enum Expr {
Int(i64), Float(f64),
Boolean(bool), String(String),
Bool(bool), String(String),
Ident(String),
Unary { op: String, expr: Box<Self> },
@ -116,11 +118,13 @@ pub enum Expr {
Let {
name: String,
type_hint: String,
value: Box<Self>,
},
Fun {
name: String,
args: Vec<String>,
type_hint: String,
args: Vec<(String, String)>,
body: Box<Self>,
},
@ -143,7 +147,7 @@ fn expr_parser() -> impl Parser<Token, Expr, Error = Simple<Token>> + Clone {
let literal = filter_map(|span, token| match token {
Token::Int(i) => Ok(Expr::Int(i)),
Token::Float(f) => Ok(Expr::Float(f.parse().unwrap())),
Token::Boolean(b) => Ok(Expr::Boolean(b)),
Token::Bool(b) => Ok(Expr::Bool(b)),
Token::String(s) => Ok(Expr::String(s)),
_ => Err(Simple::expected_input_found(span, Vec::new(), Some(token))),
}).labelled("literal");
@ -240,26 +244,39 @@ fn expr_parser() -> impl Parser<Token, Expr, Error = Simple<Token>> + Clone {
let declare_var = just(Token::Let)
.ignore_then(ident)
.then_ignore(just(Token::Colon))
.then(ident)
.then_ignore(just(Token::Assign))
.then(
do_block.clone()
.or(decl.clone())
)
.map(|(name, value)| Expr::Let {
.map(|((name, type_hint), value)| Expr::Let {
name,
type_hint,
value: Box::new(value),
}).labelled("variable");
let declare_fun = just(Token::Fun)
.ignore_then(ident)
.then(ident.repeated())
.then_ignore(just(Token::Delimiter('(')))
.then(
(ident
.then_ignore(just(Token::Colon))
.then(ident))
.repeated()
)
.then_ignore(just(Token::Delimiter(')')))
.then_ignore(just(Token::ReturnHint))
.then(ident)
.then_ignore(just(Token::Assign))
.then(
do_block.clone()
.or(decl.clone())
)
.map(|((name, args), body)| Expr::Fun {
.map(|(((name, args), type_hint), body)| Expr::Fun {
name,
type_hint,
args,
body: Box::new(body),
}).labelled("function");
@ -304,38 +321,4 @@ pub fn parser() -> impl Parser<Token, Vec<Expr>, Error = Simple<Token>> + Clone
.then_ignore(just(Token::Semicolon))
.repeated()
.then_ignore(end())
}
impl Expr {
pub fn to_sexpr(&self) -> String {
let mut out = String::new();
match self {
Self::Int(x) => out.push_str(&x.to_string()),
Self::Float(x) => out.push_str(&x.to_string()),
Self::Boolean(x) => out.push_str(&x.to_string()),
Self::String(x) => out.push_str(&format!("\"{}\"", x)),
Self::Ident(x) => out.push_str(&x),
Self::Unary{ op, expr } => out.push_str(&format!("({} {})", op, expr.to_sexpr())),
Self::Binary{ op, left, right } => out.push_str(
&format!("({} {} {})", op, left.to_sexpr(), right.to_sexpr())
),
Self::Call{ name, args } => out.push_str(
&format!("({} {})", name.to_sexpr(), args.iter().map(|x| x.to_sexpr()).collect::<Vec<_>>().join(" "))),
Self::Let{ name, value } => out.push_str(
&format!("(let {}\n {})", name, value.clone().to_sexpr())),
Self::Fun{ name, args, body } => out.push_str(
&format!("(fun {} ({})\n {})", name, args.join(" "), body.to_sexpr())),
Self::If { cond, then, else_ } => out.push_str(
&format!("(if {} {} {})", cond.to_sexpr(), then.to_sexpr(), else_.to_sexpr())),
Self::Do { body } => out.push_str(
&format!("(do {})", body.iter().map(|x| x.to_sexpr()).collect::<Vec<_>>().join(" "))),
_ => todo!(),
}
out
}
}

View file

@ -12,6 +12,11 @@ use args::{Args, Options};
pub mod front;
use front::parse::{lexer, parser};
/// Middle-end of the language.
/// Contains the intermediate representation.
pub mod middle;
use middle::ir;
/// Back-end of the language.
/// Contains code generator.
pub mod back;
@ -42,12 +47,13 @@ fn main() {
Some(ast) => {
let start = time::Instant::now();
let mut compiler = back::c::Codegen::new();
compiler.gen(&ast);
let ir = ir::ast_to_ir(&ast);
let mut codegen = back::c::Codegen::new();
codegen.gen(&ir);
let out_file_name = file_name.file_stem().unwrap().to_str().unwrap().to_string() + ".c";
let mut out_file = fs::File::create(&out_file_name).expect("Failed to create file");
write!(out_file, "{}", compiler.emitted).expect("Failed to write to file");
write!(out_file, "{}", codegen.emitted).expect("Failed to write to file");
let compile_elapsed = start.elapsed();

107
src/middle/ir.rs Normal file
View file

@ -0,0 +1,107 @@
use core::fmt;
use crate::front::parse::Expr;
#[derive(Debug, Clone)]
pub enum TypeHint {
Int,
Float, Double,
Bool,
String,
}
impl fmt::Display for TypeHint {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TypeHint::Int => write!(f, "int"),
TypeHint::Float => write!(f, "float"),
TypeHint::Double => write!(f, "double"),
TypeHint::Bool => write!(f, "bool"),
TypeHint::String => write!(f, "char*"),
}
}
}
#[derive(Debug, Clone)]
pub enum Value {
Int(i64),
Float(f32),
Double(f64),
Bool(bool),
String(String),
Ident(String),
}
#[derive(Debug, Clone)]
pub enum IR {
Define { name: String, type_hint: TypeHint, value: Box<Self> },
Fun { name: String, return_type_hint: TypeHint, args: Vec<(String, TypeHint)>, body: Box<Self> },
Call { name: String, args: Vec<Self> },
Do { body: Vec<Self> },
If { cond: Box<Self>, body: Box<Self>, else_body: Box<Self> },
Value { value: Value },
Binary { op: String, left: Box<Self>, right: Box<Self> },
}
pub fn ast_to_ir(ast: &[Expr]) -> Vec<IR> {
let mut ir = Vec::new();
for expr in ast {
ir.push(expr_to_ir(expr));
}
ir
}
pub fn expr_to_ir(expr: &Expr) -> IR {
match expr {
Expr::Let { name, type_hint, value } => IR::Define {
name: name.clone(),
type_hint: get_typehint(type_hint),
value: Box::new(expr_to_ir(value)),
},
Expr::Fun { name, type_hint, args, body } => IR::Fun {
name: name.clone(),
return_type_hint: get_typehint(type_hint),
args: args
.iter()
.map(|(name, type_hint)| (name.to_string(), get_typehint(type_hint)))
.collect::<Vec<_>>(),
body: Box::new(expr_to_ir(body)),
},
Expr::Call { name, args } => IR::Call {
name: match &**name {
Expr::Ident(s) => s.clone(),
_ => panic!("Expected ident in call"),
},
args: args.iter().map(|arg| expr_to_ir(arg)).collect(),
},
Expr::Do { body } => IR::Do {
body: body
.iter()
.map(|expr| expr_to_ir(expr))
.collect::<Vec<_>>(),
},
Expr::Binary { op, left, right } => IR::Binary {
op: op.to_string(),
left: Box::new(expr_to_ir(left)),
right: Box::new(expr_to_ir(right)),
},
Expr::Int(value) => IR::Value { value: Value::Int(*value) },
Expr::Float(value) => IR::Value { value: Value::Double(*value) }, // TODO: Actually use float
// Expr::Double(value) => IR::Value { value: Value::Double(*value) },
Expr::Bool(value) => IR::Value { value: Value::Bool(*value) },
Expr::String(value) => IR::Value { value: Value::String(value.clone()) },
Expr::Ident(name) => IR::Value { value: Value::Ident(name.clone()) },
_ => { println!("{:?}", expr); todo!() }
}
}
fn get_typehint(from: &String) -> TypeHint {
match from.as_str() {
"int" => TypeHint::Int,
"float" => TypeHint::Float,
"double" => TypeHint::Double,
"bool" => TypeHint::Bool,
"string" => TypeHint::String,
_ => panic!("Unsupported type hint: {}", from)
}
}

2
src/middle/mod.rs Normal file
View file

@ -0,0 +1,2 @@
// The intemediate representation of the AST
pub mod ir;