diff --git a/Cargo.lock b/Cargo.lock index d216c43..674573b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -88,6 +88,13 @@ dependencies = [ "syn", ] +[[package]] +name = "codegen" +version = "0.1.0" +dependencies = [ + "hir", +] + [[package]] name = "const-random" version = "0.1.13" @@ -140,6 +147,7 @@ dependencies = [ "ariadne", "chumsky", "clap", + "codegen", "hir", "lexer", "parser", diff --git a/Cargo.toml b/Cargo.toml index 338c470..8c5f47d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,4 +4,5 @@ members = [ "crates/lexer", "crates/parser", "crates/hir", + "crates/codegen", ] \ No newline at end of file diff --git a/crates/codegen/Cargo.toml b/crates/codegen/Cargo.toml new file mode 100644 index 0000000..8a330d6 --- /dev/null +++ b/crates/codegen/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "codegen" +license = "MIT OR Apache-2.0" +version = "0.1.0" +edition = "2021" + +[dependencies] +hir = { path = "../hir" } \ No newline at end of file diff --git a/crates/codegen/src/cpp.rs b/crates/codegen/src/cpp.rs new file mode 100644 index 0000000..287a3df --- /dev/null +++ b/crates/codegen/src/cpp.rs @@ -0,0 +1,44 @@ +use std::fmt::Display; + +use hir::{IR, IRKind, Value}; + +pub struct Codegen { + pub emitted: String, +} + +impl Codegen { + pub fn new() -> Self { + Self { emitted: String::new() } + } + + fn emit(&mut self, t: T) { + self.emitted.push_str(&t.to_string()); + } + + pub fn gen(&mut self, irs: Vec) { + self.emit("#include \n"); + self.emit("#include \n"); + self.emit("int main() {\n"); + for ir in irs { + self.emit(&self.gen_ir(&ir.kind)); + } + self.emit("}"); + } + + fn gen_ir(&self, ir: &IRKind) -> String { + match ir { + IRKind::Define { name, type_hint, value } => { + format!("{} {} = {};\n", type_hint, name, self.gen_ir(value)) + }, + IRKind::Value { value } => { + match value { + Value::Int(value) => format!("{}", value), + Value::Boolean(value) => format!("{}", value), + Value::String(value) => format!("\"{}\"", value), + Value::Ident(value) => format!("{}", value), + } + }, + _ => { dbg!(ir); todo!() }, + } + } +} \ No newline at end of file diff --git a/crates/codegen/src/lib.rs b/crates/codegen/src/lib.rs new file mode 100644 index 0000000..83fc028 --- /dev/null +++ b/crates/codegen/src/lib.rs @@ -0,0 +1 @@ +pub mod cpp; \ No newline at end of file diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index 7b593c2..ec5280f 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -2,7 +2,7 @@ use std::ops::Range; use parser::Expr; #[derive(Debug)] -pub enum Value { Int(i64), Float(f64), Bool(bool), String(String), Ident(String) } +pub enum Value { Int(i64), Boolean(bool), String(String), Ident(String) } #[derive(Debug)] pub enum IRKind { @@ -41,9 +41,21 @@ pub fn expr_to_ir(expr: &Expr) -> IRKind { match expr { Expr::Let { name, type_hint, value } => { let value = expr_to_ir(&value.0); - IRKind::Define { name: name.clone(), type_hint: type_hint.clone(), value: Box::new(value) } + IRKind::Define { name: name.clone(), type_hint: gen_type_hint(type_hint), value: Box::new(value) } }, - Expr::Int(value) => IRKind::Value { value: Value::Int(*value) }, + + Expr::Int(value) => IRKind::Value { value: Value::Int(*value) }, + Expr::Boolean(value) => IRKind::Value { value: Value::Boolean(*value) }, + Expr::String(value) => IRKind::Value { value: Value::String(value.clone()) }, _ => { dbg!(expr); todo!() } } +} + +fn gen_type_hint(type_hint: &str) -> String { + match type_hint { + "int" => "int".to_string(), + "bool" => "bool".to_string(), + "string" => "std::string".to_string(), + _ => { dbg!(type_hint); todo!() } + } } \ No newline at end of file diff --git a/crates/lexer/src/lib.rs b/crates/lexer/src/lib.rs index 3da34c9..0b444c6 100644 --- a/crates/lexer/src/lib.rs +++ b/crates/lexer/src/lib.rs @@ -9,7 +9,7 @@ pub enum Token { KwReturn, // Literals - Int(i64), Float(String), Boolean(bool), + Int(i64), Boolean(bool), String(String), Identifier(String), // Operators @@ -36,7 +36,6 @@ impl std::fmt::Display for Token { Token::KwReturn => write!(f, "return"), Token::Int(i) => write!(f, "{}", i), - Token::Float(s) => write!(f, "{}", s), Token::Boolean(b) => write!(f, "{}", b), Token::String(s) => write!(f, "{}", s), Token::Identifier(s) => write!(f, "{}", s), @@ -66,11 +65,6 @@ pub type Span = std::ops::Range; pub fn lexer() -> impl Parser, Error = Simple> { let int = text::int(10) .map(|s: String| Token::Int(s.parse().unwrap())); - let float = text::int(10) - .chain(just('.')) - .chain::(text::digits(10)) - .collect::() - .map(Token::Float); let string = just('"') .ignore_then(filter(|c| *c != '"').repeated()) @@ -112,7 +106,6 @@ pub fn lexer() -> impl Parser, Error = Simple> { }); let token = int - .or(float) .or(string) .or(symbol) .or(keyword) diff --git a/crates/main/Cargo.toml b/crates/main/Cargo.toml index cd63448..08c693f 100644 --- a/crates/main/Cargo.toml +++ b/crates/main/Cargo.toml @@ -9,5 +9,6 @@ clap = { version = "3.0.14", features = ["derive"] } lexer = { path = "../lexer" } parser = { path = "../parser" } hir = { path = "../hir" } +codegen = { path = "../codegen" } chumsky = "0.8.0" ariadne = "0.1.5" \ No newline at end of file diff --git a/crates/main/src/main.rs b/crates/main/src/main.rs index a606a54..73b5077 100644 --- a/crates/main/src/main.rs +++ b/crates/main/src/main.rs @@ -1,10 +1,12 @@ -use std::fs; +use std::{fs, io::Write}; use clap::Parser as ArgParser; use ariadne::{Report, ReportKind, Label, Source, Color, Fmt}; + use lexer::lex; use parser::parse; use hir::ast_to_ir; +use codegen::cpp; pub mod args; use args::{Args, Options}; @@ -19,14 +21,17 @@ fn main() { input: file_name, ast: _print_ast, } => { - // Get file contents. + // Start timer + let start = std::time::Instant::now(); + + // Get file contents let src = fs::read_to_string(&file_name).expect("Failed to read file"); - // Lex the file. + // Lex the file let (tokens, lex_error) = lex(src.clone()); let (ast, parse_error) = parse(tokens.unwrap(), src.chars().count()); - // Report errors. + // Report errors lex_error.into_iter() .map(|e| e.map(|e| e.to_string())) .chain(parse_error.into_iter().map(|e| e.map(|tok| tok.to_string()))) @@ -57,7 +62,7 @@ fn main() { )) .with_color(Color::Red) ), - + chumsky::error::SimpleReason::Unexpected => report .with_message(format!( "{}, expected {}", @@ -94,15 +99,30 @@ fn main() { }; report.finish().print(Source::from(&src)).unwrap(); - }); + } + ); // End errors reporting + log(0, format!("Parsing took {}ms", start.elapsed().as_millis())); match ast { Some(ast) => { - // Convert the AST to HIR. + // Convert the AST to HIR let ir = ast_to_ir(ast); + log(0, "Generated HIR."); + + // Generate code + let mut codegen = cpp::Codegen::new(); + codegen.gen(ir); + log(0, "Successfully generated code."); - // Print the HIR. - println!("{:#?}", ir); + // Write code to file + let mut file = fs::File::create("out.cpp").expect("Failed to create file"); + file.write_all(codegen.emitted.as_bytes()).expect("Failed to write to file"); + + // End timer + let duration = start.elapsed().as_millis(); + + log(0, format!("Compilation took {}ms", duration)); + log(0, format!("Wrote output to `out.cpp`. All done.")); }, None => { log(2, "Failed to parse."); diff --git a/crates/parser/src/lib.rs b/crates/parser/src/lib.rs index ce31520..e31c693 100644 --- a/crates/parser/src/lib.rs +++ b/crates/parser/src/lib.rs @@ -43,7 +43,6 @@ fn expr_parser() -> impl Parser>, Error = Simple let literal = filter_map(|span, token| match token { Token::Int(i) => Ok((Expr::Int(i), span)), - Token::Float(f) => Ok((Expr::Float(f.parse().unwrap()), span)), Token::Boolean(b) => Ok((Expr::Boolean(b), span)), Token::String(s) => Ok((Expr::String(s), span)), _ => Err(Simple::expected_input_found(span, Vec::new(), Some(token))), diff --git a/example/ex.hades b/example/ex.hades index 6f77848..076ebe6 100644 --- a/example/ex.hades +++ b/example/ex.hades @@ -1 +1,3 @@ -let foo: int = 1; \ No newline at end of file +let foo: int = 1; +let baz: bool = true; +let qux: string = "Hello, World"; \ No newline at end of file