diff --git a/hblang/README.md b/hblang/README.md index af824a8..9a1a5cf 100644 --- a/hblang/README.md +++ b/hblang/README.md @@ -173,7 +173,8 @@ main := fn(): int { } pass := fn(t: ^Ty): int { - return t.a - t.b; + .{ a, b } := *t; + return a - b; } odher_pass := fn(t: Ty2): Ty2 { diff --git a/hblang/src/codegen.rs b/hblang/src/codegen.rs index 7f2c99f..23e3521 100644 --- a/hblang/src/codegen.rs +++ b/hblang/src/codegen.rs @@ -1584,34 +1584,38 @@ impl Codegen { E::Ctor { pos, ty, fields, .. } => { - let Some(ty) = ty.map(|ty| self.ty(ty)).or(ctx.ty) else { - self.report(pos, "expected type, (it cannot be inferred)"); - }; - let size = self.tys.size_of(ty); - - let loc = ctx - .loc - .unwrap_or_else(|| Loc::stack(self.ci.stack.allocate(size))); - let ty::Kind::Struct(stuct) = ty.expand() else { - self.report(pos, "expected expression to evaluate to struct") - }; - let field_count = self.tys.structs[stuct as usize].fields.len(); - if field_count != fields.len() { - self.report( - pos, - format_args!("expected {} fields, got {}", field_count, fields.len()), - ); - } - - for (i, (name, field)) in fields.iter().enumerate() { - let Some((offset, ty)) = self.tys.offset_of(stuct, name.ok_or(i)) else { + let (stuct, loc) = self.prepare_struct_ctor(pos, ctx, ty, fields.len()); + for (name, field) in fields { + let Some((offset, ty)) = self.tys.offset_of(stuct, Ok(name)) else { self.report(pos, format_args!("field not found: {name:?}")); }; let loc = loc.as_ref().offset(offset); - let value = self.expr_ctx(field, Ctx::default().with_loc(loc).with_ty(ty))?; + let value = self.expr_ctx( + field.as_ref().expect("TODO"), + Ctx::default().with_loc(loc).with_ty(ty), + )?; self.ci.free_loc(value.loc); } + let ty = ty::Kind::Struct(stuct).compress(); + return Some(Value { ty, loc }); + } + E::Tupl { + pos, ty, fields, .. + } => { + let (stuct, loc) = self.prepare_struct_ctor(pos, ctx, ty, fields.len()); + let mut offset = 0; + let sfields = self.tys.structs[stuct as usize].fields.clone(); + for (sfield, field) in sfields.iter().zip(fields) { + let loc = loc.as_ref().offset(offset); + let ctx = Ctx::default().with_loc(loc).with_ty(sfield.ty); + let value = self.expr_ctx(field, ctx)?; + self.ci.free_loc(value.loc); + offset += self.tys.size_of(sfield.ty); + offset = Types::align_up(offset, self.tys.align_of(sfield.ty)); + } + + let ty = ty::Kind::Struct(stuct).compress(); return Some(Value { ty, loc }); } E::Field { target, field } => { @@ -2138,6 +2142,36 @@ impl Codegen { }) } + fn prepare_struct_ctor( + &mut self, + pos: Pos, + ctx: Ctx, + ty: Option<&Expr>, + field_len: usize, + ) -> (ty::Struct, Loc) { + let Some(ty) = ty.map(|ty| self.ty(ty)).or(ctx.ty) else { + self.report(pos, "expected type, (it cannot be inferred)"); + }; + + let size = self.tys.size_of(ty); + let loc = ctx + .loc + .unwrap_or_else(|| Loc::stack(self.ci.stack.allocate(size))); + let ty::Kind::Struct(stuct) = ty.expand() else { + self.report(pos, "expected expression to evaluate to struct") + }; + + let field_count = self.tys.structs[stuct as usize].fields.len(); + if field_count != field_len { + self.report( + pos, + format_args!("expected {field_count} fields, got {field_len}"), + ); + } + + (stuct, loc) + } + fn struct_op( &mut self, op: TokenKind, @@ -2743,13 +2777,13 @@ impl Codegen { } fn handle_ecall(&mut self) { - let local_pc = (self.ct.vm.pc.get() as usize - self.output.code.as_ptr() as usize) - .checked_sub(self.ci.snap.code); - let arr = self.ct.vm.pc.get() as *const Trap; let trap = unsafe { std::ptr::read_unaligned(arr) }; self.ct.vm.pc = self.ct.vm.pc.wrapping_add(std::mem::size_of::()); + let local_pc = (self.ct.vm.pc.get() as usize - self.output.code.as_ptr() as usize) + .checked_sub(self.ci.snap.code); + match trap { Trap::MakeStruct { file, struct_expr } => { let cfile = self.files[file as usize].clone(); @@ -2783,10 +2817,7 @@ impl Codegen { } if let Some(lpc) = local_pc { - let offset = std::mem::size_of::() - + lpc - + self.ci.snap.code - + self.output.code.as_ptr() as usize; + let offset = lpc + self.ci.snap.code + self.output.code.as_ptr() as usize; self.ct.vm.pc = hbvm::mem::Address::new(offset as _); } } diff --git a/hblang/src/parser.rs b/hblang/src/parser.rs index 23eea7a..fc6a560 100644 --- a/hblang/src/parser.rs +++ b/hblang/src/parser.rs @@ -341,6 +341,8 @@ impl<'a, 'b> Parser<'a, 'b> { }, body: self.ptr_expr(), }, + T::Ctor => self.ctor(token.start, None), + T::Tupl => self.tupl(token.start, None), T::Band | T::Mul | T::Xor => E::UnOp { pos: token.start, op: token.kind, @@ -392,23 +394,8 @@ impl<'a, 'b> Parser<'a, 'b> { args: self.collect_list(T::Comma, T::RParen, Self::expr), trailing_comma: std::mem::take(&mut self.trailing_sep), }, - T::Ctor => E::Ctor { - pos: token.start, - ty: Some(self.arena.alloc(expr)), - fields: self.collect_list(T::Comma, T::RBrace, |s| { - let name = s.expect_advance(T::Ident); - s.expect_advance(T::Colon); - let val = s.expr(); - (Some(s.move_str(name)), val) - }), - trailing_comma: std::mem::take(&mut self.trailing_sep), - }, - T::Tupl => E::Ctor { - pos: token.start, - ty: Some(self.arena.alloc(expr)), - fields: self.collect_list(T::Comma, T::RParen, |s| (None, s.expr())), - trailing_comma: std::mem::take(&mut self.trailing_sep), - }, + T::Ctor => self.ctor(token.start, Some(expr)), + T::Tupl => self.tupl(token.start, Some(expr)), T::Dot => E::Field { target: self.arena.alloc(expr), field: { @@ -431,6 +418,28 @@ impl<'a, 'b> Parser<'a, 'b> { expr } + fn tupl(&mut self, pos: Pos, ty: Option>) -> Expr<'a> { + Expr::Tupl { + pos, + ty: ty.map(|ty| self.arena.alloc(ty)), + fields: self.collect_list(TokenKind::Comma, TokenKind::RParen, Self::expr), + trailing_comma: std::mem::take(&mut self.trailing_sep), + } + } + + fn ctor(&mut self, pos: Pos, ty: Option>) -> Expr<'a> { + Expr::Ctor { + pos, + ty: ty.map(|ty| self.arena.alloc(ty)), + fields: self.collect_list(TokenKind::Comma, TokenKind::RBrace, |s| { + let name = s.advance_ident(); + let value = s.advance_if(TokenKind::Colon).then(|| s.expr()); + (s.move_str(name), value) + }), + trailing_comma: std::mem::take(&mut self.trailing_sep), + } + } + fn advance_ident(&mut self) -> Token { if matches!(self.token.kind, TokenKind::Ident | TokenKind::CtIdent) { self.next() @@ -661,7 +670,13 @@ generate_expr! { Ctor { pos: Pos, ty: Option<&'a Self>, - fields: &'a [(Option<&'a str>, Self)], + fields: &'a [(&'a str, Option)], + trailing_comma: bool, + }, + Tupl { + pos: Pos, + ty: Option<&'a Self>, + fields: &'a [Self], trailing_comma: bool, }, Field { @@ -720,26 +735,22 @@ impl<'a> std::fmt::Display for Expr<'a> { fn fmt_list( f: &mut std::fmt::Formatter, + trailing: bool, end: &str, list: &[T], fmt: impl Fn(&T, &mut std::fmt::Formatter) -> std::fmt::Result, ) -> std::fmt::Result { - let first = &mut true; - for expr in list { - if !std::mem::take(first) { - write!(f, ", ")?; + if !trailing { + let first = &mut true; + for expr in list { + if !std::mem::take(first) { + write!(f, ", ")?; + } + fmt(expr, f)?; } - fmt(expr, f)?; + return write!(f, "{end}"); } - write!(f, "{end}") - } - fn fmt_trailing_list( - f: &mut std::fmt::Formatter, - end: &str, - list: &[T], - fmt: impl Fn(&T, &mut std::fmt::Formatter) -> std::fmt::Result, - ) -> std::fmt::Result { writeln!(f)?; INDENT.with(|i| i.set(i.get() + 1)); let res = (|| { @@ -805,11 +816,13 @@ impl<'a> std::fmt::Display for Expr<'a> { Self::Field { target, field } => write!(f, "{}.{field}", Postfix(target)), Self::Directive { name, args, .. } => { write!(f, "@{name}(")?; - fmt_list(f, ")", args, std::fmt::Display::fmt) + fmt_list(f, false, ")", args, std::fmt::Display::fmt) } Self::Struct { fields, .. } => { write!(f, "struct {{")?; - fmt_list(f, "}", fields, |(name, val), f| write!(f, "{name}: {val}",)) + fmt_list(f, true, "}", fields, |(name, val), f| { + write!(f, "{name}: {val}",) + }) } Self::Ctor { ty, @@ -817,27 +830,30 @@ impl<'a> std::fmt::Display for Expr<'a> { trailing_comma, .. } => { - let (left, rith) = if fields.iter().any(|(name, _)| name.is_some()) { - ('{', "}") - } else { - ('(', ")") - }; - if let Some(ty) = ty { write!(f, "{}", Unary(ty))?; } - write!(f, ".{left}")?; + write!(f, ".{{")?; let fmt_field = |(name, val): &_, f: &mut std::fmt::Formatter| { - if let Some(name) = name { - write!(f, "{name}: ")?; + if let Some(val) = val { + write!(f, "{name}: {val}") + } else { + write!(f, "{name}") } - write!(f, "{val}") }; - if trailing_comma { - fmt_trailing_list(f, rith, fields, fmt_field) - } else { - fmt_list(f, rith, fields, fmt_field) + fmt_list(f, trailing_comma, "}", fields, fmt_field) + } + Self::Tupl { + ty, + fields, + trailing_comma, + .. + } => { + if let Some(ty) = ty { + write!(f, "{}", Unary(ty))?; } + write!(f, ".(")?; + fmt_list(f, trailing_comma, ")", fields, std::fmt::Display::fmt) } Self::UnOp { op, val, .. } => write!(f, "{op}{}", Unary(val)), Self::Break { .. } => write!(f, "break"), @@ -856,7 +872,9 @@ impl<'a> std::fmt::Display for Expr<'a> { ret, body, args, .. } => { write!(f, "fn(")?; - fmt_list(f, "", args, |arg, f| write!(f, "{}: {}", arg.name, arg.ty))?; + fmt_list(f, false, "", args, |arg, f| { + write!(f, "{}: {}", arg.name, arg.ty) + })?; write!(f, "): {ret} {body}")?; if !matches!(body, Self::Block { .. }) { write!(f, ";")?; @@ -869,11 +887,7 @@ impl<'a> std::fmt::Display for Expr<'a> { trailing_comma, } => { write!(f, "{}(", Postfix(func))?; - if trailing_comma { - fmt_trailing_list(f, ")", args, std::fmt::Display::fmt) - } else { - fmt_list(f, ")", args, std::fmt::Display::fmt) - } + fmt_list(f, trailing_comma, ")", args, std::fmt::Display::fmt) } Self::Return { val: Some(val), .. } => write!(f, "return {val}"), Self::Return { val: None, .. } => write!(f, "return"),