mirror of
https://github.com/azur1s/bobbylisp.git
synced 2024-10-16 02:37:40 -05:00
Compare commits
3 commits
1e20eb89c3
...
dccbe7c17c
Author | SHA1 | Date | |
---|---|---|---|
Natapat Samutpong | dccbe7c17c | ||
Natapat Samutpong | 722be2628d | ||
Natapat Samutpong | dd8d73c51a |
8
Cargo.lock
generated
8
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -4,4 +4,5 @@ members = [
|
|||
"crates/lexer",
|
||||
"crates/parser",
|
||||
"crates/hir",
|
||||
"crates/codegen",
|
||||
]
|
|
@ -1,10 +1,10 @@
|
|||
# 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)
|
||||
|
||||
# Prerequistie
|
||||
- `node` or any JavaScript runner
|
||||
- `clang++`(preferred) or any C++ compiler
|
||||
- Rust (if you're going to build from source)
|
||||
|
||||
# TODO
|
||||
|
|
8
crates/codegen/Cargo.toml
Normal file
8
crates/codegen/Cargo.toml
Normal 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
52
crates/codegen/src/cpp.rs
Normal 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!() },
|
||||
}
|
||||
}
|
||||
}
|
1
crates/codegen/src/lib.rs
Normal file
1
crates/codegen/src/lib.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub mod cpp;
|
|
@ -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,30 @@ 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::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!() }
|
||||
}
|
||||
}
|
||||
|
||||
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!() }
|
||||
}
|
||||
}
|
|
@ -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<usize>;
|
|||
pub fn lexer() -> impl Parser<char, Vec<(Token, Span)>, Error = Simple<char>> {
|
||||
let int = text::int(10)
|
||||
.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('"')
|
||||
.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
|
||||
.or(float)
|
||||
.or(string)
|
||||
.or(symbol)
|
||||
.or(keyword)
|
||||
|
|
|
@ -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"
|
|
@ -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.");
|
||||
|
|
|
@ -43,7 +43,6 @@ fn expr_parser() -> impl Parser<Token, Vec<Spanned<Expr>>, Error = Simple<Token>
|
|||
|
||||
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))),
|
||||
|
|
|
@ -1 +1,4 @@
|
|||
let foo: int = 1;
|
||||
let foo: int = 1;
|
||||
let baz: bool = true;
|
||||
let qux: string = "Hello, World";
|
||||
write(qux);
|
Loading…
Reference in a new issue