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

Compare commits

...

3 commits

12 changed files with 131 additions and 24 deletions

8
Cargo.lock generated
View file

@ -88,6 +88,13 @@ dependencies = [
"syn", "syn",
] ]
[[package]]
name = "codegen"
version = "0.1.0"
dependencies = [
"hir",
]
[[package]] [[package]]
name = "const-random" name = "const-random"
version = "0.1.13" version = "0.1.13"
@ -140,6 +147,7 @@ dependencies = [
"ariadne", "ariadne",
"chumsky", "chumsky",
"clap", "clap",
"codegen",
"hir", "hir",
"lexer", "lexer",
"parser", "parser",

View file

@ -4,4 +4,5 @@ members = [
"crates/lexer", "crates/lexer",
"crates/parser", "crates/parser",
"crates/hir", "crates/hir",
"crates/codegen",
] ]

View file

@ -1,10 +1,10 @@
# Hades # Hades
Programming language that compiles to JavaScript! Programming language that compiles to C++!
Note: Everything in this project can be changed at anytime! (I'm still finding out what work best for lots of thing) if you have an idea, feel free to create an issues about it, or even create a PR! (I'd be very happy) Note: Everything in this project can be changed at anytime! (I'm still finding out what work best for lots of thing) if you have an idea, feel free to create an issues about it, or even create a PR! (I'd be very happy)
# Prerequistie # Prerequistie
- `node` or any JavaScript runner - `clang++`(preferred) or any C++ compiler
- Rust (if you're going to build from source) - Rust (if you're going to build from source)
# TODO # TODO

View file

@ -0,0 +1,8 @@
[package]
name = "codegen"
license = "MIT OR Apache-2.0"
version = "0.1.0"
edition = "2021"
[dependencies]
hir = { path = "../hir" }

52
crates/codegen/src/cpp.rs Normal file
View file

@ -0,0 +1,52 @@
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<T: Display>(&mut self, t: T) {
self.emitted.push_str(&t.to_string());
}
pub fn gen(&mut self, irs: Vec<IR>) {
self.emit("#include <stdbool.h>\n");
self.emit("#include <iostream>\n");
self.emit("#include <string>\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::Call { name, args } => {
match name.as_str() {
"write" => { format!("std::cout << {};\n", self.gen_ir(&args[0])) },
"read" => { format!("std::cin >> {};\n", self.gen_ir(&args[0])) },
_ => format!("{}({});\n", name, args.iter().map(|arg| self.gen_ir(arg)).collect::<Vec<_>>().join(", ")),
}
},
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!() },
}
}
}

View file

@ -0,0 +1 @@
pub mod cpp;

View file

