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", "bitflags",
"boxcar", "boxcar",
"derive_more", "derive_more",
"fxhash",
"half", "half",
"leaf_allocators", "leaf_allocators",
"scc", "scc",
@@ -121,6 +122,8 @@ name = "leaf_compiler"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"arcstr", "arcstr",
"derive_more",
"fxhash",
"leaf_allocators", "leaf_allocators",
"leaf_assembly", "leaf_assembly",
"leaf_parser", "leaf_parser",
@@ -133,6 +136,8 @@ dependencies = [
"fxhash", "fxhash",
"leaf_allocators", "leaf_allocators",
"leaf_assembly", "leaf_assembly",
"leaf_compiler",
"leaf_parser",
"scc", "scc",
"smallvec", "smallvec",
] ]
+14
View File
@@ -37,10 +37,24 @@ impl<'l> dyn Allocator + 'l {
&mut *(self.alloc_unsafe(data, layout, drop) as *mut T) &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 { impl<'l> dyn SyncAllocator + 'l {
pub fn alloc<T: Send>(&'l self, value: T) -> &'l mut T { pub fn alloc<T: Send>(&'l self, value: T) -> &'l mut T {
<dyn Allocator>::alloc(self, value) <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" scc = "3.3.7"
leaf_allocators = { path = "../allocators" } leaf_allocators = { path = "../allocators" }
fxhash = "0.2.1"
+29 -12
View File
@@ -1,11 +1,12 @@
use crate::{ use crate::{
functions::Function, functions::Function,
types::derivations::{FuncT, TypeDerivations}, types::derivations::{FuncT, TypeDerivations},
values::AnyConst, values::Const,
}; };
use derive_more::Debug; use derive_more::Debug;
use fxhash::FxBuildHasher;
use leaf_allocators::SyncAllocator; use leaf_allocators::SyncAllocator;
use scc::{HashMap, HashSet}; use scc::HashMap;
use std::{borrow::Cow, hash::Hash, sync::OnceLock}; use std::{borrow::Cow, hash::Hash, sync::OnceLock};
#[derive(derive_more::Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[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 struct Context<'l> {
pub(crate) alloc: &'l dyn SyncAllocator, pub(crate) alloc: &'l dyn SyncAllocator,
pub(crate) derivations: TypeDerivations<'l>, pub(crate) derivations: TypeDerivations<'l>,
strings: HashMap<&'l str, &'l str, FxBuildHasher>,
assemblies: HashMap<AssemblyIdentifier, &'l Assembly<'l>>, assemblies: HashMap<AssemblyIdentifier, &'l Assembly<'l>>,
} }
@@ -49,6 +51,7 @@ impl<'l> Context<'l> {
pub fn new(alloc: &'l dyn SyncAllocator) -> &'l Self { pub fn new(alloc: &'l dyn SyncAllocator) -> &'l Self {
alloc.alloc(Self { alloc.alloc(Self {
alloc, alloc,
strings: HashMap::default(),
derivations: TypeDerivations::new(alloc), derivations: TypeDerivations::new(alloc),
assemblies: Default::default(), assemblies: Default::default(),
}) })
@@ -64,6 +67,15 @@ impl<'l> Context<'l> {
.entry_sync(ident.clone()) .entry_sync(ident.clone())
.or_insert_with(|| Assembly::new(self, ident)) .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)] #[derive(Debug)]
@@ -71,12 +83,7 @@ pub struct Assembly<'l> {
#[debug(skip)] #[debug(skip)]
ctx: &'l Context<'l>, ctx: &'l Context<'l>,
ident: AssemblyIdentifier, ident: AssemblyIdentifier,
#[debug("{:#?}", { functions: boxcar::Vec<&'l Function<'l>>,
let mut c = vec![];
constants.iter_sync(|k| {c.push(*k); true});
c
})]
constants: HashSet<AnyConst<'l>>,
} }
impl Eq for Assembly<'_> {} impl Eq for Assembly<'_> {}
@@ -98,7 +105,7 @@ impl<'l> Assembly<'l> {
let assembly = ctx.alloc.alloc(Self { let assembly = ctx.alloc.alloc(Self {
ctx, ctx,
ident, ident,
constants: HashSet::new(), functions: boxcar::vec![],
}); });
assembly assembly
} }
@@ -114,9 +121,19 @@ impl<'l> Assembly<'l> {
body: OnceLock::new(), body: OnceLock::new(),
declaring_assembly: self, declaring_assembly: self,
}); });
self.constants self.functions.push(func);
.insert_sync(AnyConst::Function(func))
.unwrap();
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 { pub fn value_flags(&self) -> ValueFlags {
match self.variant { match self.variant {
_ => todo!(), InstructionVariant::StackAlloc(_) => ValueFlags::LValue,
_ => ValueFlags::empty(),
} }
} }
@@ -73,8 +74,11 @@ impl<'l> Instruction<'l> {
_ => unreachable!(), _ => unreachable!(),
}, },
InstructionVariant::IAdd(a, _) => a.ty(), InstructionVariant::IAdd(a, _) => a.ty(),
InstructionVariant::IMul(a, _) => a.ty(),
InstructionVariant::FAdd(a, _) => a.ty(), InstructionVariant::FAdd(a, _) => a.ty(),
InstructionVariant::FMul(a, _) => a.ty(),
InstructionVariant::ICmp(_, _, _) => Type::Bool, InstructionVariant::ICmp(_, _, _) => Type::Bool,
InstructionVariant::Call(f, _) => f.ty.ret_t,
_ => todo!("{self:?}"), _ => todo!("{self:?}"),
} }
} }
@@ -98,7 +102,9 @@ pub enum InstructionVariant<'l> {
Store(Value<'l>, Value<'l>), Store(Value<'l>, Value<'l>),
IAdd(Value<'l>, Value<'l>), IAdd(Value<'l>, Value<'l>),
IMul(Value<'l>, Value<'l>),
FAdd(Value<'l>, Value<'l>), FAdd(Value<'l>, Value<'l>),
FMul(Value<'l>, Value<'l>),
ICmp(Value<'l>, Value<'l>, Cmp), ICmp(Value<'l>, Value<'l>, Cmp),
FCmp(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::Load(v) => write!(f, "load {v}"),
Self::Store(t, v) => write!(f, "store {t}, {v}"), Self::Store(t, v) => write!(f, "store {t}, {v}"),
Self::IAdd(a, b) => write!(f, "iadd {a}, {b}"), 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::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::ICmp(a, b, c) => write!(f, "icmp {c:?} {a}, {b}"),
Self::FCmp(a, b, c) => write!(f, "fcmp {c:?} {a}, {b}"), Self::FCmp(a, b, c) => write!(f, "fcmp {c:?} {a}, {b}"),
Self::Call(func, args) => { 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 = ""; let mut separator = "";
for arg in args { for arg in args {
write!(f, "{separator}{arg}")?; 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( pub fn cmp(
&mut self, &mut self,
a: Value<'l>, a: Value<'l>,
@@ -358,7 +386,7 @@ impl<'l> BlockBuilder<'l> {
pub fn build(self) -> BlockBuilderResult<'l, &'l Block<'l>> { pub fn build(self) -> BlockBuilderResult<'l, &'l Block<'l>> {
if !self.has_termination() { 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(); self.block.instructions.set(self.instructions).unwrap();
Ok(self.block) Ok(self.block)
@@ -398,15 +426,15 @@ impl<'l> FunctionBodyBuilder<'l> {
builder builder
} }
pub fn current_block(&self) -> &'l Block<'l> { pub fn current_block(&self) -> &BlockBuilder<'l> {
self.blocks[self.current_block].block &self.blocks[self.current_block]
} }
pub fn set_current_block(&mut self, block: &'l Block<'l>) -> Option<&'l Block<'l>> { pub fn set_current_block(&mut self, block: &'l Block<'l>) -> Option<&'l Block<'l>> {
match std::ptr::eq(block.func, self.func) { match std::ptr::eq(block.func, self.func) {
false => None, false => None,
true => { true => {
let current = self.current_block(); let current = &self.blocks[self.current_block].block;
self.current_block = block.id as usize; self.current_block = block.id as usize;
Some(current) Some(current)
} }
@@ -466,6 +494,10 @@ impl<'l> FunctionBodyBuilder<'l> {
self.current_builder().add(a, b) 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( pub fn cmp(
&mut self, &mut self,
a: Value<'l>, a: Value<'l>,
+1
View File
@@ -62,6 +62,7 @@ impl FmtDebug for Function<'_> {
f.debug_struct("Function") f.debug_struct("Function")
.field("ty", &format_args!("{}", self.ty)) .field("ty", &format_args!("{}", self.ty))
.field("name", &self.name.get())
.field("body", body) .field("body", body)
.finish_non_exhaustive() .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 derive_more::{Debug, *};
use half::f16; use half::f16;
use std::hash::Hash; use std::hash::Hash;
@@ -9,13 +9,17 @@ pub enum Int {
I16(i16), I16(i16),
I32(i32), I32(i32),
I64(i64), I64(i64),
#[from]
I128(i128), I128(i128),
ISize(i128),
U8(u8), U8(u8),
U16(u16), U16(u16),
U32(u32), U32(u32),
U64(u64), U64(u64),
#[from]
U128(u128), U128(u128),
USize(u128),
} }
#[derive(Debug, Display, Clone, Copy, From, TryInto, PartialEq)] #[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::I32(_)) => Type::I32,
Self::Int(Int::I64(_)) => Type::I64, Self::Int(Int::I64(_)) => Type::I64,
Self::Int(Int::I128(_)) => Type::I128, Self::Int(Int::I128(_)) => Type::I128,
Self::Int(Int::ISize(_)) => Type::ISIZE,
Self::Int(Int::U8(_)) => Type::U8, Self::Int(Int::U8(_)) => Type::U8,
Self::Int(Int::U16(_)) => Type::U16, Self::Int(Int::U16(_)) => Type::U16,
Self::Int(Int::U32(_)) => Type::U32, Self::Int(Int::U32(_)) => Type::U32,
Self::Int(Int::U64(_)) => Type::U64, Self::Int(Int::U64(_)) => Type::U64,
Self::Int(Int::U128(_)) => Type::U128, Self::Int(Int::U128(_)) => Type::U128,
Self::Int(Int::USize(_)) => Type::USIZE,
Self::Float(Float::F16(_)) => Type::F16, Self::Float(Float::F16(_)) => Type::F16,
Self::Float(Float::F32(_)) => Type::F32, Self::Float(Float::F32(_)) => Type::F32,
Self::Float(Float::F64(_)) => Type::F64, Self::Float(Float::F64(_)) => Type::F64,
_ => todo!("{self:?}"), _ => 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 bitflags::bitflags;
use derive_more::{Debug, Display, *}; use derive_more::{Debug, Display, *};
use std::hash::Hash; use std::hash::Hash;
@@ -11,6 +14,7 @@ bitflags! {
pub struct ValueFlags: u16 { pub struct ValueFlags: u16 {
const Mutable = 0b00000000_00000001; const Mutable = 0b00000000_00000001;
const Volatile = 0b00000000_00000010; const Volatile = 0b00000000_00000010;
const LValue = 0b00000000_00000100;
const Const = 0b00000001_00000000; const Const = 0b00000001_00000000;
const ConstOnly = 0b00000011_00000000; const ConstOnly = 0b00000011_00000000;
const Type = 0b00000111_00000000; const Type = 0b00000111_00000000;
@@ -20,10 +24,15 @@ bitflags! {
#[derive(Debug, Display, Clone, Copy, From, TryInto, PartialEq, Eq, Hash)] #[derive(Debug, Display, Clone, Copy, From, TryInto, PartialEq, Eq, Hash)]
pub enum Value<'l> { pub enum Value<'l> {
Uninit,
#[display("{_0}")] #[display("{_0}")]
Constant(AnyConst<'l>), Constant(AnyConst<'l>),
#[display("{_0}")] #[display("{_0}")]
Instruction(&'l Instruction<'l>), Instruction(&'l Instruction<'l>),
#[debug("${_0}")]
#[display("${_0}")]
Parameter(usize, &'l Function<'l>),
} }
impl From<Int> for Value<'_> { impl From<Int> for Value<'_> {
@@ -37,6 +46,17 @@ impl<'l> Value<'l> {
match self { match self {
Value::Constant(v) => v.ty(), Value::Constant(v) => v.ty(),
Value::Instruction(v) => v.value_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_assembly = { path = "../assembly" }
leaf_allocators = { path = "../allocators" } leaf_allocators = { path = "../allocators" }
arcstr = "1.2.0" 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 leaf_parser::{LineCol, ParseError};
use std::ops::Range; use std::ops::Range;
@@ -8,10 +9,14 @@ pub enum Kind {
Unknown = 0x0000, Unknown = 0x0000,
Parsing = 0x0100, Parsing = 0x0100,
InvalidInteger = 0x0101,
SymbolNotFound = 0x0200, SymbolNotFound = 0x0200,
UninitializedSymbol = 0x0201, UninitializedSymbol = 0x0201,
NotAType = 0x0202, NotAType = 0x0202,
NotAFunction = 0x0205,
InvalidIntegerType = 0x0203,
InvalidType = 0x0204,
FunctionCompilationFailed = 0x0301, FunctionCompilationFailed = 0x0301,
} }
@@ -20,10 +25,12 @@ pub enum Kind {
pub enum Location { pub enum Location {
None, None,
Range { Range {
#[debug("{:?}", file.file)]
file: SourceFile, file: SourceFile,
range: Range<usize>, range: Range<usize>,
}, },
Position { Position {
#[debug("{:?}", file.file)]
file: SourceFile, file: SourceFile,
position: LineCol, position: LineCol,
}, },
+11 -2
View File
@@ -3,11 +3,11 @@ use arcstr::literal_substr;
use leaf_allocators::SyncAllocator; use leaf_allocators::SyncAllocator;
use leaf_assembly::{ use leaf_assembly::{
assembly::{Assembly, AssemblyIdentifier, Context}, assembly::{Assembly, AssemblyIdentifier, Context},
types::{FloatT, IntT, Type}, types::Type,
values::{AnyConst, Value}, values::{AnyConst, Value},
}; };
use leaf_parser::SourceCode; use leaf_parser::SourceCode;
use std::{collections::HashMap, sync::Arc, u32}; use std::{ops::Deref, sync::Arc};
mod error; mod error;
mod scope; mod scope;
@@ -19,6 +19,14 @@ pub struct CompilationContext<'l> {
alloc: &'l dyn SyncAllocator, 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> { impl<'l> CompilationContext<'l> {
pub fn new(alloc: &'l dyn SyncAllocator) -> Self { pub fn new(alloc: &'l dyn SyncAllocator) -> Self {
Self { Self {
@@ -56,6 +64,7 @@ impl<'l> CompilationContext<'l> {
scope.insert( scope.insert(
literal_substr!($id), literal_substr!($id),
Value::Constant(AnyConst::Type($ty.into())), Value::Constant(AnyConst::Type($ty.into())),
false,
); );
)* )*
}; };
+1 -1
View File
@@ -19,7 +19,7 @@ fn main() {
file: PathBuf::from("../test.leaf"), file: PathBuf::from("../test.leaf"),
})], })],
) { ) {
println!("{}", err.message); println!("{:#?}", err);
} else { } else {
println!("{:#?}", context.get_assembly(&ident)); println!("{:#?}", context.get_assembly(&ident));
} }
+310 -27
View File
@@ -1,22 +1,38 @@
use arcstr::Substr; use arcstr::Substr;
use leaf_assembly::{ use leaf_assembly::{
assembly::Assembly, assembly::Assembly,
types::Type, functions::{
values::{AnyConst, Value}, Function,
ir::{Cmp, FunctionBodyBuilder},
},
types::{Type, derivations::PtrT},
values::{AnyConst, Int, Value, ValueFlags},
}; };
use leaf_parser::{ use leaf_parser::{
SourceCode, SourceCode,
ast::{self, ConstDecl, Expr, Ident}, ast::{self, BinaryExpr, BinaryOp, ConstDecl, Expr, Ident, NamePattern, While},
}; };
use std::{collections::HashMap, sync::Arc}; use std::{collections::HashMap, sync::Arc};
use crate::error::*; 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)] #[derive(Clone)]
pub struct Scope<'l> { pub struct Scope<'l> {
assembly: &'l Assembly<'l>, assembly: &'l Assembly<'l>,
source: Arc<SourceCode>, source: Arc<SourceCode>,
values: HashMap<Substr, Option<Value<'l>>>, values: HashMap<Substr, Variable<'l>>,
} }
impl<'l> Scope<'l> { impl<'l> Scope<'l> {
@@ -28,26 +44,49 @@ impl<'l> Scope<'l> {
} }
} }
pub fn insert(&mut self, name: Substr, value: Value<'l>) { pub fn insert(&mut self, name: Substr, value: Value<'l>, mutable: bool) {
self.values.insert(name, Some(value)); self.values.insert(name, Variable { value, mutable });
} }
pub fn declare_constants(&mut self, decl: &[ConstDecl]) { pub fn declare_constants(&mut self, decl: &[ConstDecl]) {
for val in decl { for val in decl {
for Ident(name) in val.names.as_slice() { 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> { pub fn define_constants(&mut self, decl: &[ConstDecl]) -> Result<(), CompilationError> {
for val in decl { 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(()) 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 { match expr {
Expr::Ident(Ident(name)) => match self.values.get(name) { Expr::Ident(Ident(name)) => match self.values.get(name) {
None => Err(CompilationError { None => Err(CompilationError {
@@ -59,7 +98,10 @@ impl<'l> Scope<'l> {
}, },
cause: None, cause: None,
}), }),
Some(None) => Err(CompilationError { Some(Variable {
value: Value::Uninit,
..
}) => Err(CompilationError {
kind: Kind::UninitializedSymbol, kind: Kind::UninitializedSymbol,
message: format!("Symbol `{name}` is not initialized at this time."), message: format!("Symbol `{name}` is not initialized at this time."),
location: Location::Range { location: Location::Range {
@@ -68,32 +110,273 @@ impl<'l> Scope<'l> {
}, },
cause: None, 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, Expr::Func(func) => self
message: format!("Could not compile function."), .compile_function(func, ctx)
location: Location::Range { .map(|f| Value::Constant(AnyConst::Function(f)))
file: self.source.clone(), .map_err(|err| CompilationError {
range: func.text.range(), 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:#?}"), _ => todo!("{expr:#?}"),
} }
} }
fn compile_function(&self, func: &ast::Function) -> Result<Value<'l>, CompilationError> { fn compile_function(
let ret_ty = self.assert_type(self.compile_expression(&func.ret)?)?; &mut self,
let mut par_ty = Vec::with_capacity(func.args.len()); ast: &ast::Function,
for arg in &func.args { ctx: &mut ExpressionContext<'l, '_>,
let ty = self.assert_type(self.compile_expression(&arg.r#type)?)?; ) -> 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); par_ty.push(ty);
} }
let fn_ty = ret_ty.make_fn(par_ty); let fn_ty = ret_ty.make_fn(par_ty);
Ok(Value::Constant(AnyConst::Function( let func = self.assembly.create_function(fn_ty);
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> { fn assert_type(&self, val: Value<'l>) -> Result<Type<'l>, CompilationError> {
+2
View File
@@ -7,5 +7,7 @@ edition = "2024"
fxhash = "0.2.1" fxhash = "0.2.1"
leaf_allocators = { path = "../allocators" } leaf_allocators = { path = "../allocators" }
leaf_assembly = { path = "../assembly" } leaf_assembly = { path = "../assembly" }
leaf_compiler = { path = "../compiler" }
leaf_parser = { path = "../parser" }
scc = "3.3.7" scc = "3.3.7"
smallvec = "1.15.1" smallvec = "1.15.1"
+2 -2
View File
@@ -6,7 +6,7 @@ use leaf_assembly::{
ir::{Cmp, Instruction, InstructionVariant}, ir::{Cmp, Instruction, InstructionVariant},
}, },
types::{IntT, Type}, types::{IntT, Type},
values::{AnyConst, Const, Int, Value}, values::{AnyConst, Int, Value},
}; };
use scc::HashMap; use scc::HashMap;
use std::{alloc::Layout, fmt::Debug, ops::Range, sync::Arc}; use std::{alloc::Layout, fmt::Debug, ops::Range, sync::Arc};
@@ -246,7 +246,7 @@ impl<'l> InstructionCacheEntry<'l> {
Value::Instruction(i) => { Value::Instruction(i) => {
ranges.push(memory_ranges[&i.id()].clone()); ranges.push(memory_ranges[&i.id()].clone());
} }
_ => todo!("Unimplemented variant `{arg}`."), _ => todo!("Unimplemented variant `{arg:?}`."),
} }
} }
opcodes.push(OpCode::Call( opcodes.push(OpCode::Call(
+33 -55
View File
@@ -1,66 +1,44 @@
use leaf_allocators::SyncArenaAllocator; use leaf_allocators::SyncArenaAllocator;
use leaf_assembly::assembly::{AssemblyIdentifier, Context, Version}; use leaf_assembly::assembly::{AssemblyIdentifier, Version};
use leaf_assembly::functions::ir::Cmp; use leaf_compiler::CompilationContext;
use leaf_assembly::types::Type;
use leaf_assembly::values::Int;
use leaf_interpreter::interpreter::{AnyValue, Error, Interpreter, NativeFunction}; use leaf_interpreter::interpreter::{AnyValue, Error, Interpreter, NativeFunction};
use leaf_parser::SourceCode;
use std::ffi::CStr; use std::ffi::CStr;
use std::path::PathBuf;
use std::sync::Arc;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
fn main() { fn main() {
let allocator = SyncArenaAllocator::default(); let allocator = SyncArenaAllocator::default();
let context = Context::new(&allocator); let context = CompilationContext::new(&allocator);
let assembly = context.get_or_create_assembly(AssemblyIdentifier { let ident = AssemblyIdentifier {
version: Version::default(), version: Version::default(),
name: "test".into(), name: "interpreter.il".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
}; };
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( interpreter.register_function(
puts, puts,
@@ -91,7 +69,7 @@ fn main() {
{ {
let now = Instant::now(); let now = Instant::now();
let result = interpreter.run(random_func, vec![]); let result = interpreter.run(main, vec![]);
let elapsed = now.elapsed(); let elapsed = now.elapsed();
println!("Ret Value: {result:?}"); println!("Ret Value: {result:?}");
println!("Cold Time: {:?}", elapsed) println!("Cold Time: {:?}", elapsed)
@@ -101,7 +79,7 @@ fn main() {
let mut duration = Duration::default(); let mut duration = Duration::default();
for _ in 0..LOOPS { for _ in 0..LOOPS {
let now = Instant::now(); let now = Instant::now();
let _ = interpreter.run(random_func, vec![]); let _ = interpreter.run(main, vec![]);
duration += now.elapsed(); duration += now.elapsed();
} }
println!("Warm Time: {:?}", duration / LOOPS); println!("Warm Time: {:?}", duration / LOOPS);
+38 -9
View File
@@ -1,3 +1,5 @@
use std::ops::Range;
use arcstr::Substr; use arcstr::Substr;
use derive_more::Deref; use derive_more::Deref;
@@ -32,6 +34,8 @@ pub enum Expr {
Block(Block), Block(Block),
#[debug("{_0:?}")] #[debug("{_0:?}")]
Func(Box<Function>), Func(Box<Function>),
#[debug("{_0:?}")]
Type(Box<Type>),
#[debug("{_0:?}")] #[debug("{_0:?}")]
ConstDecl(Box<ConstDecl>), ConstDecl(Box<ConstDecl>),
@@ -39,6 +43,8 @@ pub enum Expr {
VarDecl(Box<VarDecl>), VarDecl(Box<VarDecl>),
#[debug("{_0:?}")] #[debug("{_0:?}")]
For(Box<For>), For(Box<For>),
#[debug("{_0:?}")]
While(Box<While>),
Call { Call {
func: Box<Expr>, func: Box<Expr>,
@@ -46,6 +52,14 @@ pub enum Expr {
}, },
} }
impl Expr {
pub fn range(&self) -> Range<usize> {
match self {
_ => todo!(),
}
}
}
#[derive(Debug)] #[derive(Debug)]
pub struct BinaryExpr { pub struct BinaryExpr {
pub lhs: Expr, pub lhs: Expr,
@@ -62,14 +76,23 @@ pub struct IndexingExpr {
#[rustfmt::skip] #[rustfmt::skip]
#[derive(derive_more::Debug)] #[derive(derive_more::Debug)]
pub enum BinaryOp { pub enum BinaryOp {
#[debug("{_0:?}")] Add(Substr), #[debug("{_0}")] Add(Substr),
#[debug("{_0:?}")] Sub(Substr), #[debug("{_0}")] Sub(Substr),
#[debug("{_0:?}")] Mul(Substr), #[debug("{_0}")] Mul(Substr),
#[debug("{_0:?}")] Div(Substr), #[debug("{_0}")] Div(Substr),
#[debug("{_0:?}")] Mod(Substr), #[debug("{_0}")] Mod(Substr),
#[debug("{_0:?}")] Dot(Substr), #[debug("{_0}")] Dot(Substr),
#[debug("{_0:?}")] Range(Substr), #[debug("{_0}")] Lt(Substr),
#[debug("{_0:?}")] Assign(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)] #[derive(Debug)]
@@ -107,7 +130,7 @@ impl NamePattern {
pub struct Function { pub struct Function {
pub text: Substr, pub text: Substr,
pub args: Vec<NameValuePair>, pub args: Vec<NameValuePair>,
pub ret: Expr, pub ret: Option<Expr>,
pub block: Option<Block>, pub block: Option<Block>,
} }
@@ -133,6 +156,12 @@ pub struct For {
pub block: Block, pub block: Block,
} }
#[derive(Debug)]
pub struct While {
pub value: Expr,
pub block: Block,
}
#[derive(Debug)] #[derive(Debug)]
pub struct CompilationUnit { pub struct CompilationUnit {
pub imports: Vec<Import>, 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::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)} block:block() { Expr::Block(block)}
for_loop:for_loop() { Expr::For(Box::new(for_loop))} 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))} func:func() { Expr::Func(Box::new(func))}
var_decl:var_decl() { Expr::VarDecl(Box::new(var_decl)) } var_decl:var_decl() { Expr::VarDecl(Box::new(var_decl)) }
const_decl:const_decl() { Expr::ConstDecl(Box::new(const_decl)) } const_decl:const_decl() { Expr::ConstDecl(Box::new(const_decl)) }
"(" __ tuple:(expr() **<2,> ("," __)) __ ")" { Expr::Tuple(tuple) } "(" __ tuple:(expr() **<2,> ("," __)) __ ")" { Expr::Tuple(tuple) }
"[" __ list:(expr() **<0,> ("," __)) __ "]" { Expr::List(list) } "[" __ list:(expr() **<0,> ("," __)) __ "]" { Expr::List(list) }
"(" __ v:expr() __ ")" { v } "(" __ 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:string() { Expr::String(v) }
v:number() { Expr::Number(v) } v:number() { Expr::Number(v) }
v:ident() { Expr::Ident(v) } v:ident() { Expr::Ident(v) }
@@ -101,7 +108,7 @@ peg::parser! {
= "{" __ exprs:(i:expr() statement_separator() {i})* __ "}" { Block(exprs) } = "{" __ exprs:(i:expr() statement_separator() {i})* __ "}" { Block(exprs) }
rule func() -> Function 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), } } { Function { args, ret, block, text: t.parent().substr(s..e), } }
rule name_value_pair() -> NameValuePair rule name_value_pair() -> NameValuePair
@@ -130,6 +137,10 @@ peg::parser! {
"for" _ names:name_pattern() _ "in" _ value:expr() _ block:block() "for" _ names:name_pattern() _ "in" _ value:expr() _ block:block()
{ For { names, value, block } } { For { names, value, block } }
rule while_loop() -> While =
"while" _ value:expr() _ block:block()
{ While { value, block } }
pub rule compilation_unit() -> CompilationUnit = pub rule compilation_unit() -> CompilationUnit =
__ imports:(i:import() statement_separator() {i})* __ imports:(i:import() statement_separator() {i})*
__ decls:(d:const_decl() statement_separator() {d})* __ decls:(d:const_decl() statement_separator() {d})*
-1
View File
@@ -1,4 +1,3 @@
[toolchain] [toolchain]
channel = "1.91.0"
components = ["clippy", "rustfmt"] components = ["clippy", "rustfmt"]
profile = "default" profile = "default"
+10 -16
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 { print_up_to :: fn(max: u32) {
a + b i := 0u32
while i < max {
print_u32(i)
i = i + 1u32
}
} }
Vector :: fn(T: type, count: usize) type { main :: fn() {
NAMES :: ["x", "y", "z", "w"] print_up_to(42u32)
ty := TypeBuilder.struct()
for i in 0..count {
ty.push(Field {
name: NAMES[i],
type: T,
flags: FieldFlags.Public
})
}
ty.build()
} }