diff --git a/data/test.lang b/data/test.lang index 4182d37..e04867f 100644 --- a/data/test.lang +++ b/data/test.lang @@ -1,11 +1,24 @@ +struct Test { + a: 64, + b: 64, +} + fn start() { println("Helld!"); println("Hello World!!!!!"); thinger(); println("what"); print(tester()); + let test = Test { + a: 10, + b: 9, + }; arger("a", "b", "c"); - exit(mul(sub(add(5, 9), 1), 3)); + let z = sub(test.a, 10); + exit(add(mul(sub(add(5, test.b), 1), 3), z)); +} + +fn structer(test: Test) { } fn thinger() { diff --git a/src/compiler/arch/riscv/compile.rs b/src/compiler/arch/riscv/compile.rs index fec696a..7f29d1e 100644 --- a/src/compiler/arch/riscv/compile.rs +++ b/src/compiler/arch/riscv/compile.rs @@ -88,7 +88,23 @@ pub fn compile(program: &IRLProgram) -> UnlinkedProgram
  • { for i in &f.instructions { irli.push((v.len(), format!("{i:?}"))); match i { - IRI::Mv { dest, src } => todo!(), + IRI::Mv { + dest, + dest_offset, + src, + src_offset, + } => { + let s = align(&f.stack[src]) as u32; + mov_mem( + &mut v, + sp, + stack[src] + align(src_offset), + sp, + stack[dest] + align(dest_offset), + t0, + s, + ); + } IRI::Ref { dest, src } => todo!(), IRI::LoadAddr { dest, offset, src } => { v.extend([ diff --git a/src/ir/lower/func.rs b/src/ir/lower/func.rs index 808c0a6..6f1dc9c 100644 --- a/src/ir/lower/func.rs +++ b/src/ir/lower/func.rs @@ -16,7 +16,9 @@ pub struct IRLFunction { pub enum IRLInstruction { Mv { dest: VarID, + dest_offset: Size, src: VarID, + src_offset: Size, }, Ref { dest: VarID, diff --git a/src/ir/lower/program.rs b/src/ir/lower/program.rs index ae525f0..efc393b 100644 --- a/src/ir/lower/program.rs +++ b/src/ir/lower/program.rs @@ -41,7 +41,9 @@ impl IRLProgram { } instrs.push(IRLInstruction::Mv { dest: dest.id, + dest_offset: 0, src: src.id, + src_offset: 0, }); } IRUInstruction::Ref { dest, src } => { @@ -83,7 +85,10 @@ impl IRLProgram { src: sym, }); - let sym = builder.anon_ro_data(&(*len as u64).to_le_bytes(), Some(format!("len: {}", len))); + let sym = builder.anon_ro_data( + &(*len as u64).to_le_bytes(), + Some(format!("len: {}", len)), + ); instrs.push(IRLInstruction::LoadData { dest: dest.id, offset: 8, @@ -129,6 +134,46 @@ impl IRLProgram { }) } IRUInstruction::Ret { src } => instrs.push(IRLInstruction::Ret { src: src.id }), + IRUInstruction::Construct { dest, fields } => { + if alloc_stack(dest.id) { + continue; + } + let ty = &p.get_var(dest.id).ty; + let Type::Concrete(id) = ty else { + return Err(format!("Failed to contruct type {}", p.type_name(ty))); + }; + let struc = p.get_struct(*id); + for (name, var) in fields { + instrs.push(IRLInstruction::Mv { + dest: dest.id, + src: var.id, + dest_offset: struc.fields[name].offset, + src_offset: 0, + }) + } + } + IRUInstruction::Access { dest, src, field } => { + if alloc_stack(dest.id) { + continue; + } + let ty = &p.get_var(src.id).ty; + let Type::Concrete(id) = ty else { + return Err(format!( + "Failed to access field of struct {}", + p.type_name(ty) + )); + }; + let struc = p.get_struct(*id); + let Some(field) = struc.fields.get(field) else { + return Err(format!("No field {field} in struct {}", p.type_name(ty))); + }; + instrs.push(IRLInstruction::Mv { + dest: dest.id, + src: src.id, + src_offset: field.offset, + dest_offset: 0, + }) + } }; } builder.write_fn( diff --git a/src/ir/upper/def.rs b/src/ir/upper/def.rs index e64bc7c..646990e 100644 --- a/src/ir/upper/def.rs +++ b/src/ir/upper/def.rs @@ -1,7 +1,7 @@ -use crate::common::FileSpan; +use crate::{common::FileSpan, ir::{Len, Size}}; use super::Type; -use std::fmt::Debug; +use std::{collections::HashMap, fmt::Debug}; #[derive(Clone)] pub struct FnDef { @@ -12,9 +12,16 @@ pub struct FnDef { } #[derive(Clone)] -pub struct TypeDef { +pub struct StructField { + pub ty: Type, + pub offset: Len, +} + +#[derive(Clone)] +pub struct StructDef { pub name: String, - pub args: usize, + pub fields: HashMap, + pub size: Size, pub origin: Origin, } diff --git a/src/ir/upper/func.rs b/src/ir/upper/func.rs index 2e5af42..2ad9fa6 100644 --- a/src/ir/upper/func.rs +++ b/src/ir/upper/func.rs @@ -1,6 +1,8 @@ -use std::fmt::Write; +use std::{collections::HashMap, fmt::Write}; -use super::{arch::riscv64::RV64Instruction, inst::VarInst, DataID, FnID, IRUInstrInst, Type, VarID}; +use super::{ + arch::riscv64::RV64Instruction, inst::VarInst, DataID, FnID, IRUInstrInst, Type, VarID, +}; use crate::{common::FileSpan, compiler::arch::riscv::Reg, util::Padder}; pub struct IRUFunction { @@ -31,6 +33,11 @@ pub enum IRUInstruction { dest: VarInst, src: FnID, }, + Access { + dest: VarInst, + src: VarInst, + field: String, + }, Call { dest: VarInst, f: VarInst, @@ -43,6 +50,10 @@ pub enum IRUInstruction { Ret { src: VarInst, }, + Construct { + dest: VarInst, + fields: HashMap, + }, } pub struct IRInstructions { @@ -84,6 +95,8 @@ impl std::fmt::Debug for IRUInstruction { } => 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::Access { dest, src, field } => write!(f, "{dest:?} <- {src:?}.{field}"), } } } diff --git a/src/ir/upper/program.rs b/src/ir/upper/program.rs index ebf232d..86c5d12 100644 --- a/src/ir/upper/program.rs +++ b/src/ir/upper/program.rs @@ -11,7 +11,7 @@ use super::{inst::VarInst, *}; pub struct IRUProgram { pub fn_defs: Vec, pub var_defs: Vec, - pub type_defs: Vec, + pub type_defs: Vec, pub data_defs: Vec, pub fns: Vec>, pub data: Vec>, @@ -59,7 +59,7 @@ impl IRUProgram { pub fn get_fn_var(&self, id: VarID) -> Option<&FnDef> { Some(&self.fn_defs[self.fn_map.get(&id)?.0]) } - pub fn get_type(&self, id: TypeID) -> &TypeDef { + pub fn get_struct(&self, id: TypeID) -> &StructDef { &self.type_defs[id.0] } pub fn alias_fn(&mut self, name: &str, id: FnID) { @@ -82,10 +82,7 @@ impl IRUProgram { pub fn size_of_type(&self, ty: &Type) -> Option { // TODO: target matters Some(match ty { - Type::Concrete(id) => { - let def = &self.type_defs[id.0]; - todo!() - } + Type::Concrete(id) => self.type_defs[id.0].size, Type::Bits(b) => *b, Type::Generic { base, args } => todo!(), Type::Fn { args, ret } => todo!(), @@ -131,7 +128,7 @@ impl IRUProgram { id } - pub fn def_type(&mut self, def: TypeDef) -> TypeID { + pub fn def_type(&mut self, def: StructDef) -> TypeID { let i = self.type_defs.len(); let id = TypeID(i); self.insert(&def.name, Ident::Type(id)); @@ -148,10 +145,10 @@ impl IRUProgram { let mut str = String::new(); match ty { Type::Concrete(t) => { - str += &self.get_type(*t).name; + str += &self.get_struct(*t).name; } Type::Generic { base, args } => { - str += &self.get_type(*base).name; + str += &self.get_struct(*base).name; if let Some(arg) = args.first() { str = str + "<" + &self.type_name(arg); } diff --git a/src/ir/upper/ty.rs b/src/ir/upper/ty.rs index 3f3cb6e..35837bb 100644 --- a/src/ir/upper/ty.rs +++ b/src/ir/upper/ty.rs @@ -1,4 +1,4 @@ -use super::{IRUInstruction, IRUProgram, Len, TypeID}; +use super::{IRUProgram, Len, TypeID}; #[derive(Clone, PartialEq)] pub enum Type { @@ -30,14 +30,7 @@ pub fn resolve_types(ns: &IRUProgram) { for (i, f) in ns.iter_fns() { for inst in &f.instructions { match &inst.i { - IRUInstruction::Mv { dest, src } => todo!(), - IRUInstruction::Ref { dest, src } => todo!(), - IRUInstruction::LoadData { dest, src } => todo!(), - IRUInstruction::LoadSlice { dest, src } => todo!(), - IRUInstruction::LoadFn { dest, src } => todo!(), - IRUInstruction::Call { dest, f, args } => todo!(), - IRUInstruction::AsmBlock { instructions, args } => todo!(), - IRUInstruction::Ret { src } => todo!(), + _ => todo!(), } } } diff --git a/src/ir/upper/validate.rs b/src/ir/upper/validate.rs index c15faae..e2d041a 100644 --- a/src/ir/upper/validate.rs +++ b/src/ir/upper/validate.rs @@ -1,5 +1,5 @@ // TODO: move this into ir, not parser -use super::{IRUProgram, Type}; +use super::{IRUInstruction, IRUProgram, Type}; use crate::common::{CompilerMsg, CompilerOutput}; impl IRUProgram { @@ -8,18 +8,18 @@ impl IRUProgram { for (f, fd) in self.fns.iter().flatten().zip(&self.fn_defs) { for i in &f.instructions { match &i.i { - super::IRUInstruction::Mv { dest, src } => { + IRUInstruction::Mv { dest, src } => { let dest = self.get_var(dest.id); let src = self.get_var(src.id); output.check_assign(self, &src.ty, &dest.ty, i.span); } - super::IRUInstruction::Ref { dest, src } => todo!(), - super::IRUInstruction::LoadData { dest, src } => { + IRUInstruction::Ref { dest, src } => todo!(), + IRUInstruction::LoadData { dest, src } => { let dest = self.get_var(dest.id); let src = self.get_data(*src); output.check_assign(self, &src.ty, &dest.ty, i.span); } - super::IRUInstruction::LoadSlice { dest, src } => { + IRUInstruction::LoadSlice { dest, src } => { let dest = self.get_var(dest.id); let src = self.get_data(*src); let Type::Array(srcty, ..) = &src.ty else { @@ -27,8 +27,8 @@ impl IRUProgram { }; output.check_assign(self, &Type::Slice(srcty.clone()), &dest.ty, i.span); } - super::IRUInstruction::LoadFn { dest, src } => todo!(), - super::IRUInstruction::Call { dest, f, args } => { + IRUInstruction::LoadFn { dest, src } => todo!(), + IRUInstruction::Call { dest, f, args } => { let destty = &self.get_var(dest.id).ty; let f = self.get_var(f.id); let Type::Fn { args: argtys, ret } = &f.ty else { @@ -46,13 +46,66 @@ impl IRUProgram { output.check_assign(self, argt, &dest.ty, argv.span); } } - super::IRUInstruction::AsmBlock { instructions, args } => { + IRUInstruction::AsmBlock { instructions, args } => { // TODO } - super::IRUInstruction::Ret { src } => { + IRUInstruction::Ret { src } => { let srcty = &self.get_var(src.id).ty; output.check_assign(self, srcty, &fd.ret, src.span); - }, + } + IRUInstruction::Construct { dest, fields } => { + let dest_def = self.get_var(dest.id); + let tyid = match dest_def.ty { + Type::Concrete(id) => id, + _ => { + output.err(CompilerMsg { + msg: "uhh type is not struct".to_string(), + spans: vec![dest.span], + }); + continue; + } + }; + let def = self.get_struct(tyid); + for (name, field) in &def.fields { + if let Some(var) = fields.get(name) { + let ety = &self.get_var(var.id).ty; + output.check_assign(self, &field.ty, ety, var.span); + } else { + output.err(CompilerMsg { + msg: format!("field '{name}' missing from struct"), + spans: vec![dest.span], + }); + } + } + for name in fields.keys() { + if !def.fields.contains_key(name) { + output.err(CompilerMsg { + msg: format!("field '{name}' not in struct"), + spans: vec![dest.span], + }); + } + } + } + IRUInstruction::Access { dest, src, field } => { + let dest_def = self.get_var(dest.id); + let src_def = self.get_var(src.id); + let tyid = match src_def.ty { + Type::Concrete(id) => id, + _ => { + output.err(CompilerMsg { + msg: "uhh type is not struct".to_string(), + spans: vec![dest.span], + }); + continue; + } + }; + let def = self.get_struct(tyid); + let field = def.fields.get(field).expect( + "already validated during parse lowering... probably shouldn't be?", + ); + output.check_assign(self, &field.ty, &dest_def.ty, i.span); + // TODO + } } } } diff --git a/src/main.rs b/src/main.rs index 91970c7..7398f94 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,9 @@ #![feature(box_patterns)] #![feature(try_trait_v2)] #![feature(trait_alias)] +#![feature(let_chains)] +// dawg what +#![feature(str_as_str)] use ir::{IRLProgram, IRUProgram}; use parser::{NodeParsable, PModule, PStatement, ParserCtx}; @@ -17,10 +20,10 @@ mod compiler; mod ir; mod parser; mod util; -use common::*; fn main() { let file = std::env::args_os().nth(1); + // TODO: professional arg parsing 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 { diff --git a/src/parser/v3/lower/def.rs b/src/parser/v3/lower/def.rs index 2b8a9cc..93eaf29 100644 --- a/src/parser/v3/lower/def.rs +++ b/src/parser/v3/lower/def.rs @@ -37,7 +37,10 @@ impl PType { output: &mut CompilerOutput, span: FileSpan, ) -> Type { - match namespace.get(&self.name).and_then(|ids| ids.ty) { + let Some(name) = self.name.as_ref() else { + return Type::Error; + }; + match namespace.get(&name).and_then(|ids| ids.ty) { Some(id) => { if self.args.is_empty() { Type::Concrete(id) @@ -51,10 +54,10 @@ impl PType { } } None => { - if let Ok(num) = self.name.parse::() { + if let Ok(num) = name.parse::() { Type::Bits(num) } else { - match self.name.as_str() { + match name.as_str() { "slice" => { let inner = self.args[0].lower(namespace, output); Type::Slice(Box::new(inner)) diff --git a/src/parser/v3/lower/expr.rs b/src/parser/v3/lower/expr.rs index 129a9b1..6d2c85f 100644 --- a/src/parser/v3/lower/expr.rs +++ b/src/parser/v3/lower/expr.rs @@ -1,5 +1,8 @@ use super::{func::FnLowerCtx, FnLowerable, PExpr, UnaryOp}; -use crate::ir::{DataDef, IRUInstruction, Origin, Type, VarInst}; +use crate::{ + ir::{DataDef, IRUInstruction, Origin, Type, VarInst}, + parser::PInfixOp, +}; impl FnLowerable for PExpr { type Output = VarInst; @@ -13,7 +16,7 @@ impl FnLowerable for PExpr { DataDef { ty: Type::Bits(8).arr(data.len() as u32), origin: Origin::File(l.span), - label: format!("string \"{}\"", s.replace("\n", "\\n")) + label: format!("string \"{}\"", s.replace("\n", "\\n")), }, data, ); @@ -42,7 +45,7 @@ impl FnLowerable for PExpr { DataDef { ty, origin: Origin::File(l.span), - label: format!("num {n:?}") + label: format!("num {n:?}"), }, n.whole.parse::().unwrap().to_le_bytes().to_vec(), ); @@ -56,15 +59,39 @@ impl FnLowerable for PExpr { PExpr::Ident(i) => ctx.get_var(i)?, PExpr::BinaryOp(op, e1, e2) => { let res1 = e1.lower(ctx)?; - let res2 = e2.lower(ctx)?; - op.traitt(); - todo!(); + if *op == PInfixOp::Access { + let sty = &ctx.map.get_var(res1.id).ty; + let Type::Concrete(tid) = sty else { + ctx.err(format!("Type {:?} has no fields", ctx.map.type_name(sty))); + return None; + }; + let struc = ctx.map.get_struct(*tid); + let Some(box PExpr::Ident(ident)) = &e2.inner else { + ctx.err(format!("Field accesses must be identifiers",)); + return None; + }; + let fname = &ident.as_ref()?.0; + let Some(field) = struc.fields.get(fname) else { + ctx.err(format!("Field '{fname}' not in struct")); + return None; + }; + let temp = ctx.temp(field.ty.clone()); + ctx.push(IRUInstruction::Access { + dest: temp, + src: res1, + field: fname.to_string(), + }); + temp + } else { + let res2 = e2.lower(ctx)?; + todo!() + } } PExpr::UnaryOp(op, e) => { let res = e.lower(ctx)?; match op { UnaryOp::Ref => { - let temp = ctx.temp(ctx.map.get_var(res.id).ty.clone()); + let temp = ctx.temp(ctx.map.get_var(res.id).ty.clone().rf()); ctx.push(IRUInstruction::Ref { dest: temp, src: res, @@ -120,7 +147,7 @@ impl FnLowerable for PExpr { temp } PExpr::Group(e) => e.lower(ctx)?, - PExpr::Construct(c) => todo!(), + PExpr::Construct(c) => c.lower(ctx)?, }) } } diff --git a/src/parser/v3/lower/func.rs b/src/parser/v3/lower/func.rs index ac500e6..b6cb655 100644 --- a/src/parser/v3/lower/func.rs +++ b/src/parser/v3/lower/func.rs @@ -2,7 +2,7 @@ use super::{CompilerMsg, CompilerOutput, FileSpan, FnLowerable, Node, PFunction} use crate::{ ir::{ FnDef, FnID, IRInstructions, IRUFunction, IRUInstruction, Idents, NamespaceGuard, Origin, - Type, VarDef, VarID, VarInst, + Type, VarDef, VarInst, }, parser, }; diff --git a/src/parser/v3/lower/mod.rs b/src/parser/v3/lower/mod.rs index adeea0b..bec9736 100644 --- a/src/parser/v3/lower/mod.rs +++ b/src/parser/v3/lower/mod.rs @@ -1,10 +1,11 @@ +mod arch; mod asm; mod block; mod def; mod expr; mod func; mod module; -mod arch; +mod struc; use super::*; diff --git a/src/parser/v3/lower/module.rs b/src/parser/v3/lower/module.rs index 4d190c5..9fad98f 100644 --- a/src/parser/v3/lower/module.rs +++ b/src/parser/v3/lower/module.rs @@ -4,6 +4,9 @@ use super::{PModule, CompilerOutput}; impl PModule { pub fn lower(&self, map: &mut NamespaceGuard, output: &mut CompilerOutput) { + for s in &self.structs { + s.lower(map, output); + } let mut fns = Vec::new(); for f in &self.functions { if let Some(id) = f.lower_header(map, output) { diff --git a/src/parser/v3/lower/struc.rs b/src/parser/v3/lower/struc.rs new file mode 100644 index 0000000..a214c2b --- /dev/null +++ b/src/parser/v3/lower/struc.rs @@ -0,0 +1,96 @@ +use std::collections::HashMap; + +use crate::{ + common::{CompilerMsg, CompilerOutput, FileSpan}, + ir::{IRUInstruction, NamespaceGuard, Origin, StructDef, StructField, VarInst}, + parser::{Node, PConstruct, PConstructFields, PStruct, PStructFields}, +}; + +use super::{FnLowerCtx, FnLowerable}; + +impl FnLowerable for PConstruct { + type Output = VarInst; + fn lower(&self, ctx: &mut FnLowerCtx) -> Option { + let ty = self.name.lower(ctx.map, ctx.output); + let fields = match &self.fields { + PConstructFields::Named(nodes) => nodes + .iter() + .flat_map(|n| { + let def = n.as_ref()?; + let name = def.name.as_ref()?.to_string(); + let expr = def.val.as_ref()?.lower(ctx)?; + Some((name, expr)) + }) + .collect(), + PConstructFields::Tuple(nodes) => nodes + .iter() + .enumerate() + .flat_map(|(i, n)| { + let expr = n.as_ref()?.lower(ctx)?; + Some((format!("{i}"), expr)) + }) + .collect(), + PConstructFields::None => HashMap::new(), + }; + let id = ctx.temp(ty); + ctx.push(IRUInstruction::Construct { dest: id, fields }); + Some(id) + } +} + +impl PStruct { + pub fn lower( + &self, + map: &mut NamespaceGuard, + output: &mut CompilerOutput, + span: FileSpan, + ) -> Option<()> { + let mut offset = 0; + let fields = match &self.fields { + PStructFields::Named(nodes) => nodes + .iter() + .flat_map(|n| { + let def = n.as_ref()?; + let name = def.name.as_ref()?.to_string(); + let tynode = def.ty.as_ref()?; + let ty = tynode.lower(map, output); + let size = map.size_of_type(&ty).unwrap_or_else(|| { + output.err(CompilerMsg { + msg: format!("Size of type '{}' unknown", map.type_name(&ty)), + spans: vec![tynode.span], + }); + 0 + }); + let res = Some((name, StructField { ty, offset })); + offset += size; + res + }) + .collect(), + PStructFields::Tuple(nodes) => nodes + .iter() + .enumerate() + .flat_map(|(i, n)| { + let ty = n.as_ref()?.lower(map, output, span); + let size = map.size_of_type(&ty)?; + let res = Some((format!("{i}"), StructField { ty, offset })); + offset += size; + res + }) + .collect(), + PStructFields::None => HashMap::new(), + }; + map.def_type(StructDef { + name: self.name.as_ref()?.to_string(), + origin: Origin::File(span), + size: offset, + fields, + }); + Some(()) + } +} + +impl Node { + pub fn lower(&self, map: &mut NamespaceGuard, output: &mut CompilerOutput) { + self.as_ref().map(|i| i.lower(map, output, self.span)); + } +} diff --git a/src/parser/v3/mod.rs b/src/parser/v3/mod.rs index 682858f..1cff9c9 100644 --- a/src/parser/v3/mod.rs +++ b/src/parser/v3/mod.rs @@ -14,3 +14,6 @@ pub use node::*; pub use nodes::*; pub use parse::*; pub use token::*; + +// 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/node.rs b/src/parser/v3/node.rs index 28eb2d7..028ba2a 100644 --- a/src/parser/v3/node.rs +++ b/src/parser/v3/node.rs @@ -23,6 +23,12 @@ impl Node { span: self.span, } } + pub fn map T2>(self, f: F) -> Node { + Node { + inner: self.inner.map(f), + span: self.span, + } + } } impl Deref for Node { diff --git a/src/parser/v3/nodes/def.rs b/src/parser/v3/nodes/def.rs index 128ebb4..f2d9874 100644 --- a/src/parser/v3/nodes/def.rs +++ b/src/parser/v3/nodes/def.rs @@ -1,8 +1,8 @@ use std::fmt::Debug; use super::{ - MaybeParsable, Node, PExpr, PIdent, PType, Parsable, ParseResult, ParserCtx, Symbol, - Token, CompilerMsg + CompilerMsg, MaybeParsable, Node, PExpr, PIdent, PType, Parsable, ParseResult, ParserCtx, + Symbol, Token, }; pub struct PVarDef { @@ -18,27 +18,30 @@ pub struct PFieldDef { impl Parsable for PVarDef { fn parse(ctx: &mut ParserCtx) -> ParseResult { let name = ctx.parse()?; - if ctx.peek().is_some_and(|n| n.is_symbol(Symbol::Colon)) { + ParseResult::Ok(if ctx.peek().is_some_and(|n| n.is_symbol(Symbol::Colon)) { ctx.next(); - ctx.parse().map(|ty| Self { name, ty: Some(ty) }) + Self { + name, + ty: Some(ctx.parse()?), + } } else { - ParseResult::Ok(Self { name, ty: None }) - } + Self { name, ty: None } + }) } } impl Parsable for PFieldDef { fn parse(ctx: &mut ParserCtx) -> ParseResult { let name = ctx.parse()?; - if ctx.peek().is_some_and(|n| n.is_symbol(Symbol::Colon)) { + ParseResult::Ok(if ctx.peek().is_some_and(|n| n.is_symbol(Symbol::Colon)) { ctx.next(); - ctx.parse().map(|ty| Self { + Self { name, - val: Some(ty), - }) + val: Some(ctx.parse()?), + } } else { - ParseResult::Ok(Self { name, val: None }) - } + Self { name, val: None } + }) } } diff --git a/src/parser/v3/nodes/expr.rs b/src/parser/v3/nodes/expr.rs index f0c507f..adb2d7b 100644 --- a/src/parser/v3/nodes/expr.rs +++ b/src/parser/v3/nodes/expr.rs @@ -1,7 +1,10 @@ use std::fmt::{Debug, Write}; use super::{ - op::{PInfixOp, UnaryOp}, util::parse_list, Keyword, Node, NodeParsable, PAsmBlock, PBlock, PConstruct, PIdent, PLiteral, Parsable, ParseResult, ParserCtx, Symbol, CompilerMsg + op::{PInfixOp, UnaryOp}, + util::parse_list, + CompilerMsg, Keyword, Node, NodeParsable, PAsmBlock, PBlock, PConstruct, PIdent, PLiteral, + Parsable, ParseResult, ParserCtx, Symbol, }; type BoxNode = Node>; @@ -53,12 +56,21 @@ impl Parsable for PExpr { Self::UnaryOp(op, n) } }); - } else if let Some(val) = Node::maybe_parse(ctx) { + } else if let Some(val) = ctx.maybe_parse() { Self::Lit(val) } else { let res = ctx.parse(); if res.node.is_some() { - Self::Ident(res.node) + // TODO: this is extremely limiting + // maybe parse generically and then during lowering figure out what's a function vs + // struct vs etc like mentioned in main.rs + if let Some(next) = ctx.peek() + && next.is_symbol(Symbol::OpenCurly) + { + Self::Construct(ctx.parse_with(res.node)?) + } else { + Self::Ident(res.node) + } } else { let next = ctx.expect_peek()?; return ParseResult::Err(CompilerMsg::unexpected_token(next, "an expression")); diff --git a/src/parser/v3/nodes/ident.rs b/src/parser/v3/nodes/ident.rs index bb80ffd..c6929a9 100644 --- a/src/parser/v3/nodes/ident.rs +++ b/src/parser/v3/nodes/ident.rs @@ -5,7 +5,7 @@ use std::{ }; #[derive(Clone)] -pub struct PIdent(String); +pub struct PIdent(pub String); impl Parsable for PIdent { fn parse(ctx: &mut ParserCtx) -> ParseResult { diff --git a/src/parser/v3/nodes/struc.rs b/src/parser/v3/nodes/struc.rs index 1f45755..88382f7 100644 --- a/src/parser/v3/nodes/struc.rs +++ b/src/parser/v3/nodes/struc.rs @@ -1,8 +1,10 @@ use std::fmt::Debug; +use crate::parser::ParsableWith; + use super::{ - util::parse_list, Keyword, Node, PExpr, PFieldDef, PIdent, PType, PVarDef, Parsable, - ParseResult, ParserCtx, CompilerMsg, Symbol, + util::parse_list, CompilerMsg, Keyword, Node, PExpr, PFieldDef, PIdent, PType, PVarDef, + Parsable, ParseResult, ParserCtx, Symbol, }; #[derive(Debug)] @@ -13,7 +15,7 @@ pub struct PStruct { #[derive(Debug)] pub struct PConstruct { - pub name: Node, + pub name: Node, pub fields: PConstructFields, } @@ -57,11 +59,20 @@ impl Parsable for PStruct { } } -impl Parsable for PConstruct { - fn parse(ctx: &mut ParserCtx) -> ParseResult { - ctx.expect_kw(Keyword::Struct)?; - let name = ctx.parse()?; +impl ParsableWith for PConstruct { + type Data = Node; + fn parse(ctx: &mut ParserCtx, name_node: Self::Data) -> ParseResult { let next = ctx.expect_peek()?; + // TODO: this is not correct span; type should also span generics, which aren't even in + // here yet + let span = name_node.span; + let name = Node::new( + PType { + name: name_node, + args: Vec::new(), + }, + span, + ); let fields = if next.is_symbol(Symbol::Semicolon) { ctx.next(); PConstructFields::None diff --git a/src/parser/v3/nodes/ty.rs b/src/parser/v3/nodes/ty.rs index 1756432..8cbba0b 100644 --- a/src/parser/v3/nodes/ty.rs +++ b/src/parser/v3/nodes/ty.rs @@ -1,9 +1,11 @@ use std::fmt::Debug; -use super::{util::parse_list, Node, Parsable, ParseResult, ParserCtx, CompilerMsg, Symbol, Token}; +use super::{ + util::parse_list, CompilerMsg, Node, PIdent, Parsable, ParseResult, ParserCtx, Symbol, Token, +}; pub struct PType { - pub name: String, + pub name: Node, pub args: Vec>, } @@ -11,18 +13,15 @@ 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: "&".to_string(), + name, args: vec![arg], } } else { - let Token::Word(name) = &next.token else { - return ParseResult::Err(CompilerMsg::unexpected_token(next, "a type identifier")); - }; - let n = name.to_string(); - ctx.next(); + let n = ctx.parse()?; let mut args = Vec::new(); if let Some(next) = ctx.peek() { if next.is_symbol(Symbol::OpenAngle) { @@ -38,8 +37,8 @@ impl Parsable for PType { impl Debug for PType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.name)?; - if self.name == "&" { + 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)?;