1
1
Fork 0
mirror of https://github.com/azur1s/bobbylisp.git synced 2024-10-16 02:37:40 -05:00
bobbylisp/crates/main/src/main.rs
2022-03-28 08:31:59 +07:00

145 lines
4.7 KiB
Rust

use std::{fs, io::Write, process::Command, path::PathBuf};
use clap::Parser as ArgParser;
use lexer::lex;
use parser::parse;
use diagnostic::Diagnostics;
use hir::ast_to_ir;
use typecheck::check;
use codegen::ts;
pub mod args;
use args::{Args, Options};
pub mod util;
use crate::util::log;
fn main() {
let args = Args::parse();
match args.options {
Options::Compile {
input: file_name,
ast: print_ast,
log: should_log,
output: _output, // TODO: Custom output file
} => {
// Macro to only log if `should_log` is true
macro_rules! logif {
($level:expr, $msg:expr) => { if should_log { log($level, $msg); } };
}
// Start timer
let start = std::time::Instant::now();
// Get file contents
logif!(0, format!("Reading {}", &file_name.display()));
let src = fs::read_to_string(&file_name).expect("Failed to read file");
// Lex the file
let (tokens, lex_error) = lex(src.clone());
let (ast, parse_error) = parse(tokens.unwrap(), src.chars().count());
let mut diagnostics = Diagnostics::new();
for err in lex_error { diagnostics.add_lex_error(err); }
for err in parse_error { diagnostics.add_parse_error(err); }
// Report syntax errors if any
if diagnostics.has_error() {
diagnostics.display(src);
logif!(0, "Epic parsing fail");
std::process::exit(1);
} else {
logif!(0, format!("Parsing took {}ms", start.elapsed().as_millis()));
}
match ast {
Some(ast) => {
// Convert the AST to HIR
let (ir, lowering_error) = ast_to_ir(ast);
for err in lowering_error { diagnostics.add_lowering_error(err); }
if print_ast { log(0, format!("IR\n{:#?}", ir)); }
// Typecheck the HIR
match check(&ir) {
Ok(_) => {
logif!(0, format!("Typechecking took {}ms", start.elapsed().as_millis()));
},
Err(errs) => {
for err in errs {
diagnostics.add_typecheck_error(err);
}
diagnostics.display(src);
logif!(2, "Typechecking failed");
std::process::exit(1);
}
}
// Report lowering errors if any
if diagnostics.has_error() {
diagnostics.display(src);
logif!(0, "Epic Lowering(HIR) fail");
std::process::exit(1);
} else {
logif!(0, format!("Lowering took {}ms", start.elapsed().as_millis()));
}
// Generate code
let mut codegen = ts::Codegen::new();
codegen.gen(ir);
logif!(0, "Successfully generated code.");
// Write code to file
let output_path: PathBuf = file_name.with_extension("ts").file_name().unwrap().to_os_string().into();
let mut file = fs::File::create(&output_path).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();
logif!(0, format!("Compilation took {}ms", duration));
logif!(0, format!("Wrote output to `{}`", output_path.display()));
},
None => { unreachable!(); }
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_lexer() {
let src = "
let x: int = 1;
";
let (tokens, lex_error) = lex(src.to_string());
assert!(lex_error.is_empty());
assert_eq!(tokens.unwrap().len(), 7);
}
#[test]
fn test_parser() {
let src = "
fun main (foo: int) (bar: bool): string = do
do
let x: int = foo + 1;
end;
let y: bool = bar;
end;
";
let (tokens, lex_error) = lex(src.to_string());
assert!(lex_error.is_empty());
let (ast, parse_error) = parse(tokens.unwrap(), src.chars().count());
assert!(parse_error.is_empty());
assert!(ast.is_some());
}
}