More stuff compiles
This commit is contained in:
@@ -8,3 +8,5 @@ leaf_parser = { path = "../parser" }
|
||||
leaf_assembly = { path = "../assembly" }
|
||||
leaf_allocators = { path = "../allocators" }
|
||||
arcstr = "1.2.0"
|
||||
derive_more = { version = "2.0.1", features = ["deref", "deref_mut", "debug", "display", "try_from", "from", "try_into", "into"] }
|
||||
fxhash = "0.2.1"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
use derive_more::Debug;
|
||||
use leaf_parser::{LineCol, ParseError};
|
||||
use std::ops::Range;
|
||||
|
||||
@@ -8,10 +9,14 @@ pub enum Kind {
|
||||
Unknown = 0x0000,
|
||||
|
||||
Parsing = 0x0100,
|
||||
InvalidInteger = 0x0101,
|
||||
|
||||
SymbolNotFound = 0x0200,
|
||||
UninitializedSymbol = 0x0201,
|
||||
NotAType = 0x0202,
|
||||
NotAFunction = 0x0205,
|
||||
InvalidIntegerType = 0x0203,
|
||||
InvalidType = 0x0204,
|
||||
|
||||
FunctionCompilationFailed = 0x0301,
|
||||
}
|
||||
@@ -20,10 +25,12 @@ pub enum Kind {
|
||||
pub enum Location {
|
||||
None,
|
||||
Range {
|
||||
#[debug("{:?}", file.file)]
|
||||
file: SourceFile,
|
||||
range: Range<usize>,
|
||||
},
|
||||
Position {
|
||||
#[debug("{:?}", file.file)]
|
||||
file: SourceFile,
|
||||
position: LineCol,
|
||||
},
|
||||
|
||||
+11
-2
@@ -3,11 +3,11 @@ use arcstr::literal_substr;
|
||||
use leaf_allocators::SyncAllocator;
|
||||
use leaf_assembly::{
|
||||
assembly::{Assembly, AssemblyIdentifier, Context},
|
||||
types::{FloatT, IntT, Type},
|
||||
types::Type,
|
||||
values::{AnyConst, Value},
|
||||
};
|
||||
use leaf_parser::SourceCode;
|
||||
use std::{collections::HashMap, sync::Arc, u32};
|
||||
use std::{ops::Deref, sync::Arc};
|
||||
|
||||
mod error;
|
||||
mod scope;
|
||||
@@ -19,6 +19,14 @@ pub struct CompilationContext<'l> {
|
||||
alloc: &'l dyn SyncAllocator,
|
||||
}
|
||||
|
||||
impl<'l> Deref for CompilationContext<'l> {
|
||||
type Target = Context<'l>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.ctx
|
||||
}
|
||||
}
|
||||
|
||||
impl<'l> CompilationContext<'l> {
|
||||
pub fn new(alloc: &'l dyn SyncAllocator) -> Self {
|
||||
Self {
|
||||
@@ -56,6 +64,7 @@ impl<'l> CompilationContext<'l> {
|
||||
scope.insert(
|
||||
literal_substr!($id),
|
||||
Value::Constant(AnyConst::Type($ty.into())),
|
||||
false,
|
||||
);
|
||||
)*
|
||||
};
|
||||
|
||||
@@ -19,7 +19,7 @@ fn main() {
|
||||
file: PathBuf::from("../test.leaf"),
|
||||
})],
|
||||
) {
|
||||
println!("{}", err.message);
|
||||
println!("{:#?}", err);
|
||||
} else {
|
||||
println!("{:#?}", context.get_assembly(&ident));
|
||||
}
|
||||
|
||||
+310
-27
@@ -1,22 +1,38 @@
|
||||
use arcstr::Substr;
|
||||
use leaf_assembly::{
|
||||
assembly::Assembly,
|
||||
types::Type,
|
||||
values::{AnyConst, Value},
|
||||
functions::{
|
||||
Function,
|
||||
ir::{Cmp, FunctionBodyBuilder},
|
||||
},
|
||||
types::{Type, derivations::PtrT},
|
||||
values::{AnyConst, Int, Value, ValueFlags},
|
||||
};
|
||||
use leaf_parser::{
|
||||
SourceCode,
|
||||
ast::{self, ConstDecl, Expr, Ident},
|
||||
ast::{self, BinaryExpr, BinaryOp, ConstDecl, Expr, Ident, NamePattern, While},
|
||||
};
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
|
||||
use crate::error::*;
|
||||
|
||||
#[derive(Default)]
|
||||
struct ExpressionContext<'l, 'r> {
|
||||
decl_names: Option<&'r NamePattern>,
|
||||
builder: Option<&'r mut FunctionBodyBuilder<'l>>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Variable<'l> {
|
||||
value: Value<'l>,
|
||||
mutable: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Scope<'l> {
|
||||
assembly: &'l Assembly<'l>,
|
||||
source: Arc<SourceCode>,
|
||||
values: HashMap<Substr, Option<Value<'l>>>,
|
||||
values: HashMap<Substr, Variable<'l>>,
|
||||
}
|
||||
|
||||
impl<'l> Scope<'l> {
|
||||
@@ -28,26 +44,49 @@ impl<'l> Scope<'l> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn insert(&mut self, name: Substr, value: Value<'l>) {
|
||||
self.values.insert(name, Some(value));
|
||||
pub fn insert(&mut self, name: Substr, value: Value<'l>, mutable: bool) {
|
||||
self.values.insert(name, Variable { value, mutable });
|
||||
}
|
||||
|
||||
pub fn declare_constants(&mut self, decl: &[ConstDecl]) {
|
||||
for val in decl {
|
||||
for Ident(name) in val.names.as_slice() {
|
||||
self.values.insert(name.clone(), None);
|
||||
self.values.insert(
|
||||
name.clone(),
|
||||
Variable {
|
||||
value: Value::Uninit,
|
||||
mutable: false,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn define_constants(&mut self, decl: &[ConstDecl]) -> Result<(), CompilationError> {
|
||||
for val in decl {
|
||||
let expr = self.compile_expression(&val.value)?;
|
||||
let expr = self.compile_expression(
|
||||
&val.value,
|
||||
&mut ExpressionContext {
|
||||
decl_names: Some(&val.names),
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
match &val.names {
|
||||
NamePattern::Single(ident) => {
|
||||
self.values.get_mut(&ident.0).unwrap().value = expr;
|
||||
}
|
||||
NamePattern::Tuple(_) => todo!(),
|
||||
NamePattern::List(_) => todo!(),
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn compile_expression(&self, expr: &Expr) -> Result<Value<'l>, CompilationError> {
|
||||
fn compile_expression(
|
||||
&mut self,
|
||||
expr: &Expr,
|
||||
ctx: &mut ExpressionContext<'l, '_>,
|
||||
) -> Result<Value<'l>, CompilationError> {
|
||||
match expr {
|
||||
Expr::Ident(Ident(name)) => match self.values.get(name) {
|
||||
None => Err(CompilationError {
|
||||
@@ -59,7 +98,10 @@ impl<'l> Scope<'l> {
|
||||
},
|
||||
cause: None,
|
||||
}),
|
||||
Some(None) => Err(CompilationError {
|
||||
Some(Variable {
|
||||
value: Value::Uninit,
|
||||
..
|
||||
}) => Err(CompilationError {
|
||||
kind: Kind::UninitializedSymbol,
|
||||
message: format!("Symbol `{name}` is not initialized at this time."),
|
||||
location: Location::Range {
|
||||
@@ -68,32 +110,273 @@ impl<'l> Scope<'l> {
|
||||
},
|
||||
cause: None,
|
||||
}),
|
||||
Some(Some(value)) => Ok(*value),
|
||||
Some(Variable { value, .. }) => Ok(*value),
|
||||
},
|
||||
Expr::Func(func) => self.compile_function(func).map_err(|err| CompilationError {
|
||||
kind: Kind::FunctionCompilationFailed,
|
||||
message: format!("Could not compile function."),
|
||||
location: Location::Range {
|
||||
file: self.source.clone(),
|
||||
range: func.text.range(),
|
||||
|
||||
Expr::Func(func) => self
|
||||
.compile_function(func, ctx)
|
||||
.map(|f| Value::Constant(AnyConst::Function(f)))
|
||||
.map_err(|err| CompilationError {
|
||||
kind: Kind::FunctionCompilationFailed,
|
||||
message: format!("Could not compile function."),
|
||||
location: Location::Range {
|
||||
file: self.source.clone(),
|
||||
range: func.text.range(),
|
||||
},
|
||||
cause: Some(Box::new(err)),
|
||||
}),
|
||||
|
||||
Expr::ConstDecl(decl) => self.compile_decl(&decl.names, &decl.value, false, ctx),
|
||||
Expr::VarDecl(decl) => self.compile_decl(&decl.names, &decl.value, true, ctx),
|
||||
|
||||
Expr::Number(n) => {
|
||||
macro_rules! parse_number {
|
||||
($ty: ty, $id: ident) => {
|
||||
match n.text.split_at_checked(2) {
|
||||
Some(("0b", value)) => <$ty>::from_str_radix(value, 2),
|
||||
Some(("0x", value)) => <$ty>::from_str_radix(value, 16),
|
||||
_ => <$ty>::from_str_radix(&n.text, 10),
|
||||
}
|
||||
.map(|v| Value::Constant(AnyConst::Int(Int::$id(v))))
|
||||
.map_err(|_| CompilationError {
|
||||
kind: Kind::InvalidInteger,
|
||||
message: format!("`{}` is not a valid integer.", n.text),
|
||||
location: Location::Range {
|
||||
file: self.source.clone(),
|
||||
range: n.text.range(),
|
||||
},
|
||||
cause: None,
|
||||
})
|
||||
};
|
||||
}
|
||||
match n.r#type.as_ref().map(|v| v.as_str()) {
|
||||
None => parse_number!(i128, ISize),
|
||||
Some("i8") => parse_number!(i8, I8),
|
||||
Some("i16") => parse_number!(i16, I16),
|
||||
Some("i32") => parse_number!(i32, I32),
|
||||
Some("i64") => parse_number!(i64, I64),
|
||||
Some("i128") => parse_number!(i128, I128),
|
||||
Some("isize") => parse_number!(i128, ISize),
|
||||
Some("u8") => parse_number!(u8, U8),
|
||||
Some("u16") => parse_number!(u16, U16),
|
||||
Some("u32") => parse_number!(u32, U32),
|
||||
Some("u64") => parse_number!(u64, U64),
|
||||
Some("u128") => parse_number!(u128, U128),
|
||||
Some("usize") => parse_number!(u128, USize),
|
||||
Some(ty) => {
|
||||
return Err(CompilationError {
|
||||
kind: Kind::InvalidIntegerType,
|
||||
message: format!("`{ty}` is not a valid integer type."),
|
||||
location: Location::Range {
|
||||
file: self.source.clone(),
|
||||
range: n.text.range(),
|
||||
},
|
||||
cause: None,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Expr::Binary(expr) => {
|
||||
let BinaryExpr {
|
||||
lhs: lhs_expr,
|
||||
rhs: rhs_expr,
|
||||
op,
|
||||
} = &**expr;
|
||||
|
||||
let mut lhs = self.compile_expression(lhs_expr, ctx)?;
|
||||
if lhs.flags().contains(ValueFlags::LValue) && !matches!(op, BinaryOp::Assign(_)) {
|
||||
lhs = ctx.builder.as_mut().unwrap().load(lhs).unwrap();
|
||||
}
|
||||
|
||||
let mut rhs = self.compile_expression(rhs_expr, ctx)?;
|
||||
if rhs.flags().contains(ValueFlags::LValue) {
|
||||
rhs = ctx.builder.as_mut().unwrap().load(rhs).unwrap();
|
||||
}
|
||||
|
||||
let builder = ctx.builder.as_mut().unwrap();
|
||||
match (lhs.ty(), rhs.ty(), op) {
|
||||
(Type::U32, Type::U32, BinaryOp::Add(_)) => Ok(builder.add(lhs, rhs).unwrap()),
|
||||
(Type::U32, Type::U32, BinaryOp::Mul(_)) => Ok(builder.mul(lhs, rhs).unwrap()),
|
||||
(Type::U32, Type::U32, BinaryOp::Lt(_)) => {
|
||||
Ok(builder.cmp(lhs, rhs, Cmp::Lt).unwrap())
|
||||
}
|
||||
(Type::Ptr(PtrT { base, mutable, .. }), ty, BinaryOp::Assign(_)) => {
|
||||
match *base == ty {
|
||||
true => Ok(builder.store(lhs, rhs).unwrap()),
|
||||
false => Err(CompilationError {
|
||||
kind: Kind::InvalidType,
|
||||
message: format!(
|
||||
"Cannot assign a value of type `{ty}` to a value of type `{base}`."
|
||||
),
|
||||
location: Location::Range {
|
||||
file: self.source.clone(),
|
||||
range: rhs_expr.range(),
|
||||
},
|
||||
cause: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
(a, b, _) => unimplemented!("{a} {op:?} {b} | {lhs:?} {op:?} {rhs:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
Expr::While(expr) => {
|
||||
let While { value, block } = &**expr;
|
||||
|
||||
let mut builder = ctx.builder.as_mut().unwrap();
|
||||
let cond_block = builder.create_block();
|
||||
let exec_block = builder.create_block();
|
||||
let exit_block = builder.create_block();
|
||||
|
||||
builder.jump(cond_block).unwrap();
|
||||
builder.set_current_block(cond_block);
|
||||
let condition = self.compile_expression(value, ctx)?;
|
||||
|
||||
builder = ctx.builder.as_mut().unwrap();
|
||||
builder.branch(condition, exec_block, exit_block).unwrap();
|
||||
builder.set_current_block(exec_block);
|
||||
|
||||
let mut scope = self.clone();
|
||||
let mut ctx = ExpressionContext {
|
||||
builder: Some(builder),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut last_expr = None;
|
||||
for expr in &block.0 {
|
||||
last_expr = Some(scope.compile_expression(expr, &mut ctx)?);
|
||||
}
|
||||
builder.jump(cond_block).unwrap();
|
||||
builder.set_current_block(exit_block);
|
||||
Ok(last_expr.unwrap_or(Value::Constant(AnyConst::Void)))
|
||||
}
|
||||
|
||||
Expr::Call {
|
||||
func,
|
||||
args: args_exprs,
|
||||
} => {
|
||||
let func = match self.compile_expression(func, ctx)? {
|
||||
Value::Constant(AnyConst::Function(func)) => func,
|
||||
_ => {
|
||||
return Err(CompilationError {
|
||||
kind: Kind::NotAFunction,
|
||||
message: "Value is not a function".into(),
|
||||
location: Location::Range {
|
||||
file: self.source.clone(),
|
||||
range: func.range(),
|
||||
},
|
||||
cause: None,
|
||||
});
|
||||
}
|
||||
};
|
||||
let mut args = Vec::with_capacity(args_exprs.len());
|
||||
for expr in args_exprs {
|
||||
let mut arg = self.compile_expression(expr, ctx)?;
|
||||
if arg.flags().contains(ValueFlags::LValue) {
|
||||
arg = ctx.builder.as_mut().unwrap().load(arg).unwrap();
|
||||
}
|
||||
args.push(arg);
|
||||
}
|
||||
let builder = ctx.builder.as_mut().unwrap();
|
||||
Ok(builder.call(func, args).unwrap())
|
||||
}
|
||||
|
||||
Expr::Type(expr) => match &**expr {
|
||||
ast::Type::Ptr { base, mutable } => match self.compile_expression(base, ctx)? {
|
||||
Value::Constant(AnyConst::Type(ty)) => {
|
||||
Ok(AnyConst::Type(Type::Ptr(ty.make_ptr(*mutable))).into())
|
||||
}
|
||||
_ => todo!(),
|
||||
},
|
||||
cause: Some(Box::new(err)),
|
||||
}),
|
||||
},
|
||||
|
||||
_ => todo!("{expr:#?}"),
|
||||
}
|
||||
}
|
||||
|
||||
fn compile_function(&self, func: &ast::Function) -> Result<Value<'l>, CompilationError> {
|
||||
let ret_ty = self.assert_type(self.compile_expression(&func.ret)?)?;
|
||||
let mut par_ty = Vec::with_capacity(func.args.len());
|
||||
for arg in &func.args {
|
||||
let ty = self.assert_type(self.compile_expression(&arg.r#type)?)?;
|
||||
fn compile_function(
|
||||
&mut self,
|
||||
ast: &ast::Function,
|
||||
ctx: &mut ExpressionContext<'l, '_>,
|
||||
) -> Result<&'l Function<'l>, CompilationError> {
|
||||
let ret_ty = match ast.ret.as_ref() {
|
||||
None => Value::Constant(AnyConst::Type(Type::Void)),
|
||||
Some(ty) => self.compile_expression(ty, ctx)?,
|
||||
};
|
||||
let ret_ty = self.assert_type(ret_ty)?;
|
||||
let mut par_ty = Vec::with_capacity(ast.args.len());
|
||||
for arg in &ast.args {
|
||||
let ty = self.compile_expression(&arg.r#type, ctx)?;
|
||||
let ty = self.assert_type(ty)?;
|
||||
par_ty.push(ty);
|
||||
}
|
||||
|
||||
let fn_ty = ret_ty.make_fn(par_ty);
|
||||
Ok(Value::Constant(AnyConst::Function(
|
||||
self.assembly.create_function(fn_ty),
|
||||
)))
|
||||
let func = self.assembly.create_function(fn_ty);
|
||||
if let Some(NamePattern::Single(func_name)) = &ctx.decl_names {
|
||||
func.name.set(func.ctx().intern_str(&func_name.0)).unwrap();
|
||||
if let Some(variable) = self.values.get_mut(func_name.as_str()) {
|
||||
variable.value = Value::Constant(AnyConst::Function(func));
|
||||
}
|
||||
};
|
||||
|
||||
let Some(block) = &ast.block else {
|
||||
return Ok(func);
|
||||
};
|
||||
|
||||
let mut scope = self.clone();
|
||||
for (i, arg) in ast.args.iter().enumerate() {
|
||||
scope.insert(arg.name.0.clone(), Value::Parameter(i, func), false);
|
||||
}
|
||||
|
||||
let mut builder = func.create_body().unwrap();
|
||||
let mut ctx = ExpressionContext {
|
||||
builder: Some(&mut builder),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let mut last_expr = None;
|
||||
for expr in &block.0 {
|
||||
last_expr = Some(scope.compile_expression(expr, &mut ctx)?);
|
||||
}
|
||||
|
||||
if !builder.current_block().has_termination() {
|
||||
builder.ret(last_expr).unwrap();
|
||||
}
|
||||
|
||||
builder.build().unwrap();
|
||||
|
||||
Ok(func)
|
||||
}
|
||||
|
||||
fn compile_decl(
|
||||
&mut self,
|
||||
names: &NamePattern,
|
||||
value: &Expr,
|
||||
mutable: bool,
|
||||
ctx: &mut ExpressionContext<'l, '_>,
|
||||
) -> Result<Value<'l>, CompilationError> {
|
||||
let mut sub_ctx = ExpressionContext {
|
||||
decl_names: Some(names),
|
||||
builder: ctx.builder.as_mut().map(|v| &mut **v),
|
||||
};
|
||||
let mut value = self.compile_expression(value, &mut sub_ctx)?;
|
||||
if mutable {
|
||||
let builder = sub_ctx.builder.unwrap();
|
||||
let variable = builder.stack_alloc(value.ty()).unwrap();
|
||||
builder.store(variable, value).unwrap();
|
||||
value = variable;
|
||||
}
|
||||
match names {
|
||||
NamePattern::Single(ident) => {
|
||||
self.values
|
||||
.insert(ident.0.clone(), Variable { value, mutable });
|
||||
}
|
||||
NamePattern::Tuple(_) => todo!(),
|
||||
NamePattern::List(_) => todo!(),
|
||||
}
|
||||
Ok(AnyConst::Void.into())
|
||||
}
|
||||
|
||||
fn assert_type(&self, val: Value<'l>) -> Result<Type<'l>, CompilationError> {
|
||||
|
||||
Reference in New Issue
Block a user