diff --git a/data/util.lang b/data/util.lang index 87388d6..8fba2eb 100644 --- a/data/util.lang +++ b/data/util.lang @@ -11,7 +11,7 @@ fn println(msg: slice<8>) { } fn print(msg: slice<8>) { - asm (a1 = &msg) { + asm (a1 = msg@) { ld a2, 8, a1 ld a1, 0, a1 li a0, 1 @@ -29,7 +29,7 @@ fn print_hex(x: 64) { c = add(c, 7); }; c = add(c, 48); - asm (a1 = &c) { + asm (a1 = c@) { li a2, 1 li a0, 1 li a7, 64 @@ -65,7 +65,7 @@ fn print_dec(x: 64) { c = add(c, 7); }; c = add(c, 48); - asm (a1 = &c) { + asm (a1 = c@) { li a2, 1 li a0, 1 li a7, 64 diff --git a/src/common/file.rs b/src/common/file.rs index dec78a5..52fce47 100644 --- a/src/common/file.rs +++ b/src/common/file.rs @@ -9,14 +9,14 @@ pub struct SrcFile { pub text: String, } -#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct FilePos { pub file: FileID, pub line: usize, pub col: usize, } -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)] pub struct FileSpan { pub file: FileID, pub start: FilePos, diff --git a/src/common/output.rs b/src/common/output.rs index 172ba14..7b1f4fe 100644 --- a/src/common/output.rs +++ b/src/common/output.rs @@ -19,7 +19,7 @@ impl CompilerMsg { spans: Vec::new(), } } - pub fn from_span(span: FileSpan, msg: String) -> Self { + pub fn new(msg: String, span: FileSpan) -> Self { Self { msg, spans: vec![span], diff --git a/src/ir/id.rs b/src/ir/id.rs index 98fd4da..01c8d20 100644 --- a/src/ir/id.rs +++ b/src/ir/id.rs @@ -1,4 +1,8 @@ -use std::{fmt::Debug, marker::PhantomData}; +use std::{ + fmt::Debug, + marker::PhantomData, + ops::{Index, IndexMut}, +}; // I had an idea for why these were different... now I don't pub type Size = u32; @@ -12,19 +16,15 @@ impl ID { } } -pub trait Named { - const NAME: &str; -} - impl From for ID { fn from(value: usize) -> Self { Self(value, PhantomData) } } -impl Debug for ID { +impl Debug for ID { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}{}", K::NAME, self.0) + write!(f, "{{{}}}", self.0) } } @@ -49,3 +49,60 @@ impl Clone for ID { } impl Copy for ID {} + +// :fear: +impl Index> for Vec { + type Output = T; + + fn index(&self, i: ID) -> &Self::Output { + &self[i.0] + } +} + +impl IndexMut> for Vec { + fn index_mut(&mut self, i: ID) -> &mut Self::Output { + &mut self[i.0] + } +} + +impl Index<&ID> for Vec { + type Output = T; + + fn index(&self, i: &ID) -> &Self::Output { + &self[i.0] + } +} + +impl IndexMut<&ID> for Vec { + fn index_mut(&mut self, i: &ID) -> &mut Self::Output { + &mut self[i.0] + } +} + +impl Index> for [T] { + type Output = T; + + fn index(&self, i: ID) -> &Self::Output { + &self[i.0] + } +} + +impl IndexMut> for [T] { + fn index_mut(&mut self, i: ID) -> &mut Self::Output { + &mut self[i.0] + } +} + +impl Index<&ID> for [T] { + type Output = T; + + fn index(&self, i: &ID) -> &Self::Output { + &self[i.0] + } +} + +impl IndexMut<&ID> for [T] { + fn index_mut(&mut self, i: &ID) -> &mut Self::Output { + &mut self[i.0] + } +} diff --git a/src/ir/lower/program.rs b/src/ir/lower/program.rs index 8c86480..b0e3b76 100644 --- a/src/ir/lower/program.rs +++ b/src/ir/lower/program.rs @@ -128,7 +128,7 @@ impl<'a> LFunctionBuilder<'a> { } pub fn insert_instr(&mut self, i: &UInstrInst) -> Option> { match &i.i { - UInstruction::Mv { dest, src } => { + UInstruction::Mv { dst: dest, src } => { self.alloc_stack(dest.id)?; self.map_subvar(src.id); self.instrs.push(LInstruction::Mv { @@ -138,7 +138,7 @@ impl<'a> LFunctionBuilder<'a> { src_offset: 0, }); } - UInstruction::Ref { dest, src } => { + UInstruction::Ref { dst: dest, src } => { self.alloc_stack(dest.id)?; self.map_subvar(src.id); self.instrs.push(LInstruction::Ref { @@ -146,7 +146,7 @@ impl<'a> LFunctionBuilder<'a> { src: src.id, }); } - UInstruction::LoadData { dest, src } => { + UInstruction::LoadData { dst: dest, src } => { self.alloc_stack(dest.id)?; let data = self.program.expect(*src); let sym = self.data.builder.ro_data( @@ -161,7 +161,7 @@ impl<'a> LFunctionBuilder<'a> { src: sym, }); } - UInstruction::LoadSlice { dest, src } => { + UInstruction::LoadSlice { dst: dest, src } => { self.alloc_stack(dest.id)?; let data = self.program.expect(*src); let Type::Array(_, len) = &data.ty else { @@ -191,7 +191,7 @@ impl<'a> LFunctionBuilder<'a> { src: sym, }); } - UInstruction::LoadFn { dest, src } => { + UInstruction::LoadFn { dst: dest, src } => { self.alloc_stack(dest.id)?; let sym = self.builder.func(src); self.instrs.push(LInstruction::LoadAddr { @@ -200,7 +200,7 @@ impl<'a> LFunctionBuilder<'a> { src: sym, }); } - UInstruction::Call { dest, f, args } => { + UInstruction::Call { dst: dest, f, args } => { self.alloc_stack(dest.id); self.makes_call = true; let fid = &self.program.fn_var.fun(f.id).expect("a"); @@ -267,7 +267,7 @@ impl<'a> LFunctionBuilder<'a> { }; self.data.instrs.push(LInstruction::Ret { src }) } - UInstruction::Construct { dest, fields } => { + UInstruction::Construct { dst: dest, fields } => { let sty = &self.program.expect_type(dest.id); let Type::Struct(sty) = sty else { panic!("bruh htis aint' a struct"); diff --git a/src/ir/upper/assoc.rs b/src/ir/upper/assoc.rs index 30ac780..d81d443 100644 --- a/src/ir/upper/assoc.rs +++ b/src/ir/upper/assoc.rs @@ -1,157 +1,31 @@ -use super::{FnID, Kind, Origin, VarID, NAMED_KINDS}; -use crate::ir::ID; -use std::collections::HashMap; -pub struct OriginMap { - origins: [Vec; NAMED_KINDS], -} - -impl OriginMap { - pub fn new() -> Self { - Self { - origins: core::array::from_fn(|_| Vec::new()), - } - } - pub fn get(&self, id: ID) -> Origin { - self.origins[K::INDEX][id.0] - } - pub fn push(&mut self, origin: Origin) { - self.origins[K::INDEX].push(origin); - } -} - -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], name: &str) -> Option> { - self.get(&path)?.ids[K::INDEX] - .get(name) - .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], - tree: NameTree, -} - -impl NameMap { - pub fn new() -> Self { - Self { - names: core::array::from_fn(|_| Vec::new()), - tree: NameTree::new(), - } - } - pub fn path(&self, id: ID) -> &str { - &self.names[K::INDEX][id.0] - } - pub fn name(&self, id: ID) -> &str { - let mut path = self.path(id); - while let Some(i) = path.find("::") { - path = &path[i + 2..]; - } - path - } - pub fn id(&self, path: &[String], name: &str) -> Option> { - Some(self.tree.id(path, name)?) - } - 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); - } -} - -pub struct FnVarMap { - vtf: HashMap, - ftv: Vec, -} - -impl FnVarMap { - pub fn new() -> Self { - Self { - vtf: HashMap::new(), - ftv: Vec::new(), - } - } - pub fn insert(&mut self, f: FnID, v: VarID) { - self.vtf.insert(v, f); - self.ftv.push(v); - } - pub fn var(&self, f: FnID) -> VarID { - self.ftv[f.0] - } - pub fn fun(&self, v: VarID) -> Option { - self.vtf.get(&v).copied() - } -} - -#[derive(Debug, Clone, Copy)] -pub struct Ident { - id: usize, - kind: usize, -} - -impl From> for Ident { - fn from(id: ID) -> Self { - Self { - id: id.0, - kind: K::INDEX, - } - } -} - -// this isn't really a map... but also keeps track of "side data" -#[derive(Debug, Clone, Copy)] -pub struct Idents { - pub latest: Ident, - pub kinds: [Option; NAMED_KINDS], -} - -impl Idents { - pub fn new(latest: Ident) -> Self { - let mut s = Self { - latest, - kinds: [None; NAMED_KINDS], - }; - s.insert(latest); - s - } - pub fn insert(&mut self, i: Ident) { - self.latest = i; - self.kinds[i.kind] = Some(i.id); - } - pub fn get(&self) -> Option> { - self.kinds[K::INDEX].map(|i| i.into()) - } -} +// #[derive(Debug, Clone, Copy)] +// pub struct Ident { +// id: usize, +// kind: usize, +// } +// +// // this isn't really a map... but also keeps track of "side data" +// #[derive(Debug, Clone, Copy)] +// pub struct Idents { +// pub latest: Ident, +// pub kinds: [Option; NAMED_KINDS], +// } +// +// impl Idents { +// pub fn new(latest: Ident) -> Self { +// let mut s = Self { +// latest, +// kinds: [None; NAMED_KINDS], +// }; +// s.insert(latest); +// s +// } +// pub fn insert(&mut self, i: Ident) { +// self.latest = i; +// self.kinds[i.kind] = Some(i.id); +// } +// pub fn get(&self) -> Option> { +// self.kinds[K::INDEX].map(|i| i.into()) +// } +// } diff --git a/src/ir/upper/inst.rs b/src/ir/upper/inst.rs index 2cf51dc..147a10d 100644 --- a/src/ir/upper/inst.rs +++ b/src/ir/upper/inst.rs @@ -1,18 +1,18 @@ -use crate::{common::FileSpan, ir::VarID}; +use crate::ir::VarID; use std::fmt::Debug; -use super::UInstruction; +use super::{Origin, UInstruction}; #[derive(Clone, Copy)] pub struct VarInst { pub id: VarID, - pub span: FileSpan, + pub origin: Origin, } #[derive(Clone)] pub struct UInstrInst { pub i: UInstruction, - pub span: FileSpan, + pub origin: Origin, } impl Debug for VarInst { diff --git a/src/ir/upper/instr.rs b/src/ir/upper/instr.rs index 263d017..f369996 100644 --- a/src/ir/upper/instr.rs +++ b/src/ir/upper/instr.rs @@ -6,27 +6,31 @@ use crate::{compiler::arch::riscv::Reg, util::Padder}; #[derive(Clone)] pub enum UInstruction { Mv { - dest: VarInst, + dst: VarInst, src: VarInst, }, Ref { - dest: VarInst, + dst: VarInst, + src: VarInst, + }, + Deref { + dst: VarInst, src: VarInst, }, LoadData { - dest: VarInst, + dst: VarInst, src: DataID, }, LoadSlice { - dest: VarInst, + dst: VarInst, src: DataID, }, LoadFn { - dest: VarInst, + dst: VarInst, src: FnID, }, Call { - dest: VarInst, + dst: VarInst, f: VarInst, args: Vec, }, @@ -38,7 +42,7 @@ pub enum UInstruction { src: VarInst, }, Construct { - dest: VarInst, + dst: VarInst, fields: HashMap, }, If { @@ -68,19 +72,19 @@ pub enum AsmBlockArgType { impl std::fmt::Debug for UInstruction { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Self::Mv { dest, src } => write!(f, "{dest:?} <- {src:?}")?, - Self::Ref { dest, src } => write!(f, "{dest:?} <- &{src:?}")?, - Self::LoadData { dest, src } => write!(f, "{dest:?} <- {src:?}")?, - Self::LoadFn { dest, src } => write!(f, "{dest:?} <- {src:?}")?, - Self::LoadSlice { dest, src } => write!(f, "{dest:?} <- &[{src:?}]")?, + Self::Mv { dst: dest, src } => write!(f, "{dest:?} <- {src:?}")?, + Self::Ref { dst: dest, src } => write!(f, "{dest:?} <- &{src:?}")?, + Self::LoadData { dst: dest, src } => write!(f, "{dest:?} <- {src:?}")?, + Self::LoadFn { dst: dest, src } => write!(f, "{dest:?} <- {src:?}")?, + Self::LoadSlice { dst: dest, src } => write!(f, "{dest:?} <- &[{src:?}]")?, Self::Call { - dest, + dst: dest, f: func, args, } => write!(f, "{dest:?} <- {func:?}({args:?})")?, Self::AsmBlock { args, instructions } => write!(f, "asm {args:?} {instructions:#?}")?, Self::Ret { src } => f.debug_struct("Ret").field("src", src).finish()?, - Self::Construct { dest, fields } => write!(f, "{dest:?} <- {fields:?}")?, + Self::Construct { dst: dest, fields } => write!(f, "{dest:?} <- {fields:?}")?, Self::If { cond, body } => { write!(f, "if {cond:?}:")?; if !body.is_empty() { diff --git a/src/ir/upper/kind.rs b/src/ir/upper/kind.rs index 607ad7a..8197a52 100644 --- a/src/ir/upper/kind.rs +++ b/src/ir/upper/kind.rs @@ -1,14 +1,19 @@ use super::{Type, UInstrInst, UInstruction, UProgram}; use crate::{ common::FileSpan, - ir::{Len, Named, ID}, + ir::{Len, ID}, }; use std::{collections::HashMap, fmt::Debug}; +pub type NamePath = Vec; + #[derive(Clone)] pub struct UFunc { + pub name: String, + pub origin: Origin, pub args: Vec, - pub ret: Type, + pub argtys: Vec, + pub ret: TypeID, pub instructions: Vec, } @@ -19,16 +24,61 @@ pub struct StructField { #[derive(Clone)] pub struct UStruct { + pub name: String, + pub origin: Origin, pub fields: HashMap, pub generics: Vec, } #[derive(Clone)] -pub struct UGeneric {} +pub struct UGeneric { + pub name: String, + pub origin: Origin, +} #[derive(Clone)] pub struct UVar { + pub name: String, + pub origin: Origin, pub ty: TypeID, + pub res: UVarTy, +} + +#[derive(Clone)] +pub struct VarParent { + id: VarID, + path: Vec, +} + +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct MemberID { + pub name: String, + pub origin: Origin, +} + +#[derive(Clone, PartialEq, Eq, Hash)] +pub struct ModPath { + pub m: ModID, + pub path: Vec, +} + +#[derive(Clone)] +pub enum UVarTy { + Ptr(VarID), + /// fully resolved & typed + Res { + parent: Option, + }, + /// module doesn't exist yet + Unres { + path: ModPath, + fields: Vec, + }, + /// parent var exists but not typed enough for this field path + Partial { + v: VarID, + fields: Vec, + }, } #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] @@ -39,17 +89,38 @@ pub struct VarOffset { #[derive(Clone)] pub struct UData { + pub name: String, pub ty: Type, pub content: Vec, } +#[derive(Clone)] +pub struct UModule { + pub name: String, + pub members: HashMap, + pub children: HashMap, + pub parent: Option, +} + +pub struct ModMissing { + pub import_all: Vec, + pub vars: Vec, +} + +#[derive(Clone)] +pub enum Member { + Fn(FnID), + Struct(StructID), + Var(VarID), +} + pub type Origin = FileSpan; impl UFunc { pub fn ty(&self, program: &UProgram) -> Type { Type::Fn { - args: self.args.iter().map(|a| program.expect(*a).ty).collect(), - ret: self.ret, + args: self.argtys.clone(), + ret: Box::new(self.ret.clone()), } } pub fn flat_iter(&self) -> impl Iterator { @@ -85,62 +156,12 @@ impl<'a> Iterator for InstrIter<'a> { } } -macro_rules! impl_kind { - // TRUST THIS IS SANE!!! KEEP THE CODE DRY AND SAFE!!!!!! - ($struc:ty, $idx:expr, $field:ident, $name:expr) => { - impl_kind!($struc, $idx, $field, $name, nofin); - impl Finish for $struc { - fn finish(_: &mut UProgram, _: ID, _: &str) {} - } - }; - ($struc:ty, $idx:expr, $field:ident, $name:expr, nofin) => { - impl Kind for $struc { - const INDEX: usize = $idx; - fn from_program_mut(program: &mut UProgram) -> &mut Vec> { - &mut program.$field - } - fn from_program(program: &UProgram) -> &Vec> { - &program.$field - } - } - impl Named for $struc { - const NAME: &str = $name; - } - }; -} - -impl_kind!(UFunc, 0, fns, "func", nofin); -impl_kind!(UVar, 1, vars, "var"); -impl_kind!(UStruct, 2, structs, "struct"); -impl_kind!(Type, 3, types, "type"); -impl_kind!(UData, 4, data, "data"); pub const NAMED_KINDS: usize = 5; pub type FnID = ID; pub type VarID = ID; +pub type TypeID = ID; +pub type GenericID = ID; pub type StructID = ID; pub type DataID = ID; -pub type TypeID = ID; - -impl Finish for UFunc { - fn finish(p: &mut UProgram, id: ID, name: &str) { - let var = p.def_searchable( - name, - Some(UVar { - ty: Type::Placeholder, - }), - p.origins.get(id), - ); - p.fn_var.insert(id, var); - } -} - -pub trait Kind: Sized { - const INDEX: usize; - fn from_program_mut(program: &mut UProgram) -> &mut Vec>; - fn from_program(program: &UProgram) -> &Vec>; -} - -pub trait Finish: Sized { - fn finish(program: &mut UProgram, id: ID, name: &str); -} +pub type ModID = ID>; diff --git a/src/ir/upper/mod.rs b/src/ir/upper/mod.rs index 2d8a188..f407590 100644 --- a/src/ir/upper/mod.rs +++ b/src/ir/upper/mod.rs @@ -6,6 +6,7 @@ mod kind; mod program; mod ty; mod validate; +mod resolve; use super::*; use assoc::*; diff --git a/src/ir/upper/program.rs b/src/ir/upper/program.rs index fb671e5..bd7cbe1 100644 --- a/src/ir/upper/program.rs +++ b/src/ir/upper/program.rs @@ -1,21 +1,22 @@ use super::*; -use std::collections::HashMap; +use std::{ + collections::HashMap, + ops::{Deref, DerefMut}, +}; pub struct UProgram { - // kinds - pub fns: Vec>, - pub vars: Vec>, - pub structs: Vec>, - pub types: Vec>, - pub data: Vec>, - // associated data - pub names: NameMap, - pub origins: OriginMap, - pub fn_var: FnVarMap, - // utils for creation - error: Option, - pub path: Vec, - pub name_stack: Vec>, + pub fns: Vec, + pub structs: Vec, + pub modules: Vec>, + pub data: Vec, + pub generics: Vec, + pub vars: Vec, + pub types: Vec, +} + +pub struct UModuleBuilder<'a> { + pub p: &'a mut UProgram, + pub module: ModID, pub temp: usize, } @@ -26,179 +27,64 @@ impl UProgram { vars: Vec::new(), structs: Vec::new(), types: Vec::new(), + generics: Vec::new(), data: Vec::new(), - names: NameMap::new(), - origins: OriginMap::new(), - fn_var: FnVarMap::new(), - error: None, - path: Vec::new(), - name_stack: Vec::new(), - temp: 0, - } - } - pub fn error_type(&mut self) -> TypeID { - self.error - .unwrap_or_else(|| self.def("error", Some(Type::Error), Origin::builtin())) - } - pub fn set_module(&mut self, path: Vec) { - self.path = path; - self.name_stack = vec![HashMap::new()]; - } - pub fn push(&mut self) { - self.name_stack.push(HashMap::new()); - } - pub fn pop(&mut self) { - self.name_stack.pop(); - } - pub fn push_name(&mut self, name: &str) { - self.path.push(name.to_string()); - } - pub fn pop_name(&mut self) { - self.path.pop(); - } - pub fn get_idents(&self, name: &str) -> Option { - for map in self.name_stack.iter().rev() { - let res = map.get(name); - if res.is_some() { - return res.cloned(); - } - } - None - } - pub fn get(&self, id: ID) -> Option<&K> { - K::from_program(self)[id.0].as_ref() - } - pub fn get_mut(&mut self, id: ID) -> Option<&mut K> { - K::from_program_mut(self)[id.0].as_mut() - } - pub fn expect(&self, id: ID) -> &K { - self.get(id) - .unwrap_or_else(|| panic!("{id:?} not defined yet!")) - } - pub fn expect_type(&self, var: VarID) -> &Type { - self.expect(self.expect(var).ty) - } - pub fn expect_mut(&mut self, id: ID) -> &mut K { - self.get_mut(id) - .unwrap_or_else(|| panic!("{id:?} not defined yet!")) - } - pub fn get_fn_var(&self, id: VarID) -> Option<&UFunc> { - self.fns[self.fn_var.fun(id)?.0].as_ref() - } - - pub fn temp_var(&mut self, origin: Origin, ty: TypeID) -> VarInst { - self.temp_var_inner(origin, ty) - } - - fn temp_var_inner(&mut self, origin: Origin, ty: TypeID) -> VarInst { - let v = self.def(&format!("temp{}", self.temp), Some(UVar { ty }), origin); - self.temp += 1; - VarInst { - id: v, - span: origin, + modules: Vec::new(), } } - pub fn write(&mut self, id: ID, k: K) { - K::from_program_mut(self)[id.0] = Some(k); + pub fn instantiate_type(&mut self, ty: Type) { + self.def_ty(match ty { + Type::Ref(node) => Type::Ref(node.lower()), + Type::Generic(node, nodes) => todo!(), + Type::Ident(node) => todo!(), + }); } - pub fn def(&mut self, name: &str, k: Option, origin: Origin) -> ID { - self.names.push::(&self.path_for(name)); - self.origins.push::(origin); - let vec = K::from_program_mut(self); - let id = ID::new(vec.len()); - vec.push(k); - K::finish(self, id, name); + pub fn infer(&mut self) -> TypeID { + self.def_ty(Type::Infer) + } + + pub fn error(&mut self) -> TypeID { + self.def_ty(Type::Error) + } + + pub fn def_var(&mut self, v: UVar) -> VarID { + Self::push_id(&mut self.vars, v) + } + + pub fn def_fn(&mut self, f: UFunc) -> FnID { + Self::push_id(&mut self.fns, f) + } + + pub fn def_ty(&mut self, t: Type) -> TypeID { + Self::push_id(&mut self.types, t) + } + + pub fn def_generic(&mut self, g: UGeneric) -> GenericID { + Self::push_id(&mut self.generics, g) + } + + pub fn def_data(&mut self, d: UData) -> DataID { + Self::push_id(&mut self.data, d) + } + + pub fn def_struct(&mut self, s: UStruct) -> StructID { + Self::push_id(&mut self.structs, s) + } + + fn push_id(v: &mut Vec, t: T) -> ID { + let id = ID::new(v.len()); + v.push(t); id } - pub fn path_for(&self, name: &str) -> Vec { - if self.path.is_empty() { - return vec![name.to_string()]; - } - let mut path = self.path.clone(); - path.push(name.to_string()); - path - } - - pub fn def_searchable( - &mut self, - name: &str, - k: Option, - origin: Origin, - ) -> ID { - let id = self.def(&name, k, origin); - self.name_on_stack(id, name.to_string()); - id - } - - // hopefully these are not needed after redoing types - // pub fn field_type<'a>(&'a self, ty: &'a Type, field: &str) -> Option<&'a Type> { - // if let Type::Struct(sty) = ty { - // Some(&self.get(*sty.fields.get(field)?)?.ty) - // } else if let Type::Module(path) = ty { - // let id = self.names.id::(path, field)?; - // Some(&self.get(id)?.ty) - // } else { - // None - // } - // } - // - // pub fn follow_ref(&self, m: &FieldRef) -> Option<&Type> { - // let parent = self.get(m.parent)?; - // self.field_type(self.follow_type(&parent.ty)?, &m.name) - // } - // - // pub fn get_type<'a>(&'a self, v: VarID) -> Option<&'a Type> { - // self.follow_type(&self.get(v)?.ty) - // } - // - // pub fn set_type(&mut self, mut var: VarID, set: Type) -> Option<()> { - // let mut path = Vec::new(); - // while let Type::Field(parent) = &self.get(var)?.ty { - // var = parent.parent; - // path.push(parent.name.clone()); - // } - // let mut ty = &mut self.vars[var.0].as_mut()?.ty; - // 'outer: while let Type::Struct(sty) = ty { - // let Some(name) = path.pop() else { - // break; - // }; - // let struc = &self.structs[sty.id.0].as_ref()?; - // let field = struc.fields.get(&name)?; - // let Type::Generic { id } = field.ty else { - // return None; - // }; - // for (i, g) in struc.generics.iter().enumerate() { - // if *g == id { - // ty = &mut sty.args[i]; - // continue 'outer; - // } - // } - // return None; - // } - // *ty = set; - // Some(()) - // } - // - // pub fn follow_type<'a>(&'a self, ty: &'a Type) -> Option<&'a Type> { - // match ty { - // Type::Field(m) => { - // let parent = self.get(m.parent)?; - // self.field_type(self.follow_type(&parent.ty)?, &m.name) - // } - // ty => Some(ty), - // } - // } - - pub fn type_name(&self, ty: &Type) -> String { + pub fn type_name(&self, ty: T) -> String { let mut str = String::new(); - match ty { + match ty.ty(self) { Type::Struct(ty) => { - let base = ty.id; + str += &self.structs[ty.id].name; let args = &ty.args; - str += self.names.name(base); if let Some(arg) = args.first() { str = str + "<" + &self.type_name(arg); } @@ -221,9 +107,16 @@ impl UProgram { str += &self.type_name(ret); } Type::Ref(t) => { - str = str + "&" + &self.type_name(t); + str += &self.type_name(t); + str += "&"; + } + Type::Deref(t) => { + str += &self.type_name(t); + str += "^"; + } + Type::Unres(_) => { + str += "{unresolved}"; } - Type::Generic { id } => str += self.names.name(*id), Type::Bits(size) => str += &format!("b{}", size), Type::Array(t, len) => str += &format!("[{}; {len}]", self.type_name(t)), Type::Unit => str += "()", @@ -231,50 +124,111 @@ impl UProgram { Type::Error => str += "{error}", Type::Infer => str += "{inferred}", Type::Placeholder => str += "{placeholder}", - Type::Module(path) => str += &path.join("::"), - Type::Field(m) => { - str += &self - .follow_ref(m) - .map(|t| self.type_name(t)) - .unwrap_or("{error}".to_string()) - } } str } - pub fn path_var(&self, path: &NamePath, name: &str) -> Option { - self.names.id(path, name) - } - pub fn path_ty(&self, path: &NamePath, name: &str) -> Option<&Type> { - Some(&self.get(self.path_var(path, name)?)?.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]; - if let Some(l) = last.get_mut(&name) { - l.insert(id.into()); - } else { - last.insert(name, Idents::new(id.into())); +} + +impl<'a> UModuleBuilder<'a> { + pub fn new(program: &'a mut UProgram, id: ModID, error: TypeID) -> Self { + Self { + p: program, + module: id, + error, + name_stack: Vec::new(), + temp: 0, } } - pub fn iter_vars(&self) -> impl Iterator { - self.vars - .iter() - .flatten() - .enumerate() - .map(|(i, x)| (ID::new(i), x)) + pub fn push(&mut self) { + self.name_stack.push(HashMap::new()); } - pub fn iter_fns(&self) -> impl Iterator { - self.fns - .iter() - .flatten() - .enumerate() - .map(|(i, x)| (ID::new(i), x)) + pub fn pop(&mut self) { + self.name_stack.pop(); } - pub fn cloned_fns(&self) -> impl Iterator + use<'_> { - self.fns - .iter() - .flatten() - .enumerate() - .map(|(i, x)| (ID::new(i), x.clone())) + pub fn get_idents(&self, name: &str) -> Option { + for map in self.name_stack.iter().rev() { + let res = map.get(name); + if res.is_some() { + return res.cloned(); + } + } + None + } + + pub fn def_var(&mut self, name: &str, v: UVar, origin: Origin) -> VarID { + let id = self.p.def_var(name, v, origin); + self.name_on_stack(id, name.to_string()); + id + } + + pub fn temp_var(&mut self, origin: Origin, ty: T) -> VarInst { + self.temp_var_inner(origin, ty) + } + fn temp_var_inner(&mut self, origin: Origin, ty: T) -> VarInst { + let t = self.temp; + let v = self + .p + .def_var(&format!("temp{}", t), UVar { ty: ty.ty(self) }, origin); + self.temp += 1; + VarInst { id: v, origin } + } +} + +// I'm done with names... +pub trait Typer { + fn ty<'a>(&'a self, p: &'a UProgram) -> &'a Type; +} + +impl Typer for &Type { + fn ty(&self, _: &UProgram) -> &Type { + self + } +} + +impl Typer for TypeID { + fn ty<'a>(&'a self, p: &'a UProgram) -> &'a Type { + &p.types[self] + } +} + +impl Typer for &TypeID { + fn ty<'a>(&'a self, p: &'a UProgram) -> &'a Type { + &p.types[*self] + } +} + +impl Typer for &Box { + fn ty<'a>(&'a self, _: &'a UProgram) -> &'a Type { + &**self + } +} + +pub trait Typable { + fn ty(self, p: &mut UProgram) -> TypeID; +} + +impl Typable for Type { + fn ty(self, p: &mut UProgram) -> TypeID { + p.def_ty(self) + } +} + +impl Typable for TypeID { + fn ty(self, p: &mut UProgram) -> TypeID { + self + } +} + +impl Deref for UModuleBuilder<'_> { + type Target = UProgram; + + fn deref(&self) -> &Self::Target { + self.p + } +} + +impl DerefMut for UModuleBuilder<'_> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.p } } diff --git a/src/ir/upper/resolve.rs b/src/ir/upper/resolve.rs new file mode 100644 index 0000000..7054b96 --- /dev/null +++ b/src/ir/upper/resolve.rs @@ -0,0 +1,343 @@ +use super::{Origin, StructTy, Type, TypeID, TypeIDed, UInstruction, UProgram, UStruct, UVar}; +use crate::common::{CompilerMsg, CompilerOutput}; +use std::{collections::HashMap, ops::BitOrAssign}; + +impl UProgram { + pub fn resolve(&mut self, output: &mut CompilerOutput) { + let mut unfinished = Vec::new(); + let mut unfinished_new = Vec::new(); + let data = &mut ResData { + changed: false, + types: &mut self.types, + vars: &self.vars, + structs: &self.structs, + errs: Vec::new(), + }; + for f in &self.fns { + for i in f.flat_iter() { + if resolve_instr(data, &i.i).unfinished() { + unfinished.push(i); + } + } + } + while !unfinished.is_empty() && data.changed { + data.changed = false; + for &i in &unfinished { + if resolve_instr(data, &i.i).unfinished() { + unfinished_new.push(i); + } + } + std::mem::swap(&mut unfinished, &mut unfinished_new); + unfinished_new.clear(); + } + for err in &data.errs { + match err { + &ResErr::Type { + dst, + src, + ref errs, + origin, + } => { + let mut msg = type_assign_err(self, dst, src); + for inner in errs { + if inner.dst != dst && inner.src != src { + msg.push_str("\n "); + msg.push_str(&type_assign_err(self, inner.dst, inner.src)); + } + } + output.err(CompilerMsg::new(msg, origin)); + } + &ResErr::NotCallable { origin, ty } => { + output.err(CompilerMsg::new( + format!("Cannot call type {}", self.type_name(ty)), + origin, + )); + } + } + } + for var in &self.vars { + match &self.types[var.ty] { + Type::Error => output.err(CompilerMsg::new( + format!("Var {:?} is error type!", var.name), + var.origin, + )), + Type::Infer => output.err(CompilerMsg::new( + format!("Type of {:?} cannot be inferred", var.name), + var.origin, + )), + Type::Placeholder => output.err(CompilerMsg::new( + format!("Var {:?} still placeholder!", var.name), + var.origin, + )), + Type::Unres(_) => output.err(CompilerMsg::new( + format!("Var {:?} type still unresolved!", var.name), + var.origin, + )), + _ => (), + } + } + } +} + +pub fn type_assign_err(p: &mut UProgram, dst: TypeID, src: TypeID) -> String { + format!( + "Cannot assign type {} to {}", + p.type_name(src), + p.type_name(dst) + ) +} + +pub fn resolve_instr(data: &mut ResData, i: &UInstruction) -> InstrRes { + let mut uf = InstrRes::Finished; + match &i { + UInstruction::Call { dst, f, args } => { + let fty = data.vars[f.id].ty; + let Type::Fn { args: fargs, ret } = &data.types[fty] else { + data.errs.push(ResErr::NotCallable { + origin: f.origin, + ty: fty, + }); + return InstrRes::Finished; + }; + uf |= data.match_types(dst, ret, dst.origin); + for (src, dest) in args.iter().zip(fargs) { + uf |= data.match_types(dest, src, src.origin); + } + } + UInstruction::Mv { dst, src } => { + uf |= data.match_types(dst, src, src.origin); + } + UInstruction::Ref { dst, src } => { + let Type::Ref(dest_ty) = data.types[data.vars[dst.id].ty] else { + // TODO: this is probably a compiler error / should never happen + panic!("how could this happen to me (you)"); + }; + uf |= data.match_types(dest_ty, src, src.origin); + } + UInstruction::LoadData { dst, src } => { + // TODO + } + UInstruction::LoadSlice { dst, src } => { + // TODO + } + UInstruction::LoadFn { dst, src } => { + // TODO + } + UInstruction::AsmBlock { instructions, args } => { + // TODO + } + UInstruction::Ret { .. } => {} + UInstruction::Construct { dst, fields } => { + let dest_ty = get(vars, dst.id)?; + let Type::Struct(sty) = dest_ty else {}; + let id = sty.id; + let Some(struc) = self.get(id) else {}; + let mut new = HashMap::new(); + for (name, field) in &struc.fields { + let Some(src) = fields.get(name) else { + continue; + }; + let src_ty = get(vars, src.id)?; + if let Some(ty) = match_types(vars, types, &field.ty, src_ty) { + if let Type::Generic { id } = field.ty { + new.insert(id, ty.clone()); + } + set(vars, src.id, ty); + } + } + let mut args: Vec<_> = struc + .generics + .iter() + .map(|&id| Type::Generic { id }) + .collect(); + for (i, g) in struc.generics.iter().enumerate() { + if let Some(ty) = new.remove(g) { + args[i] = ty; + } + } + set(vars, dst.id, Type::Struct(StructTy { id, args })); + } + UInstruction::If { cond, body } => { + for i in body { + uf |= resolve_instr(data, &i.i); + } + } + UInstruction::Loop { body } => {} + UInstruction::Break => {} + UInstruction::Continue => {} + } + uf +} + +pub fn match_types( + data: &mut TypeResData, + dst: T1, + src: T2, +) -> MatchRes { + let dst = dst.type_id(data.vars); + let src = src.type_id(data.vars); + if dst == src { + return MatchRes::Finished; + } + let error = || MatchRes::Error(vec![TypeMismatch { dst, src }]); + match (&data.types[dst], &data.types[src]) { + (Type::Error, _) | (_, Type::Error) => MatchRes::Finished, + (Type::Placeholder, _) | (_, Type::Placeholder) => MatchRes::Unfinished, + (Type::Infer, Type::Infer) => MatchRes::Unfinished, + (Type::Infer, x) => { + *data.changed = true; + data.types[dst] = x.clone(); + MatchRes::Finished + } + (x, Type::Infer) => { + *data.changed = true; + data.types[src] = x.clone(); + MatchRes::Finished + } + (Type::Struct(dest), Type::Struct(src)) => { + if dest.id != src.id { + return error(); + } + let mut finished = true; + let mut errors = Vec::new(); + let dargs = dest.args.clone(); + let sargs = dest.args.clone(); + for (darg, sarg) in dargs.iter().zip(&sargs) { + match match_types(data, darg, sarg) { + MatchRes::Unfinished => finished = false, + MatchRes::Error(errs) => errors.extend(errs), + MatchRes::Finished => (), + } + } + if finished { + if errors.is_empty() { + MatchRes::Finished + } else { + MatchRes::Error(errors) + } + } else { + MatchRes::Unfinished + } + } + ( + Type::Fn { + args: dest_args, + ret: dest_ret, + }, + Type::Fn { + args: src_args, + ret: src_ret, + }, + ) => { + // TODO + MatchRes::Finished + } + (&Type::Ref(dest), &Type::Ref(src)) => match_types(data, dest, src), + (&Type::Slice(dest), &Type::Slice(src)) => match_types(data, dest, src), + (&Type::Array(dest, dlen), &Type::Array(src, slen)) => { + if dlen == slen { + match_types(data, dest, src) + } else { + error() + } + } + _ => error(), + } +} + +struct ResData<'a> { + changed: bool, + types: &'a mut [Type], + vars: &'a [UVar], + structs: &'a [UStruct], + errs: Vec, +} + +struct TypeResData<'a> { + changed: &'a mut bool, + types: &'a mut [Type], + vars: &'a [UVar], + structs: &'a [UStruct], +} + +enum ResErr { + NotCallable { + origin: Origin, + ty: TypeID, + }, + Type { + dst: TypeID, + src: TypeID, + errs: Vec, + origin: Origin, + }, +} + +impl<'a> ResData<'a> { + pub fn match_types( + &'a mut self, + dst: T1, + src: T2, + origin: Origin, + ) -> InstrRes { + let dst = dst.type_id(self.vars); + let src = src.type_id(self.vars); + let res = match_types( + &mut TypeResData { + changed: &mut self.changed, + types: self.types, + vars: self.vars, + structs: self.structs, + }, + dst, + src, + ); + match res { + MatchRes::Unfinished => InstrRes::Unfinished, + MatchRes::Finished => InstrRes::Finished, + MatchRes::Error(es) => { + self.errs.push(ResErr::Type { + errs: es, + origin, + dst, + src, + }); + InstrRes::Finished + } + } + } +} + +pub struct TypeMismatch { + dst: TypeID, + src: TypeID, +} + +pub enum MatchRes { + Unfinished, + Finished, + Error(Vec), +} + +pub enum InstrRes { + Finished, + Unfinished, +} + +impl BitOrAssign for InstrRes { + fn bitor_assign(&mut self, rhs: Self) { + match rhs { + InstrRes::Finished => (), + InstrRes::Unfinished => *self = InstrRes::Unfinished, + } + } +} + +impl InstrRes { + pub fn unfinished(&self) -> bool { + match self { + Self::Finished => false, + Self::Unfinished => true, + } + } +} diff --git a/src/ir/upper/ty.rs b/src/ir/upper/ty.rs index a73dbf6..771b680 100644 --- a/src/ir/upper/ty.rs +++ b/src/ir/upper/ty.rs @@ -1,6 +1,4 @@ -use std::collections::HashMap; - -use super::{assoc::NamePath, Len, StructID, TypeID, UInstruction, UProgram, VarID}; +use super::{FnID, GenericID, Len, ModPath, StructID, TypeID, UVar, VarID, VarInst}; #[derive(Debug, Clone, Hash, Eq, PartialEq)] pub struct FieldRef { @@ -8,211 +6,84 @@ pub struct FieldRef { pub name: String, } -#[derive(Clone, Eq, PartialEq)] +#[derive(Clone, Eq, PartialEq, Hash)] pub struct StructTy { pub id: StructID, - pub fields: HashMap, + pub args: Vec, } -#[derive(Clone, PartialEq, Eq)] +#[derive(Clone)] pub enum Type { Bits(u32), Struct(StructTy), - Fn { args: Vec, ret: TypeID }, + Fn(FnID), Ref(TypeID), + Deref(TypeID), Slice(TypeID), Array(TypeID, Len), Unit, - // fake types - Field(FieldRef), - Module(NamePath), + // "fake" types + Unres(ModPath), + Generic(GenericID), Infer, Error, Placeholder, } +impl TypeID { + pub fn rf(self) -> Type { + Type::Ref(self) + } + pub fn derf(self) -> Type { + Type::Deref(self) + } + pub fn arr(self, len: Len) -> Type { + Type::Array(self, len) + } + pub fn slice(self) -> Type { + Type::Slice(self) + } +} + impl Type { pub fn is_resolved(&self) -> bool { !matches!(self, Self::Error | Self::Placeholder | Self::Infer) } -} - -impl UProgram { - pub fn resolve_types(&mut self) { - // PARTIAL BORROWING :SOB: - let fns: Vec<_> = self.cloned_fns().collect(); - for (i, f) in &fns { - let vi = self.fn_var.var(*i); - self.expect_mut(vi).ty = f.ty(self); - } - for (i, f) in &fns { - let mut redo_iter = Vec::new(); - let mut redo_new = Vec::new(); - for i in f.flat_iter() { - if self.resolve_instr_types(&i.i).is_none() { - redo_iter.push(i); - } - } - while !redo_iter.is_empty() { - for &i in &redo_iter { - if self.resolve_instr_types(&i.i).is_none() { - redo_new.push(i); - } - } - std::mem::swap(&mut redo_iter, &mut redo_new); - redo_new.clear(); - } - } - } - - pub fn resolve_instr_types(&mut self, i: &UInstruction) -> Option<()> { - 'outer: { - match &i { - UInstruction::Call { dest, f, args } => { - let fun = self.get_fn_var(f.id).expect("bruh"); - self.expect_mut(dest.id).ty = fun.ret.clone(); - for (src, &dest) in args.iter().zip(&fun.args) { - let dest_ty = self.get_type(dest)?; - let src_ty = self.get_type(src.id)?; - if let Some(ty) = self.match_types(dest_ty, src_ty) { - self.expect_mut(dest).ty = ty.clone(); - set(vars, dest, ty.clone()); - set(vars, src.id, ty); - } - } - } - UInstruction::Mv { dest, src } => { - let dest_ty = get(vars, dest.id)?; - let src_ty = get(vars, src.id)?; - if let Some(ty) = self.match_types(dest_ty, src_ty) { - set(vars, dest.id, ty.clone()); - set(vars, src.id, ty); - } - } - UInstruction::Ref { dest, src } => { - let dest_ty = get(vars, dest.id)?; - let src_ty = get(vars, src.id)?; - let Type::Ref(dest_ty) = dest_ty else { - break 'outer; - }; - if let Some(ty) = self.match_types(dest_ty, src_ty) { - set(vars, dest.id, ty.clone().rf()); - set(vars, src.id, ty); - } - } - UInstruction::LoadData { dest, src } => { - // TODO - } - UInstruction::LoadSlice { dest, src } => { - // TODO - } - UInstruction::LoadFn { dest, src } => { - // TODO - } - UInstruction::AsmBlock { instructions, args } => { - // TODO - } - UInstruction::Ret { .. } => {} - UInstruction::Construct { dest, fields } => { - let dest_ty = get(vars, dest.id)?; - let Type::Struct(sty) = dest_ty else { - break 'outer; - }; - let id = sty.id; - let Some(struc) = self.get(id) else { - break 'outer; - }; - let mut new = HashMap::new(); - for (name, field) in &struc.fields { - let Some(src) = fields.get(name) else { - continue; - }; - let src_ty = get(vars, src.id)?; - if let Some(ty) = self.match_types(&field.ty, src_ty) { - if let Type::Generic { id } = field.ty { - new.insert(id, ty.clone()); - } - set(vars, src.id, ty); - } - } - let mut args: Vec<_> = struc - .generics - .iter() - .map(|&id| Type::Generic { id }) - .collect(); - for (i, g) in struc.generics.iter().enumerate() { - if let Some(ty) = new.remove(g) { - args[i] = ty; - } - } - set(vars, dest.id, Type::Struct(StructTy { id, args })); - } - UInstruction::If { cond, body: _ } => {} - UInstruction::Loop { body: _ } => {} - UInstruction::Break => {} - UInstruction::Continue => {} - } - } - Some(()) - } - - pub fn match_types<'a>(&'a self, mut dest: &'a Type, mut src: &'a Type) -> Option { - dest = self.follow_type(dest)?; - src = self.follow_type(src)?; - if dest == src { - return None; - } - match (dest, src) { - (Type::Error, _) | (_, Type::Error) => None, - (Type::Placeholder, _) | (_, Type::Placeholder) => None, - (Type::Infer, Type::Infer) => 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 bx(self) -> Box { + Box::new(self) + } +} + +pub trait TypeIDed { + fn type_id(&self, vars: &[UVar]) -> TypeID; +} + +impl TypeIDed for TypeID { + fn type_id(&self, _: &[UVar]) -> TypeID { + *self + } +} + +impl TypeIDed for &TypeID { + fn type_id(&self, _: &[UVar]) -> TypeID { + **self + } +} + +impl TypeIDed for VarID { + fn type_id(&self, vars: &[UVar]) -> TypeID { + vars[self].ty + } +} + +impl TypeIDed for VarInst { + fn type_id(&self, vars: &[UVar]) -> TypeID { + self.id.type_id(vars) + } +} + +impl TypeIDed for &VarInst { + fn type_id(&self, vars: &[UVar]) -> TypeID { + self.id.type_id(vars) } } diff --git a/src/ir/upper/validate.rs b/src/ir/upper/validate.rs index 97c9292..8a49c42 100644 --- a/src/ir/upper/validate.rs +++ b/src/ir/upper/validate.rs @@ -13,26 +13,6 @@ impl UProgram { false, ); } - for (id, var) in self.iter_vars() { - if var.ty == Type::Error { - output.err(CompilerMsg { - msg: format!("Var {:?} is error type!", id), - spans: vec![self.origins.get(id)], - }); - } - if var.ty == Type::Infer { - output.err(CompilerMsg { - msg: format!("Var {:?} cannot be inferred!", id), - spans: vec![self.origins.get(id)], - }); - } - if var.ty == Type::Placeholder { - output.err(CompilerMsg { - msg: format!("Var {:?} still placeholder!", id), - spans: vec![self.origins.get(id)], - }); - } - } } pub fn validate_fn( @@ -47,50 +27,50 @@ impl UProgram { let mut no_ret = true; for i in instructions { match &i.i { - UInstruction::Mv { dest, src } => { + UInstruction::Mv { dst: dest, src } => { let dest = self.expect(dest.id); let src = self.expect(src.id); - output.check_assign(self, &src.ty, &dest.ty, i.span); + output.check_assign(self, &src.ty, &dest.ty, i.origin); } - UInstruction::Ref { dest, src } => { + UInstruction::Ref { dst: dest, src } => { let dest = self.expect(dest.id); let src = self.expect(src.id); - output.check_assign(self, &src.ty.clone().rf(), &dest.ty, i.span); + output.check_assign(self, &src.ty.clone().rf(), &dest.ty, i.origin); } - UInstruction::LoadData { dest, src } => { + UInstruction::LoadData { dst: dest, src } => { let dest = self.expect(dest.id); let src = self.expect(*src); - output.check_assign(self, &src.ty, &dest.ty, i.span); + output.check_assign(self, &src.ty, &dest.ty, i.origin); } - UInstruction::LoadSlice { dest, src } => { + UInstruction::LoadSlice { dst: dest, src } => { let dest = self.expect(dest.id); let src = self.expect(*src); let Type::Array(srcty, ..) = &src.ty else { todo!() }; - output.check_assign(self, &Type::Slice(srcty.clone()), &dest.ty, i.span); + output.check_assign(self, &Type::Slice(srcty.clone()), &dest.ty, i.origin); } - UInstruction::LoadFn { dest, src } => todo!(), - UInstruction::Call { dest, f, args } => { + UInstruction::LoadFn { dst: dest, src } => todo!(), + UInstruction::Call { dst: dest, f, args } => { let destty = &self.expect(dest.id).ty; let f = self.expect(f.id); let Type::Fn { args: argtys, ret } = &f.ty else { output.err(CompilerMsg { msg: format!("Type {} is not callable", self.type_name(&f.ty)), - spans: vec![dest.span], + spans: vec![dest.origin], }); continue; }; - output.check_assign(self, ret, destty, dest.span); + output.check_assign(self, ret, destty, dest.origin); if args.len() != argtys.len() { output.err(CompilerMsg { msg: "Wrong number of arguments to function".to_string(), - spans: vec![dest.span], + spans: vec![dest.origin], }); } for (dst_ty, src) in argtys.iter().zip(args) { let src_var = self.expect(src.id); - output.check_assign(self, &src_var.ty, dst_ty, src.span); + output.check_assign(self, &src_var.ty, dst_ty, src.origin); } } UInstruction::AsmBlock { instructions, args } => { @@ -109,10 +89,10 @@ impl UProgram { } UInstruction::Ret { src } => { let srcty = &self.expect(src.id).ty; - output.check_assign(self, srcty, ret, src.span); + output.check_assign(self, srcty, ret, src.origin); no_ret = false; } - UInstruction::Construct { dest, fields } => { + UInstruction::Construct { dst: dest, fields } => { let dest_def = self.expect(dest.id); let sty = match &dest_def.ty { Type::Struct(sty) => sty, @@ -122,7 +102,7 @@ impl UProgram { "Type {} cannot be constructed", self.type_name(&dest_def.ty) ), - spans: vec![dest.span], + spans: vec![dest.origin], }); continue; } @@ -139,18 +119,18 @@ impl UProgram { } } let ety = &self.expect(var.id).ty; - output.check_assign(self, ety, fty, var.span); + output.check_assign(self, ety, fty, var.origin); } else { output.err(CompilerMsg { msg: format!("field '{}' missing from struct", name), - spans: vec![dest.span], + spans: vec![dest.origin], }); } } } UInstruction::If { cond, body } => { let cond = self.expect(cond.id); - output.check_assign(self, &cond.ty, &Type::Bits(64), i.span); + output.check_assign(self, &cond.ty, &Type::Bits(64), i.origin); self.validate_fn(body, origin, ret, output, false, breakable); } UInstruction::Loop { body } => { @@ -160,7 +140,7 @@ impl UProgram { if !breakable { output.err(CompilerMsg { msg: "Can't break here (outside of loop)".to_string(), - spans: vec![i.span], + spans: vec![i.origin], }); } // TODO @@ -169,7 +149,7 @@ impl UProgram { if !breakable { output.err(CompilerMsg { msg: "Can't continue here (outside of loop)".to_string(), - spans: vec![i.span], + spans: vec![i.origin], }); } // TODO diff --git a/src/main.rs b/src/main.rs index 18d3a0b..f32ad8a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,12 +9,11 @@ pub const FILE_EXT: &str = "lang"; use common::{CompilerOutput, SrcFile}; use ir::{LProgram, UProgram}; -use parser::{Import, Imports, PModule, ParseResult, ParserCtx}; +use parser::{Import, Imports, PModule, ParserCtx}; use std::{ - collections::{HashMap, HashSet}, - ffi::OsString, + collections::HashSet, fs::{create_dir_all, OpenOptions}, - io::{stdout, BufRead, BufReader}, + io::stdout, os::unix::fs::OpenOptionsExt, path::{Path, PathBuf}, process::Command, @@ -88,8 +87,7 @@ impl UProgram { 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); + program.resolve(&mut output); // println!("vars:"); // for (id, def) in program.iter_vars() { // println!(" {id:?} = {}: {}", program.names.path(id), program.type_name(&def.ty)); diff --git a/src/parser/v3/ctx.rs b/src/parser/v3/ctx.rs index 452d456..2b9bb10 100644 --- a/src/parser/v3/ctx.rs +++ b/src/parser/v3/ctx.rs @@ -3,8 +3,7 @@ use std::ops::{Deref, DerefMut}; use crate::common::FileID; use super::{ - CompilerMsg, CompilerOutput, MaybeParsable, Node, NodeParseResult, Parsable, ParsableWith, - ParseResult, TokenCursor, + CompilerMsg, CompilerOutput, Node, NodeParseResult, Parsable, ParsableWith, TokenCursor, }; pub struct ParserCtx<'a> { @@ -36,12 +35,15 @@ impl<'a> ParserCtx<'a> { pub fn parse(&mut self) -> NodeParseResult { Node::parse(self) } + pub fn maybe_parse(&mut self) -> Option> + where + Option: Parsable, + { + Node::maybe_parse(self) + } pub fn parse_with(&mut self, data: T::Data) -> NodeParseResult { Node::parse_with(self, data) } - pub fn maybe_parse(&mut self) -> Option> { - Node::maybe_parse(self) - } pub fn new(file: FileID, string: &'a str, output: &'a mut CompilerOutput) -> Self { Self { cursor: TokenCursor::from_file_str(file, string), diff --git a/src/parser/v3/lower/block.rs b/src/parser/v3/lower/block.rs index bb3f469..5c8d490 100644 --- a/src/parser/v3/lower/block.rs +++ b/src/parser/v3/lower/block.rs @@ -27,15 +27,15 @@ impl FnLowerable for PBlock { }, } } - ctx.program.push(); + ctx.b.push(); // then lower imports for i_n in &import_nodes { if let Some(i) = i_n.as_ref() { let name = &i.0; - let path = ctx.program.path_for(name); + let path = ctx.b.path_for(name); let import = Import(path.clone()); if ctx.imports.insert(import) { - ctx.program.def_searchable::( + ctx.b.def_searchable::( name, Some(UVar { ty: Type::Module(path), @@ -48,27 +48,27 @@ impl FnLowerable for PBlock { // then lower const things let mut structs = Vec::new(); for s in &struct_nodes { - structs.push(s.lower_name(ctx.program)); + structs.push(s.lower_name(ctx.b)); } for (s, id) in struct_nodes.iter().zip(structs) { if let Some(id) = id { - s.lower(id, ctx.program, ctx.output); + s.lower(id, ctx.b, ctx.output); } } let mut fns = Vec::new(); for f in &fn_nodes { - fns.push(f.lower_name(ctx.program)); + fns.push(f.lower_name(ctx.b)); } for (f, id) in fn_nodes.iter().zip(fns) { if let Some(id) = id { - f.lower(id, ctx.program, ctx.imports, ctx.output) + f.lower(id, ctx.b, ctx.imports, ctx.output) } } // then lower statements for s in statements { last = s.lower(ctx); } - ctx.program.pop(); + ctx.b.pop(); last } } @@ -78,11 +78,11 @@ impl FnLowerable for PStatement { fn lower(&self, ctx: &mut FnLowerCtx) -> Option { match self { PStatement::Let(def, e) => { - let def = def.lower(ctx.program, ctx.output)?; + let def = def.lower(ctx.b, ctx.output)?; let res = e.lower(ctx); if let Some(res) = res { ctx.push(UInstruction::Mv { - dest: def, + dst: def, src: res, }); } @@ -91,10 +91,10 @@ impl FnLowerable for PStatement { PStatement::Return(e) => { if let Some(e) = e { let src = e.lower(ctx)?; - ctx.push_at(UInstruction::Ret { src }, src.span); + ctx.push_at(UInstruction::Ret { src }, src.origin); } else { let src = ctx.temp(Type::Unit); - ctx.push_at(UInstruction::Ret { src }, src.span); + ctx.push_at(UInstruction::Ret { src }, src.origin); } None } diff --git a/src/parser/v3/lower/def.rs b/src/parser/v3/lower/def.rs index 6023b0d..bcb8f23 100644 --- a/src/parser/v3/lower/def.rs +++ b/src/parser/v3/lower/def.rs @@ -1,21 +1,18 @@ -use crate::ir::{Type, UProgram, UVar, VarInst}; +use crate::ir::{UProgram, UVar, VarInst}; use super::{CompilerOutput, Node, PVarDef}; impl Node { pub fn lower(&self, program: &mut UProgram, output: &mut CompilerOutput) -> Option { let s = self.as_ref()?; - let name = s - .name - .as_ref() - .map_or("{error}", |v| v); + let name = s.name.as_ref().map_or("{error}", |v| v); let ty = match &s.ty { Some(ty) => ty.lower(program, output), - None => Type::Infer, + None => program.infer(self.origin), }; Some(VarInst { id: program.def_searchable(name, Some(UVar { ty }), self.origin), - span: self.origin, + origin: self.origin, }) } } diff --git a/src/parser/v3/lower/expr.rs b/src/parser/v3/lower/expr.rs index 9a86934..aef350a 100644 --- a/src/parser/v3/lower/expr.rs +++ b/src/parser/v3/lower/expr.rs @@ -1,60 +1,61 @@ use super::{func::FnLowerCtx, FnLowerable, PExpr, PostfixOp}; use crate::{ - ir::{FieldRef, Type, UData, UInstruction, VarInst}, + ir::{Type, UData, UInstruction, VarInst}, parser::InfixOp, }; impl FnLowerable for PExpr { type Output = VarInst; fn lower(&self, ctx: &mut FnLowerCtx) -> Option { - Some(match self { - PExpr::Lit(l) => match l.as_ref()? { + let mut e = self; + let mut path = Vec::new(); + while let PExpr::Member(node, ident) = e { + e = if let Some(t) = node.as_ref() { + path.push(ident); + &**t + } else { + return None; + }; + } + Some(match e { + PExpr::Lit(l) => match l { super::PLiteral::String(s) => { - let dest = ctx.program.temp_var(l.origin, Type::Bits(8).slice()); + let dest = ctx.b.temp_var(ctx.origin, Type::Bits(8).slice()); let data = s.as_bytes().to_vec(); - let src = ctx.program.def( - &format!("string \"{}\"", s.replace("\n", "\\n")), - Some(UData { - ty: Type::Bits(8).arr(data.len() as u32), - content: data, - }), - l.origin, - ); - ctx.push(UInstruction::LoadSlice { dest, src }); + let src = ctx.b.def_data(UData { + name: format!("string \"{}\"", s.replace("\n", "\\n")), + ty: Type::Bits(8).arr(data.len() as u32), + content: data, + }); + ctx.push(UInstruction::LoadSlice { dst: dest, src }); dest } super::PLiteral::Char(c) => { let ty = Type::Bits(8); - let dest = ctx.program.temp_var(l.origin, ty.clone()); - let src = ctx.program.def( - &format!("char '{c}'"), - Some(UData { - ty, - content: c.to_string().as_bytes().to_vec(), - }), - l.origin, - ); - ctx.push(UInstruction::LoadData { dest, src }); + let dest = ctx.b.temp_var(ctx.origin, ty.clone()); + let src = ctx.b.def_data(UData { + name: format!("char '{c}'"), + ty, + content: c.to_string().as_bytes().to_vec(), + }); + ctx.push(UInstruction::LoadData { dst: dest, src }); dest } super::PLiteral::Number(n) => { // TODO: temp let ty = Type::Bits(64); - let dest = ctx.program.temp_var(l.origin, ty.clone()); - let src = ctx.program.def( - &format!("num {n:?}"), - Some(UData { - ty, - content: n.whole.parse::().unwrap().to_le_bytes().to_vec(), - }), - l.origin, - ); - ctx.push(UInstruction::LoadData { dest, src }); + let dest = ctx.b.temp_var(ctx.origin, ty.clone()); + let src = ctx.b.def_data(UData { + name: format!("num {n:?}"), + ty, + content: n.whole.parse::().unwrap().to_le_bytes().to_vec(), + }); + ctx.push(UInstruction::LoadData { dst: dest, src }); dest } - super::PLiteral::Unit => ctx.program.temp_var(l.origin, Type::Unit), + super::PLiteral::Unit => ctx.b.temp_var(ctx.origin, Type::Unit), }, - PExpr::Ident(i) => ctx.get_var(i)?, + PExpr::Ident(i) => ctx.get_var(i), PExpr::BinaryOp(op, e1, e2) => match op { InfixOp::Add => todo!(), InfixOp::Sub => todo!(), @@ -62,49 +63,30 @@ impl FnLowerable for PExpr { InfixOp::Div => todo!(), InfixOp::LessThan => todo!(), InfixOp::GreaterThan => todo!(), - InfixOp::Member => { - let res1 = e1.lower(ctx)?; - let Some(box PExpr::Ident(ident)) = &e2.inner else { - ctx.err("Field accessors must be identifiers".to_string()); - return None; - }; - let fname = ident.as_ref()?.0.clone(); - ctx.temp(Type::Field(FieldRef { - parent: res1.id, - name: fname, - })) - } InfixOp::Assign => { let res1 = e1.lower(ctx)?; let res2 = e2.lower(ctx)?; ctx.push(UInstruction::Mv { - dest: res1, + dst: res1, src: res2, }); res1 } }, - PExpr::PostfixOp(op, e) => { + PExpr::PostfixOp(e, op) => { let res = e.lower(ctx)?; match op { PostfixOp::Ref => { - let temp = ctx.temp(ctx.program.expect(res.id).ty.clone().rf()); - ctx.push(UInstruction::Ref { - dest: temp, - src: res, - }); - temp + let ty = ctx.b.vars[res.id].ty.rf(); + let dest = ctx.temp(ty); + ctx.push(UInstruction::Ref { dst: dest, src: res }); + dest } PostfixOp::Deref => { - let t = &ctx.program.expect(res.id).ty; - let Type::Ref(_) = t else { - ctx.err(format!( - "Cannot dereference type {:?}", - ctx.program.type_name(t) - )); - return None; - }; - todo!(); + let ty = ctx.b.vars[res.id].ty.derf(); + let dest = ctx.temp(ty); + ctx.push(UInstruction::Deref { dst: dest, src: res }); + dest } PostfixOp::Not => todo!(), } @@ -118,37 +100,36 @@ impl FnLowerable for PExpr { let arg = arg.lower(ctx)?; nargs.push(arg); } - let ty = ctx - .program - .get_fn_var(fe.id) - .map(|f| f.ret.clone()) - .unwrap_or(Type::Placeholder); - let temp = ctx.temp(ty); + let dest = ctx.temp(Type::Placeholder); ctx.push(UInstruction::Call { - dest: temp, + dst: dest, f: fe, args: nargs, }); - temp + dest } PExpr::Group(e) => e.lower(ctx)?, - PExpr::Construct(c) => c.lower(ctx)?, + PExpr::Construct(e, map) => { + let dest = ctx.temp(Type::Placeholder); + ctx.push(UInstruction::Construct { dst: dest, fields: () }); + dest + } PExpr::If(cond, body) => { let cond = cond.lower(ctx)?; - ctx.program.push(); + ctx.b.push(); let mut body_ctx = ctx.branch(); body.lower(&mut body_ctx); let body = body_ctx.instructions; - ctx.program.pop(); + ctx.b.pop(); ctx.push(UInstruction::If { cond, body }); return None; } PExpr::Loop(body) => { - ctx.program.push(); + ctx.b.push(); let mut body_ctx = ctx.branch(); body.lower(&mut body_ctx); let body = body_ctx.instructions; - ctx.program.pop(); + ctx.b.pop(); ctx.push(UInstruction::Loop { body }); return None; } @@ -160,6 +141,13 @@ impl FnLowerable for PExpr { ctx.push(UInstruction::Continue); return None; } + PExpr::Member(e, name) => { + ctx.err("Can't access a member here".to_string()); + return None; + } + PExpr::Field(e, name) => { + todo!() + } }) } } diff --git a/src/parser/v3/lower/func.rs b/src/parser/v3/lower/func.rs index f1703fc..cedc690 100644 --- a/src/parser/v3/lower/func.rs +++ b/src/parser/v3/lower/func.rs @@ -1,6 +1,11 @@ +use std::collections::HashMap; + use super::{CompilerMsg, CompilerOutput, FileSpan, FnLowerable, Imports, Node, PFunction}; use crate::{ - ir::{FnID, Idents, Type, UFunc, UInstrInst, UInstruction, UProgram, UVar, VarInst}, + ir::{ + FnID, Idents, Typable, Type, UFunc, UInstrInst, UInstruction, UModuleBuilder, UProgram, + UVar, VarID, VarInst, + }, parser, }; @@ -54,7 +59,7 @@ impl PFunction { }; let mut ctx = FnLowerCtx { instructions: Vec::new(), - program: p, + b: p, output, origin: self.body.origin, imports, @@ -62,7 +67,7 @@ impl PFunction { if let Some(src) = self.body.lower(&mut ctx) { ctx.instructions.push(UInstrInst { i: UInstruction::Ret { src }, - span: src.span, + origin: src.origin, }); } let instructions = ctx.instructions; @@ -77,23 +82,24 @@ impl PFunction { } pub struct FnLowerCtx<'a> { - pub program: &'a mut UProgram, + pub b: &'a mut UModuleBuilder<'a>, pub instructions: Vec, pub output: &'a mut CompilerOutput, pub origin: FileSpan, pub imports: &'a mut Imports, + pub var_stack: Vec>, } impl FnLowerCtx<'_> { pub fn get_idents(&mut self, node: &Node) -> Option { let name = node.inner.as_ref()?; - let res = self.program.get_idents(name); + let res = self.b.get_idents(name); if res.is_none() { self.err_at(node.origin, format!("Identifier '{}' not found", name)); } res } - pub fn get_var(&mut self, node: &Node) -> Option { + pub fn get_var(&mut self, node: &Node) -> VarInst { let ids = self.get_idents(node)?; if ids.get::().is_none() { self.err_at( @@ -103,27 +109,42 @@ impl FnLowerCtx<'_> { } ids.get::().map(|id| VarInst { id, - span: node.origin, + origin: node.origin, }) } pub fn err(&mut self, msg: String) { - self.output.err(CompilerMsg::from_span(self.origin, msg)) + self.output.err(CompilerMsg::new(self.origin, msg)) } pub fn err_at(&mut self, span: FileSpan, msg: String) { - self.output.err(CompilerMsg::from_span(span, msg)) + self.output.err(CompilerMsg::new(span, msg)) } - pub fn temp(&mut self, ty: impl Into) -> VarInst { - self.program.temp_var(self.origin, ty) + pub fn temp(&mut self, ty: Type) -> VarInst { + self.b.temp_var(self.origin, ty) } pub fn push(&mut self, i: UInstruction) { self.push_at(i, self.origin); } pub fn push_at(&mut self, i: UInstruction, span: FileSpan) { - self.instructions.push(UInstrInst { i, span }); + match i { + UInstruction::Mv { dst: dest, src } => todo!(), + UInstruction::Ref { dst: dest, src } => todo!(), + UInstruction::LoadData { dst: dest, src } => todo!(), + UInstruction::LoadSlice { dst: dest, src } => todo!(), + UInstruction::LoadFn { dst: dest, src } => todo!(), + UInstruction::Call { dst: dest, f, args } => todo!(), + UInstruction::AsmBlock { instructions, args } => todo!(), + UInstruction::Ret { src } => todo!(), + UInstruction::Construct { dst: dest, fields } => todo!(), + UInstruction::If { cond, body } => todo!(), + UInstruction::Loop { body } => todo!(), + UInstruction::Break => todo!(), + UInstruction::Continue => todo!(), + } + self.instructions.push(UInstrInst { i, origin: span }); } pub fn branch<'a>(&'a mut self) -> FnLowerCtx<'a> { FnLowerCtx { - program: self.program, + b: self.b, instructions: Vec::new(), output: self.output, origin: self.origin, diff --git a/src/parser/v3/lower/mod.rs b/src/parser/v3/lower/mod.rs index c815991..8eca6c8 100644 --- a/src/parser/v3/lower/mod.rs +++ b/src/parser/v3/lower/mod.rs @@ -23,7 +23,7 @@ impl PModule { let fid = p.def_searchable(&name, None, self.block.origin); p.push_name(&name); let mut fctx = FnLowerCtx { - program: p, + b: p, instructions: Vec::new(), output, origin: self.block.origin, diff --git a/src/parser/v3/lower/struc.rs b/src/parser/v3/lower/struc.rs index 38f5cae..9b5d949 100644 --- a/src/parser/v3/lower/struc.rs +++ b/src/parser/v3/lower/struc.rs @@ -1,7 +1,7 @@ use crate::{ common::{CompilerOutput, FileSpan}, - ir::{StructField, StructID, UInstruction, UProgram, UStruct, VarInst}, - parser::{Node, PMap, PConstructFields, PStruct, PStructFields}, + ir::{StructField, StructID, UInstruction, UModuleBuilder, UProgram, UStruct, VarInst}, + parser::{Node, PMap, PStruct, PStructFields}, }; use super::{FnLowerCtx, FnLowerable}; @@ -9,7 +9,7 @@ use super::{FnLowerCtx, FnLowerable}; impl FnLowerable for PMap { type Output = VarInst; fn lower(&self, ctx: &mut FnLowerCtx) -> Option { - let ty = self.name.lower(ctx.program, ctx.output); + let ty = self.name.lower(ctx.b, ctx.output); let fields = match &self.fields { PConstructFields::Named(nodes) => nodes .iter() @@ -32,7 +32,7 @@ impl FnLowerable for PMap { PConstructFields::None => Default::default(), }; let id = ctx.temp(ty); - ctx.push(UInstruction::Construct { dest: id, fields }); + ctx.push(UInstruction::Construct { dst: id, fields }); Some(id) } } @@ -41,7 +41,7 @@ impl PStruct { pub fn lower( &self, id: StructID, - p: &mut UProgram, + p: &mut UModuleBuilder, output: &mut CompilerOutput, span: FileSpan, ) -> Option<()> { @@ -71,20 +71,23 @@ impl PStruct { .into_iter() .map(|(name, ty)| (name, StructField { ty })) .collect(); - p.write(id, UStruct { generics, fields }); + let name = self.name.as_ref()?.to_string(); + p.def_data(UStruct { + name, + generics, + fields, + origin: span, + }); p.pop(); Some(()) } } impl Node { - pub fn lower_name(&self, p: &mut UProgram) -> Option { + pub fn lower(&self, id: StructID, p: &mut UProgram, output: &mut CompilerOutput) -> Option<()> { let s = self.as_ref()?; let name = s.name.as_ref()?; - let id = p.def_searchable(name, None, s.name.origin); - Some(id) - } - pub fn lower(&self, id: StructID, p: &mut UProgram, output: &mut CompilerOutput) { - self.as_ref().map(|i| i.lower(id, p, output, self.origin)); + s.lower(id, p, output, self.origin); + Some(()) } } diff --git a/src/parser/v3/lower/ty.rs b/src/parser/v3/lower/ty.rs index ea768da..d7d362f 100644 --- a/src/parser/v3/lower/ty.rs +++ b/src/parser/v3/lower/ty.rs @@ -1,52 +1,110 @@ use crate::{ - ir::{StructInst, Type, TypeID, UGeneric, UProgram, UStruct}, + ir::{GenericID, MemberID, ModPath, Type, TypeID, UGeneric, UModuleBuilder, UProgram}, parser::PGenericDef, }; -use super::{CompilerMsg, CompilerOutput, FileSpan, Node, PType}; +use super::{CompilerOutput, FileSpan, Node, PType}; -impl Node { - pub fn lower(&self, namespace: &mut UProgram, output: &mut CompilerOutput) -> TypeID { +impl Node> { + pub fn lower(&self, p: &mut UModuleBuilder, output: &mut CompilerOutput) -> TypeID { self.as_ref() - .map(|t| t.lower(namespace, output, self.origin)) - .unwrap_or(Type::Error) + .map(|t| t.lower(p, output, self.origin)) + .unwrap_or(p.error()) + } +} +impl Node { + pub fn lower(&self, p: &mut UModuleBuilder, output: &mut CompilerOutput) -> TypeID { + self.as_ref() + .map(|t| t.lower(p, output, self.origin)) + .unwrap_or(p.error()) } } +fn test() {} + impl PType { - pub fn lower(&self, p: &mut UProgram, output: &mut CompilerOutput, span: FileSpan) -> TypeID { - let Some(name) = self.name.as_ref() else { - return p.error_type(); - }; - let ids = p.get_idents(name); - // TODO: should generics always take precedence? - if let Some(id) = ids.and_then(|ids| ids.get::()) { - 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(StructInst { id, args }) - } else if let Ok(num) = name.parse::() { - Type::Bits(num) - } else { - match name.as_str() { - "slice" => { - let inner = self.args[0].lower(p, output); - Type::Slice(Box::new(inner)) - } - "_" => Type::Infer, - _ => { - output.err(CompilerMsg::from_span(span, "Type not found".to_string())); - Type::Error - } - } + pub fn lower( + &self, + p: &mut UModuleBuilder, + output: &mut CompilerOutput, + mut origin: FileSpan, + ) -> TypeID { + let mut ty = self; + let mut path = Vec::new(); + while let PType::Member(node, ident) = ty { + ty = if let Some(t) = node.as_ref() { + let Some(name) = ident.as_ref() else { + return p.error(); + }; + origin = node.origin; + path.push(MemberID { + name: name.0.clone(), + origin: ident.origin, + }); + &**t + } else { + return p.error(); + }; } + if !path.is_empty() { + let PType::Ident(id) = ty else { + return p.error(); + }; + path.push(MemberID { + name: id.0.clone(), + origin, + }); + return p.def_ty(Type::Unres(ModPath { m: p.module, path })); + } + let ty = match ty { + PType::Member(_, _) => unreachable!(), + PType::Ident(node) => { + path.push(MemberID { + name: node.0.clone(), + origin, + }); + path.reverse(); + Type::Unres(ModPath { m: p.module, path }) + } + PType::Ref(node) => node.lower(p, output).rf(), + PType::Generic(node, nodes) => todo!(), + }; + p.def_ty(ty) + // let Some(name) = self.name.as_ref() else { + // return p.error(); + // }; + // let ids = p.get_idents(name); + // // TODO: should generics always take precedence? + // if let Some(id) = ids.and_then(|ids| ids.get::()) { + // 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(StructInst { id, args }) + // } else if let Ok(num) = name.parse::() { + // Type::Bits(num) + // } else { + // match name.as_str() { + // "slice" => { + // let inner = self.args[0].lower(p, output); + // Type::Slice(Box::new(inner)) + // } + // "_" => Type::Infer, + // _ => { + // output.err(CompilerMsg::from_span(span, "Type not found".to_string())); + // Type::Error + // } + // } + // } } } impl Node { pub fn lower(&self, p: &mut UProgram) -> Option { let s = self.as_ref()?; - let name = s.name.as_ref()?; - Some(p.def_searchable(name, Some(UGeneric {}), self.origin)) + let name = s.name.as_ref()?.to_string(); + Some(p.def_generic(UGeneric { + name, + origin: self.origin, + })) } } diff --git a/src/parser/v3/nodes/asm_instr.rs b/src/parser/v3/nodes/asm_instr.rs index 6573daf..53ef927 100644 --- a/src/parser/v3/nodes/asm_instr.rs +++ b/src/parser/v3/nodes/asm_instr.rs @@ -6,7 +6,7 @@ pub struct PInstruction { } pub enum PAsmArg { - Value(Node), + Value(PIdent), Ref(Node), } @@ -33,7 +33,7 @@ impl Parsable for PInstruction { impl Parsable for PAsmArg { fn parse(ctx: &mut ParserCtx) -> ParseResult { if let Some(ident) = ctx.maybe_parse() { - return ParseResult::Ok(Self::Value(ident)); + return ParseResult::Wrap(ident.map(Self::Value)); } let mut next = ctx.expect_peek()?; @@ -41,10 +41,10 @@ impl Parsable for PAsmArg { ctx.next(); if let Some(mut ident) = ctx.maybe_parse::() { // TODO: this is so messed up - if let Some(i) = ident.as_mut() { + if let Some(i) = ident.node.as_mut() { i.0.insert(0, '-') } - return ParseResult::Ok(Self::Value(ident)); + return ParseResult::Wrap(ident.map(Self::Value)); } next = ctx.expect_peek()?; } diff --git a/src/parser/v3/nodes/def.rs b/src/parser/v3/nodes/def.rs index f2d9874..e484d45 100644 --- a/src/parser/v3/nodes/def.rs +++ b/src/parser/v3/nodes/def.rs @@ -1,7 +1,7 @@ use std::fmt::Debug; use super::{ - CompilerMsg, MaybeParsable, Node, PExpr, PIdent, PType, Parsable, ParseResult, ParserCtx, + CompilerMsg, Node, PExpr, PIdent, PType, Parsable, ParseResult, ParserCtx, Symbol, Token, }; @@ -55,28 +55,28 @@ pub enum SelfType { Take, } -impl MaybeParsable for SelfVar { - fn maybe_parse(ctx: &mut ParserCtx) -> Result, CompilerMsg> { - if let Some(mut next) = ctx.peek() { - let mut ty = SelfType::Take; - if next.is_symbol(Symbol::Ampersand) { - ctx.next(); - ty = SelfType::Ref; - next = ctx.expect_peek()?; - } - if let Token::Word(name) = &next.token { - if name == "self" { - ctx.next(); - return Ok(Some(Self { ty })); - } - } - if ty != SelfType::Take { - return Err(CompilerMsg::unexpected_token(next, "self")); - } - } - Ok(None) - } -} +// impl Parsable for Option { +// fn maybe_parse(ctx: &mut ParserCtx) -> Result, CompilerMsg> { +// if let Some(mut next) = ctx.peek() { +// let mut ty = SelfType::Take; +// if next.is_symbol(Symbol::Ampersand) { +// ctx.next(); +// ty = SelfType::Ref; +// next = ctx.expect_peek()?; +// } +// if let Token::Word(name) = &next.token { +// if name == "self" { +// ctx.next(); +// return Ok(Some(Self { ty })); +// } +// } +// if ty != SelfType::Take { +// return Err(CompilerMsg::unexpected_token(next, "self")); +// } +// } +// Ok(None) +// } +// } impl Debug for PVarDef { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/src/parser/v3/nodes/expr.rs b/src/parser/v3/nodes/expr.rs index 4a5c401..73501c7 100644 --- a/src/parser/v3/nodes/expr.rs +++ b/src/parser/v3/nodes/expr.rs @@ -12,14 +12,15 @@ use super::{ type BoxNode = Node>; pub enum PExpr { - Lit(Node), + Lit(PLiteral), Ident(Node), BinaryOp(InfixOp, BoxNode, BoxNode), PostfixOp(BoxNode, PostfixOp), Block(Node), Call(BoxNode, Vec>), Group(BoxNode), - Access(BoxNode, Node), + Field(BoxNode, Node), + Member(BoxNode, Node), AsmBlock(Node), Construct(BoxNode, Node), If(BoxNode, BoxNode), @@ -69,7 +70,11 @@ impl PExpr { continue; } else if next.is_symbol(Symbol::Dot) { let field = ctx.parse()?; - e1 = Self::Access(Node::new(e1, span).bx(), field); + e1 = Self::Field(Node::new(e1, span).bx(), field); + continue; + } else if next.is_symbol(Symbol::DoubleColon) { + let field = ctx.parse()?; + e1 = Self::Member(Node::new(e1, span).bx(), field); continue; } else if let Some(op) = PostfixOp::from_token(next) { ctx.next(); @@ -86,10 +91,7 @@ impl PExpr { ctx.next(); if ctx.expect_peek()?.is_symbol(Symbol::CloseParen) { ctx.next(); - return ParseResult::Ok(PExpr::Lit(Node::new( - PLiteral::Unit, - ctx.next_start().char_span(), - ))); + return ParseResult::Ok(PExpr::Lit(PLiteral::Unit)); } let res = ctx.parse(); if res.recover { @@ -118,8 +120,8 @@ impl PExpr { } else if next.is_keyword(Keyword::Asm) { ctx.next(); Self::AsmBlock(ctx.parse()?) - } else if let Some(val) = ctx.maybe_parse() { - Self::Lit(val) + } else if let Some(res) = ctx.maybe_parse::() { + return ParseResult::Wrap(res.map(Self::Lit)); } else { let res = ctx.parse(); if res.node.is_some() { @@ -188,7 +190,8 @@ impl Debug for PExpr { PExpr::Loop(res) => write!(f, "loop -> {res:?}")?, PExpr::Break => write!(f, "break")?, PExpr::Continue => write!(f, "continue")?, - PExpr::Access(e1, name) => write!(f, "{:?}.{:?}", e1, name)?, + PExpr::Field(e1, name) => write!(f, "{:?}.{:?}", e1, name)?, + PExpr::Member(e1, name) => write!(f, "{:?}::{:?}", e1, name)?, } Ok(()) } diff --git a/src/parser/v3/nodes/ident.rs b/src/parser/v3/nodes/ident.rs index c6929a9..0816dba 100644 --- a/src/parser/v3/nodes/ident.rs +++ b/src/parser/v3/nodes/ident.rs @@ -1,4 +1,4 @@ -use super::{MaybeParsable, Parsable, ParseResult, ParserCtx, Token, CompilerMsg}; +use super::{CompilerMsg, Parsable, ParseResult, ParserCtx, Token}; use std::{ fmt::{Debug, Display}, ops::Deref, @@ -19,15 +19,17 @@ impl Parsable for PIdent { } } -impl MaybeParsable for PIdent { - fn maybe_parse(ctx: &mut ParserCtx) -> Result, CompilerMsg> { - let Some(next) = ctx.peek() else { return Ok(None) }; +impl Parsable for Option { + fn parse(ctx: &mut ParserCtx) -> ParseResult { + let Some(next) = ctx.peek() else { + return ParseResult::Ok(None); + }; let Token::Word(name) = &next.token else { - return Ok(None); + return ParseResult::Ok(None); }; let name = name.to_string(); ctx.next(); - Ok(Some(Self(name))) + ParseResult::Ok(Some(PIdent(name))) } } diff --git a/src/parser/v3/nodes/lit.rs b/src/parser/v3/nodes/lit.rs index bd3aaf3..96b76f8 100644 --- a/src/parser/v3/nodes/lit.rs +++ b/src/parser/v3/nodes/lit.rs @@ -1,4 +1,6 @@ -use super::{CharCursor, MaybeParsable, ParserCtx, CompilerMsg, Symbol, Token}; +use crate::parser::{Parsable, ParseResult}; + +use super::{PString, ParserCtx, Symbol, Token}; use std::fmt::Debug; #[derive(Clone, PartialEq, Eq)] @@ -16,26 +18,29 @@ pub struct PNumber { pub ty: Option, } -impl MaybeParsable for PLiteral { - fn maybe_parse(ctx: &mut ParserCtx) -> Result, CompilerMsg> { +impl Parsable for Option { + fn parse(ctx: &mut ParserCtx) -> ParseResult { let inst = ctx.expect_peek()?; - Ok(Some(match &inst.token { + ParseResult::Ok(Some(match &inst.token { Token::Symbol(Symbol::SingleQuote) => { let chars = ctx.chars(); let c = chars.expect_next()?; chars.expect('\'')?; ctx.next(); - Self::Char(c) + PLiteral::Char(c) } Token::Symbol(Symbol::DoubleQuote) => { - let res = Self::String(string_from(ctx.chars())?); ctx.next(); - res + let s = ctx.parse::()?; + return match s.inner { + Some(s) => ParseResult::Ok(Some(PLiteral::String(s.0))), + None => ParseResult::SubErr, + }; } Token::Word(text) => { let first = text.chars().next().unwrap(); if !first.is_ascii_digit() { - return Ok(None); + return ParseResult::Ok(None); } let (whole, ty) = parse_whole_num(text); let mut num = PNumber { @@ -57,9 +62,9 @@ impl MaybeParsable for PLiteral { } } } - Self::Number(num) + PLiteral::Number(num) } - _ => return Ok(None), + _ => return ParseResult::Ok(None), })) } } @@ -81,32 +86,6 @@ pub fn parse_whole_num(text: &str) -> (String, Option) { (whole, if ty.is_empty() { None } else { Some(ty) }) } -pub fn string_from(cursor: &mut CharCursor) -> Result { - let mut str = String::new(); - loop { - let c = cursor.expect_next()?; - if c == '"' { - return Ok(str); - } - str.push(match c { - '\\' => { - let next = cursor.expect_next()?; - match next { - '"' => '"', - '\'' => '\'', - 't' => '\t', - 'n' => '\n', - '0' => '\0', - _ => { - todo!(); - } - } - } - _ => c, - }) - } -} - impl Debug for PLiteral { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { diff --git a/src/parser/v3/nodes/mod.rs b/src/parser/v3/nodes/mod.rs index dc62c4a..8c0c55e 100644 --- a/src/parser/v3/nodes/mod.rs +++ b/src/parser/v3/nodes/mod.rs @@ -9,6 +9,7 @@ mod ident; mod lit; mod op; mod statement; +mod string; mod struc; mod trai; mod ty; @@ -28,10 +29,9 @@ pub use statement::*; pub use struc::*; pub use trai::*; pub use ty::*; +pub use string::*; -use crate::ir::UProgram; - -use super::{lower::{FnLowerCtx, FnLowerable}, *}; +use super::*; pub struct PModule { pub block: Node, diff --git a/src/parser/v3/nodes/op.rs b/src/parser/v3/nodes/op.rs index 71bf647..b3b2c48 100644 --- a/src/parser/v3/nodes/op.rs +++ b/src/parser/v3/nodes/op.rs @@ -78,7 +78,7 @@ impl PostfixOp { match self { Self::Not => "!", Self::Ref => "@", - Self::Deref => "*", + Self::Deref => "^", } } pub fn from_token(token: &Token) -> Option { @@ -88,7 +88,7 @@ impl PostfixOp { Some(match symbol { Symbol::At => Self::Ref, Symbol::Bang => Self::Not, - Symbol::Asterisk => Self::Deref, + Symbol::Carrot => Self::Deref, _ => { return None; } diff --git a/src/parser/v3/nodes/statement.rs b/src/parser/v3/nodes/statement.rs index af58b78..8a717a7 100644 --- a/src/parser/v3/nodes/statement.rs +++ b/src/parser/v3/nodes/statement.rs @@ -29,7 +29,7 @@ impl Parsable for PStatementLike { let def = ctx.parse()?; ctx.expect_sym(Symbol::Equals)?; ctx.parse() - .map(|expr| Self::Statement(PStatement::Let(def, expr))) + .map_res(|expr| Self::Statement(PStatement::Let(def, expr))) } Token::Keyword(Keyword::Return) => { ctx.next(); @@ -37,7 +37,7 @@ impl Parsable for PStatementLike { ParseResult::Ok(Self::Statement(PStatement::Return(None))) } else { ctx.parse() - .map(|res| Self::Statement(PStatement::Return(Some(res)))) + .map_res(|res| Self::Statement(PStatement::Return(Some(res)))) } } Token::Keyword(Keyword::Fn) => { @@ -52,7 +52,7 @@ impl Parsable for PStatementLike { ctx.next(); ParseResult::Ok(Self::Const(PConstStatement::Import(ctx.parse()?))) } - _ => ctx.parse().map(|n| Self::Statement(PStatement::Expr(n))), + _ => ctx.parse().map_res(|n| Self::Statement(PStatement::Expr(n))), } } } diff --git a/src/parser/v3/nodes/string.rs b/src/parser/v3/nodes/string.rs new file mode 100644 index 0000000..2d4b5a6 --- /dev/null +++ b/src/parser/v3/nodes/string.rs @@ -0,0 +1,41 @@ +use crate::{ + common::CompilerMsg, + parser::{Parsable, ParseResult}, +}; + +pub struct PString(pub String); + +impl Parsable for PString { + fn parse(ctx: &mut crate::parser::ParserCtx) -> ParseResult { + let cursor = ctx.cursor.chars(); + let mut str = String::new(); + loop { + let c = cursor.expect_next()?; + if c == '"' { + return ParseResult::Ok(Self(str)); + } + str.push(match c { + '\\' => { + let start = cursor.prev_pos(); + let next = cursor.expect_next()?; + match next { + '"' => '"', + '\'' => '\'', + 't' => '\t', + 'n' => '\n', + '0' => '\0', + other => { + let end = cursor.prev_pos(); + ctx.output.err(CompilerMsg { + msg: format!("Unknown escape sequence '\\{}'", other), + spans: vec![start.to(end)], + }); + other + } + } + } + _ => c, + }) + } + } +} diff --git a/src/parser/v3/nodes/ty.rs b/src/parser/v3/nodes/ty.rs index 23d8fef..78a4f63 100644 --- a/src/parser/v3/nodes/ty.rs +++ b/src/parser/v3/nodes/ty.rs @@ -2,9 +2,13 @@ use std::fmt::Debug; use super::{util::parse_list, Node, PIdent, Parsable, ParseResult, ParserCtx, Symbol}; -pub struct PType { - pub name: Node, - pub args: Vec>, +type BoxNode = Node>; + +pub enum PType { + Member(BoxNode, Node), + Ref(BoxNode), + Generic(BoxNode, Vec>), + Ident(PIdent), } pub struct PGenericDef { @@ -13,27 +17,30 @@ pub struct PGenericDef { impl Parsable for PType { fn parse(ctx: &mut ParserCtx) -> ParseResult { - let next = ctx.expect_peek()?; - let res = if next.is_symbol(Symbol::Ampersand) { - let name = Node::new(PIdent("&".to_string()), next.span); - ctx.next(); - let arg = ctx.parse()?; - Self { - name, - args: vec![arg], + let start = ctx.next_start(); + let mut cur = ctx.parse()?.map(PType::Ident); + loop { + let span = start.to(ctx.prev_end()); + let Some(next) = ctx.peek() else { + break; + }; + if next.is_symbol(Symbol::Ampersand) { + ctx.next(); + cur = Node::new(PType::Ref(cur.bx()), span); + continue; + } else if next.is_symbol(Symbol::OpenAngle) { + ctx.next(); + let args = parse_list(ctx, Symbol::CloseAngle)?; + cur = Node::new(PType::Generic(cur.bx(), args), span); + continue; + } else if next.is_symbol(Symbol::DoubleColon) { + ctx.next(); + let mem = ctx.parse()?; + cur = Node::new(PType::Member(cur.bx(), mem), span); } - } else { - let n = ctx.parse()?; - let mut args = Vec::new(); - if let Some(next) = ctx.peek() { - if next.is_symbol(Symbol::OpenAngle) { - ctx.next(); - args = parse_list(ctx, Symbol::CloseAngle)?; - } - } - Self { name: n, args } - }; - ParseResult::Ok(res) + break; + } + ParseResult::Wrap(cur) } } @@ -45,11 +52,11 @@ impl Parsable for PGenericDef { impl Debug for PType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{:?}", self.name)?; - if self.name.as_ref().is_some_and(|n| n.0 == "&") { - write!(f, "{:?}", self.args[0])?; - } else if !self.args.is_empty() { - write!(f, "<{:?}>", self.args)?; + match self { + PType::Member(node, name) => write!(f, "{:?}.{:?}", node, name)?, + PType::Ref(node) => write!(f, "{:?}&", node)?, + PType::Generic(node, args) => write!(f, "{:?}<{:?}>", node, args)?, + PType::Ident(node) => node.fmt(f)?, } Ok(()) } diff --git a/src/parser/v3/parse.rs b/src/parser/v3/parse.rs index d0748bc..3acfd48 100644 --- a/src/parser/v3/parse.rs +++ b/src/parser/v3/parse.rs @@ -7,6 +7,8 @@ use super::{CompilerMsg, FilePos, Node, ParserCtx}; pub enum ParseResult { Ok(T), + Wrap(NodeParseResult), + Node(Node), Recover(T), Err(CompilerMsg), SubErr, @@ -33,6 +35,20 @@ impl Try for ParseResult { ParseResult::Ok(v) => ControlFlow::Continue(v), // TODO: this is messed up; need to break w a Result> or smth :woozy: ParseResult::Recover(v) => ControlFlow::Break(None), + ParseResult::Wrap(n) => { + if n.recover { + ControlFlow::Break(None) + } else { + match n.node.inner { + Some(v) => ControlFlow::Continue(v), + None => ControlFlow::Break(None), + } + } + } + ParseResult::Node(n) => match n.inner { + Some(v) => ControlFlow::Continue(v), + None => ControlFlow::Break(None), + }, ParseResult::Err(e) => ControlFlow::Break(Some(e)), ParseResult::SubErr => ControlFlow::Break(None), } @@ -72,7 +88,7 @@ pub struct NodeParseResult { } impl NodeParseResult { - pub fn map) -> U, U>(self, op: F) -> ParseResult { + pub fn map_res) -> U, U>(self, op: F) -> ParseResult { let res = op(self.node); if self.recover { ParseResult::Recover(res) @@ -80,6 +96,15 @@ impl NodeParseResult { ParseResult::Ok(res) } } + pub fn map U, U>(self, op: F) -> NodeParseResult { + NodeParseResult { + node: Node { + inner: self.node.inner.map(op), + origin: self.node.origin, + }, + recover: self.recover, + } + } } impl Try for NodeParseResult { @@ -119,10 +144,6 @@ pub trait ParsableWith: Sized { fn parse(ctx: &mut ParserCtx, data: Self::Data) -> ParseResult; } -pub trait MaybeParsable: Sized { - fn maybe_parse(ctx: &mut ParserCtx) -> Result, CompilerMsg>; -} - impl ParsableWith for T { type Data = (); @@ -140,6 +161,13 @@ impl Node { let (inner, recover) = match T::parse(ctx, data) { ParseResult::Ok(v) => (Some(v), false), ParseResult::Recover(v) => (Some(v), true), + ParseResult::Wrap(r) => return r, + ParseResult::Node(node) => { + return NodeParseResult { + node, + recover: false, + } + } ParseResult::Err(e) => { ctx.err(e); (None, true) @@ -163,21 +191,33 @@ impl Node { } } -impl Node { - pub fn maybe_parse(ctx: &mut ParserCtx) -> Option { - let start = ctx.next_start(); - let inner = match T::maybe_parse(ctx) { - Ok(v) => Some(v?), - Err(e) => { - ctx.err(e); - None - } - }; - let end = ctx.prev_end(); - Some(Self { - inner, - origin: start.to(end), - }) +impl Node> +where + Option: Parsable, +{ + pub fn maybe_parse(ctx: &mut ParserCtx) -> Option> { + let res = Node::>::parse_with(ctx, ()); + let origin = res.node.origin; + let recover = res.recover; + match res.node.inner { + Some(val) => match val { + Some(v) => Some(NodeParseResult { + node: Node { + inner: Some(v), + origin, + }, + recover, + }), + None => None, + }, + None => Some(NodeParseResult { + node: Node { + inner: None, + origin, + }, + recover, + }), + } } } diff --git a/src/parser/v3/token/symbol.rs b/src/parser/v3/token/symbol.rs index 1887b72..10fe1d8 100644 --- a/src/parser/v3/token/symbol.rs +++ b/src/parser/v3/token/symbol.rs @@ -36,6 +36,7 @@ pub enum Symbol { Comma, Hash, At, + Carrot, } impl Symbol { @@ -72,6 +73,7 @@ impl Symbol { ',' => Self::Comma, '#' => Self::Hash, '@' => Self::At, + '^' => Self::Carrot, _ => return None, }) } @@ -147,6 +149,7 @@ impl Symbol { Self::DoublePipe => "||", Self::Hash => "#", Self::At => "@", + Self::Carrot => "^", } } }