Basic variable implementation
This commit is contained in:
parent
bcc2bf937f
commit
daaf859f81
5 changed files with 99 additions and 27 deletions
4
examples/variables.e0
Normal file
4
examples/variables.e0
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
fn main() -> int {
|
||||||
|
let x: int = 42;
|
||||||
|
return x;
|
||||||
|
}
|
|
@ -1,23 +1,33 @@
|
||||||
|
use std::{collections::HashMap, marker::PhantomData};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use inkwell::{
|
use inkwell::{
|
||||||
builder::Builder,
|
builder::Builder,
|
||||||
context::Context,
|
context::Context,
|
||||||
module::Module,
|
module::Module,
|
||||||
types::{AnyTypeEnum, BasicMetadataTypeEnum, BasicTypeEnum, FunctionType},
|
types::{BasicMetadataTypeEnum, BasicTypeEnum, FunctionType},
|
||||||
values::{
|
values::{AnyValueEnum, BasicValueEnum, PointerValue},
|
||||||
AnyValue, AnyValueEnum, BasicValue, BasicValueEnum, FunctionValue, IntValue,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{Decl, Expr, Stmt, Type};
|
use super::{Decl, Expr, Stmt, Type};
|
||||||
|
|
||||||
impl Expr {
|
impl Expr {
|
||||||
pub fn into_llvm<'ctx>(
|
fn into_llvm<'ctx>(
|
||||||
&self,
|
&self,
|
||||||
context: &'ctx Context,
|
context: &'ctx Context,
|
||||||
builder: &Builder,
|
builder: &Builder,
|
||||||
|
env: &Env<'_, 'ctx>,
|
||||||
) -> Result<BasicValueEnum<'ctx>> {
|
) -> Result<BasicValueEnum<'ctx>> {
|
||||||
match self {
|
match self {
|
||||||
|
Expr::Var(name) => {
|
||||||
|
let (_, value) = match env.lookup(&name) {
|
||||||
|
Some(v) => v,
|
||||||
|
None => bail!("Unbound name {name:?}"),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(BasicValueEnum::PointerValue(*value))
|
||||||
|
}
|
||||||
|
|
||||||
Expr::Int(n) => Ok(BasicValueEnum::IntValue(
|
Expr::Int(n) => Ok(BasicValueEnum::IntValue(
|
||||||
context.i64_type().const_int(*n as u64, false),
|
context.i64_type().const_int(*n as u64, false),
|
||||||
)),
|
)),
|
||||||
|
@ -51,6 +61,65 @@ fn fn_type_basic<'ctx>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type EnvValue<'a, 'ctx> = (&'a Type, PointerValue<'ctx>);
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct Env<'a, 'ctx> {
|
||||||
|
parent: Option<Box<Env<'a, 'ctx>>>,
|
||||||
|
local_type_map: HashMap<String, EnvValue<'a, 'ctx>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'ctx> Env<'a, 'ctx> {
|
||||||
|
pub fn lookup(&self, name: impl AsRef<str>) -> Option<&EnvValue<'a, 'ctx>> {
|
||||||
|
match self.local_type_map.get(name.as_ref()) {
|
||||||
|
Some(v) => Some(v),
|
||||||
|
None => match &self.parent {
|
||||||
|
Some(p) => p.lookup(name),
|
||||||
|
None => None,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn convert_lexical_block(
|
||||||
|
context: &Context,
|
||||||
|
builder: &Builder,
|
||||||
|
parent_env: Env,
|
||||||
|
stmts: impl AsRef<[Stmt]>,
|
||||||
|
) -> Result<()> {
|
||||||
|
let stmts = stmts.as_ref();
|
||||||
|
let mut scope_env = Env {
|
||||||
|
parent: Some(Box::new(parent_env)),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
for stmt in stmts.iter() {
|
||||||
|
match stmt {
|
||||||
|
Stmt::Let(name, ty, expr) => {
|
||||||
|
let llvm_ty = ty.into_llvm_basic_type(context);
|
||||||
|
let alloca = builder.build_alloca(llvm_ty, name);
|
||||||
|
|
||||||
|
let expr_val = expr.into_llvm(context, builder, &scope_env)?;
|
||||||
|
builder.build_store(alloca, expr_val);
|
||||||
|
|
||||||
|
scope_env.local_type_map.insert(name.clone(), (ty, alloca));
|
||||||
|
}
|
||||||
|
|
||||||
|
Stmt::Return(ret_val) => {
|
||||||
|
if let Some(ret_val) = ret_val {
|
||||||
|
let value = ret_val.into_llvm(context, builder, &scope_env)?;
|
||||||
|
builder.build_return(Some(&value));
|
||||||
|
} else {
|
||||||
|
builder.build_return(None);
|
||||||
|
}
|
||||||
|
println!("Emitted return.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn convert(context: &mut Context, program: Vec<Decl>) -> Result<Module> {
|
pub fn convert(context: &mut Context, program: Vec<Decl>) -> Result<Module> {
|
||||||
let module = context.create_module("program");
|
let module = context.create_module("program");
|
||||||
let builder = context.create_builder();
|
let builder = context.create_builder();
|
||||||
|
@ -64,19 +133,8 @@ pub fn convert(context: &mut Context, program: Vec<Decl>) -> Result<Module> {
|
||||||
|
|
||||||
builder.position_at_end(entry_block);
|
builder.position_at_end(entry_block);
|
||||||
|
|
||||||
for stmt in func.stmts.iter() {
|
let env = Env::default();
|
||||||
match stmt {
|
convert_lexical_block(context, &builder, env, &func.stmts)?;
|
||||||
Stmt::Return(ret_val) => {
|
|
||||||
if let Some(ret_val) = ret_val {
|
|
||||||
let value = ret_val.into_llvm(context, &builder)?;
|
|
||||||
builder.build_return(Some(&value));
|
|
||||||
} else {
|
|
||||||
builder.build_return(None);
|
|
||||||
}
|
|
||||||
println!("Emitted return.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(module)
|
Ok(module)
|
||||||
|
|
|
@ -23,12 +23,14 @@ pub struct Func {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Stmt {
|
pub enum Stmt {
|
||||||
|
Let(String, Type, Expr),
|
||||||
Return(Option<Expr>),
|
Return(Option<Expr>),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Expr {
|
pub enum Expr {
|
||||||
Int(i64),
|
Int(i64),
|
||||||
|
Var(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
|
@ -20,8 +20,13 @@ use crate::parser::ProgramParser;
|
||||||
struct Opt {
|
struct Opt {
|
||||||
path: PathBuf,
|
path: PathBuf,
|
||||||
|
|
||||||
#[clap(short = 'o', long = "out")]
|
/// Path to output the bitcode to.
|
||||||
|
#[clap(short = 'o', long = "out", name = "bitcode-out")]
|
||||||
out_path: PathBuf,
|
out_path: PathBuf,
|
||||||
|
|
||||||
|
/// Path to output the AST to, if at all.
|
||||||
|
#[clap(long = "emit-ast", name = "ast-out")]
|
||||||
|
emit_ast: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
|
|
|
@ -5,29 +5,32 @@ grammar;
|
||||||
pub Program: Vec<Decl> = Decl* => <>;
|
pub Program: Vec<Decl> = Decl* => <>;
|
||||||
|
|
||||||
Decl: Decl = {
|
Decl: Decl = {
|
||||||
Func => Decl::Func(<>),
|
Func => Decl::Func(<>),
|
||||||
};
|
};
|
||||||
|
|
||||||
Func: Func = {
|
Func: Func = {
|
||||||
"fn" <name:Ident> "(" ")" "->" <return_ty:Type> "{" <stmts:Stmt*> "}" =>
|
"fn" <name:Ident> "(" ")" "->" <return_ty:Type> "{" <stmts:Stmt*> "}" =>
|
||||||
Func { name, return_ty, stmts },
|
Func { name, return_ty, stmts },
|
||||||
};
|
};
|
||||||
|
|
||||||
Stmt: Stmt = {
|
Stmt: Stmt = {
|
||||||
"return" <expr:Expr?> ";" => Stmt::Return(expr),
|
"let" <name:Ident> ":" <ty:Type> "=" <expr:Expr> ";" =>
|
||||||
|
Stmt::Let(name, ty, expr),
|
||||||
|
"return" <expr:Expr?> ";" => Stmt::Return(expr),
|
||||||
};
|
};
|
||||||
|
|
||||||
Expr: Expr = {
|
Expr: Expr = {
|
||||||
Expr1 => <>,
|
Expr1 => <>,
|
||||||
};
|
};
|
||||||
|
|
||||||
Expr1: Expr = {
|
Expr1: Expr = {
|
||||||
r"[0-9]+" => Expr::Int(<>.parse::<i64>().unwrap()),
|
r"[0-9]+" => Expr::Int(<>.parse::<i64>().unwrap()),
|
||||||
"(" <expr:Expr> ")" => expr,
|
Ident => Expr::Var(<>),
|
||||||
|
"(" <expr:Expr> ")" => expr,
|
||||||
};
|
};
|
||||||
|
|
||||||
Type: Type = {
|
Type: Type = {
|
||||||
"int" => Type::Int,
|
"int" => Type::Int,
|
||||||
};
|
};
|
||||||
|
|
||||||
Ident: String = r"([A-Za-z][A-Za-z0-9_]*)|(_[A-Za-z0-9_]+)" => <>.to_string();
|
Ident: String = r"([A-Za-z][A-Za-z0-9_]*)|(_[A-Za-z0-9_]+)" => <>.to_string();
|
||||||
|
|
Loading…
Reference in a new issue