Add basic namespace and scope support

This commit is contained in:
Sameer Rahmani 2020-07-06 14:18:29 +01:00
parent 04cadf7152
commit 402e267e7c
11 changed files with 166 additions and 37 deletions

View File

@ -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::<Expr>::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()),
}
}
}

View File

@ -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<String, &'a Namespace<'a, 'ctx>>,
pub namespaces: &'a HashMap<&'a str, &'a Namespace<'a, 'ctx>>,
pub fpm: &'a PassManager<FunctionValue<'ctx>>,
current_ns_name: Option<&'a str>,
}
impl<'a, 'ctx> Compiler<'a, 'ctx> {
pub fn new(
context: Context,
builder: Builder<'ctx>,
fpm: PassManager<FunctionValue<'ctx>>,
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<Result<PointerValue<'ctx>, 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) {}
}

View File

@ -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)?;

View File

@ -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<String, PointerValue<'ctx>>,
scope: Scope<'a>,
// The option of the current function being compiled
current_fn_opt: Option<FunctionValue<'ctx>>,
}
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<FunctionValue<'ctx>> {

View File

@ -2,7 +2,7 @@ use crate::ast::Expr;
use crate::types::Number;
use std::io::{BufReader, Read};
pub type ReadResult = Result<Expr, String>;
pub type ReadResult<'a> = Result<Expr, String>;
pub struct ExprReader {
location: i32,

29
src/scope.rs Normal file
View File

@ -0,0 +1,29 @@
use inkwell::values::PointerValue;
use std::collections::HashMap;
pub struct Scope<'a> {
parent: Option<Box<Scope<'a>>>,
symbol_table: HashMap<String, PointerValue<'a>>,
}
impl<'a> Scope<'a> {
pub fn new(_parent: Option<Scope>) -> 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<PointerValue> {
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);
}
}

View File

@ -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;

View File

@ -1,4 +1,9 @@
pub trait Expression {
use crate::namespace::Namespace;
use inkwell::values::PointerValue;
pub type ExprResult<'a> = Result<PointerValue<'a>, String>;
pub trait Expression<'a> {
fn eval();
fn code_gen();
fn code_gen(&self, ns: &Namespace) -> ExprResult<'a>;
}

View File

@ -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<T: Expression> {
pub struct List<T>
where
for<'a> T: Expression<'a>,
{
first: Box<T>,
rest: Box<T>,
}
impl<T: Expression> List<T> {
pub fn new<S: Expression>(first: Box<S>, rest: Box<S>) -> List<S> {
impl<T> List<T>
where
for<'a> T: Expression<'a>,
{
pub fn new<S>(first: Box<S>, rest: Box<S>) -> List<S>
where
for<'a> S: Expression<'a>,
{
List { first, rest }
}
}
impl<T: Expression> Expression for List<T> {
impl<'a, T> Expression<'a> for List<T>
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())
}
}

View File

@ -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())
}
}

View File

@ -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 {