This commit is contained in:
Edgar 2024-01-15 11:42:24 +01:00
parent 5b4b38a276
commit bf13468214
No known key found for this signature in database
GPG key ID: 70ADAE8F35904387
17 changed files with 825 additions and 136 deletions

1
.gitignore vendored
View file

@ -1 +1,2 @@
/target /target
/target_ed

14
Cargo.lock generated
View file

@ -539,6 +539,7 @@ dependencies = [
name = "edlang_codegen_mlir" name = "edlang_codegen_mlir"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bumpalo",
"cc", "cc",
"edlang_ast", "edlang_ast",
"edlang_parser", "edlang_parser",
@ -553,6 +554,7 @@ dependencies = [
name = "edlang_driver" name = "edlang_driver"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"ariadne",
"clap", "clap",
"color-eyre", "color-eyre",
"edlang_ast", "edlang_ast",
@ -570,6 +572,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"ariadne", "ariadne",
"edlang_ast", "edlang_ast",
"itertools 0.12.0",
"lalrpop", "lalrpop",
"lalrpop-util", "lalrpop-util",
"logos", "logos",
@ -754,6 +757,15 @@ dependencies = [
"either", "either",
] ]
[[package]]
name = "itertools"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25db6b064527c5d482d0423354fcd07a89a2dfe07b67892e62411946db7f07b0"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "1.0.10" version = "1.0.10"
@ -771,7 +783,7 @@ dependencies = [
"diff", "diff",
"ena", "ena",
"is-terminal", "is-terminal",
"itertools", "itertools 0.10.5",
"lalrpop-util", "lalrpop-util",
"petgraph", "petgraph",
"pico-args", "pico-args",

View file

@ -12,6 +12,7 @@ impl Span {
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Module { pub struct Module {
pub name: Ident,
pub imports: Vec<Import>, pub imports: Vec<Import>,
pub contents: Vec<ModuleStatement>, pub contents: Vec<ModuleStatement>,
pub span: Span, pub span: Span,
@ -22,6 +23,7 @@ pub enum ModuleStatement {
Function(Function), Function(Function),
Constant(Constant), Constant(Constant),
Struct(Struct), Struct(Struct),
Module(Module),
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -56,6 +58,7 @@ pub struct Ident {
pub struct Type { pub struct Type {
pub name: Ident, pub name: Ident,
pub generics: Vec<Type>, pub generics: Vec<Type>,
pub span: Span,
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
@ -73,15 +76,61 @@ pub struct Block {
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Statement { pub enum Statement {
Let, Let(LetStmt),
Assign, Assign(AssignStmt),
For, For(ForStmt),
While, While(WhileStmt),
If, If(IfStmt),
Return, Return(ReturnStmt),
FnCall(FnCallExpr), FnCall(FnCallExpr),
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LetStmt {
pub name: Ident,
pub is_mut: bool,
pub r#type: Type,
pub value: Expression,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct AssignStmt {
pub name: PathExpr,
pub value: Expression,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct IfStmt {
pub condition: Expression,
pub then_block: Block,
pub else_block: Option<Block>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ForStmt {
pub name: Ident,
pub from: Expression,
pub to: Option<Expression>,
pub block: Block,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct WhileStmt {
pub condition: Expression,
pub block: Block,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ReturnStmt {
pub value: Option<Expression>,
pub span: Span,
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Function { pub struct Function {
pub name: Ident, pub name: Ident,
@ -102,7 +151,7 @@ pub struct Constant {
} }
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Field { pub struct StructField {
pub name: Ident, pub name: Ident,
pub r#type: Type, pub r#type: Type,
pub span: Span, pub span: Span,
@ -112,7 +161,7 @@ pub struct Field {
pub struct Struct { pub struct Struct {
pub name: Ident, pub name: Ident,
pub generics: Vec<Type>, pub generics: Vec<Type>,
pub fields: Vec<Field>, pub fields: Vec<StructField>,
pub span: Span, pub span: Span,
} }
@ -137,7 +186,6 @@ pub enum ValueExpr {
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FnCallExpr { pub struct FnCallExpr {
pub name: Ident, pub name: Ident,
pub generic_params: Vec<Type>,
pub params: Vec<Expression>, pub params: Vec<Expression>,
pub span: Span, pub span: Span,
} }

View file

@ -11,6 +11,7 @@ categories = ["compilers"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
bumpalo = { version = "3.14.0", features = ["std"] }
edlang_ast = { version = "0.1.0", path = "../edlang_ast" } edlang_ast = { version = "0.1.0", path = "../edlang_ast" }
edlang_parser = { version = "0.1.0", path = "../edlang_parser" } edlang_parser = { version = "0.1.0", path = "../edlang_parser" }
edlang_session = { version = "0.1.0", path = "../edlang_session" } edlang_session = { version = "0.1.0", path = "../edlang_session" }

View file

@ -1 +1,559 @@
use std::{cell::Cell, collections::HashMap, error::Error};
use bumpalo::Bump;
use edlang_ast::{
ArithOp, AssignStmt, BinaryOp, Constant, Expression, Function, LetStmt, Module,
ModuleStatement, ReturnStmt, Span, Statement, Struct, ValueExpr,
};
use edlang_session::Session;
use melior::{
dialect::{arith, cf, func, memref},
ir::{
attribute::{FlatSymbolRefAttribute, IntegerAttribute, StringAttribute, TypeAttribute},
r#type::{FunctionType, IntegerType, MemRefType},
Attribute, Block, BlockRef, Location, Module as MeliorModule, Region, Type, Value,
ValueLike,
},
Context as MeliorContext,
};
use crate::context::Context;
#[derive(Debug, Clone)]
pub struct LocalVar<'ctx, 'parent: 'ctx> {
pub ast_type: edlang_ast::Type,
// If it's none its on a register, otherwise allocated on the stack.
pub is_alloca: bool,
pub value: Value<'ctx, 'parent>,
}
impl<'ctx, 'parent> LocalVar<'ctx, 'parent> {
pub fn param(value: Value<'ctx, 'parent>, ast_type: edlang_ast::Type) -> Self {
Self {
value,
ast_type,
is_alloca: false,
}
}
pub fn alloca(value: Value<'ctx, 'parent>, ast_type: edlang_ast::Type) -> Self {
Self {
value,
ast_type,
is_alloca: true,
}
}
}
#[derive(Debug, Clone, Default)]
struct ScopeContext<'ctx, 'parent: 'ctx> {
pub locals: HashMap<String, LocalVar<'ctx, 'parent>>,
pub functions: HashMap<String, &'parent Function>,
pub structs: HashMap<String, &'parent Struct>,
pub constants: HashMap<String, &'parent Constant>,
pub ret_type: Option<&'parent edlang_ast::Type>,
}
struct BlockHelper<'ctx, 'this: 'ctx> {
region: &'this Region<'ctx>,
blocks_arena: &'this Bump,
}
impl<'ctx, 'this: 'ctx> BlockHelper<'ctx, 'this> {
pub fn append_block(&self, block: Block<'ctx>) -> &'this BlockRef<'ctx, 'this> {
let block = self.region.append_block(block);
let block_ref: &'this mut BlockRef<'ctx, 'this> = self.blocks_arena.alloc(block);
block_ref
}
}
impl<'ctx, 'parent: 'ctx> ScopeContext<'ctx, 'parent> {
fn resolve_type_name(
&self,
context: &'ctx MeliorContext,
name: &str,
) -> Result<Type<'ctx>, Box<dyn Error>> {
Ok(match name {
"u128" | "i128" => IntegerType::new(context, 128).into(),
"u64" | "i64" => IntegerType::new(context, 64).into(),
"u32" | "i32" => IntegerType::new(context, 32).into(),
"u16" | "i16" => IntegerType::new(context, 16).into(),
"u8" | "i8" => IntegerType::new(context, 8).into(),
"f32" => Type::float32(context),
"f64" => Type::float64(context),
"bool" => IntegerType::new(context, 1).into(),
_ => todo!("custom type lookup"),
})
}
fn resolve_type(
&self,
context: &'ctx MeliorContext,
r#type: &edlang_ast::Type,
) -> Result<Type<'ctx>, Box<dyn Error>> {
self.resolve_type_name(context, &r#type.name.name)
}
}
pub fn compile_module(
session: &Session,
context: &MeliorContext,
mlir_module: &MeliorModule,
module: &Module,
) -> Result<(), Box<dyn Error>> {
let mut scope_ctx: ScopeContext = Default::default();
let block = mlir_module.body();
// Save types
for statement in &module.contents {
match statement {
ModuleStatement::Function(info) => {
scope_ctx.functions.insert(info.name.name.clone(), info);
}
ModuleStatement::Constant(info) => {
scope_ctx.constants.insert(info.name.name.clone(), info);
}
ModuleStatement::Struct(info) => {
scope_ctx.structs.insert(info.name.name.clone(), info);
}
ModuleStatement::Module(_) => todo!(),
}
}
for statement in &module.contents {
match statement {
ModuleStatement::Function(info) => {
compile_function_def(session, context, &scope_ctx, &block, info)?;
}
ModuleStatement::Constant(_) => todo!(),
ModuleStatement::Struct(_) => todo!(),
ModuleStatement::Module(_) => todo!(),
}
}
tracing::debug!("compiled module");
Ok(())
}
fn get_location<'c>(context: &'c MeliorContext, session: &Session, offset: usize) -> Location<'c> {
let (_, line, col) = session.source.get_offset_line(offset).unwrap();
Location::new(
context,
&session.file_path.display().to_string(),
line + 1,
col + 1,
)
}
fn compile_function_def<'ctx, 'parent>(
session: &Session,
context: &'ctx MeliorContext,
scope_ctx: &ScopeContext<'ctx, 'parent>,
block: &'parent Block<'ctx>,
info: &Function,
) -> Result<(), Box<dyn Error>> {
tracing::debug!("compiling function: {}", info.name.name);
let region = Region::new();
let location = get_location(context, session, info.name.span.lo);
let mut args = Vec::with_capacity(info.params.len());
let mut fn_args_types = Vec::with_capacity(info.params.len());
for param in &info.params {
let param_type = scope_ctx.resolve_type(context, &param.arg_type)?;
let loc = get_location(context, session, param.name.span.lo);
args.push((param_type, loc));
fn_args_types.push(param_type);
}
let return_type = if let Some(return_type) = &info.return_type {
vec![scope_ctx.resolve_type(context, return_type)?]
} else {
vec![]
};
let func_type =
TypeAttribute::new(FunctionType::new(context, &fn_args_types, &return_type).into());
let blocks_arena = Bump::new();
{
let helper = BlockHelper {
region: &region,
blocks_arena: &blocks_arena,
};
let fn_block = helper.append_block(Block::new(&args));
let mut scope_ctx = scope_ctx.clone();
scope_ctx.ret_type = info.return_type.as_ref();
// Push arguments into locals
for (i, param) in info.params.iter().enumerate() {
scope_ctx.locals.insert(
param.name.name.clone(),
LocalVar::param(fn_block.argument(i)?.into(), param.arg_type.clone()),
);
}
let final_block = compile_block(
session,
context,
&mut scope_ctx,
&helper,
fn_block,
&info.body,
)?;
if final_block.terminator().is_none() {
final_block.append_operation(func::r#return(
&[],
get_location(context, session, info.span.hi),
));
}
}
let op = func::func(
context,
StringAttribute::new(context, &info.name.name),
func_type,
region,
&[],
location,
);
assert!(op.verify());
block.append_operation(op);
Ok(())
}
fn compile_block<'ctx, 'parent: 'ctx>(
session: &Session,
context: &'ctx MeliorContext,
scope_ctx: &mut ScopeContext<'ctx, 'parent>,
helper: &BlockHelper<'ctx, 'parent>,
mut block: &'parent BlockRef<'ctx, 'parent>,
info: &edlang_ast::Block,
) -> Result<&'parent BlockRef<'ctx, 'parent>, Box<dyn std::error::Error>> {
tracing::debug!("compiling block");
for stmt in &info.body {
match stmt {
Statement::Let(info) => {
compile_let(session, context, scope_ctx, helper, block, info)?;
}
Statement::Assign(info) => {
compile_assign(session, context, scope_ctx, helper, block, info)?;
}
Statement::For(_) => todo!(),
Statement::While(_) => todo!(),
Statement::If(_) => todo!(),
Statement::Return(info) => {
compile_return(session, context, scope_ctx, helper, block, info)?;
}
Statement::FnCall(_) => todo!(),
}
}
Ok(block)
}
fn compile_let<'ctx, 'parent: 'ctx>(
session: &Session,
context: &'ctx MeliorContext,
scope_ctx: &mut ScopeContext<'ctx, 'parent>,
helper: &BlockHelper<'ctx, 'parent>,
block: &'parent BlockRef<'ctx, 'parent>,
info: &LetStmt,
) -> Result<(), Box<dyn std::error::Error>> {
tracing::debug!("compiling let");
let value = compile_expression(
session,
context,
scope_ctx,
helper,
block,
&info.value,
Some(scope_ctx.resolve_type(context, &info.r#type)?),
)?;
let location = get_location(context, session, info.name.span.lo);
let memref_type = MemRefType::new(value.r#type(), &[1], None, None);
let alloca: Value = block
.append_operation(memref::alloca(
context,
memref_type,
&[],
&[],
None,
location,
))
.result(0)?
.into();
let k0 = block
.append_operation(arith::constant(
context,
IntegerAttribute::new(0, Type::index(context)).into(),
location,
))
.result(0)?
.into();
block.append_operation(memref::store(value, alloca, &[k0], location));
scope_ctx.locals.insert(
info.name.name.clone(),
LocalVar::alloca(alloca, info.r#type.clone()),
);
Ok(())
}
fn compile_assign<'ctx, 'parent: 'ctx>(
session: &Session,
context: &'ctx MeliorContext,
scope_ctx: &mut ScopeContext<'ctx, 'parent>,
helper: &BlockHelper<'ctx, 'parent>,
block: &'parent BlockRef<'ctx, 'parent>,
info: &AssignStmt,
) -> Result<(), Box<dyn std::error::Error>> {
tracing::debug!("compiling assign");
let local = scope_ctx
.locals
.get(&info.name.first.name)
.expect("local should exist")
.clone();
assert!(local.is_alloca, "can only mutate local stack variables");
let location = get_location(context, session, info.name.first.span.lo);
let value = compile_expression(
session,
context,
scope_ctx,
helper,
block,
&info.value,
Some(scope_ctx.resolve_type(context, &local.ast_type)?),
)?;
let k0 = block
.append_operation(arith::constant(
context,
IntegerAttribute::new(0, Type::index(context)).into(),
location,
))
.result(0)?
.into();
block.append_operation(memref::store(value, local.value, &[k0], location));
Ok(())
}
fn compile_return<'ctx, 'parent: 'ctx>(
session: &Session,
context: &'ctx MeliorContext,
scope_ctx: &mut ScopeContext<'ctx, 'parent>,
helper: &BlockHelper<'ctx, 'parent>,
block: &'parent BlockRef<'ctx, 'parent>,
info: &ReturnStmt,
) -> Result<(), Box<dyn std::error::Error>> {
tracing::debug!("compiling return");
let location = get_location(context, session, info.span.lo);
if let Some(value) = &info.value {
let value = compile_expression(
session,
context,
scope_ctx,
helper,
block,
value,
scope_ctx
.ret_type
.map(|x| scope_ctx.resolve_type(context, x).unwrap()),
)?;
block.append_operation(func::r#return(&[value], location));
} else {
block.append_operation(func::r#return(&[], location));
}
Ok(())
}
fn compile_expression<'ctx, 'parent: 'ctx>(
session: &Session,
context: &'ctx MeliorContext,
scope_ctx: &ScopeContext<'ctx, 'parent>,
helper: &BlockHelper<'ctx, 'parent>,
block: &'parent BlockRef<'ctx, 'parent>,
info: &Expression,
type_hint: Option<Type<'ctx>>,
) -> Result<Value<'ctx, 'parent>, Box<dyn std::error::Error>> {
tracing::debug!("compiling expression");
Ok(match info {
Expression::Value(info) => match info {
ValueExpr::Bool { value, span } => block
.append_operation(arith::constant(
context,
IntegerAttribute::new((*value) as i64, IntegerType::new(context, 1).into())
.into(),
get_location(context, session, span.lo),
))
.result(0)?
.into(),
ValueExpr::Char { value, span } => block
.append_operation(arith::constant(
context,
IntegerAttribute::new((*value) as i64, IntegerType::new(context, 32).into())
.into(),
get_location(context, session, span.lo),
))
.result(0)?
.into(),
ValueExpr::Int { value, span } => {
let type_it = match type_hint {
Some(info) => info,
None => IntegerType::new(context, 32).into(),
};
block
.append_operation(arith::constant(
context,
IntegerAttribute::new((*value) as i64, type_it).into(),
get_location(context, session, span.lo),
))
.result(0)?
.into()
}
ValueExpr::Float { value, span } => {
let type_it = match type_hint {
Some(info) => info,
None => Type::float32(context),
};
block
.append_operation(arith::constant(
context,
Attribute::parse(context, &format!("{value} : {type_it}")).unwrap(),
get_location(context, session, span.lo),
))
.result(0)?
.into()
}
ValueExpr::Str { value: _, span: _ } => todo!(),
ValueExpr::Path(path) => {
let local = scope_ctx
.locals
.get(&path.first.name)
.expect("local not found");
let location = get_location(context, session, path.first.span.lo);
if local.is_alloca {
let k0 = block
.append_operation(arith::constant(
context,
IntegerAttribute::new(0, Type::index(context)).into(),
location,
))
.result(0)?
.into();
block
.append_operation(memref::load(local.value, &[k0], location))
.result(0)?
.into()
} else {
local.value
}
}
},
Expression::FnCall(info) => {
let mut args = Vec::with_capacity(info.params.len());
let location = get_location(context, session, info.name.span.lo);
let target_fn = scope_ctx
.functions
.get(&info.name.name)
.expect("function not found");
assert_eq!(
info.params.len(),
target_fn.params.len(),
"parameter length doesnt match"
);
for (arg, arg_info) in info.params.iter().zip(&target_fn.params) {
let value = compile_expression(
session,
context,
scope_ctx,
helper,
block,
arg,
Some(scope_ctx.resolve_type(context, &arg_info.arg_type)?),
)?;
args.push(value);
}
let return_type = if let Some(return_type) = &target_fn.return_type {
vec![scope_ctx.resolve_type(context, return_type)?]
} else {
vec![]
};
block
.append_operation(func::call(
context,
FlatSymbolRefAttribute::new(context, &info.name.name),
&args,
&return_type,
location,
))
.result(0)?
.into()
}
Expression::Unary(_, _) => todo!(),
Expression::Binary(lhs, op, rhs) => {
let lhs =
compile_expression(session, context, scope_ctx, helper, block, lhs, type_hint)?;
let rhs =
compile_expression(session, context, scope_ctx, helper, block, rhs, type_hint)?;
match op {
BinaryOp::Arith(op, span) => {
match op {
// todo check if its a float or unsigned
ArithOp::Add => block.append_operation(arith::addi(
lhs,
rhs,
get_location(context, session, span.lo),
)),
ArithOp::Sub => block.append_operation(arith::subi(
lhs,
rhs,
get_location(context, session, span.lo),
)),
ArithOp::Mul => block.append_operation(arith::muli(
lhs,
rhs,
get_location(context, session, span.lo),
)),
ArithOp::Div => block.append_operation(arith::divsi(
lhs,
rhs,
get_location(context, session, span.lo),
)),
ArithOp::Mod => block.append_operation(arith::remsi(
lhs,
rhs,
get_location(context, session, span.lo),
)),
}
}
BinaryOp::Logic(_, _) => todo!(),
BinaryOp::Compare(_, _) => todo!(),
BinaryOp::Bitwise(_, _) => todo!(),
}
.result(0)?
.into()
}
})
}

View file

@ -0,0 +1,87 @@
use std::error::Error;
use edlang_ast::Module;
use edlang_session::Session;
use melior::{
dialect::DialectRegistry,
ir::{Location, Module as MeliorModule},
pass::{self, PassManager},
utility::{register_all_dialects, register_all_llvm_translations, register_all_passes},
Context as MeliorContext,
};
#[derive(Debug, Eq, PartialEq)]
pub struct Context {
melior_context: MeliorContext,
}
impl Default for Context {
fn default() -> Self {
Self::new()
}
}
impl Context {
pub fn new() -> Self {
let melior_context = initialize_mlir();
Self { melior_context }
}
pub fn compile(
&self,
session: &Session,
module: &Module,
) -> Result<MeliorModule, Box<dyn Error>> {
let file_path = session.file_path.display().to_string();
let location = Location::new(&self.melior_context, &file_path, 0, 0);
let mut melior_module = MeliorModule::new(location);
super::codegen::compile_module(session, &self.melior_context, &melior_module, module)?;
assert!(melior_module.as_operation().verify());
tracing::debug!(
"MLIR Code before passes:\n{:#?}",
melior_module.as_operation()
);
// TODO: Add proper error handling.
self.run_pass_manager(&mut melior_module)?;
tracing::debug!(
"MLIR Code after passes:\n{:#?}",
melior_module.as_operation()
);
Ok(melior_module)
}
fn run_pass_manager(&self, module: &mut MeliorModule) -> Result<(), melior::Error> {
let pass_manager = PassManager::new(&self.melior_context);
pass_manager.enable_verifier(true);
pass_manager.add_pass(pass::transform::create_canonicalizer());
pass_manager.add_pass(pass::conversion::create_scf_to_control_flow());
pass_manager.add_pass(pass::conversion::create_arith_to_llvm());
pass_manager.add_pass(pass::conversion::create_control_flow_to_llvm());
pass_manager.add_pass(pass::conversion::create_func_to_llvm());
pass_manager.add_pass(pass::conversion::create_index_to_llvm());
pass_manager.add_pass(pass::conversion::create_finalize_mem_ref_to_llvm());
pass_manager.add_pass(pass::conversion::create_reconcile_unrealized_casts());
pass_manager.run(module)
}
}
/// Initialize an MLIR context.
pub fn initialize_mlir() -> MeliorContext {
let context = MeliorContext::new();
context.append_dialect_registry(&{
let registry = DialectRegistry::new();
register_all_dialects(&registry);
registry
});
context.load_all_available_dialects();
register_all_passes();
register_all_llvm_translations(&context);
context
}

View file

@ -8,6 +8,8 @@ use std::{
sync::OnceLock, sync::OnceLock,
}; };
use context::Context;
use edlang_ast::Module;
use edlang_session::{OptLevel, Session}; use edlang_session::{OptLevel, Session};
use llvm_sys::{ use llvm_sys::{
core::{LLVMContextCreate, LLVMContextDispose, LLVMDisposeMessage, LLVMDisposeModule}, core::{LLVMContextCreate, LLVMContextDispose, LLVMDisposeMessage, LLVMDisposeModule},
@ -22,14 +24,24 @@ use llvm_sys::{
LLVMTargetRef, LLVMTargetRef,
}, },
}; };
use melior::ir::Module; use melior::ir::Module as MeliorModule;
use crate::ffi::mlirTranslateModuleToLLVMIR; use crate::ffi::mlirTranslateModuleToLLVMIR;
pub mod codegen; pub mod codegen;
mod context;
mod ffi; mod ffi;
pub mod linker; pub mod linker;
pub fn compile(session: &Session, program: &Module) -> Result<PathBuf, Box<dyn std::error::Error>> {
let context = Context::new();
let mlir_module = context.compile(session, program)?;
let object_path = compile_to_object(session, &mlir_module)?;
Ok(object_path)
}
/// Converts a module to an object. /// Converts a module to an object.
/// The object will be written to the specified target path. /// The object will be written to the specified target path.
/// TODO: error handling /// TODO: error handling
@ -37,7 +49,7 @@ pub mod linker;
/// Returns the path to the object. /// Returns the path to the object.
pub fn compile_to_object( pub fn compile_to_object(
session: &Session, session: &Session,
module: &Module, module: &MeliorModule,
) -> Result<PathBuf, Box<dyn std::error::Error>> { ) -> Result<PathBuf, Box<dyn std::error::Error>> {
tracing::debug!("Compiling to object file"); tracing::debug!("Compiling to object file");
if !session.target_dir.exists() { if !session.target_dir.exists() {

View file

@ -11,6 +11,7 @@ categories = ["compilers"]
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
ariadne = { version = "0.4.0", features = ["auto-color"] }
clap = { version = "4.4.16", features = ["derive"] } clap = { version = "4.4.16", features = ["derive"] }
color-eyre = "0.6.2" color-eyre = "0.6.2"
edlang_ast = { version = "0.1.0", path = "../edlang_ast" } edlang_ast = { version = "0.1.0", path = "../edlang_ast" }

View file

@ -1,6 +1,9 @@
use std::{error::Error, path::PathBuf, time::Instant}; use std::{error::Error, path::PathBuf, time::Instant};
use ariadne::Source;
use clap::Parser; use clap::Parser;
use edlang_codegen_mlir::linker::{link_binary, link_shared_lib};
use edlang_session::{DebugInfo, OptLevel, Session};
#[derive(Parser, Debug)] #[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)] #[command(author, version, about, long_about = None)]
@ -24,21 +27,17 @@ pub fn main() -> Result<(), Box<dyn Error>> {
let args = CompilerArgs::parse(); let args = CompilerArgs::parse();
/* let path = args.input.display().to_string();
let db = crate::db::Database::default(); let source = std::fs::read_to_string(&args.input)?;
let source = ProgramSource::new(&db, std::fs::read_to_string(args.input.clone())?);
tracing::debug!("source code:\n{}", source.input(&db)); let module = edlang_parser::parse_ast(&source);
let program = match concrete_parser::parse_ast(&db, source) {
Some(x) => x, let module = match module {
None => { Ok(module) => module,
Diagnostics::dump( Err(error) => {
&db, let report = edlang_parser::error_to_report(&path, &error)?;
source, edlang_parser::print_report(&path, &source, report)?;
&concrete_parser::parse_ast::accumulated::<concrete_parser::error::Diagnostics>( std::process::exit(1)
&db, source,
),
);
panic!();
} }
}; };
@ -64,21 +63,20 @@ pub fn main() -> Result<(), Box<dyn Error>> {
} else { } else {
OptLevel::None OptLevel::None
}, },
source: source.input(&db).to_string(), source: Source::from(source),
library: args.library, library: args.library,
target_dir, target_dir,
output_file, output_file,
}; };
tracing::debug!("Compiling with session: {:#?}", session); tracing::debug!("Compiling with session: {:#?}", session);
// let object_path = concrete_codegen_mlir::compile(&session, &program)?; let object_path = edlang_codegen_mlir::compile(&session, &module)?;
if session.library { if session.library {
link_shared_lib(&object_path, &session.output_file.with_extension("so"))?; link_shared_lib(&object_path, &session.output_file.with_extension("so"))?;
} else { } else {
link_binary(&object_path, &session.output_file.with_extension(""))?; link_binary(&object_path, &session.output_file.with_extension(""))?;
} }
*/
let elapsed = start_time.elapsed(); let elapsed = start_time.elapsed();
tracing::debug!("Done in {:?}", elapsed); tracing::debug!("Done in {:?}", elapsed);

View file

@ -13,6 +13,7 @@ categories = ["compilers"]
[dependencies] [dependencies]
ariadne = { version = "0.4.0", features = ["auto-color"] } ariadne = { version = "0.4.0", features = ["auto-color"] }
edlang_ast = { version = "0.1.0", path = "../edlang_ast" } edlang_ast = { version = "0.1.0", path = "../edlang_ast" }
itertools = "0.12.0"
lalrpop-util = { version = "0.20.0", features = ["lexer"] } lalrpop-util = { version = "0.20.0", features = ["lexer"] }
logos = "0.13.0" logos = "0.13.0"
tracing = { workspace = true } tracing = { workspace = true }

View file

@ -403,4 +403,5 @@ pub(crate) ModuleStatement: ast::ModuleStatement = {
<Function> => ast::ModuleStatement::Function(<>), <Function> => ast::ModuleStatement::Function(<>),
<Constant> => ast::ModuleStatement::Constant(<>), <Constant> => ast::ModuleStatement::Constant(<>),
<Struct> => ast::ModuleStatement::Struct(<>), <Struct> => ast::ModuleStatement::Struct(<>),
<Module> => ast::ModuleStatement::Module(<>),
} }

View file

@ -1,9 +1,11 @@
use std::{ops::Range, path::Path}; use std::ops::Range;
use ariadne::{Color, ColorGenerator, Fmt, Label, Report, ReportKind, Source}; use crate::error::Error;
use error::Error; use ariadne::{ColorGenerator, Label, Report, ReportKind, Source};
use itertools::Itertools;
use lalrpop_util::ParseError; use lalrpop_util::ParseError;
use lexer::{Lexer, LexicalError}; use lexer::{Lexer, LexicalError};
use tokens::Token;
pub mod error; pub mod error;
pub mod lexer; pub mod lexer;
@ -18,57 +20,76 @@ pub mod grammar {
lalrpop_mod!(pub grammar); lalrpop_mod!(pub grammar);
} }
pub fn parse_ast(source: &str) { pub fn parse_ast(
source: &str,
) -> Result<edlang_ast::Module, ParseError<usize, Token, LexicalError>> {
let lexer = Lexer::new(source); let lexer = Lexer::new(source);
let parser = grammar::IdentParser::new(); let parser = grammar::ModuleParser::new();
parser.parse(lexer)
} }
pub fn print_error(path: &str, source: &str, error: &Error) -> Result<(), std::io::Error> { pub fn print_report<'a>(
path: &'a str,
source: &'a str,
report: Report<'static, (&'a str, Range<usize>)>,
) -> Result<(), std::io::Error> {
let source = Source::from(source); let source = Source::from(source);
match error { report.eprint((path, source))
}
pub fn error_to_report<'a>(
path: &'a str,
error: &Error,
) -> Result<Report<'static, (&'a str, Range<usize>)>, std::io::Error> {
let mut colors = ColorGenerator::new();
let report = match error {
ParseError::InvalidToken { location } => { ParseError::InvalidToken { location } => {
let loc = *location; let loc = *location;
Report::build(ReportKind::Error, path, loc) Report::build(ReportKind::Error, path, loc)
.with_code(1) .with_code("P1")
.with_message("Invalid token") .with_label(
.with_label(Label::new((path, loc..(loc + 1))).with_message("invalid token")) Label::new((path, loc..(loc + 1)))
.with_color(colors.next())
.with_message("invalid token"),
)
.finish() .finish()
.eprint((path, source))?;
} }
ParseError::UnrecognizedEof { location, expected } => { ParseError::UnrecognizedEof { location, expected } => {
let loc = *location; let loc = *location;
Report::build(ReportKind::Error, path, loc) Report::build(ReportKind::Error, path, loc)
.with_code(2) .with_code("P2")
.with_message("Unrecognized end of file") .with_label(
.with_label(Label::new((path, loc..(loc + 1))).with_message(format!( Label::new((path, loc..(loc + 1)))
"unrecognized eof, expected one of the following: {:?}", .with_message(format!(
expected "unrecognized eof, expected one of the following: {}",
))) expected.iter().join(", ")
))
.with_color(colors.next()),
)
.finish() .finish()
.eprint((path, source))?;
} }
ParseError::UnrecognizedToken { token, expected } => { ParseError::UnrecognizedToken { token, expected } => {
Report::build(ReportKind::Error, path, token.0) Report::build(ReportKind::Error, path, token.0)
.with_code(3) .with_code(3)
.with_message("Unrecognized token")
.with_label(Label::new((path, token.0..token.2)).with_message(format!(
"unrecognized token {:?}, expected one of the following: {:?}",
token.1, expected
)))
.finish()
.eprint((path, source))?;
}
ParseError::ExtraToken { token } => {
Report::build(ReportKind::Error, path, token.0)
.with_code(4)
.with_message("Extra token")
.with_label( .with_label(
Label::new((path, token.0..token.2)) Label::new((path, token.0..token.2))
.with_message(format!("unexpected extra token {:?}", token.1)), .with_message(format!(
"unrecognized token {:?}, expected one of the following: {}",
token.1,
expected.iter().join(", ")
))
.with_color(colors.next()),
) )
.finish() .finish()
.eprint((path, source))?;
} }
ParseError::ExtraToken { token } => Report::build(ReportKind::Error, path, token.0)
.with_code("P3")
.with_message("Extra token")
.with_label(
Label::new((path, token.0..token.2))
.with_message(format!("unexpected extra token {:?}", token.1)),
)
.finish(),
ParseError::User { error } => match error { ParseError::User { error } => match error {
LexicalError::InvalidToken(err, range) => match err { LexicalError::InvalidToken(err, range) => match err {
tokens::LexingError::NumberParseError => { tokens::LexingError::NumberParseError => {
@ -77,26 +98,25 @@ pub fn print_error(path: &str, source: &str, error: &Error) -> Result<(), std::i
.with_message("Error parsing literal number") .with_message("Error parsing literal number")
.with_label( .with_label(
Label::new((path, range.start..range.end)) Label::new((path, range.start..range.end))
.with_message("error parsing literal number"), .with_message("error parsing literal number")
.with_color(colors.next()),
) )
.finish() .finish()
.eprint((path, source))?;
}
tokens::LexingError::Other => {
Report::build(ReportKind::Error, path, range.start)
.with_code(4)
.with_message("Other error")
.with_label(
Label::new((path, range.start..range.end)).with_message("other error"),
)
.finish()
.eprint((path, source))?;
} }
tokens::LexingError::Other => Report::build(ReportKind::Error, path, range.start)
.with_code(4)
.with_message("Other error")
.with_label(
Label::new((path, range.start..range.end))
.with_message("other error")
.with_color(colors.next()),
)
.finish(),
}, },
}, },
} };
Ok(()) Ok(report)
} }
#[cfg(test)] #[cfg(test)]

View file

@ -1,9 +1,11 @@
fn add(a: i32, b: i32) -> i32 { mod Main {
return a + b; fn add(a: i32, b: i32) -> i32 {
} return a + b;
}
fn main() -> i32 { fn main() -> i32 {
let x = 2 + 3; let x: i32 = 2 + 3;
let y = add(x, 4); let y: i32 = add(x, 4);
return y; return y;
}
} }

View file

@ -1,15 +0,0 @@
fn works(x: i64) -> i64 {
let z = 0i64;
if 2i64 == x {
z = x * 2i64;
} else {
z = x * 3i64;
}
return z;
}
fn main() -> i64 {
let y = 2i64;
let z = y;
return works(z);
}

View file

@ -1,24 +0,0 @@
struct Hello {
x: i32,
y: i32,
}
fn test(x: Hello) {
return;
}
fn works(x: i64) -> i64 {
let z = 0i64;
if 2i64 == x {
z = x * 2i64;
} else {
z = x * 3i64;
}
return z;
}
fn main() -> i64 {
let y = 2i64;
let z = y;
return works(z);
}

View file

@ -1,4 +0,0 @@
struct String {
}

View file

@ -1,10 +0,0 @@
struct Hello {
y: i16,
x: i32,
}
fn test(x: Hello) {
return;
}