diff --git a/src/common/file.rs b/src/common/file.rs index 6402f4a..c517225 100644 --- a/src/common/file.rs +++ b/src/common/file.rs @@ -1,24 +1,45 @@ +use std::{collections::HashMap, path::PathBuf}; + +pub type FileID = usize; +pub type FileMap = HashMap; + +#[derive(Debug, Clone)] +pub struct SrcFile { + pub path: PathBuf, + pub text: String, +} + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub struct FilePos { + pub file: FileID, pub line: usize, pub col: usize, } #[derive(Debug, Clone, Copy)] pub struct FileSpan { + pub file: FileID, pub start: FilePos, pub end: FilePos, } impl FilePos { - pub fn start() -> Self { - Self { line: 0, col: 0 } + pub fn start(file: FileID) -> Self { + Self { + line: 0, + col: 0, + file, + } } } impl FilePos { pub fn to(self, end: FilePos) -> FileSpan { - FileSpan { start: self, end } + FileSpan { + start: self, + end, + file: self.file, + } } pub fn char_span(self) -> FileSpan { FileSpan::at(self) @@ -33,6 +54,7 @@ impl FileSpan { Self { start: pos, end: pos, + file: pos.file, } } pub fn write_for(&self, writer: &mut impl std::io::Write, file: &str) -> std::io::Result<()> { diff --git a/src/common/output.rs b/src/common/output.rs index 87e42ea..172ba14 100644 --- a/src/common/output.rs +++ b/src/common/output.rs @@ -1,4 +1,4 @@ -use super::{FilePos, FileSpan}; +use super::{FileMap, FilePos, FileSpan}; #[derive(Debug, Clone)] pub struct CompilerMsg { @@ -7,6 +7,7 @@ pub struct CompilerMsg { } pub struct CompilerOutput { + pub file_map: FileMap, pub errs: Vec, pub hints: Vec, } @@ -30,11 +31,18 @@ impl CompilerMsg { spans: vec![FileSpan::at(pos)], } } - pub fn write_for(&self, ty: &str, writer: &mut impl std::io::Write, file: &str) -> std::io::Result<()> { + pub fn write_to( + &self, + ty: &str, + writer: &mut impl std::io::Write, + map: &FileMap, + ) -> std::io::Result<()> { let after = if self.spans.is_empty() { "" } else { ":" }; writeln!(writer, "{}: {}{}", ty, self.msg, after)?; for span in &self.spans { - span.write_for(writer, file)?; + let file = map.get(&span.file).expect("unknown file id"); + writeln!(writer, "{:?}", &file.path)?; + span.write_for(writer, &file.text)?; } Ok(()) } @@ -45,6 +53,7 @@ impl CompilerOutput { Self { errs: Vec::new(), hints: Vec::new(), + file_map: FileMap::new(), } } pub fn err(&mut self, msg: CompilerMsg) { @@ -53,12 +62,12 @@ impl CompilerOutput { pub fn hint(&mut self, msg: CompilerMsg) { self.hints.push(msg); } - pub fn write_for(&self, out: &mut impl std::io::Write, file: &str) { + pub fn write_to(&self, out: &mut impl std::io::Write) { for err in &self.errs { - err.write_for("error", out, file).unwrap(); + err.write_to("error", out, &self.file_map).unwrap(); } for hint in &self.hints { - hint.write_for("hint", out, file).unwrap(); + hint.write_to("hint", out, &self.file_map).unwrap(); } } } diff --git a/src/ir/lower/program.rs b/src/ir/lower/program.rs index 22f7816..1dba694 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, Size, SymbolSpace, UFunc, UInstrInst, VarOffset}; +use crate::ir::{AsmBlockArgType, Size, StructTy, SymbolSpace, UFunc, UInstrInst, VarOffset}; use super::{ IRLFunction, LInstruction, Len, Symbol, SymbolSpaceBuilder, Type, UInstruction, UProgram, VarID, @@ -17,7 +17,7 @@ impl LProgram { pub fn create(p: &UProgram) -> Result { let start = p .names - .id::("crate") + .id::(&["crate".to_string()]) .ok_or("no start method found")?; let mut ssbuilder = SymbolSpaceBuilder::with_entries(&[start]); let entry = ssbuilder.func(&start); @@ -44,10 +44,20 @@ impl LProgram { pub struct StructInst { offsets: Vec, + types: Vec, order: HashMap, size: Size, } +impl StructInst { + pub fn offset(&self, name: &str) -> Option { + Some(self.offsets[*self.order.get(name)?]) + } + pub fn ty(&self, name: &str) -> Option<&Type> { + Some(&self.types[*self.order.get(name)?]) + } +} + pub struct LFunctionBuilder<'a> { data: LFunctionBuilderData<'a>, program: &'a UProgram, @@ -72,7 +82,7 @@ pub struct LFunctionBuilderData<'a> { instrs: Vec, stack: HashMap, subvar_map: HashMap, - struct_insts: HashMap, + struct_insts: HashMap, makes_call: bool, loopp: Option, } @@ -258,6 +268,10 @@ impl<'a> LFunctionBuilder<'a> { self.data.instrs.push(LInstruction::Ret { src }) } UInstruction::Construct { dest, fields } => { + let sty = &self.program.expect(dest.id).ty; + let Type::Struct(sty) = sty else { + panic!("bruh htis aint' a struct"); + }; self.alloc_stack(dest.id)?; for (field, var) in fields { self.map_subvar(var.id); @@ -266,7 +280,7 @@ impl<'a> LFunctionBuilder<'a> { src: var.id, dest_offset: self .data - .field_offset(self.program, dest.id, field) + .field_offset(self.program, sty, field) .expect("field offset"), src_offset: 0, }; @@ -344,22 +358,25 @@ impl<'a> LFunctionBuilder<'a> { impl LFunctionBuilderData<'_> { pub fn var_offset(&mut self, p: &UProgram, var: VarID) -> Option { let mut current = VarOffset { id: var, offset: 0 }; - while let Some(parent) = &p.get(current.id)?.parent { - current.id = parent.var; - current.offset += self.field_offset(p, parent.var, &parent.field)?; + while let Type::Member(parent) = &p.get(current.id)?.ty { + current.id = parent.parent; + // ... should it be set to 0 if not? what does that even mean?? + // "the field of this struct is a reference to another variable" ???? + if let Type::Struct(sty) = &p.get(parent.parent)?.ty { + current.offset += self.field_offset(p, sty, &parent.name)?; + } } Some(current) } pub fn addr_size(&self) -> Size { 64 } - pub fn struct_inst(&mut self, p: &UProgram, ty: &Type) -> Option<&StructInst> { + pub fn struct_inst(&mut self, p: &UProgram, ty: &StructTy) -> &StructInst { // normally I'd let Some(..) here and return, but polonius does not exist :grief: if self.struct_insts.get(ty).is_none() { - let Type::Struct { id, args } = ty else { - return None; - }; - let struc = p.get(*id)?; + let StructTy { id, args } = ty; + let struc = p.expect(*id); + let mut types = Vec::new(); let mut sizes = struc .fields .iter() @@ -374,6 +391,7 @@ impl LFunctionBuilderData<'_> { } else { &f.ty }; + types.push(ty.clone()); (n, self.size_of_type(p, ty).expect("unsized type")) }) .collect::>(); @@ -392,25 +410,24 @@ impl LFunctionBuilderData<'_> { StructInst { offsets, order, + types, size: offset, }, ); } - self.struct_insts.get(ty) + self.struct_insts.get(ty).unwrap() } - pub fn field_offset(&mut self, p: &UProgram, var: VarID, field: &str) -> Option { - let ty = &p.get(var)?.ty; - let inst = self.struct_inst(p, ty)?; - let i = *inst.order.get(field)?; - Some(*inst.offsets.get(i)?) + pub fn field_offset(&mut self, p: &UProgram, sty: &StructTy, field: &str) -> Option { + let inst = self.struct_inst(p, sty); + Some(inst.offset(field)?) } pub fn size_of_type(&mut self, p: &UProgram, ty: &Type) -> Option { // TODO: target matters Some(match ty { Type::Bits(b) => *b, - Type::Struct { id, args } => self.struct_inst(p, ty)?.size, + Type::Struct(ty) => self.struct_inst(p, ty).size, Type::Generic { id } => return None, Type::Fn { args, ret } => todo!(), Type::Ref(_) => self.addr_size(), @@ -420,6 +437,18 @@ impl LFunctionBuilderData<'_> { Type::Error => return None, Type::Unit => 0, Type::Placeholder => return None, + Type::Module(_) => return None, + Type::Member(m) => { + let pty = &p.expect(m.parent).ty; + let ty = match pty { + Type::Struct(ty) => self.struct_inst(p, ty).ty(&m.name)?, + Type::Module(path) => p.path_ty(path)?, + Type::Member(_) => return self.size_of_type(p, pty), + _ => return None, + } + .clone(); + self.size_of_type(p, &ty)? + } }) } diff --git a/src/ir/upper/assoc.rs b/src/ir/upper/assoc.rs index 8ebdfb4..b50e02c 100644 --- a/src/ir/upper/assoc.rs +++ b/src/ir/upper/assoc.rs @@ -20,16 +20,56 @@ impl OriginMap { } } +pub type NamePath = Vec; + +pub struct NameTree { + ids: [HashMap; NAMED_KINDS], + children: HashMap, +} + +impl NameTree { + pub fn new() -> Self { + Self { + ids: core::array::from_fn(|_| HashMap::new()), + children: HashMap::new(), + } + } + pub fn get(&self, path: &[String]) -> Option<&NameTree> { + let first = path.first()?; + self.children.get(first)?.get(&path[1..]) + } + pub fn id(&self, path: &[String]) -> Option> { + let last = path.last()?; + self.get(&path[..path.len() - 1])?.ids[K::INDEX] + .get(last) + .copied() + .map(ID::new) + } + pub fn insert(&mut self, path: &[String], id: usize) { + if let [key] = &path[..] { + self.ids[K::INDEX].insert(key.to_string(), id); + return; + } + let Some(key) = path.first() else { + return; + }; + self.children + .entry(key.to_string()) + .or_insert_with(|| NameTree::new()) + .insert::(&path[1..], id); + } +} + pub struct NameMap { names: [Vec; NAMED_KINDS], - inv_names: [HashMap; NAMED_KINDS], + tree: NameTree, } impl NameMap { pub fn new() -> Self { Self { names: core::array::from_fn(|_| Vec::new()), - inv_names: core::array::from_fn(|_| HashMap::new()), + tree: NameTree::new(), } } pub fn path(&self, id: ID) -> &str { @@ -42,11 +82,13 @@ impl NameMap { } path } - pub fn id(&self, name: &str) -> Option> { - Some(ID::new(*self.inv_names[K::INDEX].get(name)?)) + pub fn id(&self, path: &[String]) -> Option> { + Some(self.tree.id(path)?) } - pub fn push(&mut self, name: String) { - self.inv_names[K::INDEX].insert(name.clone(), self.names[K::INDEX].len()); + pub fn push(&mut self, path: &[String]) { + let id = self.names[K::INDEX].len(); + self.tree.insert::(path, id); + let name = path.join("::"); self.names[K::INDEX].push(name); } } diff --git a/src/ir/upper/kind.rs b/src/ir/upper/kind.rs index 9abf9d8..2d8494b 100644 --- a/src/ir/upper/kind.rs +++ b/src/ir/upper/kind.rs @@ -27,7 +27,6 @@ pub struct UGeneric {} #[derive(Clone)] pub struct UVar { - pub parent: Option, pub ty: Type, } @@ -37,12 +36,6 @@ pub struct VarOffset { pub offset: Len, } -#[derive(Debug, Clone, Hash, Eq, PartialEq)] -pub struct FieldRef { - pub var: VarID, - pub field: String, -} - #[derive(Clone)] pub struct UData { pub ty: Type, @@ -124,7 +117,7 @@ impl_kind!(UVar, 1, vars, "var"); impl_kind!(UStruct, 2, structs, "struct"); impl_kind!(UGeneric, 3, types, "type"); impl_kind!(UData, 4, data, "data"); -pub const NAMED_KINDS: usize = 5; +pub const NAMED_KINDS: usize = 6; pub type FnID = ID; pub type VarID = ID; @@ -137,7 +130,6 @@ impl Finish for UFunc { let var = p.def_searchable( name.to_string(), Some(UVar { - parent: None, ty: Type::Placeholder, }), p.origins.get(id), diff --git a/src/ir/upper/program.rs b/src/ir/upper/program.rs index a0b420b..298dca2 100644 --- a/src/ir/upper/program.rs +++ b/src/ir/upper/program.rs @@ -71,19 +71,13 @@ impl UProgram { pub fn get_fn_var(&self, id: VarID) -> Option<&UFunc> { self.fns[self.fn_var.fun(id)?.0].as_ref() } - 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) + self.temp_var_inner(origin, ty) } - fn temp_var_inner(&mut self, origin: Origin, ty: Type, parent: Option) -> VarInst { - let v = self.def( - &format!("temp{}", self.temp), - Some(UVar { parent, ty }), - origin, - ); + fn temp_var_inner(&mut self, origin: Origin, ty: Type) -> VarInst { + let v = self.def(&format!("temp{}", self.temp), Some(UVar { ty }), origin); self.temp += 1; VarInst { id: v, @@ -96,7 +90,7 @@ impl UProgram { } pub fn def(&mut self, name: &str, k: Option, origin: Origin) -> ID { - self.names.push::(self.path_for(name)); + self.names.push::(&self.path_for(name)); self.origins.push::(origin); let vec = K::from_program_mut(self); let id = ID::new(vec.len()); @@ -105,12 +99,12 @@ impl UProgram { id } - pub fn path_for(&self, name: &str) -> String { + pub fn path_for(&self, name: &str) -> Vec { if self.path.is_empty() { - return name.to_string(); + return vec![name.to_string()]; } - let mut path = self.path.join("::"); - path = path + "::" + name; + let mut path = self.path.clone(); + path.push(name.to_string()); path } @@ -125,27 +119,46 @@ impl UProgram { id } + pub fn ref_ty<'a>(&'a self, mem: &MemberRef) -> Option<&'a Type> { + self.follow_ref(mem).and_then(|r| Some(&self.get(r)?.ty)) + } + + pub fn follow_ref<'a>(&'a self, mem: &MemberRef) -> Option { + let parent = self.get(mem.parent)?; + if let Type::Member(mem) = &parent.ty { + self.follow_ref(mem) + } else { + Some(mem.parent) + } + } + pub fn field_type<'a>(&'a self, sty: &'a Type, field: &str) -> Option<&'a Type> { - let Type::Struct { id, args } = sty else { - return None; - }; - let struc = self.get(*id)?; - let field = struc.fields.get(field)?; - if let Type::Generic { id } = field.ty { - for (i, g) in struc.generics.iter().enumerate() { - if *g == id { - return Some(&args[i]); + if let Type::Struct(st) = sty { + let struc = self.get(st.id)?; + let field = struc.fields.get(field)?; + if let Type::Generic { id } = field.ty { + for (i, g) in struc.generics.iter().enumerate() { + if *g == id { + return Some(&st.args[i]); + } } } + Some(&field.ty) + } else if let Type::Module(path) = sty { + let id = self.names.id::(path)?; + Some(&self.get(id)?.ty) + } else { + None } - Some(&field.ty) } pub fn type_name(&self, ty: &Type) -> String { let mut str = String::new(); match ty { - Type::Struct { id: base, args } => { - str += self.names.name(*base); + Type::Struct(ty) => { + let base = ty.id; + let args = &ty.args; + str += self.names.name(base); if let Some(arg) = args.first() { str = str + "<" + &self.type_name(arg); } @@ -178,9 +191,22 @@ impl UProgram { Type::Unit => str += "()", Type::Slice(t) => str += &format!("&[{}]", self.type_name(t)), Type::Placeholder => str += "{placeholder}", + Type::Module(path) => str += &path.join("::"), + Type::Member(m) => { + str += &self + .ref_ty(m) + .map(|t| self.type_name(t)) + .unwrap_or("{error}".to_string()) + } } str } + pub fn path_var(&self, path: &NamePath) -> Option { + self.names.id(path) + } + pub fn path_ty(&self, path: &NamePath) -> Option<&Type> { + Some(&self.get(self.path_var(path)?)?.ty) + } fn name_on_stack(&mut self, id: ID, name: String) { let idx = self.name_stack.len() - 1; let last = &mut self.name_stack[idx]; diff --git a/src/ir/upper/ty.rs b/src/ir/upper/ty.rs index 25a020d..f0016f8 100644 --- a/src/ir/upper/ty.rs +++ b/src/ir/upper/ty.rs @@ -1,16 +1,30 @@ use std::collections::HashMap; -use super::{GenericID, Len, StructID, UInstruction, UProgram, UVar, VarID}; +use super::{assoc::NamePath, GenericID, Len, StructID, UInstruction, UProgram, UVar, VarID}; + +#[derive(Debug, Clone, Hash, Eq, PartialEq)] +pub struct MemberRef { + pub parent: VarID, + pub name: String, +} + +#[derive(Clone, Hash, Eq, PartialEq)] +pub struct StructTy { + pub id: StructID, + pub args: Vec, +} #[derive(Clone, PartialEq, Eq, Hash)] pub enum Type { Bits(u32), - Struct { id: StructID, args: Vec }, + Struct(StructTy), Generic { id: GenericID }, Fn { args: Vec, ret: Box }, Ref(Box), Slice(Box), Array(Box, Len), + Member(MemberRef), + Module(NamePath), Infer, Error, Placeholder, @@ -43,37 +57,16 @@ impl UProgram { } for (i, f) in self.iter_fns() { let mut redo_iter = Vec::new(); - let mut ph_vars = Vec::new(); let mut redo_new = Vec::new(); for i in f.flat_iter() { - if let Err(id) = self.resolve_instr_types(&mut vars, &i.i) { + if self.resolve_instr_types(&mut vars, &i.i).is_none() { redo_iter.push(i); - ph_vars.push(id); } } while !redo_iter.is_empty() { - let mut new_ph = Vec::new(); - for id in &ph_vars { - let i = id.0; - let Some(var) = &vars[i] else { - continue; - }; - if var.ty == Type::Placeholder - && let Some(parent) = var.parent.as_ref() - { - let pty = &vars[parent.var.0].as_ref().unwrap().ty; - if let Some(ft) = self.field_type(pty, &parent.field) { - vars[i].as_mut().unwrap().ty = ft.clone(); - } else { - new_ph.push(parent.var); - } - } - } - ph_vars = new_ph; for &i in &redo_iter { - if let Err(id) = self.resolve_instr_types(&mut vars, &i.i) { + if self.resolve_instr_types(&mut vars, &i.i).is_none() { redo_new.push(i); - ph_vars.push(id); } } std::mem::swap(&mut redo_iter, &mut redo_new); @@ -83,11 +76,7 @@ impl UProgram { self.vars = vars; } - pub fn resolve_instr_types( - &self, - vars: &mut [Option], - i: &UInstruction, - ) -> Result<(), VarID> { + pub fn resolve_instr_types(&self, vars: &mut [Option], i: &UInstruction) -> Option<()> { 'outer: { match &i { UInstruction::Call { dest, f, args } => { @@ -96,7 +85,7 @@ impl UProgram { for (src, &dest) in args.iter().zip(&fun.args) { let dest_ty = get(vars, dest)?; let src_ty = get(vars, src.id)?; - if let Some(ty) = match_types(dest_ty, src_ty) { + if let Some(ty) = self.match_types(dest_ty, src_ty) { set(vars, dest, ty.clone()); set(vars, src.id, ty); } @@ -105,7 +94,7 @@ impl UProgram { UInstruction::Mv { dest, src } => { let dest_ty = get(vars, dest.id)?; let src_ty = get(vars, src.id)?; - if let Some(ty) = match_types(dest_ty, src_ty) { + if let Some(ty) = self.match_types(dest_ty, src_ty) { set(vars, dest.id, ty.clone()); set(vars, src.id, ty); } @@ -116,7 +105,7 @@ impl UProgram { let Type::Ref(dest_ty) = dest_ty else { break 'outer; }; - if let Some(ty) = match_types(dest_ty, src_ty) { + if let Some(ty) = self.match_types(dest_ty, src_ty) { set(vars, dest.id, ty.clone().rf()); set(vars, src.id, ty); } @@ -136,10 +125,10 @@ impl UProgram { UInstruction::Ret { .. } => {} UInstruction::Construct { dest, fields } => { let dest_ty = get(vars, dest.id)?; - let Type::Struct { id, args } = dest_ty else { + let Type::Struct(sty) = dest_ty else { break 'outer; }; - let id = *id; + let id = sty.id; let Some(struc) = self.get(id) else { break 'outer; }; @@ -149,7 +138,7 @@ impl UProgram { continue; }; let src_ty = get(vars, src.id)?; - if let Some(ty) = match_types(&field.ty, src_ty) { + if let Some(ty) = self.match_types(&field.ty, src_ty) { if let Type::Generic { id } = field.ty { new.insert(id, ty.clone()); } @@ -166,7 +155,7 @@ impl UProgram { args[i] = ty; } } - set(vars, dest.id, Type::Struct { id, args }); + set(vars, dest.id, Type::Struct(StructTy { id, args })); } UInstruction::If { cond, body: _ } => {} UInstruction::Loop { body: _ } => {} @@ -174,18 +163,88 @@ impl UProgram { UInstruction::Continue => {} } } - Ok(()) + Some(()) + } + + pub fn match_types(&self, dest: &Type, src: &Type) -> Option { + if dest == src { + return None; + } + match (dest, src) { + (Type::Error, _) | (_, Type::Error) => None, + (Type::Placeholder, _) | (_, Type::Placeholder) => None, + (Type::Infer, x) | (x, Type::Infer) => Some(x.clone()), + // TODO: handle constraints? + (Type::Generic { id }, x) | (x, Type::Generic { id }) => Some(x.clone()), + (Type::Struct(dest), Type::Struct(src)) => { + if dest.id != src.id { + return None; + } + let mut args = Vec::new(); + let mut changed = false; + for (darg, sarg) in dest.args.iter().zip(&src.args) { + if let Some(ty) = self.match_types(darg, sarg) { + args.push(ty); + changed = true; + } else if darg != sarg { + return None; + } else { + args.push(darg.clone()); + } + } + if changed { + Some(Type::Struct(StructTy { id: dest.id, args })) + } else { + None + } + } + ( + Type::Fn { + args: dest_args, + ret: dest_ret, + }, + Type::Fn { + args: src_args, + ret: src_ret, + }, + ) => { + // TODO + None + } + (Type::Ref(dest), Type::Ref(src)) => Some(self.match_types(dest, src)?.rf()), + (Type::Slice(dest), Type::Slice(src)) => Some(self.match_types(dest, src)?.slice()), + (Type::Array(dest, dlen), Type::Array(src, slen)) => { + if dlen != slen { + return None; + } + Some(self.match_types(dest, src)?.arr(*dlen)) + } + _ => None, + } } } -pub fn get(vars: &[Option], id: VarID) -> Result<&Type, VarID> { - let var = vars[id.0] +pub fn get(vars: &[Option], id: VarID) -> Option<&Type> { + let mut var = vars[id.0] .as_ref() .expect("PARTIAL BORROWING WOULD BE REALLY COOL"); if var.ty == Type::Placeholder { - return Err(id); + return None; } - Ok(&var.ty) + while let Type::Member(m) = &var.ty { + var = vars[m.parent.0].as_ref().expect("xd"); + } + // x.y().z == a.b.c() + // 0 ------- member(1, z) + // 1 ----- call(2) + // 2 --- member(3, y) + // 3 - x + // + // 0 ------- call(1) + // 1 ----- member(c, 2) + // 2 --- member(b, 3) + // 3 - a + Some(&var.ty) } pub fn set(vars: &mut [Option], id: VarID, ty: Type) { @@ -194,69 +253,3 @@ pub fn set(vars: &mut [Option], id: VarID, ty: Type) { .expect("PARTIAL BORROWING WOULD BE REALLY COOL") .ty = ty; } - -pub fn match_types(dest: &Type, src: &Type) -> Option { - if dest == src { - return None; - } - match (dest, src) { - (Type::Error, _) | (_, Type::Error) => None, - (Type::Placeholder, _) | (_, Type::Placeholder) => None, - (Type::Infer, x) | (x, Type::Infer) => Some(x.clone()), - // TODO: handle constraints? - (Type::Generic { id }, x) | (x, Type::Generic { id }) => Some(x.clone()), - ( - Type::Struct { - id: dest_id, - args: dest_args, - }, - Type::Struct { - id: src_id, - args: src_args, - }, - ) => { - if dest_id != src_id { - return None; - } - let mut args = Vec::new(); - let mut changed = false; - for (darg, sarg) in dest_args.iter().zip(src_args) { - if let Some(ty) = match_types(darg, sarg) { - args.push(ty); - changed = true; - } else if darg != sarg { - return None; - } else { - args.push(darg.clone()); - } - } - if changed { - Some(Type::Struct { id: *dest_id, args }) - } else { - None - } - } - ( - Type::Fn { - args: dest_args, - ret: dest_ret, - }, - Type::Fn { - args: src_args, - ret: src_ret, - }, - ) => { - // TODO - None - } - (Type::Ref(dest), Type::Ref(src)) => Some(match_types(dest, src)?.rf()), - (Type::Slice(dest), Type::Slice(src)) => Some(match_types(dest, src)?.slice()), - (Type::Array(dest, dlen), Type::Array(src, slen)) => { - if dlen != slen { - return None; - } - Some(match_types(dest, src)?.arr(*dlen)) - } - _ => None, - } -} diff --git a/src/ir/upper/validate.rs b/src/ir/upper/validate.rs index 20e19f1..80e4017 100644 --- a/src/ir/upper/validate.rs +++ b/src/ir/upper/validate.rs @@ -3,14 +3,13 @@ use super::{Type, UInstrInst, UInstruction, UProgram}; use crate::common::{CompilerMsg, CompilerOutput, FileSpan}; impl UProgram { - pub fn validate(&self) -> CompilerOutput { - let mut output = CompilerOutput::new(); + pub fn validate(&self, output: &mut CompilerOutput) { for (id, f) in self.iter_fns() { self.validate_fn( &f.instructions, self.origins.get(id), &f.ret, - &mut output, + output, true, false, ); @@ -46,7 +45,6 @@ impl UProgram { } } } - output } pub fn validate_fn( diff --git a/src/main.rs b/src/main.rs index fdcf584..903b524 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,13 +7,16 @@ pub const FILE_EXT: &str = "lang"; +use common::{CompilerOutput, SrcFile}; use ir::{LProgram, UProgram}; -use parser::{PModule, ParseResult, ParserCtx}; +use parser::{Import, Imports, PModule, ParseResult, ParserCtx}; use std::{ + collections::{HashMap, HashSet}, + ffi::OsString, fs::{create_dir_all, OpenOptions}, io::{stdout, BufRead, BufReader}, os::unix::fs::OpenOptionsExt, - path::Path, + path::{Path, PathBuf}, process::Command, }; @@ -29,52 +32,84 @@ fn main() { let gdb = std::env::args().nth(2).is_some_and(|a| a == "--debug"); let asm = std::env::args().nth(2).is_some_and(|a| a == "--asm"); if let Some(path) = file { - let file = std::fs::read_to_string(path).expect("failed to read file"); - run_file(&file, gdb, asm); + let path = PathBuf::from(path); + run_file(&path, gdb, asm); } else { run_stdin(); } } -fn run_file(file: &str, gdb: bool, asm: bool) { - let mut ctx = ParserCtx::from(file); - let res = PModule::parse(&mut ctx); - let mut output = ctx.output; - 'outer: { - if !output.errs.is_empty() { - break 'outer; - } - // println!("Parsed:"); - // println!("{:#?}", res.node); - let mut program = UProgram::new(); - res.lower("crate".to_string(), &mut program, &mut output); - if !output.errs.is_empty() { - break 'outer; - } - program.resolve_types(); - // println!("vars:"); - // for (id, def) in program.iter_vars() { - // println!(" {id:?} = {}: {}", program.names.path(id), program.type_name(&def.ty)); - // } - // for (id, f) in program.iter_fns() { - // println!("{}:{id:?} = {:#?}", program.names.path(id), f); - // } - output = program.validate(); - if !output.errs.is_empty() { - break 'outer; - } - output.write_for(&mut stdout(), file); - let program = LProgram::create(&program).expect("morir"); - let unlinked = compiler::compile(&program); - if asm { - println!("{:?}", unlinked); - } else { - let bin = unlinked.link().to_elf(); - println!("compiled"); - save_run(&bin, gdb); +impl UProgram { + pub fn from_path(path: &Path) -> (Self, CompilerOutput) { + let parent = path.parent().expect("bruh"); + let mut program = Self::new(); + let mut output = CompilerOutput::new(); + + let mut imports = Imports::new(); + imports.insert(Import( + path.file_name() + .expect("bruh") + .to_str() + .expect("bruh") + .to_string(), + )); + let mut imported = HashSet::new(); + let mut fid = 0; + + while !imports.is_empty() { + let iter = std::mem::take(&mut imports); + for i in iter { + let name = &i.0; + if imported.contains(&i) { + continue; + } + let path = parent.join(name).with_extension(FILE_EXT); + let text = std::fs::read_to_string(&path).expect("failed to read file"); + output.file_map.insert( + fid, + SrcFile { + path, + text: text.clone(), + }, + ); + let mut ctx = ParserCtx::new(fid, text.as_str(), &mut output); + fid += 1; + let res = PModule::parse(&mut ctx); + // println!("Parsed:"); + // println!("{:#?}", res.node); + res.lower(name.clone(), &mut program, &mut imports, &mut output); + imported.insert(i); + } } + (program, output) } - output.write_for(&mut stdout(), file); +} + +fn run_file(path: &Path, gdb: bool, asm: bool) { + let (mut program, mut output) = UProgram::from_path(path); + program.resolve_types(); + program.validate(&mut output); + // println!("vars:"); + // for (id, def) in program.iter_vars() { + // println!(" {id:?} = {}: {}", program.names.path(id), program.type_name(&def.ty)); + // } + // for (id, f) in program.iter_fns() { + // println!("{}:{id:?} = {:#?}", program.names.path(id), f); + // } + if !output.errs.is_empty() { + output.write_to(&mut stdout()); + return; + } + let program = LProgram::create(&program).expect("morir"); + let unlinked = compiler::compile(&program); + if asm { + println!("{:?}", unlinked); + } else { + let bin = unlinked.link().to_elf(); + println!("compiled"); + save_run(&bin, gdb); + } + output.write_to(&mut stdout()); } fn save_run(binary: &[u8], run_gdb: bool) { diff --git a/src/parser/v3/ctx.rs b/src/parser/v3/ctx.rs index 0b31515..c487929 100644 --- a/src/parser/v3/ctx.rs +++ b/src/parser/v3/ctx.rs @@ -1,13 +1,15 @@ use std::ops::{Deref, DerefMut}; +use crate::common::FileID; + use super::{ - MaybeParsable, Node, NodeParseResult, Parsable, ParsableWith, CompilerMsg, CompilerOutput, + CompilerMsg, CompilerOutput, MaybeParsable, Node, NodeParseResult, Parsable, ParsableWith, TokenCursor, }; pub struct ParserCtx<'a> { pub cursor: TokenCursor<'a>, - pub output: CompilerOutput, + pub output: &'a mut CompilerOutput, } impl<'a> Deref for ParserCtx<'a> { @@ -40,22 +42,10 @@ impl<'a> ParserCtx<'a> { pub fn maybe_parse(&mut self) -> Option> { Node::maybe_parse(self) } -} - -impl<'a> From> for ParserCtx<'a> { - fn from(cursor: TokenCursor<'a>) -> Self { + pub fn new(file: FileID, string: &'a str, output: &'a mut CompilerOutput) -> Self { Self { - cursor, - output: CompilerOutput::new(), - } - } -} - -impl<'a> From<&'a str> for ParserCtx<'a> { - fn from(string: &'a str) -> Self { - Self { - cursor: TokenCursor::from(string), - output: CompilerOutput::new(), + cursor: TokenCursor::from_file_str(file, string), + output, } } } diff --git a/src/parser/v3/cursor.rs b/src/parser/v3/cursor.rs index 3cabf9e..d9f30d4 100644 --- a/src/parser/v3/cursor.rs +++ b/src/parser/v3/cursor.rs @@ -1,3 +1,5 @@ +use crate::common::FileID; + use super::{ token::{CharCursor, Keyword, Symbol, Token, TokenInstance}, CompilerMsg, FilePos, @@ -17,7 +19,7 @@ impl<'a> TokenCursor<'a> { self.next_start = next .as_ref() .map(|i| i.span.end) - .unwrap_or(FilePos::start()); + .unwrap_or(FilePos::start(self.file())); std::mem::replace(&mut self.next, next) } pub fn expect_next(&mut self) -> Result { @@ -78,11 +80,11 @@ impl<'a> TokenCursor<'a> { pub fn next_start(&self) -> FilePos { self.next_start } -} - -impl<'a> From<&'a str> for TokenCursor<'a> { - fn from(string: &'a str) -> Self { - Self::from(CharCursor::from(string)) + pub fn from_file_str(id: FileID, string: &'a str) -> Self { + Self::from(CharCursor::from_file_str(id, string)) + } + pub fn file(&self) -> FileID { + self.cursor.file() } } @@ -90,10 +92,10 @@ impl<'a> From> for TokenCursor<'a> { fn from(mut cursor: CharCursor<'a>) -> Self { let cur = TokenInstance::parse(&mut cursor); Self { + next_start: FilePos::start(cursor.file()), + prev_end: FilePos::start(cursor.file()), cursor, next: cur, - next_start: FilePos::start(), - prev_end: FilePos::start(), } } } diff --git a/src/parser/v3/import.rs b/src/parser/v3/import.rs new file mode 100644 index 0000000..8fbc8fa --- /dev/null +++ b/src/parser/v3/import.rs @@ -0,0 +1,5 @@ +use std::collections::HashSet; + +#[derive(Clone, Eq, PartialEq, Hash)] +pub struct Import(pub Vec); +pub type Imports = HashSet; diff --git a/src/parser/v3/lower/block.rs b/src/parser/v3/lower/block.rs index 916efd0..e33080e 100644 --- a/src/parser/v3/lower/block.rs +++ b/src/parser/v3/lower/block.rs @@ -1,14 +1,13 @@ use crate::{ - ir::{Type, UInstruction, VarInst}, + ir::{Type, UInstruction, UVar, VarInst}, parser::{PConstStatement, PStatementLike}, }; -use super::{import::Import, FnLowerCtx, FnLowerable, PBlock, PStatement}; +use super::{FnLowerCtx, FnLowerable, Import, PBlock, PStatement}; impl FnLowerable for PBlock { type Output = VarInst; fn lower(&self, ctx: &mut FnLowerCtx) -> Option { - ctx.program.push(); let mut last = None; let mut statements = Vec::new(); let mut fn_nodes = Vec::new(); @@ -29,12 +28,26 @@ impl FnLowerable for PBlock { } } // then lower imports - for i in &import_nodes { - if let Some(i) = i.as_ref() { - let import = Import(i.0.clone()); - ctx.imports.push(import); + for i_n in &import_nodes { + if let Some(i) = i_n.as_ref() { + let name = &i.0; + let import = Import(ctx.program.path_for(name)); + ctx + .imports + .entry(import) + .or_insert(ctx.program.def(name, None, i_n.origin)); + // I could prevent this if someone imports something twice, + // but that doesn't seem worth it at all + ctx.program.def_searchable::( + name.clone(), + Some(UVar { + ty: Type::Module, + }), + i_n.origin, + ); } } + ctx.program.push(); // then lower const things let mut structs = Vec::new(); for s in &struct_nodes { diff --git a/src/parser/v3/lower/def.rs b/src/parser/v3/lower/def.rs index d2ab2ff..c508443 100644 --- a/src/parser/v3/lower/def.rs +++ b/src/parser/v3/lower/def.rs @@ -15,7 +15,7 @@ impl Node { None => Type::Infer, }; Some(VarInst { - id: program.def_searchable(name, Some(UVar { ty, parent: None }), self.origin), + id: program.def_searchable(name, Some(UVar { ty }), self.origin), span: self.origin, }) } diff --git a/src/parser/v3/lower/expr.rs b/src/parser/v3/lower/expr.rs index f8f75ba..7bebed5 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::{FieldRef, Type, UData, UInstruction, VarInst}, + ir::{MemberRef, Type, UData, UInstruction, VarInst}, parser::PInfixOp, }; @@ -69,13 +69,10 @@ impl FnLowerable for PExpr { return None; }; let fname = ident.as_ref()?.0.clone(); - ctx.temp_subvar( - Type::Placeholder, - FieldRef { - var: res1.id, - field: fname, - }, - ) + ctx.temp(Type::Member(MemberRef { + parent: res1.id, + name: fname, + })) } PInfixOp::Assign => { let res1 = e1.lower(ctx)?; diff --git a/src/parser/v3/lower/func.rs b/src/parser/v3/lower/func.rs index cbb4206..78611e6 100644 --- a/src/parser/v3/lower/func.rs +++ b/src/parser/v3/lower/func.rs @@ -1,6 +1,6 @@ -use super::{import::Import, CompilerMsg, CompilerOutput, FileSpan, FnLowerable, Node, PFunction}; +use super::{Imports, CompilerMsg, CompilerOutput, FileSpan, FnLowerable, Node, PFunction}; use crate::{ - ir::{FieldRef, FnID, Idents, Type, UFunc, UInstrInst, UInstruction, UProgram, UVar, VarInst}, + ir::{MemberRef, FnID, Idents, Type, UFunc, UInstrInst, UInstruction, UProgram, UVar, VarInst}, parser, }; @@ -8,7 +8,13 @@ impl Node { pub fn lower_name(&self, p: &mut UProgram) -> Option { self.as_ref()?.lower_name(p) } - pub fn lower(&self, id: FnID, p: &mut UProgram, imports: &mut Vec, output: &mut CompilerOutput) { + pub fn lower( + &self, + id: FnID, + p: &mut UProgram, + imports: &mut Imports, + output: &mut CompilerOutput, + ) { if let Some(s) = self.as_ref() { s.lower(id, p, imports, output) } @@ -22,7 +28,13 @@ impl PFunction { let id = p.def_searchable(name.to_string(), None, self.header.origin); Some(id) } - pub fn lower(&self, id: FnID, p: &mut UProgram, imports: &mut Vec, output: &mut CompilerOutput) { + pub fn lower( + &self, + id: FnID, + p: &mut UProgram, + imports: &mut Imports, + output: &mut CompilerOutput, + ) { let name = p.names.name(id).to_string(); p.push_name(&name); let (args, ret) = if let Some(header) = self.header.as_ref() { @@ -69,7 +81,7 @@ pub struct FnLowerCtx<'a> { pub instructions: Vec, pub output: &'a mut CompilerOutput, pub origin: FileSpan, - pub imports: &'a mut Vec + pub imports: &'a mut Imports, } impl FnLowerCtx<'_> { @@ -103,9 +115,6 @@ impl FnLowerCtx<'_> { pub fn temp(&mut self, ty: Type) -> VarInst { self.program.temp_var(self.origin, ty) } - pub fn temp_subvar(&mut self, ty: Type, parent: FieldRef) -> VarInst { - self.program.temp_subvar(self.origin, ty, parent) - } pub fn push(&mut self, i: UInstruction) { self.push_at(i, self.origin); } diff --git a/src/parser/v3/lower/import.rs b/src/parser/v3/lower/import.rs deleted file mode 100644 index 11bcddc..0000000 --- a/src/parser/v3/lower/import.rs +++ /dev/null @@ -1 +0,0 @@ -pub struct Import(pub String); diff --git a/src/parser/v3/lower/mod.rs b/src/parser/v3/lower/mod.rs index b749218..44d0368 100644 --- a/src/parser/v3/lower/mod.rs +++ b/src/parser/v3/lower/mod.rs @@ -6,21 +6,26 @@ mod expr; mod func; mod struc; mod ty; -mod import; use super::*; use crate::ir::{Type, UFunc, UProgram}; impl PModule { - pub fn lower(&self, name: String, p: &mut UProgram, output: &mut CompilerOutput) { - let id = p.def_searchable(name.clone(), None, self.block.origin); + pub fn lower( + &self, + name: String, + p: &mut UProgram, + imports: &mut Imports, + output: &mut CompilerOutput, + ) { + let fid = p.def_searchable(name.clone(), None, self.block.origin); p.push_name(&name); let mut fctx = FnLowerCtx { program: p, instructions: Vec::new(), output, origin: self.block.origin, - imports: Vec::new(), + imports, }; self.block.lower(&mut fctx); let f = UFunc { @@ -28,12 +33,13 @@ impl PModule { instructions: fctx.instructions, ret: Type::Unit, }; - p.write(id, f); + p.write(fid, f); p.pop_name(); } } pub use func::FnLowerCtx; +use import::Imports; pub trait FnLowerable { type Output; diff --git a/src/parser/v3/lower/ty.rs b/src/parser/v3/lower/ty.rs index 68dc862..c8a8b1b 100644 --- a/src/parser/v3/lower/ty.rs +++ b/src/parser/v3/lower/ty.rs @@ -1,5 +1,5 @@ use crate::{ - ir::{GenericID, Type, UGeneric, UProgram, UStruct}, + ir::{GenericID, StructTy, Type, UGeneric, UProgram, UStruct}, parser::PGenericDef, }; @@ -24,7 +24,7 @@ impl PType { Type::Generic { id } } else if let Some(id) = ids.and_then(|ids| ids.get::()) { let args = self.args.iter().map(|n| n.lower(p, output)).collect(); - Type::Struct { id, args } + Type::Struct(StructTy { id, args }) } else if let Ok(num) = name.parse::() { Type::Bits(num) } else { diff --git a/src/parser/v3/mod.rs b/src/parser/v3/mod.rs index 1cff9c9..d15d6b4 100644 --- a/src/parser/v3/mod.rs +++ b/src/parser/v3/mod.rs @@ -6,6 +6,7 @@ mod node; mod nodes; mod parse; mod token; +mod import; use crate::common::{CompilerMsg, CompilerOutput, FileSpan, FilePos}; pub use ctx::*; @@ -14,6 +15,7 @@ pub use node::*; pub use nodes::*; pub use parse::*; pub use token::*; +pub use import::*; // idea: create generic "map" and "tuple" types which are used for function calls, tuples, struct // creation, etc. instead of specializing at the parsing level diff --git a/src/parser/v3/parse.rs b/src/parser/v3/parse.rs index 0f328dc..d816eb3 100644 --- a/src/parser/v3/parse.rs +++ b/src/parser/v3/parse.rs @@ -133,7 +133,7 @@ impl ParsableWith for T { impl Node { pub fn parse_with(ctx: &mut ParserCtx, data: T::Data) -> NodeParseResult { - let start = ctx.peek().map(|t| t.span.start).unwrap_or(FilePos::start()); + let start = ctx.peek().map(|t| t.span.start).unwrap_or(FilePos::start(ctx.cursor.file())); let (inner, recover) = match T::parse(ctx, data) { ParseResult::Ok(v) => (Some(v), false), ParseResult::Recover(v) => (Some(v), true), diff --git a/src/parser/v3/token/cursor.rs b/src/parser/v3/token/cursor.rs index 4aa0fb3..dbe4a74 100644 --- a/src/parser/v3/token/cursor.rs +++ b/src/parser/v3/token/cursor.rs @@ -1,14 +1,17 @@ use std::{iter::Peekable, str::Chars}; +use crate::common::FileID; + use super::super::{CompilerMsg, FilePos}; pub struct CharCursor<'a> { + file: FileID, chars: Peekable>, next_pos: FilePos, prev_pos: FilePos, } -impl CharCursor<'_> { +impl<'a> CharCursor<'a> { pub fn next(&mut self) -> Option { let res = self.peek()?; self.advance(); @@ -54,14 +57,15 @@ impl CharCursor<'_> { pub fn prev_pos(&self) -> FilePos { self.prev_pos } -} - -impl<'a> From<&'a str> for CharCursor<'a> { - fn from(value: &'a str) -> Self { + pub fn from_file_str(file: FileID, value: &'a str) -> Self { Self { chars: value.chars().peekable(), - next_pos: FilePos::start(), - prev_pos: FilePos::start(), + next_pos: FilePos::start(file), + prev_pos: FilePos::start(file), + file, } } + pub fn file(&self) -> FileID { + self.file + } } diff --git a/src/parser/v3/token/mod.rs b/src/parser/v3/token/mod.rs index 0624702..d608a5f 100644 --- a/src/parser/v3/token/mod.rs +++ b/src/parser/v3/token/mod.rs @@ -4,10 +4,10 @@ mod symbol; use std::ops::Deref; +use super::FileSpan; pub use cursor::*; pub use keyword::*; pub use symbol::*; -use super::FileSpan; #[derive(Debug, PartialEq, Eq, Clone)] pub enum Token { @@ -35,7 +35,11 @@ impl TokenInstance { let end = cursor.prev_pos(); return Some(Self { token: Token::Symbol(s), - span: FileSpan { start, end }, + span: FileSpan { + start, + end, + file: cursor.file(), + }, }); } let mut word = String::new(); @@ -54,7 +58,11 @@ impl TokenInstance { }; Some(Self { token, - span: FileSpan { start, end }, + span: FileSpan { + start, + end, + file: cursor.file(), + }, }) } }