More stuff compiles

This commit is contained in:
Mia
2026-02-27 15:48:16 +01:00
parent 4b07d93ae3
commit 0b4f169c6f
20 changed files with 552 additions and 134 deletions
Generated
+5
View File
@@ -111,6 +111,7 @@ dependencies = [
"bitflags",
"boxcar",
"derive_more",
"fxhash",
"half",
"leaf_allocators",
"scc",
@@ -121,6 +122,8 @@ name = "leaf_compiler"
version = "0.1.0"
dependencies = [
"arcstr",
"derive_more",
"fxhash",
"leaf_allocators",
"leaf_assembly",
"leaf_parser",
@@ -133,6 +136,8 @@ dependencies = [
"fxhash",
"leaf_allocators",
"leaf_assembly",
"leaf_compiler",
"leaf_parser",
"scc",
"smallvec",
]
+14
View File
@@ -37,10 +37,24 @@ impl<'l> dyn Allocator + 'l {
&mut *(self.alloc_unsafe(data, layout, drop) as *mut T)
}
}
pub fn alloc_str(&'l self, str: &str) -> &'l str {
unsafe {
let ptr =
self.alloc_unsafe(str.as_ptr(), Layout::array::<u8>(str.len()).unwrap(), None);
let slice = std::slice::from_raw_parts_mut(ptr, str.len());
slice.copy_from_slice(str.as_bytes());
std::str::from_utf8_unchecked(slice)
}
}
}
impl<'l> dyn SyncAllocator + 'l {
pub fn alloc<T: Send>(&'l self, value: T) -> &'l mut T {
<dyn Allocator>::alloc(self, value)
}
pub fn alloc_str(&'l self, value: &str) -> &'l str {
<dyn Allocator>::alloc_str(self, value)
}
}
+1
View File
@@ -11,3 +11,4 @@ half = "2.7.1"
scc = "3.3.7"
leaf_allocators = { path = "../allocators" }
fxhash = "0.2.1"
+29 -12
View File
@@ -1,11 +1,12 @@
use crate::{
functions::Function,
types::derivations::{FuncT, TypeDerivations},
values::AnyConst,
values::Const,
};
use derive_more::Debug;
use fxhash::FxBuildHasher;
use leaf_allocators::SyncAllocator;
use scc::{HashMap, HashSet};
use scc::HashMap;
use std::{borrow::Cow, hash::Hash, sync::OnceLock};
#[derive(derive_more::Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
@@ -28,6 +29,7 @@ pub type Ctx<'l> = &'l Context<'l>;
pub struct Context<'l> {
pub(crate) alloc: &'l dyn SyncAllocator,
pub(crate) derivations: TypeDerivations<'l>,
strings: HashMap<&'l str, &'l str, FxBuildHasher>,
assemblies: HashMap<AssemblyIdentifier, &'l Assembly<'l>>,
}
@@ -49,6 +51,7 @@ impl<'l> Context<'l> {
pub fn new(alloc: &'l dyn SyncAllocator) -> &'l Self {
alloc.alloc(Self {
alloc,
strings: HashMap::default(),
derivations: TypeDerivations::new(alloc),
assemblies: Default::default(),
})
@@ -64,6 +67,15 @@ impl<'l> Context<'l> {
.entry_sync(ident.clone())
.or_insert_with(|| Assembly::new(self, ident))
}
pub fn intern_str(&'l self, str: &str) -> &'l str {
if let Some(value) = self.strings.get_sync(str) {
return (*value).into();
}
let str = self.alloc.alloc_str(str);
let _ = self.strings.insert_sync(str, str);
str
}
}
#[derive(Debug)]
@@ -71,12 +83,7 @@ pub struct Assembly<'l> {
#[debug(skip)]
ctx: &'l Context<'l>,
ident: AssemblyIdentifier,
#[debug("{:#?}", {
let mut c = vec![];
constants.iter_sync(|k| {c.push(*k); true});
c
})]
constants: HashSet<AnyConst<'l>>,
functions: boxcar::Vec<&'l Function<'l>>,
}
impl Eq for Assembly<'_> {}
@@ -98,7 +105,7 @@ impl<'l> Assembly<'l> {
let assembly = ctx.alloc.alloc(Self {
ctx,
ident,
constants: HashSet::new(),
functions: boxcar::vec![],
});
assembly
}
@@ -114,9 +121,19 @@ impl<'l> Assembly<'l> {
body: OnceLock::new(),
declaring_assembly: self,
});
self.constants
.insert_sync(AnyConst::Function(func))
.unwrap();
self.functions.push(func);
func
}
pub fn find_function(
&'l self,
filter: impl Fn(&'l Function<'l>) -> bool,
) -> Option<&'l Function<'l>> {
for (_, func) in self.functions.iter() {
if filter(func) {
return Some(func);
}
}
None
}
}
+38 -6
View File
@@ -59,7 +59,8 @@ impl<'l> Instruction<'l> {
pub fn value_flags(&self) -> ValueFlags {
match self.variant {
_ => todo!(),
InstructionVariant::StackAlloc(_) => ValueFlags::LValue,
_ => ValueFlags::empty(),
}
}
@@ -73,8 +74,11 @@ impl<'l> Instruction<'l> {
_ => unreachable!(),
},
InstructionVariant::IAdd(a, _) => a.ty(),
InstructionVariant::IMul(a, _) => a.ty(),
InstructionVariant::FAdd(a, _) => a.ty(),
InstructionVariant::FMul(a, _) => a.ty(),
InstructionVariant::ICmp(_, _, _) => Type::Bool,
InstructionVariant::Call(f, _) => f.ty.ret_t,
_ => todo!("{self:?}"),
}
}
@@ -98,7 +102,9 @@ pub enum InstructionVariant<'l> {
Store(Value<'l>, Value<'l>),
IAdd(Value<'l>, Value<'l>),
IMul(Value<'l>, Value<'l>),
FAdd(Value<'l>, Value<'l>),
FMul(Value<'l>, Value<'l>),
ICmp(Value<'l>, Value<'l>, Cmp),
FCmp(Value<'l>, Value<'l>, Cmp),
@@ -132,11 +138,17 @@ impl std::fmt::Debug for InstructionVariant<'_> {
Self::Load(v) => write!(f, "load {v}"),
Self::Store(t, v) => write!(f, "store {t}, {v}"),
Self::IAdd(a, b) => write!(f, "iadd {a}, {b}"),
Self::IMul(a, b) => write!(f, "imul {a}, {b}"),
Self::FAdd(a, b) => write!(f, "fadd {a}, {b}"),
Self::FMul(a, b) => write!(f, "fmul {a}, {b}"),
Self::ICmp(a, b, c) => write!(f, "icmp {c:?} {a}, {b}"),
Self::FCmp(a, b, c) => write!(f, "fcmp {c:?} {a}, {b}"),
Self::Call(func, args) => {
write!(f, "call {:#?}(", *func as *const Function)?;
write!(
f,
"call {}(",
func.name.get().unwrap_or(&"<anonymous function>".into())
)?;
let mut separator = "";
for arg in args {
write!(f, "{separator}{arg}")?;
@@ -252,6 +264,22 @@ impl<'l> BlockBuilder<'l> {
}
}
pub fn mul(&mut self, a: Value<'l>, b: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
let [a_ty, b_ty] = [a.ty(), b.ty()];
match (a_ty, b_ty) {
(Type::Int(a_ty), Type::Int(b_ty)) if a_ty == b_ty => {
let inst = self.push_instruction(InstructionVariant::IMul(a, b))?;
Ok(inst.into())
}
(Type::Float(a_ty), Type::Float(b_ty)) if a_ty == b_ty => {
let inst = self.push_instruction(InstructionVariant::FMul(a, b))?;
Ok(inst.into())
}
_ => Err(format!("Cannot add values of type `{a_ty}` and `b_ty`.").into()),
}
}
pub fn cmp(
&mut self,
a: Value<'l>,
@@ -358,7 +386,7 @@ impl<'l> BlockBuilder<'l> {
pub fn build(self) -> BlockBuilderResult<'l, &'l Block<'l>> {
if !self.has_termination() {
return Err(format!("Block #{} has not termination.", self.block.id).into());
return Err(format!("Block #{} has no termination.", self.block.id).into());
}
self.block.instructions.set(self.instructions).unwrap();
Ok(self.block)
@@ -398,15 +426,15 @@ impl<'l> FunctionBodyBuilder<'l> {
builder
}
pub fn current_block(&self) -> &'l Block<'l> {
self.blocks[self.current_block].block
pub fn current_block(&self) -> &BlockBuilder<'l> {
&self.blocks[self.current_block]
}
pub fn set_current_block(&mut self, block: &'l Block<'l>) -> Option<&'l Block<'l>> {
match std::ptr::eq(block.func, self.func) {
false => None,
true => {
let current = self.current_block();
let current = &self.blocks[self.current_block].block;
self.current_block = block.id as usize;
Some(current)
}
@@ -466,6 +494,10 @@ impl<'l> FunctionBodyBuilder<'l> {
self.current_builder().add(a, b)
}
pub fn mul(&mut self, a: Value<'l>, b: Value<'l>) -> BlockBuilderResult<'l, Value<'l>> {
self.current_builder().mul(a, b)
}
pub fn cmp(
&mut self,
a: Value<'l>,
+1
View File
@@ -62,6 +62,7 @@ impl FmtDebug for Function<'_> {
f.debug_struct("Function")
.field("ty", &format_args!("{}", self.ty))
.field("name", &self.name.get())
.field("body", body)
.finish_non_exhaustive()
}
+15 -1
View File
@@ -1,4 +1,4 @@
use crate::{functions::Function, types::Type};
use crate::{functions::Function, types::Type, values::ValueFlags};
use derive_more::{Debug, *};
use half::f16;
use std::hash::Hash;
@@ -9,13 +9,17 @@ pub enum Int {
I16(i16),
I32(i32),
I64(i64),
#[from]
I128(i128),
ISize(i128),
U8(u8),
U16(u16),
U32(u32),
U64(u64),
#[from]
U128(u128),
USize(u128),
}
#[derive(Debug, Display, Clone, Copy, From, TryInto, PartialEq)]
@@ -90,15 +94,25 @@ impl<'l> AnyConst<'l> {
Self::Int(Int::I32(_)) => Type::I32,
Self::Int(Int::I64(_)) => Type::I64,
Self::Int(Int::I128(_)) => Type::I128,
Self::Int(Int::ISize(_)) => Type::ISIZE,
Self::Int(Int::U8(_)) => Type::U8,
Self::Int(Int::U16(_)) => Type::U16,
Self::Int(Int::U32(_)) => Type::U32,
Self::Int(Int::U64(_)) => Type::U64,
Self::Int(Int::U128(_)) => Type::U128,
Self::Int(Int::USize(_)) => Type::USIZE,
Self::Float(Float::F16(_)) => Type::F16,
Self::Float(Float::F32(_)) => Type::F32,
Self::Float(Float::F64(_)) => Type::F64,
_ => todo!("{self:?}"),
}
}
pub fn flags(&self) -> super::ValueFlags {
match self {
AnyConst::Function(function) => ValueFlags::Function,
AnyConst::Type(_) => ValueFlags::Type,
_ => ValueFlags::Const,
}
}
}
+21 -1
View File
@@ -1,4 +1,7 @@
use crate::{functions::ir::Instruction, types::Type};
use crate::{
functions::{Function, ir::Instruction},
types::Type,
};
use bitflags::bitflags;
use derive_more::{Debug, Display, *};
use std::hash::Hash;
@@ -11,6 +14,7 @@ bitflags! {
pub struct ValueFlags: u16 {
const Mutable = 0b00000000_00000001;
const Volatile = 0b00000000_00000010;
const LValue = 0b00000000_00000100;
const Const = 0b00000001_00000000;
const ConstOnly = 0b00000011_00000000;
const Type = 0b00000111_00000000;
@@ -20,10 +24,15 @@ bitflags! {
#[derive(Debug, Display, Clone, Copy, From, TryInto, PartialEq, Eq, Hash)]
pub enum Value<'l> {
Uninit,
#[display("{_0}")]
Constant(AnyConst<'l>),
#[display("{_0}")]
Instruction(&'l Instruction<'l>),
#[debug("${_0}")]
#[display("${_0}")]
Parameter(usize, &'l Function<'l>),
}
impl From<Int> for Value<'_> {
@@ -37,6 +46,17 @@ impl<'l> Value<'l> {
match self {
Value::Constant(v) => v.ty(),
Value::Instruction(v) => v.value_ty(),
Value::Parameter(i, f) => f.ty.par_t[*i],
Value::Uninit => unreachable!(),
}
}
pub fn flags(&self) -> ValueFlags {
match self {
Value::Instruction(instruction) => instruction.value_flags(),
Value::Parameter(_, _) => ValueFlags::empty(),
Value::Constant(c) => c.flags(),
_ => todo!("{self:?}"),
}
}
}
+2
View File
@@ -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"
+7
View File
@@ -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
View File
@@ -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,
);
)*
};
+1 -1
View File
@@ -19,7 +19,7 @@ fn main() {
file: PathBuf::from("../test.leaf"),
})],
) {
println!("{}", err.message);
println!("{:#?}", err);
} else {
println!("{:#?}", context.get_assembly(&ident));
}
+303 -20
View File
@@ -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,9 +110,13 @@ 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 {
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 {
@@ -79,21 +125,258 @@ impl<'l> Scope<'l> {
},
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!(),
},
},
_ => 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> {
+2
View File
@@ -7,5 +7,7 @@ edition = "2024"
fxhash = "0.2.1"
leaf_allocators = { path = "../allocators" }
leaf_assembly = { path = "../assembly" }
leaf_compiler = { path = "../compiler" }
leaf_parser = { path = "../parser" }
scc = "3.3.7"
smallvec = "1.15.1"
+2 -2
View File
@@ -6,7 +6,7 @@ use leaf_assembly::{
ir::{Cmp, Instruction, InstructionVariant},
},
types::{IntT, Type},
values::{AnyConst, Const, Int, Value},
values::{AnyConst, Int, Value},
};
use scc::HashMap;
use std::{alloc::Layout, fmt::Debug, ops::Range, sync::Arc};
@@ -246,7 +246,7 @@ impl<'l> InstructionCacheEntry<'l> {
Value::Instruction(i) => {
ranges.push(memory_ranges[&i.id()].clone());
}
_ => todo!("Unimplemented variant `{arg}`."),
_ => todo!("Unimplemented variant `{arg:?}`."),
}
}
opcodes.push(OpCode::Call(
+33 -55
View File
@@ -1,66 +1,44 @@
use leaf_allocators::SyncArenaAllocator;
use leaf_assembly::assembly::{AssemblyIdentifier, Context, Version};
use leaf_assembly::functions::ir::Cmp;
use leaf_assembly::types::Type;
use leaf_assembly::values::Int;
use leaf_assembly::assembly::{AssemblyIdentifier, Version};
use leaf_compiler::CompilationContext;
use leaf_interpreter::interpreter::{AnyValue, Error, Interpreter, NativeFunction};
use leaf_parser::SourceCode;
use std::ffi::CStr;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::{Duration, Instant};
fn main() {
let allocator = SyncArenaAllocator::default();
let context = Context::new(&allocator);
let assembly = context.get_or_create_assembly(AssemblyIdentifier {
let context = CompilationContext::new(&allocator);
let ident = AssemblyIdentifier {
version: Version::default(),
name: "test".into(),
});
let puts = assembly.create_function(Type::Void.make_fn([Type::U8.make_ptr(false).into()]));
let print_u32 = assembly.create_function(Type::Void.make_fn([Type::U32.into()]));
let random_func = {
let func = assembly.create_function(Type::U32.make_fn([]));
let mut builder = func.create_body().unwrap();
let loop_check = builder.create_block();
let loop_body = builder.create_block();
let loop_end = builder.create_block();
let var0 = builder.stack_alloc(Type::U32).unwrap();
let var1 = builder.stack_alloc(Type::U32).unwrap();
builder.store(var0, Int::U32(42).into()).unwrap();
builder.store(var1, Int::U32(0u32).into()).unwrap();
builder.jump(loop_check).unwrap();
builder.set_current_block(loop_check);
let a = builder.load(var0).unwrap();
let b = builder.load(var1).unwrap();
let c = builder.add(a, b).unwrap();
builder.call(print_u32, vec![c]).unwrap();
let cond = builder.cmp(c, Int::U32(69u32).into(), Cmp::Lt).unwrap();
builder.branch(cond, loop_body, loop_end).unwrap();
builder.set_current_block(loop_body);
let v = builder.load(var1).unwrap();
let v = builder.add(v, Int::U32(1u32).into()).unwrap();
builder.store(var1, v).unwrap();
builder.jump(loop_check).unwrap();
builder.set_current_block(loop_end);
let v = builder.load(var1).unwrap();
builder.ret(Some(v)).unwrap();
let body = builder.build().unwrap();
println!("{body:#?}");
func
name: "interpreter.il".into(),
};
context
.extend(
ident.clone(),
&[Arc::new(SourceCode {
text: std::fs::read_to_string("../test.leaf").unwrap().into(),
file: PathBuf::from("../test.leaf"),
})],
)
.unwrap();
let mut interpreter = Interpreter::new(context);
let mut interpreter = Interpreter::new(&context);
let assembly = context.get_assembly(&ident).unwrap();
let puts = assembly
.find_function(|f| f.name.get() == Some(&"puts"))
.unwrap();
let print_u32 = assembly
.find_function(|f| f.name.get() == Some(&"print_u32"))
.unwrap();
let main = assembly
.find_function(|f| f.name.get() == Some(&"main"))
.unwrap();
interpreter.register_function(
puts,
@@ -91,7 +69,7 @@ fn main() {
{
let now = Instant::now();
let result = interpreter.run(random_func, vec![]);
let result = interpreter.run(main, vec![]);
let elapsed = now.elapsed();
println!("Ret Value: {result:?}");
println!("Cold Time: {:?}", elapsed)
@@ -101,7 +79,7 @@ fn main() {
let mut duration = Duration::default();
for _ in 0..LOOPS {
let now = Instant::now();
let _ = interpreter.run(random_func, vec![]);
let _ = interpreter.run(main, vec![]);
duration += now.elapsed();
}
println!("Warm Time: {:?}", duration / LOOPS);
+38 -9
View File
@@ -1,3 +1,5 @@
use std::ops::Range;
use arcstr::Substr;
use derive_more::Deref;
@@ -32,6 +34,8 @@ pub enum Expr {
Block(Block),
#[debug("{_0:?}")]
Func(Box<Function>),
#[debug("{_0:?}")]
Type(Box<Type>),
#[debug("{_0:?}")]
ConstDecl(Box<ConstDecl>),
@@ -39,6 +43,8 @@ pub enum Expr {
VarDecl(Box<VarDecl>),
#[debug("{_0:?}")]
For(Box<For>),
#[debug("{_0:?}")]
While(Box<While>),
Call {
func: Box<Expr>,
@@ -46,6 +52,14 @@ pub enum Expr {
},
}
impl Expr {
pub fn range(&self) -> Range<usize> {
match self {
_ => todo!(),
}
}
}
#[derive(Debug)]
pub struct BinaryExpr {
pub lhs: Expr,
@@ -62,14 +76,23 @@ pub struct IndexingExpr {
#[rustfmt::skip]
#[derive(derive_more::Debug)]
pub enum BinaryOp {
#[debug("{_0:?}")] Add(Substr),
#[debug("{_0:?}")] Sub(Substr),
#[debug("{_0:?}")] Mul(Substr),
#[debug("{_0:?}")] Div(Substr),
#[debug("{_0:?}")] Mod(Substr),
#[debug("{_0:?}")] Dot(Substr),
#[debug("{_0:?}")] Range(Substr),
#[debug("{_0:?}")] Assign(Substr),
#[debug("{_0}")] Add(Substr),
#[debug("{_0}")] Sub(Substr),
#[debug("{_0}")] Mul(Substr),
#[debug("{_0}")] Div(Substr),
#[debug("{_0}")] Mod(Substr),
#[debug("{_0}")] Dot(Substr),
#[debug("{_0}")] Lt(Substr),
#[debug("{_0}")] Gt(Substr),
#[debug("{_0}")] Le(Substr),
#[debug("{_0}")] Ge(Substr),
#[debug("{_0}")] Range(Substr),
#[debug("{_0}")] Assign(Substr),
}
#[derive(Debug)]
pub enum Type {
Ptr { base: Expr, mutable: bool },
}
#[derive(Debug)]
@@ -107,7 +130,7 @@ impl NamePattern {
pub struct Function {
pub text: Substr,
pub args: Vec<NameValuePair>,
pub ret: Expr,
pub ret: Option<Expr>,
pub block: Option<Block>,
}
@@ -133,6 +156,12 @@ pub struct For {
pub block: Block,
}
#[derive(Debug)]
pub struct While {
pub value: Expr,
pub block: Block,
}
#[derive(Debug)]
pub struct CompilationUnit {
pub imports: Vec<Import>,
+12 -1
View File
@@ -84,14 +84,21 @@ peg::parser! {
--
lhs:(@) __ op:$("..") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Range(op), rhs })) }
--
lhs:(@) __ op:$("<") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Lt(op), rhs })) }
lhs:(@) __ op:$(">") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Gt(op), rhs })) }
lhs:(@) __ op:$("<=") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Le(op), rhs })) }
lhs:(@) __ op:$(">=") __ rhs:@ { Expr::Binary(Box::new(BinaryExpr { lhs, op: BinaryOp::Ge(op), rhs })) }
--
block:block() { Expr::Block(block)}
for_loop:for_loop() { Expr::For(Box::new(for_loop))}
while_loop:while_loop() { Expr::While(Box::new(while_loop))}
func:func() { Expr::Func(Box::new(func))}
var_decl:var_decl() { Expr::VarDecl(Box::new(var_decl)) }
const_decl:const_decl() { Expr::ConstDecl(Box::new(const_decl)) }
"(" __ tuple:(expr() **<2,> ("," __)) __ ")" { Expr::Tuple(tuple) }
"[" __ list:(expr() **<0,> ("," __)) __ "]" { Expr::List(list) }
"(" __ v:expr() __ ")" { v }
"*" __ m:"mut"? __ v:expr() { Expr::Type(Box::new(Type::Ptr { base:v, mutable: m.is_some() })) }
v:string() { Expr::String(v) }
v:number() { Expr::Number(v) }
v:ident() { Expr::Ident(v) }
@@ -101,7 +108,7 @@ peg::parser! {
= "{" __ exprs:(i:expr() statement_separator() {i})* __ "}" { Block(exprs) }
rule func() -> Function
= s:position!() t:$"fn" __ "(" __ args:name_value_pairs() __ ")" __ ret:expr() __ block:block()? e:position!()
= s:position!() t:$"fn" __ "(" __ args:name_value_pairs() __ ")" __ ret:("->" __ e:expr() {e})? __ block:block()? e:position!()
{ Function { args, ret, block, text: t.parent().substr(s..e), } }
rule name_value_pair() -> NameValuePair
@@ -130,6 +137,10 @@ peg::parser! {
"for" _ names:name_pattern() _ "in" _ value:expr() _ block:block()
{ For { names, value, block } }
rule while_loop() -> While =
"while" _ value:expr() _ block:block()
{ While { value, block } }
pub rule compilation_unit() -> CompilationUnit =
__ imports:(i:import() statement_separator() {i})*
__ decls:(d:const_decl() statement_separator() {d})*
-1
View File
@@ -1,4 +1,3 @@
[toolchain]
channel = "1.91.0"
components = ["clippy", "rustfmt"]
profile = "default"
+11 -17
View File
@@ -1,20 +1,14 @@
import leaf.reflection.comptime
puts :: fn(v: *u8) -> i32;
print_u32 :: fn(v: u32);
add :: fn(a: u32, b: u32) u32 {
a + b
}
Vector :: fn(T: type, count: usize) type {
NAMES :: ["x", "y", "z", "w"]
ty := TypeBuilder.struct()
for i in 0..count {
ty.push(Field {
name: NAMES[i],
type: T,
flags: FieldFlags.Public
})
print_up_to :: fn(max: u32) {
i := 0u32
while i < max {
print_u32(i)
i = i + 1u32
}
ty.build()
}
main :: fn() {
print_up_to(42u32)
}