mirror of
https://github.com/edg-l/edlang.git
synced 2024-11-22 16:08:24 +00:00
prog
This commit is contained in:
parent
5b4b38a276
commit
bf13468214
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1 +1,2 @@
|
||||||
/target
|
/target
|
||||||
|
/target_ed
|
||||||
|
|
14
Cargo.lock
generated
14
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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" }
|
||||||
|
|
|
@ -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, ¶m.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: ®ion,
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
87
lib/edlang_codegen_mlir/src/context.rs
Normal file
87
lib/edlang_codegen_mlir/src/context.rs
Normal 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(®istry);
|
||||||
|
registry
|
||||||
|
});
|
||||||
|
context.load_all_available_dialects();
|
||||||
|
register_all_passes();
|
||||||
|
register_all_llvm_translations(&context);
|
||||||
|
context
|
||||||
|
}
|
|
@ -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() {
|
||||||
|
|
|
@ -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" }
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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 }
|
||||||
|
|
|
@ -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(<>),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -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);
|
|
||||||
}
|
|
|
@ -1,4 +0,0 @@
|
||||||
|
|
||||||
struct String {
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,10 +0,0 @@
|
||||||
|
|
||||||
|
|
||||||
struct Hello {
|
|
||||||
y: i16,
|
|
||||||
x: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
fn test(x: Hello) {
|
|
||||||
return;
|
|
||||||
}
|
|
Loading…
Reference in a new issue