diff --git a/src/ast.rs b/src/ast.rs index 8b20c69..6f5fb1a 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,4 +1,5 @@ -use crate::types::{Expression, List, Number, Symbol}; +use crate::namespace::Namespace; +use crate::types::{ExprResult, Expression, List, Number, Symbol}; #[derive(Debug, Eq, PartialEq, Clone)] pub enum Expr { @@ -12,7 +13,7 @@ pub enum Expr { NoMatch, } -impl Expr { +impl<'a> Expr { pub fn make_list(first: Expr, rest: Expr) -> Expr { Expr::Cons(List::::new(Box::new(first), Box::new(rest))) } @@ -30,7 +31,12 @@ impl Expr { } } -impl Expression for Expr { +impl<'a> Expression<'a> for Expr { fn eval() {} - fn code_gen() {} + fn code_gen(&self, ns: &Namespace) -> ExprResult<'a> { + match self { + Expr::Sym(s) => s.code_gen(ns), + _ => Err("NotImplemented".to_string()), + } + } } diff --git a/src/compiler.rs b/src/compiler.rs index 2f0c5d9..672e71e 100644 --- a/src/compiler.rs +++ b/src/compiler.rs @@ -7,9 +7,32 @@ use crate::namespace::Namespace; use crate::types::Expression; use std::collections::HashMap; +pub fn create_compiler<'a, 'ctx>() -> Compiler<'a, 'ctx> { + let default_ns_name = "user"; + let context = Context::create(); + let builder = context.create_builder(); + let mut namespaces = HashMap::new(); + let user_ns = Namespace::new(&context, default_ns_name); + namespaces.insert(default_ns_name, &user_ns); + let fpm = PassManager::create(&user_ns.module); + + fpm.add_instruction_combining_pass(); + fpm.add_reassociate_pass(); + fpm.add_gvn_pass(); + fpm.add_cfg_simplification_pass(); + fpm.add_basic_alias_analysis_pass(); + fpm.add_promote_memory_to_register_pass(); + fpm.add_instruction_combining_pass(); + fpm.add_reassociate_pass(); + + fpm.initialize(); + + Compiler::new(context, builder, fpm, namespaces, Some(&default_ns_name)) +} + pub struct Compiler<'a, 'ctx> { - pub context: &'ctx Context, - pub builder: &'a Builder<'ctx>, + pub context: Context, + pub builder: Builder<'ctx>, /// This hashmap contains all the namespaces that has to be compiled and /// maps two different keys to the same namespace. Since namespace names /// can not contain `/` char, the keys of this map are the namespace @@ -19,13 +42,28 @@ pub struct Compiler<'a, 'ctx> { /// two entries in this hashmap. One would be the ns name itself which /// is `abc.xyz` in this case and the otherone would be /// `/path/to/abc/xyz.srn` file that contains the ns. - pub namespaces: HashMap>, + pub namespaces: &'a HashMap<&'a str, &'a Namespace<'a, 'ctx>>, pub fpm: &'a PassManager>, current_ns_name: Option<&'a str>, } impl<'a, 'ctx> Compiler<'a, 'ctx> { + pub fn new( + context: Context, + builder: Builder<'ctx>, + fpm: PassManager>, + namespaces: HashMap<&'a str, &'a Namespace<'a, 'ctx>>, + ns_name: Option<&'a str>, + ) -> Compiler<'a, 'ctx> { + Compiler { + context: context, + namespaces: &namespaces, + fpm: &fpm, + builder: builder, + current_ns_name: ns_name, + } + } #[inline] pub fn current_ns(&self) -> Option<&'a Namespace<'a, 'ctx>> { let ns = self.current_ns_name?; @@ -39,18 +77,35 @@ impl<'a, 'ctx> Compiler<'a, 'ctx> { } /// Creates a new stack allocation instruction in the entry block of the function. - fn create_entry_block_alloca(&self, name: &str) -> PointerValue<'ctx> { - let builder = self.context.create_builder(); + // fn create_entry_block_alloca(&self, name: &str) -> PointerValue<'ctx> { + // let builder = self.context.create_builder(); - let entry = self.current_fn().get_first_basic_block().unwrap(); + // let entry = self.current_fn().get_first_basic_block().unwrap(); - match entry.get_first_instruction() { - Some(first_instr) => builder.position_before(&first_instr), - None => builder.position_at_end(entry), + // match entry.get_first_instruction() { + // Some(first_instr) => builder.position_before(&first_instr), + // None => builder.position_at_end(entry), + // } + + // builder.build_alloca(self.context.f64_type(), name) + // } + + pub fn compile( + &self, + exprs: Vec<&impl Expression<'ctx>>, + ) -> Vec, String>> { + let current_ns = match self.current_ns() { + Some(ns) => ns, + None => panic!("Current namespace is not set."), + }; + + let mut generated_code = vec![]; + + for expr in &exprs { + let code = expr.code_gen(current_ns); + generated_code.push(code); } - builder.build_alloca(self.context.f64_type(), name) + generated_code } - - pub fn compile(exprs: &impl Expression) {} } diff --git a/src/main.rs b/src/main.rs index 72a4b3d..f79f20d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,11 +10,15 @@ pub mod ast; pub mod compiler; pub mod namespace; pub mod reader; +pub mod scope; pub mod types; +use crate::compiler::create_compiler; + fn main() -> io::Result<()> { let yaml = load_yaml!("cli.yml"); let args = App::from(yaml).get_matches(); + let compiler = create_compiler(); if let Some(input) = args.value_of("INPUT") { let mut f = File::open(input)?; diff --git a/src/namespace.rs b/src/namespace.rs index 07c7b92..55564ec 100644 --- a/src/namespace.rs +++ b/src/namespace.rs @@ -1,20 +1,29 @@ +use crate::scope::Scope; +use inkwell::context::Context; use inkwell::module::Module; -use inkwell::values::{FunctionValue, PointerValue}; -use std::collections::HashMap; +use inkwell::values::FunctionValue; pub struct Namespace<'a, 'ctx> { /// Each namespace in serene contains it's own LLVM module. You can /// think of modules as compilation units. Object files if you prefer. /// This way we should be able to hot swap the namespaces. - pub module: &'a Module<'ctx>, + pub module: Module<'ctx>, - scope: HashMap>, + scope: Scope<'a>, // The option of the current function being compiled current_fn_opt: Option>, } impl<'a, 'ctx> Namespace<'a, 'ctx> { + pub fn new(context: &'ctx Context, name: &str) -> Namespace<'a, 'ctx> { + let module = context.create_module(&name); + Namespace { + module: module, + scope: Scope::new(None), + current_fn_opt: None, + } + } /// Gets a defined function given its name. #[inline] pub fn get_function(&self, name: &str) -> Option> { diff --git a/src/reader.rs b/src/reader.rs index d1e6445..d8c608d 100644 --- a/src/reader.rs +++ b/src/reader.rs @@ -2,7 +2,7 @@ use crate::ast::Expr; use crate::types::Number; use std::io::{BufReader, Read}; -pub type ReadResult = Result; +pub type ReadResult<'a> = Result; pub struct ExprReader { location: i32, diff --git a/src/scope.rs b/src/scope.rs new file mode 100644 index 0000000..cc8a169 --- /dev/null +++ b/src/scope.rs @@ -0,0 +1,29 @@ +use inkwell::values::PointerValue; +use std::collections::HashMap; + +pub struct Scope<'a> { + parent: Option>>, + symbol_table: HashMap>, +} + +impl<'a> Scope<'a> { + pub fn new(_parent: Option) -> Scope { + let p = match _parent { + Some(x) => Some(Box::new(x)), + None => None, + }; + + Scope { + parent: p, + symbol_table: HashMap::new(), + } + } + + pub fn lookup(&self, key: &str) -> Option { + self.symbol_table.get(key).map(|x| *x) + } + + pub fn insert(&mut self, key: &str, val: PointerValue<'a>) { + self.symbol_table.insert(key.to_string(), val); + } +} diff --git a/src/types.rs b/src/types.rs index 173f3da..cda7cf3 100644 --- a/src/types.rs +++ b/src/types.rs @@ -3,7 +3,7 @@ pub mod list; pub mod number; pub mod symbol; -pub use self::core::Expression; +pub use self::core::{ExprResult, Expression}; pub use self::list::List; pub use self::number::Number; pub use self::symbol::Symbol; diff --git a/src/types/core.rs b/src/types/core.rs index 4b94c62..b36e2cb 100644 --- a/src/types/core.rs +++ b/src/types/core.rs @@ -1,4 +1,9 @@ -pub trait Expression { +use crate::namespace::Namespace; +use inkwell::values::PointerValue; + +pub type ExprResult<'a> = Result, String>; + +pub trait Expression<'a> { fn eval(); - fn code_gen(); + fn code_gen(&self, ns: &Namespace) -> ExprResult<'a>; } diff --git a/src/types/list.rs b/src/types/list.rs index c485e6e..d023f14 100644 --- a/src/types/list.rs +++ b/src/types/list.rs @@ -1,18 +1,33 @@ -use crate::types::core::Expression; +use crate::namespace::Namespace; +use crate::types::core::{ExprResult, Expression}; #[derive(Debug, Eq, PartialEq, Clone)] -pub struct List { +pub struct List +where + for<'a> T: Expression<'a>, +{ first: Box, rest: Box, } -impl List { - pub fn new(first: Box, rest: Box) -> List { +impl List +where + for<'a> T: Expression<'a>, +{ + pub fn new(first: Box, rest: Box) -> List + where + for<'a> S: Expression<'a>, + { List { first, rest } } } -impl Expression for List { +impl<'a, T> Expression<'a> for List +where + for<'b> T: Expression<'b>, +{ fn eval() {} - fn code_gen() {} + fn code_gen(&self, ns: &Namespace) -> ExprResult<'a> { + Err("Not implemented on list".to_string()) + } } diff --git a/src/types/number.rs b/src/types/number.rs index bd02496..3020abf 100644 --- a/src/types/number.rs +++ b/src/types/number.rs @@ -1,5 +1,5 @@ -use crate::types::Expression; - +use crate::namespace::Namespace; +use crate::types::core::{ExprResult, Expression}; // Note: I kept the number implementation simple for now // but we need to decide on our approach to numbers, are // we going to only support the 64bit variants? or should @@ -28,7 +28,9 @@ impl PartialEq for Number { impl Eq for Number {} -impl Expression for Number { +impl<'a> Expression<'a> for Number { fn eval() {} - fn code_gen() {} + fn code_gen(&self, ns: &Namespace) -> ExprResult<'a> { + Err("Not implemented on numbers".to_string()) + } } diff --git a/src/types/symbol.rs b/src/types/symbol.rs index 2b170a1..babb0bc 100644 --- a/src/types/symbol.rs +++ b/src/types/symbol.rs @@ -1,4 +1,6 @@ -use crate::types::Expression; +use crate::namespace::Namespace; +use crate::types::core::{ExprResult, Expression}; +use inkwell::values::PointerValue; #[derive(Debug, Clone)] pub struct Symbol { @@ -13,9 +15,11 @@ impl PartialEq for Symbol { impl Eq for Symbol {} -impl Expression for Symbol { +impl<'a> Expression<'a> for Symbol { fn eval() {} - fn code_gen() {} + fn code_gen(&self, ns: &Namespace) -> ExprResult<'a> { + Err("Not implemented on symbol".to_string()) + } } impl Symbol {