@ -2,7 +2,7 @@ use std::ops::Range;
use parser::Expr; use parser::Expr;
#[derive(Debug)] #[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)] #[derive(Debug)]
pub enum IRKind { pub enum IRKind {
@ -41,9 +41,30 @@ pub fn expr_to_ir(expr: &Expr) -> IRKind {
match expr { match expr {
Expr::Let { name, type_hint, value } => { Expr::Let { name, type_hint, value } => {
let value = expr_to_ir(&value.0); 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::Call { name, args } => {
let name = match &name.0 {
Expr::Identifier(s) => s.clone(),
_ => panic!("Expected identifier") // TODO: Remove panic and use error handling
};
let args = args.0.iter().map(|arg| expr_to_ir(&arg.0)).collect::<Vec<_>>();
IRKind::Call { name, args }
},
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()) },
Expr::Identifier(value) => IRKind::Value { value: Value::Ident(value.clone()) },
_ => { dbg!(expr); todo!() } _ => { 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!() }
}
} }

View file

@ -9,7 +9,7 @@ pub enum Token {
KwReturn, KwReturn,
// Literals // Literals
Int(i64), Float(String), Boolean(bool), Int(i64), Boolean(bool),
String(String), Identifier(String), String(String), Identifier(String),
// Operators // Operators
@ -36,7 +36,6 @@ impl std::fmt::Display for Token {
Token::KwReturn => write!(f, "return"), Token::KwReturn => write!(f, "return"),
Token::Int(i) => write!(f, "{}", i), Token::Int(i) => write!(f, "{}", i),
Token::Float(s) => write!(f, "{}", s),
Token::Boolean(b) => write!(f, "{}", b), Token::Boolean(b) => write!(f, "{}", b),
Token::String(s) => write!(f, "{}", s), Token::String(s) => write!(f, "{}", s),
Token::Identifier(s) => write!(f, "{}", s), Token::Identifier(s) => write!(f, "{}", s),
@ -66,11 +65,6 @@ pub type Span = std::ops::Range<usize>;
pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>> { pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>> {
let int = text::int(10) let int = text::int(10)
.map(|s: String| Token::Int(s.parse().unwrap())); .map(|s: String| Token::Int(s.parse().unwrap()));
let float = text::int(10)
.chain(just('.'))
.chain::<char, _, _>(text::digits(10))
.collect::<String>()
.map(Token::Float);
let string = just('"') let string = just('"')
.ignore_then(filter(|c| *c != '"').repeated()) .ignore_then(filter(|c| *c != '"').repeated())
@ -112,7 +106,6 @@ pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>> {
}); });
let token = int let token = int
.or(float)
.or(string) .or(string)
.or(symbol) .or(symbol)
.or(keyword) .or(keyword)

View file

@ -9,5 +9,6 @@ clap = { version = "3.0.14", features = ["derive"] }
lexer = { path = "../lexer" } lexer = { path = "../lexer" }
parser = { path = "../parser" } parser = { path = "../parser" }
hir = { path = "../hir" } hir = { path = "../hir" }
codegen = { path = "../codegen" }
chumsky = "0.8.0" chumsky = "0.8.0"
ariadne = "0.1.5" ariadne = "0.1.5"

View file

@ -1,10 +1,12 @@
use std::fs; use std::{fs, io::Write};
use clap::Parser as ArgParser; use clap::Parser as ArgParser;
use ariadne::{Report, ReportKind, Label, Source, Color, Fmt}; use ariadne::{Report, ReportKind, Label, Source, Color, Fmt};
use lexer::lex; use lexer::lex;
use parser::parse; use parser::parse;
use hir::ast_to_ir; use hir::ast_to_ir;
use codegen::cpp;
pub mod args; pub mod args;
use args::{Args, Options}; use args::{Args, Options};
@ -19,14 +21,17 @@ fn main() {
input: file_name, input: file_name,
ast: _print_ast, 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"); 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 (tokens, lex_error) = lex(src.clone());
let (ast, parse_error) = parse(tokens.unwrap(), src.chars().count()); let (ast, parse_error) = parse(tokens.unwrap(), src.chars().count());
// Report errors. // Report errors
lex_error.into_iter() lex_error.into_iter()
.map(|e| e.map(|e| e.to_string())) .map(|e| e.map(|e| e.to_string()))
.chain(parse_error.into_iter().map(|e| e.map(|tok| tok.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) .with_color(Color::Red)
), ),
chumsky::error::SimpleReason::Unexpected => report chumsky::error::SimpleReason::Unexpected => report
.with_message(format!( .with_message(format!(
"{}, expected {}", "{}, expected {}",
@ -94,15 +99,30 @@ fn main() {
}; };
report.finish().print(Source::from(&src)).unwrap(); report.finish().print(Source::from(&src)).unwrap();
}); }
); // End errors reporting
log(0, format!("Parsing took {}ms", start.elapsed().as_millis()));
match ast { match ast {
Some(ast) => { Some(ast) => {
// Convert the AST to HIR. // Convert the AST to HIR
let ir = ast_to_ir(ast); 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. // Write code to file
println!("{:#?}", ir); 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 => { None => {
log(2, "Failed to parse."); log(2, "Failed to parse.");

View file

@ -43,7 +43,6 @@ fn expr_parser() -> impl Parser<Token, Vec<Spanned<Expr>>, Error = Simple<Token>
let literal = filter_map(|span, token| match token { let literal = filter_map(|span, token| match token {
Token::Int(i) => Ok((Expr::Int(i), span)), 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::Boolean(b) => Ok((Expr::Boolean(b), span)),
Token::String(s) => Ok((Expr::String(s), span)), Token::String(s) => Ok((Expr::String(s), span)),
_ => Err(Simple::expected_input_found(span, Vec::new(), Some(token))), _ => Err(Simple::expected_input_found(span, Vec::new(), Some(token))),

View file

@ -1 +1,4 @@
let foo: int = 1; let foo: int = 1;
let baz: bool = true;
let qux: string = "Hello, World";
write(qux);