From 26e7a4da4afca8763270fcc19529415fb3a8e69d Mon Sep 17 00:00:00 2001 From: shadow cat Date: Tue, 8 Apr 2025 20:00:16 -0400 Subject: [PATCH] structs r a lot more sane in code, can now actually assign & stuff --- README.md | 19 +++++ data/test.lang | 61 ++++++++++++++- src/compiler/arch/riscv/compile.rs | 33 ++++++--- src/compiler/elf.rs | 13 ++-- src/compiler/program.rs | 4 +- src/ir/id.rs | 13 +++- src/ir/lower/func.rs | 11 ++- src/ir/lower/program.rs | 115 +++++++++++++++++------------ src/ir/upper/def.rs | 39 +++++++++- src/ir/upper/func.rs | 12 +-- src/ir/upper/program.rs | 64 +++++++++++----- src/ir/upper/ty.rs | 6 +- src/ir/upper/validate.rs | 46 ++++-------- src/parser/v3/lower/block.rs | 13 +++- src/parser/v3/lower/def.rs | 19 ++--- src/parser/v3/lower/expr.rs | 34 ++++++--- src/parser/v3/lower/func.rs | 8 +- src/parser/v3/lower/struc.rs | 73 ++++++++++++------ src/parser/v3/nodes/expr.rs | 5 ++ src/parser/v3/nodes/statement.rs | 10 ++- src/parser/v3/token/keyword.rs | 4 +- 21 files changed, 405 insertions(+), 197 deletions(-) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..15dd0d5 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +# the + +my child (programming language) + +everything is subject to change rn, and this probably isn't up to date + +`cargo run -- data/test.lang` + +currently working!!: +- functions (arguments, returning) +- assembly blocks (input, output for expression) +- kind of structs (construction, field access, but not modifying lmao) + +todo: +- actually handle jumps & LIs that are too large +- iterators? +- borrow checking +- multiple var instances for struct fields / fix all that stuff +- basic optimization: use registers, remove temp var moves diff --git a/data/test.lang b/data/test.lang index fb0ce53..1960e98 100644 --- a/data/test.lang +++ b/data/test.lang @@ -1,6 +1,7 @@ struct Test { a: 64, b: 64, + c: 64, } fn start() { @@ -11,7 +12,7 @@ fn start() { thinger(); let x = 3; if not(not(lt(x, 5))) { - println("tada!"); + println("tada!"); }; println("before:"); x = 0; @@ -26,11 +27,13 @@ fn start() { print(tester()); let test = Test { a: 10, - b: 9, + b: 4, + c: 0, }; + structer(test); arger("a", "b", "c"); let z = sub(test.a, 10); - // exit(add(mul(sub(add(5, test.b), 1), 3), z)); + print_hex(add(mul(sub(add(10, test.b), 1), 3), z)); print("test: 0x"); print_hex(31); println(""); @@ -38,6 +41,17 @@ fn start() { } fn structer(test: Test) { + print("test {\n a: "); + print_dec(test.a); + print("\n b: "); + print_dec(test.b); + print("\n c: "); + print_dec(test.c); + println("\n}"); + print("update c: "); + test.c = add(test.a, test.b); + print_dec(test.c); + println(""); } fn thinger() { @@ -64,7 +78,7 @@ fn print(msg: slice<8>) { } fn print_hex(x: 64) { - let i = 32; + let i = 64; loop { i = sub(i, 4); let c = and(shr(x, i), 15); @@ -84,6 +98,45 @@ fn print_hex(x: 64) { } } +fn print_dec(x: 64) { + let i = 1; + loop { + if gt(i, x) { + if lt(i, 2) { + print("0"); + return; + }; + break; + }; + i = mul(i, 10); + }; + let found = 0; + loop { + i = div(i, 10); + let c = rem(div(x, i), 10); + if and(lt(c, 1), not(found)) { + continue; + }; + found = 1; + if gt(c, 9) { + c = add(c, 7); + }; + c = add(c, 48); + asm (a1 = &c) { + li a2, 1 + li a0, 1 + li a7, 64 + ecall + }; + if lt(i, 2) { + break; + }; + }; + if not(found) { + print("0"); + } +} + fn add(a: 64, b: 64) -> 64 { asm (t0 = a, t1 = b, out = t0) { add t0, t0, t1 diff --git a/src/compiler/arch/riscv/compile.rs b/src/compiler/arch/riscv/compile.rs index 33318ff..d14f19c 100644 --- a/src/compiler/arch/riscv/compile.rs +++ b/src/compiler/arch/riscv/compile.rs @@ -78,6 +78,10 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram
  • { stack_len += align(&f.ret_size); } v.push(LI::addi(sp, sp, -stack_len)); + for (id, var) in &f.subvar_map { + // TODO: ALIGN DOES NOT MAKE SENSE HERE!!! need to choose to decide in lower or asm + stack.insert(id, stack[&var.id] + align(&var.offset)); + } let has_stack = stack_len > 0; if has_stack { if let Some(stack_ra) = stack_ra { @@ -86,6 +90,15 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram
  • { } let mut locations = HashMap::new(); let mut irli = Vec::new(); + let mut ret = Vec::new(); + if has_stack { + if let Some(stack_ra) = stack_ra { + ret.push(LI::ld(ra, stack_ra, sp)); + } + ret.push(LI::addi(sp, sp, stack_len)); + } + ret.push(LI::Ret); + for i in &f.instructions { irli.push((v.len(), format!("{i:?}"))); match i { @@ -235,11 +248,14 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram
  • { } } IRI::Ret { src } => { - let Some(rva) = stack_rva else { - panic!("no return value address on stack!") - }; - v.push(LI::ld(t0, rva, sp)); - mov_mem(&mut v, sp, stack[src], t0, 0, t1, align(&f.ret_size) as u32); + if let Some(src) = src { + let Some(rva) = stack_rva else { + panic!("no return value address on stack!") + }; + v.push(LI::ld(t0, rva, sp)); + mov_mem(&mut v, sp, stack[src], t0, 0, t1, align(&f.ret_size) as u32); + } + v.extend(&ret); } IRI::Jump(location) => { v.push(LI::J(*location)); @@ -259,13 +275,6 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram
  • { } } dbg.push_fn(irli); - if has_stack { - if let Some(stack_ra) = stack_ra { - v.push(LI::ld(ra, stack_ra, sp)); - } - v.push(LI::addi(sp, sp, stack_len)); - } - v.push(LI::Ret); fns.push(UnlinkedFunction { instrs: v, sym: *sym, diff --git a/src/compiler/elf.rs b/src/compiler/elf.rs index 6f0ef1c..20ff70a 100644 --- a/src/compiler/elf.rs +++ b/src/compiler/elf.rs @@ -51,18 +51,19 @@ pub struct SectionHeader { } // this is currently specialized for riscv64; obviously add params later -pub fn create(program: Vec, start_offset: Addr) -> Vec { +pub fn create(program: &[u8], start_offset: Addr) -> Vec { let addr_start = 0x1000; let page_size = 0x1000; - let progam_size = std::mem::size_of_val(&program[..]) as u64; + // I don't know if I have to add addr_start here, idk how it maps the memory + let program_size = std::mem::size_of_val(program) as u64 + addr_start; let program_header = ProgramHeader { ty: 0x1, // LOAD flags: 0b101, // executable, readable offset: 0x0, vaddr: addr_start, paddr: addr_start, - filesz: progam_size, - memsz: progam_size, + filesz: program_size, + memsz: program_size, align: page_size, }; let header_len = (size_of::() + size_of::()) as u64; @@ -104,7 +105,7 @@ unsafe fn as_u8_slice(p: &T) -> &[u8] { } impl LinkedProgram { - pub fn to_elf(self) -> Vec { - create(self.code, self.start.expect("no start found")) + pub fn to_elf(&self) -> Vec { + create(&self.code, self.start.expect("no start found")) } } diff --git a/src/compiler/program.rs b/src/compiler/program.rs index 3af2f2b..dcc0236 100644 --- a/src/compiler/program.rs +++ b/src/compiler/program.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; use crate::{ ir::Symbol, - util::{Labelable, LabeledFmt, Labeler}, + util::{Labelable, LabeledFmt}, }; use super::debug::DebugInfo; @@ -26,7 +26,7 @@ pub struct UnlinkedFunction { pub locations: HashMap, } -impl UnlinkedProgram { +impl UnlinkedProgram { pub fn link(self) -> LinkedProgram { let mut data = Vec::new(); let mut sym_table = SymTable::new(self.sym_count); diff --git a/src/ir/id.rs b/src/ir/id.rs index ad8dcc2..0b06019 100644 --- a/src/ir/id.rs +++ b/src/ir/id.rs @@ -1,14 +1,17 @@ use std::fmt::Debug; #[derive(Clone, Copy, Eq, Hash, PartialEq)] -pub struct TypeID(pub usize); +pub struct StructID(pub usize); #[derive(Clone, Copy, Eq, Hash, PartialEq)] pub struct VarID(pub usize); #[derive(Clone, Copy, Eq, Hash, PartialEq)] pub struct FnID(pub usize); #[derive(Clone, Copy, Eq, Hash, PartialEq)] pub struct DataID(pub usize); +#[derive(Clone, Copy, Eq, Hash, PartialEq)] +pub struct FieldID(pub usize); +// I had an idea for why these were different... now I don't pub type Size = u32; pub type Len = u32; @@ -18,7 +21,7 @@ impl Debug for VarID { } } -impl Debug for TypeID { +impl Debug for StructID { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "ty{}", self.0) } @@ -35,3 +38,9 @@ impl Debug for DataID { write!(f, "data{}", self.0) } } + +impl Debug for FieldID { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "field{}", self.0) + } +} diff --git a/src/ir/lower/func.rs b/src/ir/lower/func.rs index a1d58fa..b1b6ab4 100644 --- a/src/ir/lower/func.rs +++ b/src/ir/lower/func.rs @@ -7,6 +7,7 @@ use std::collections::HashMap; pub struct IRLFunction { pub instructions: Vec, pub stack: HashMap, + pub subvar_map: HashMap, pub args: Vec<(VarID, Size)>, pub ret_size: Size, pub makes_call: bool, @@ -46,7 +47,7 @@ pub enum IRLInstruction { outputs: Vec<(Reg, VarID)>, }, Ret { - src: VarID, + src: Option, }, // TODO I feel like this should be turned into control flow instructions, maybe... // not sure but LLVM has them so might be right play; seems optimal for optimization @@ -58,3 +59,11 @@ pub enum IRLInstruction { Mark(Symbol), } +impl IRLInstruction { + pub fn is_ret(&self) -> bool { + match self { + Self::Ret { .. } => true, + _ => false, + } + } +} diff --git a/src/ir/lower/program.rs b/src/ir/lower/program.rs index 611e76d..eada48b 100644 --- a/src/ir/lower/program.rs +++ b/src/ir/lower/program.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use crate::ir::{AsmBlockArgType, IRUFunction, IRUInstrInst, Size, SymbolSpace}; +use crate::ir::{AsmBlockArgType, IRUFunction, IRUInstrInst, Size, SymbolSpace, VarOffset}; use super::{ IRLFunction, IRLInstruction, IRUInstruction, IRUProgram, Len, Symbol, SymbolSpaceBuilder, Type, @@ -31,6 +31,9 @@ impl IRLProgram { for i in &f.instructions { fbuilder.insert_instr(i); } + if fbuilder.instrs.last().is_none_or(|i| !i.is_ret()) { + fbuilder.instrs.push(IRLInstruction::Ret { src: None }); + } let res = fbuilder.finish(f); ssbuilder.write_fn(sym, res, Some(f.name.clone())); } @@ -48,8 +51,15 @@ pub struct IRLFunctionBuilder<'a> { builder: &'a mut SymbolSpaceBuilder, instrs: Vec, stack: HashMap, + subvar_map: HashMap, makes_call: bool, - outer: Option, + loopp: Option, +} + +#[derive(Clone, Copy)] +pub struct LoopCtx { + top: Symbol, + bot: Symbol, } impl<'a> IRLFunctionBuilder<'a> { @@ -57,27 +67,36 @@ impl<'a> IRLFunctionBuilder<'a> { Self { instrs: Vec::new(), stack: HashMap::new(), + subvar_map: HashMap::new(), makes_call: false, program, builder, - outer: None, + loopp: None, } } pub fn alloc_stack(&mut self, i: VarID) -> Option<()> { - let size = *self + if self.program.size_of_var(i).expect("unsized type") == 0 { + return None; + }; + self.map_subvar(i); + let var = self.program.var_offset(i).expect("var offset"); + *self .stack - .entry(i) - .or_insert(self.program.size_of_var(i).expect("unsized type")); - if size == 0 { - None - } else { - Some(()) + .entry(var.id) + .or_insert(self.program.size_of_var(var.id).expect("unsized type")); + Some(()) + } + pub fn map_subvar(&mut self, i: VarID) { + let off = self.program.var_offset(i).expect("var offset"); + if off.id != i { + self.subvar_map.insert(i, off); } } pub fn insert_instr(&mut self, i: &IRUInstrInst) -> Option> { match &i.i { IRUInstruction::Mv { dest, src } => { self.alloc_stack(dest.id)?; + self.map_subvar(src.id); self.instrs.push(IRLInstruction::Mv { dest: dest.id, dest_offset: 0, @@ -87,6 +106,7 @@ impl<'a> IRLFunctionBuilder<'a> { } IRUInstruction::Ref { dest, src } => { self.alloc_stack(dest.id)?; + self.map_subvar(src.id); self.instrs.push(IRLInstruction::Ref { dest: dest.id, src: src.id, @@ -151,21 +171,28 @@ impl<'a> IRLFunctionBuilder<'a> { } else { None }; - self.instrs.push(IRLInstruction::Call { + let call = IRLInstruction::Call { dest, f: sym, args: args .iter() - .map(|a| (a.id, self.program.size_of_var(a.id).expect("unsized type"))) + .map(|a| { + self.map_subvar(a.id); + (a.id, self.program.size_of_var(a.id).expect("unsized type")) + }) .collect(), - }); + }; + self.instrs.push(call); } IRUInstruction::AsmBlock { instructions, args } => { let mut inputs = Vec::new(); let mut outputs = Vec::new(); for a in args { match a.ty { - AsmBlockArgType::In => inputs.push((a.reg, a.var.id)), + AsmBlockArgType::In => { + self.map_subvar(a.var.id); + inputs.push((a.reg, a.var.id)) + } AsmBlockArgType::Out => { self.alloc_stack(a.var.id)?; outputs.push((a.reg, a.var.id)); @@ -178,50 +205,37 @@ impl<'a> IRLFunctionBuilder<'a> { outputs, }) } - IRUInstruction::Ret { src } => self.instrs.push(IRLInstruction::Ret { src: src.id }), + IRUInstruction::Ret { src } => { + self.map_subvar(src.id); + self.instrs.push(IRLInstruction::Ret { + src: if self.program.size_of_var(src.id).expect("unsized var") == 0 { + None + } else { + Some(src.id) + }, + }) + } IRUInstruction::Construct { dest, fields } => { self.alloc_stack(dest.id)?; let ty = &self.program.get_var(dest.id).ty; - let Type::Concrete(id) = ty else { + let &Type::Struct { id, ref args } = ty else { return Some(Some(format!( "Failed to contruct type {}", self.program.type_name(ty) ))); }; - let struc = self.program.get_struct(*id); - for (name, var) in fields { + for (&fid, var) in fields { + self.map_subvar(var.id); self.instrs.push(IRLInstruction::Mv { dest: dest.id, src: var.id, - dest_offset: struc.fields[name].offset, + dest_offset: self.program.field_offset(id, fid).expect("field offset"), src_offset: 0, }) } } - IRUInstruction::Access { dest, src, field } => { - self.alloc_stack(dest.id)?; - let ty = &self.program.get_var(src.id).ty; - let Type::Concrete(id) = ty else { - return Some(Some(format!( - "Failed to access field of struct {}", - self.program.type_name(ty) - ))); - }; - let struc = self.program.get_struct(*id); - let Some(field) = struc.fields.get(field) else { - return Some(Some(format!( - "No field {field} in struct {}", - self.program.type_name(ty) - ))); - }; - self.instrs.push(IRLInstruction::Mv { - dest: dest.id, - src: src.id, - src_offset: field.offset, - dest_offset: 0, - }) - } IRUInstruction::If { cond, body } => { + self.map_subvar(cond.id); let sym = self.builder.reserve(); self.instrs.push(IRLInstruction::Branch { to: *sym, @@ -235,19 +249,27 @@ impl<'a> IRLFunctionBuilder<'a> { IRUInstruction::Loop { body } => { let top = self.builder.reserve(); let bot = self.builder.reserve(); - let old = self.outer; - self.outer = Some(*bot); + let old = self.loopp; + self.loopp = Some(LoopCtx { + bot: *bot, + top: *top, + }); self.instrs.push(IRLInstruction::Mark(*top)); for i in body { self.insert_instr(i); } self.instrs.push(IRLInstruction::Jump(*top)); self.instrs.push(IRLInstruction::Mark(*bot)); - self.outer = old; + self.loopp = old; } IRUInstruction::Break => { self.instrs.push(IRLInstruction::Jump( - self.outer.expect("Tried to break outside of loop"), + self.loopp.expect("Tried to break outside of loop").bot, + )); + } + IRUInstruction::Continue => { + self.instrs.push(IRLInstruction::Jump( + self.loopp.expect("Tried to break outside of loop").top, )); } }; @@ -265,6 +287,7 @@ impl<'a> IRLFunctionBuilder<'a> { .collect(), ret_size: self.program.size_of_type(&f.ret).expect("unsized type"), stack: self.stack, + subvar_map: self.subvar_map, } } } diff --git a/src/ir/upper/def.rs b/src/ir/upper/def.rs index 9f450bc..c581b72 100644 --- a/src/ir/upper/def.rs +++ b/src/ir/upper/def.rs @@ -1,4 +1,7 @@ -use crate::{common::FileSpan, ir::{Len, Size}}; +use crate::{ + common::FileSpan, + ir::{FieldID, Len, StructID, VarID}, +}; use super::Type; use std::{collections::HashMap, fmt::Debug}; @@ -13,25 +16,41 @@ pub struct FnDef { #[derive(Clone)] pub struct StructField { + pub name: String, pub ty: Type, - pub offset: Len, } #[derive(Clone)] pub struct StructDef { pub name: String, - pub fields: HashMap, - pub size: Size, + pub fields: Vec, + pub field_map: HashMap, pub origin: Origin, } #[derive(Clone)] pub struct VarDef { pub name: String, + pub parent: Option, pub ty: Type, pub origin: Origin, } +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] +pub struct VarOffset { + pub id: VarID, + pub offset: Len, +} + +#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] +pub struct FieldRef { + pub var: VarID, + // this is technically redundant bc you can get it from the var... + // but it makes things a lot easier, and you'd have to recheck the fields anyways + pub struc: StructID, + pub field: FieldID, +} + #[derive(Clone)] pub struct DataDef { pub ty: Type, @@ -49,3 +68,15 @@ impl FnDef { } } } + +impl StructDef { + pub fn field(&self, id: FieldID) -> &StructField { + &self.fields[id.0] + } + pub fn get_field(&self, name: &str) -> Option<&StructField> { + self.field_map.get(name).map(|id| self.field(*id)) + } + pub fn iter_fields(&self) -> impl Iterator { + self.fields.iter().enumerate().map(|(i, f)| (FieldID(i), f)) + } +} diff --git a/src/ir/upper/func.rs b/src/ir/upper/func.rs index 4c99827..58eec34 100644 --- a/src/ir/upper/func.rs +++ b/src/ir/upper/func.rs @@ -3,7 +3,7 @@ use std::{collections::HashMap, fmt::Write}; use super::{ arch::riscv64::RV64Instruction, inst::VarInst, DataID, FnID, IRUInstrInst, Type, VarID, }; -use crate::{compiler::arch::riscv::Reg, util::Padder}; +use crate::{compiler::arch::riscv::Reg, ir::FieldID, util::Padder}; pub struct IRUFunction { pub name: String, @@ -33,11 +33,6 @@ pub enum IRUInstruction { dest: VarInst, src: FnID, }, - Access { - dest: VarInst, - src: VarInst, - field: String, - }, Call { dest: VarInst, f: VarInst, @@ -52,7 +47,7 @@ pub enum IRUInstruction { }, Construct { dest: VarInst, - fields: HashMap, + fields: HashMap, }, If { cond: VarInst, @@ -62,6 +57,7 @@ pub enum IRUInstruction { body: Vec, }, Break, + Continue, } #[derive(Debug)] @@ -95,7 +91,6 @@ impl std::fmt::Debug for IRUInstruction { } Self::Ret { src } => f.debug_struct("Ret").field("src", src).finish()?, Self::Construct { dest, fields } => write!(f, "{dest:?} <- {fields:?}")?, - Self::Access { dest, src, field } => write!(f, "{dest:?} <- {src:?}.{field}")?, Self::If { cond, body } => { write!(f, "if {cond:?}:")?; if !body.is_empty() { @@ -125,6 +120,7 @@ impl std::fmt::Debug for IRUInstruction { } } Self::Break => write!(f, "break")?, + Self::Continue => write!(f, "continue")?, } Ok(()) } diff --git a/src/ir/upper/program.rs b/src/ir/upper/program.rs index 8bff225..e3ac8f5 100644 --- a/src/ir/upper/program.rs +++ b/src/ir/upper/program.rs @@ -1,13 +1,11 @@ use std::{collections::HashMap, fmt::Debug}; -use crate::common::FileSpan; - use super::{inst::VarInst, *}; pub struct IRUProgram { pub fn_defs: Vec, pub var_defs: Vec, - pub type_defs: Vec, + pub struct_defs: Vec, pub data_defs: Vec, pub fns: Vec>, pub data: Vec>, @@ -21,7 +19,7 @@ impl IRUProgram { Self { fn_defs: Vec::new(), var_defs: Vec::new(), - type_defs: Vec::new(), + struct_defs: Vec::new(), data_defs: Vec::new(), data: Vec::new(), fn_map: HashMap::new(), @@ -57,8 +55,8 @@ impl IRUProgram { pub fn get_fn_var(&self, id: VarID) -> Option<&FnDef> { Some(&self.fn_defs[self.fn_map.get(&id)?.0]) } - pub fn get_struct(&self, id: TypeID) -> &StructDef { - &self.type_defs[id.0] + pub fn get_struct(&self, id: StructID) -> &StructDef { + &self.struct_defs[id.0] } pub fn alias_fn(&mut self, name: &str, id: FnID) { self.insert(name, Ident::Fn(id)); @@ -80,9 +78,11 @@ impl IRUProgram { pub fn size_of_type(&self, ty: &Type) -> Option { // TODO: target matters Some(match ty { - Type::Concrete(id) => self.type_defs[id.0].size, Type::Bits(b) => *b, - Type::Generic { base, args } => todo!(), + Type::Struct { id, args } => self.struct_defs[id.0] + .fields + .iter() + .try_fold(0, |sum, f| Some(sum + self.size_of_type(&f.ty)?))?, Type::Fn { args, ret } => todo!(), Type::Ref(_) => 64, Type::Array(ty, len) => self.size_of_type(ty)? * len, @@ -92,12 +92,21 @@ impl IRUProgram { Type::Unit => 0, }) } + pub fn struct_layout() {} pub fn size_of_var(&self, var: VarID) -> Option { self.size_of_type(&self.var_defs[var.0].ty) } + pub fn temp_subvar(&mut self, origin: Origin, ty: Type, parent: FieldRef) -> VarInst { + self.temp_var_inner(origin, ty, Some(parent)) + } pub fn temp_var(&mut self, origin: Origin, ty: Type) -> VarInst { + self.temp_var_inner(origin, ty, None) + } + + fn temp_var_inner(&mut self, origin: Origin, ty: Type, parent: Option) -> VarInst { let v = self.def_var(VarDef { name: format!("temp{}", self.temp), + parent, origin, ty, }); @@ -107,11 +116,13 @@ impl IRUProgram { span: origin, } } + pub fn def_fn(&mut self, def: FnDef) -> FnID { let i = self.fn_defs.len(); let id = FnID(i); let var_def = VarDef { name: def.name.clone(), + parent: None, origin: def.origin, ty: def.ty(), }; @@ -126,11 +137,11 @@ impl IRUProgram { id } - pub fn def_type(&mut self, def: StructDef) -> TypeID { - let i = self.type_defs.len(); - let id = TypeID(i); + pub fn def_struct(&mut self, def: StructDef) -> StructID { + let i = self.struct_defs.len(); + let id = StructID(i); self.insert(&def.name, Ident::Type(id)); - self.type_defs.push(def); + self.struct_defs.push(def); id } pub fn def_data(&mut self, def: DataDef, bytes: Vec) -> DataID { @@ -142,10 +153,7 @@ impl IRUProgram { pub fn type_name(&self, ty: &Type) -> String { let mut str = String::new(); match ty { - Type::Concrete(t) => { - str += &self.get_struct(*t).name; - } - Type::Generic { base, args } => { + Type::Struct { id: base, args } => { str += &self.get_struct(*base).name; if let Some(arg) = args.first() { str = str + "<" + &self.type_name(arg); @@ -201,13 +209,29 @@ impl IRUProgram { .enumerate() .flat_map(|(i, f)| Some((FnID(i), f.as_ref()?))) } + pub fn var_offset(&self, var: VarID) -> Option { + let mut current = VarOffset { id: var, offset: 0 }; + while let Some(parent) = self.var_defs[current.id.0].parent { + current.id = parent.var; + current.offset += self.field_offset(parent.struc, parent.field)?; + } + Some(current) + } + pub fn field_offset(&self, struct_id: StructID, field: FieldID) -> Option { + let struc = self.get_struct(struct_id); + let mut offset = 0; + for i in 0..field.0 { + offset += self.size_of_type(&struc.fields[i].ty)?; + } + Some(offset) + } } #[derive(Debug, Clone, Copy)] pub enum Ident { Var(VarID), Fn(FnID), - Type(TypeID), + Type(StructID), } #[derive(Debug, Clone, Copy)] @@ -215,7 +239,7 @@ pub struct Idents { pub latest: Ident, pub var: Option, pub func: Option, - pub ty: Option, + pub struc: Option, } impl Idents { @@ -224,7 +248,7 @@ impl Idents { latest, var: None, func: None, - ty: None, + struc: None, }; s.insert(latest); s @@ -238,7 +262,7 @@ impl Idents { Ident::Fn(f) => { self.func = Some(f); } - Ident::Type(t) => self.ty = Some(t), + Ident::Type(t) => self.struc = Some(t), } } } diff --git a/src/ir/upper/ty.rs b/src/ir/upper/ty.rs index 35837bb..0a949d0 100644 --- a/src/ir/upper/ty.rs +++ b/src/ir/upper/ty.rs @@ -1,10 +1,9 @@ -use super::{IRUProgram, Len, TypeID}; +use super::{IRUProgram, Len, StructID}; #[derive(Clone, PartialEq)] pub enum Type { - Concrete(TypeID), Bits(u32), - Generic { base: TypeID, args: Vec }, + Struct { id: StructID, args: Vec }, Fn { args: Vec, ret: Box }, Ref(Box), Slice(Box), @@ -26,6 +25,7 @@ impl Type { } } +// should impl instead pub fn resolve_types(ns: &IRUProgram) { for (i, f) in ns.iter_fns() { for inst in &f.instructions { diff --git a/src/ir/upper/validate.rs b/src/ir/upper/validate.rs index 72afa8a..0cd88b9 100644 --- a/src/ir/upper/validate.rs +++ b/src/ir/upper/validate.rs @@ -80,8 +80,8 @@ impl IRUProgram { } IRUInstruction::Construct { dest, fields } => { let dest_def = self.get_var(dest.id); - let tyid = match dest_def.ty { - Type::Concrete(id) => id, + let tyid = match &dest_def.ty { + Type::Struct { id, args } => *id, _ => { output.err(CompilerMsg { msg: "uhh type is not struct".to_string(), @@ -91,44 +91,17 @@ impl IRUProgram { } }; let def = self.get_struct(tyid); - for (name, field) in &def.fields { - if let Some(var) = fields.get(name) { + for (id, field) in def.iter_fields() { + if let Some(var) = fields.get(&id) { let ety = &self.get_var(var.id).ty; output.check_assign(self, &field.ty, ety, var.span); } else { output.err(CompilerMsg { - msg: format!("field '{name}' missing from struct"), + msg: format!("field '{}' missing from struct", field.name), spans: vec![dest.span], }); } } - for name in fields.keys() { - if !def.fields.contains_key(name) { - output.err(CompilerMsg { - msg: format!("field '{name}' not in struct"), - spans: vec![dest.span], - }); - } - } - } - IRUInstruction::Access { dest, src, field } => { - let dest_def = self.get_var(dest.id); - let src_def = self.get_var(src.id); - let tyid = match src_def.ty { - Type::Concrete(id) => id, - _ => { - output.err(CompilerMsg { - msg: "uhh type is not struct".to_string(), - spans: vec![dest.span], - }); - continue; - } - }; - let def = self.get_struct(tyid); - let field = def.fields.get(field).expect( - "already validated during parse lowering... probably shouldn't be?", - ); - output.check_assign(self, &field.ty, &dest_def.ty, i.span); } IRUInstruction::If { cond, body } => { let cond = self.get_var(cond.id); @@ -147,6 +120,15 @@ impl IRUProgram { } // TODO } + IRUInstruction::Continue => { + if !breakable { + output.err(CompilerMsg { + msg: "Can't continue here (outside of loop)".to_string(), + spans: vec![i.span], + }); + } + // TODO + } } } if needs_ret && no_ret && *ret != Type::Unit { diff --git a/src/parser/v3/lower/block.rs b/src/parser/v3/lower/block.rs index 6f652c7..b3f7701 100644 --- a/src/parser/v3/lower/block.rs +++ b/src/parser/v3/lower/block.rs @@ -1,4 +1,4 @@ -use crate::ir::{IRUInstruction, VarInst}; +use crate::ir::{IRUInstruction, Type, VarInst}; use super::{FnLowerCtx, FnLowerable, PBlock, PStatement}; @@ -9,7 +9,7 @@ impl FnLowerable for PBlock { for statement in &self.statements { statement.lower(ctx); } - let res = self.result.as_ref().map(|r| r.lower(ctx)).flatten(); + let res = self.result.as_ref().and_then(|r| r.lower(ctx)); ctx.program.pop(); res } @@ -28,8 +28,13 @@ impl FnLowerable for PStatement { None } super::PStatement::Return(e) => { - let src = e.lower(ctx)?; - ctx.push_at(IRUInstruction::Ret { src }, src.span); + if let Some(e) = e { + let src = e.lower(ctx)?; + ctx.push_at(IRUInstruction::Ret { src }, src.span); + } else { + let src = ctx.temp(Type::Unit); + ctx.push_at(IRUInstruction::Ret { src }, src.span); + } None } super::PStatement::Expr(e) => e.lower(ctx), diff --git a/src/parser/v3/lower/def.rs b/src/parser/v3/lower/def.rs index e3adc53..82e2c31 100644 --- a/src/parser/v3/lower/def.rs +++ b/src/parser/v3/lower/def.rs @@ -13,6 +13,7 @@ impl Node { Some(VarDef { name, ty, + parent: None, origin: self.span, }) } @@ -36,18 +37,14 @@ impl PType { let Some(name) = self.name.as_ref() else { return Type::Error; }; - match namespace.get(&name).and_then(|ids| ids.ty) { + match namespace.get(&name).and_then(|ids| ids.struc) { Some(id) => { - if self.args.is_empty() { - Type::Concrete(id) - } else { - let args = self - .args - .iter() - .map(|n| n.lower(namespace, output)) - .collect(); - Type::Generic { base: id, args } - } + let args = self + .args + .iter() + .map(|n| n.lower(namespace, output)) + .collect(); + Type::Struct { id, args } } None => { if let Ok(num) = name.parse::() { diff --git a/src/parser/v3/lower/expr.rs b/src/parser/v3/lower/expr.rs index a92ce20..69e52bc 100644 --- a/src/parser/v3/lower/expr.rs +++ b/src/parser/v3/lower/expr.rs @@ -1,6 +1,6 @@ use super::{func::FnLowerCtx, FnLowerable, PExpr, UnaryOp}; use crate::{ - ir::{DataDef, IRUInstruction, Type, VarInst}, + ir::{DataDef, FieldRef, IRUInstruction, Type, VarInst}, parser::PInfixOp, }; @@ -61,30 +61,36 @@ impl FnLowerable for PExpr { let res1 = e1.lower(ctx)?; if *op == PInfixOp::Access { let sty = &ctx.program.get_var(res1.id).ty; - let Type::Concrete(tid) = sty else { + let Type::Struct { + id: struct_id, + args, + } = sty + else { ctx.err(format!( "Type {:?} has no fields", ctx.program.type_name(sty) )); return None; }; - let struc = ctx.program.get_struct(*tid); + let struc = ctx.program.get_struct(*struct_id); let Some(box PExpr::Ident(ident)) = &e2.inner else { - ctx.err(format!("Field accesses must be identifiers",)); + ctx.err("Field accesses must be identifiers".to_string()); return None; }; let fname = &ident.as_ref()?.0; - let Some(field) = struc.fields.get(fname) else { + let Some(&field) = struc.field_map.get(fname) else { ctx.err(format!("Field '{fname}' not in struct")); return None; }; - let temp = ctx.temp(field.ty.clone()); - ctx.push(IRUInstruction::Access { - dest: temp, - src: res1, - field: fname.to_string(), - }); - temp + let fdef = struc.field(field); + ctx.temp_subvar( + fdef.ty.clone(), + FieldRef { + var: res1.id, + struc: *struct_id, + field, + }, + ) } else { let res2 = e2.lower(ctx)?; match op { @@ -186,6 +192,10 @@ impl FnLowerable for PExpr { ctx.push(IRUInstruction::Break); return None; } + PExpr::Continue => { + ctx.push(IRUInstruction::Continue); + return None; + } }) } } diff --git a/src/parser/v3/lower/func.rs b/src/parser/v3/lower/func.rs index 778a09e..5a2f354 100644 --- a/src/parser/v3/lower/func.rs +++ b/src/parser/v3/lower/func.rs @@ -1,8 +1,8 @@ use super::{CompilerMsg, CompilerOutput, FileSpan, FnLowerable, Node, PFunction}; use crate::{ ir::{ - FnDef, FnID, IRUFunction, IRUInstrInst, IRUInstruction, IRUProgram, Idents, Origin, Type, - VarDef, VarInst, + FnDef, FnID, IRUFunction, IRUInstrInst, IRUInstruction, IRUProgram, Idents, Type, VarDef, + VarInst, FieldRef, }, parser, }; @@ -32,6 +32,7 @@ impl PFunction { a.lower(map, output).unwrap_or(VarDef { name: "{error}".to_string(), origin: a.span, + parent: None, ty: Type::Error, }) }) @@ -117,6 +118,9 @@ impl FnLowerCtx<'_> { pub fn temp(&mut self, ty: Type) -> VarInst { self.program.temp_var(self.span, ty) } + pub fn temp_subvar(&mut self, ty: Type, parent: FieldRef) -> VarInst { + self.program.temp_subvar(self.span, ty, parent) + } pub fn push(&mut self, i: IRUInstruction) { self.instructions.push(IRUInstrInst { i, span: self.span }); } diff --git a/src/parser/v3/lower/struc.rs b/src/parser/v3/lower/struc.rs index 720de40..82b2751 100644 --- a/src/parser/v3/lower/struc.rs +++ b/src/parser/v3/lower/struc.rs @@ -1,8 +1,8 @@ use std::collections::HashMap; use crate::{ - common::{CompilerMsg, CompilerOutput, FileSpan}, - ir::{IRUInstruction, IRUProgram, Origin, StructDef, StructField, VarInst}, + common::{CompilerOutput, FileSpan}, + ir::{FieldID, IRUInstruction, IRUProgram, StructDef, StructField, Type, VarInst}, parser::{Node, PConstruct, PConstructFields, PStruct, PStructFields}, }; @@ -12,6 +12,18 @@ impl FnLowerable for PConstruct { type Output = VarInst; fn lower(&self, ctx: &mut FnLowerCtx) -> Option { let ty = self.name.lower(ctx.program, ctx.output); + let field_map = match ty { + Type::Struct { id, .. } => ctx.program.get_struct(id), + _ => { + ctx.err(format!( + "Type {} cannot be constructed", + ctx.program.type_name(&ty) + )); + return None; + } + } + .field_map + .clone(); let fields = match &self.fields { PConstructFields::Named(nodes) => nodes .iter() @@ -19,7 +31,15 @@ impl FnLowerable for PConstruct { let def = n.as_ref()?; let name = def.name.as_ref()?.to_string(); let expr = def.val.as_ref()?.lower(ctx)?; - Some((name, expr)) + let Some(&field) = field_map.get(&name) else { + ctx.err(format!( + "Struct {} has no field {}", + ctx.program.type_name(&ty), + name + )); + return None; + }; + Some((field, expr)) }) .collect(), PConstructFields::Tuple(nodes) => nodes @@ -27,10 +47,19 @@ impl FnLowerable for PConstruct { .enumerate() .flat_map(|(i, n)| { let expr = n.as_ref()?.lower(ctx)?; - Some((format!("{i}"), expr)) + let name = format!("{i}"); + let Some(&field) = field_map.get(&name) else { + ctx.err(format!( + "Struct {} has no field {}", + ctx.program.type_name(&ty), + name + )); + return None; + }; + Some((field, expr)) }) .collect(), - PConstructFields::None => HashMap::new(), + PConstructFields::None => Default::default(), }; let id = ctx.temp(ty); ctx.push(IRUInstruction::Construct { dest: id, fields }); @@ -45,7 +74,7 @@ impl PStruct { output: &mut CompilerOutput, span: FileSpan, ) -> Option<()> { - let mut offset = 0; + let mut field_map = HashMap::new(); let fields = match &self.fields { PStructFields::Named(nodes) => nodes .iter() @@ -54,16 +83,7 @@ impl PStruct { let name = def.name.as_ref()?.to_string(); let tynode = def.ty.as_ref()?; let ty = tynode.lower(p, output); - let size = p.size_of_type(&ty).unwrap_or_else(|| { - output.err(CompilerMsg { - msg: format!("Size of type '{}' unknown", p.type_name(&ty)), - spans: vec![tynode.span], - }); - 0 - }); - let res = Some((name, StructField { ty, offset })); - offset += size; - res + Some((name, ty)) }) .collect(), PStructFields::Tuple(nodes) => nodes @@ -71,18 +91,23 @@ impl PStruct { .enumerate() .flat_map(|(i, n)| { let ty = n.as_ref()?.lower(p, output, span); - let size = p.size_of_type(&ty)?; - let res = Some((format!("{i}"), StructField { ty, offset })); - offset += size; - res + Some((format!("{i}"), ty)) }) .collect(), - PStructFields::None => HashMap::new(), - }; - p.def_type(StructDef { + PStructFields::None => vec![], + } + .into_iter() + .enumerate() + .map(|(i, (name, ty))| { + let id = FieldID(i); + field_map.insert(name.clone(), id); + StructField { name, ty } + }) + .collect(); + p.def_struct(StructDef { name: self.name.as_ref()?.to_string(), origin: span, - size: offset, + field_map, fields, }); Some(()) diff --git a/src/parser/v3/nodes/expr.rs b/src/parser/v3/nodes/expr.rs index d71494e..4ab9ddf 100644 --- a/src/parser/v3/nodes/expr.rs +++ b/src/parser/v3/nodes/expr.rs @@ -22,6 +22,7 @@ pub enum PExpr { If(BoxNode, BoxNode), Loop(BoxNode), Break, + Continue, } impl Parsable for PExpr { @@ -57,6 +58,9 @@ impl Parsable for PExpr { } else if next.is_keyword(Keyword::Break) { ctx.next(); Self::Break + } else if next.is_keyword(Keyword::Continue) { + ctx.next(); + Self::Continue } else if next.is_keyword(Keyword::Asm) { ctx.next(); Self::AsmBlock(ctx.parse()?) @@ -165,6 +169,7 @@ impl Debug for PExpr { PExpr::If(cond, res) => write!(f, "if {cond:?} then {res:?}")?, PExpr::Loop(res) => write!(f, "loop -> {res:?}")?, PExpr::Break => write!(f, "break")?, + PExpr::Continue => write!(f, "continue")?, } Ok(()) } diff --git a/src/parser/v3/nodes/statement.rs b/src/parser/v3/nodes/statement.rs index 7d52269..8809a9b 100644 --- a/src/parser/v3/nodes/statement.rs +++ b/src/parser/v3/nodes/statement.rs @@ -1,8 +1,8 @@ -use super::{PExpr, Keyword, Node, Parsable, ParseResult, ParserCtx, Symbol, Token, PVarDef}; +use super::{Keyword, Node, PExpr, PVarDef, Parsable, ParseResult, ParserCtx, Symbol, Token}; pub enum PStatement { Let(Node, Node), - Return(Node), + Return(Option>), Expr(Node), } @@ -18,7 +18,11 @@ impl Parsable for PStatement { } Token::Keyword(Keyword::Return) => { ctx.next(); - ctx.parse().map(Self::Return) + if ctx.peek().is_some_and(|t| t.is_symbol(Symbol::Semicolon)) { + ParseResult::Ok(Self::Return(None)) + } else { + ctx.parse().map(|res| Self::Return(Some(res))) + } } _ => ctx.parse().map(Self::Expr), } diff --git a/src/parser/v3/token/keyword.rs b/src/parser/v3/token/keyword.rs index 261aa76..c4a8ecb 100644 --- a/src/parser/v3/token/keyword.rs +++ b/src/parser/v3/token/keyword.rs @@ -4,8 +4,9 @@ pub enum Keyword { Let, If, Return, - Break, Loop, + Break, + Continue, Struct, Trait, Impl, @@ -24,6 +25,7 @@ impl Keyword { "for" => Self::For, "return" => Self::Return, "break" => Self::Break, + "continue" => Self::Continue, "loop" => Self::Loop, "trait" => Self::Trait, "impl" => Self::Impl,