diff --git a/hblang/examples/directives.hb b/hblang/examples/directives.hb new file mode 100644 index 000000000..54540cfb8 --- /dev/null +++ b/hblang/examples/directives.hb @@ -0,0 +1,9 @@ + +Type := struct { + brah: int, + blah: int, +} + +main := fn(): int { + return @eca(int, 1, Type.(10, 20), @sizeof(Type), @alignof(Type), 5, 6); +} diff --git a/hblang/src/codegen.rs b/hblang/src/codegen.rs index 2aa82502c..f4f7c9499 100644 --- a/hblang/src/codegen.rs +++ b/hblang/src/codegen.rs @@ -129,6 +129,7 @@ enum Ctx { None, Inferred(Type), Dest(Value), + DestUntyped(Loc, u64), } impl Ctx { @@ -139,6 +140,14 @@ impl Ctx { _ => return None, }) } + + fn loc(self) -> Option { + Some(match self { + Self::Dest(Value { loc, .. }) => loc, + Self::DestUntyped(loc, ..) => loc, + _ => return None, + }) + } } pub mod bt { @@ -734,8 +743,129 @@ impl<'a> Codegen<'a> { self.expr_ctx(expr, Ctx::default()) } - fn expr_ctx(&mut self, expr: &'a parser::Expr<'a>, ctx: Ctx) -> Option { + fn expr_ctx(&mut self, expr: &'a parser::Expr<'a>, mut ctx: Ctx) -> Option { let value = match *expr { + E::Directive { + name: "eca", + args: [ret_ty, args @ ..], + .. + } => { + let mut parama = 3..12; + let mut values = Vec::with_capacity(args.len()); + for arg in args { + let arg = self.expr(arg)?; + self.pass_arg(&arg, &mut parama); + values.push(arg.loc); + } + drop(values); + + let ty = self.ty(ret_ty); + let loc = self.alloc_ret_loc(ty, ctx); + + self.code.encode(instrs::eca()); + + self.post_process_ret_loc(ty, &loc); + + return Some(Value { ty, loc }); + } + E::Directive { + name: "sizeof", + args: [ty], + .. + } => { + let ty = self.ty(ty); + let loc = Loc::Imm(self.size_of(ty)); + return Some(Value { ty: bt::UINT, loc }); + } + E::Directive { + name: "alignof", + args: [ty], + .. + } => { + let ty = self.ty(ty); + let loc = Loc::Imm(self.align_of(ty)); + return Some(Value { ty: bt::UINT, loc }); + } + E::Directive { + name: "intcast", + args: [val], + .. + } => { + let Some(ty) = ctx.ty() else { + self.report( + expr.pos(), + "type to cast to is unknown, use `@as(, )`", + ); + }; + let mut val = self.expr(val)?; + + let from_size = self.size_of(val.ty); + let to_size = self.size_of(ty); + + if from_size < to_size && bt::is_signed(val.ty) { + let reg = self.loc_to_reg(val.loc, from_size); + let op = + [instrs::sxt8, instrs::sxt16, instrs::sxt32][from_size.ilog2() as usize]; + self.code.encode(op(reg.0, reg.0)); + val.loc = Loc::Reg(reg); + } + + Some(Value { ty, loc: val.loc }) + } + E::Directive { + name: "bitcast", + args: [val], + .. + } => { + let Some(ty) = ctx.ty() else { + self.report( + expr.pos(), + "type to cast to is unknown, use `@as(, )`", + ); + }; + + let size = self.size_of(ty); + + ctx = match ctx { + Ctx::Dest(Value { loc, .. }) | Ctx::DestUntyped(loc, ..) => { + Ctx::DestUntyped(loc, size as _) + } + _ => Ctx::None, + }; + + let val = self.expr_ctx(val, ctx)?; + + if self.size_of(val.ty) != size { + self.report( + expr.pos(), + format_args!( + "cannot bitcast {} to {} (different sizes: {} != {size})", + self.display_ty(val.ty), + self.display_ty(ty), + self.size_of(val.ty), + ), + ); + } + + // TODO: maybe check align + + return Some(Value { ty, loc: val.loc }); + } + E::Directive { + name: "as", + args: [ty, val], + .. + } => { + let ty = self.ty(ty); + let ctx = match ctx { + Ctx::Dest(dest) => Ctx::Dest(dest), + Ctx::DestUntyped(loc, size) if self.size_of(ty) == size => { + Ctx::Dest(Value { ty, loc }) + } + _ => Ctx::Inferred(ty), + }; + return self.expr_ctx(val, ctx); + } E::Bool { value, .. } => Some(Value { ty: bt::BOOL, loc: Loc::Imm(value as u64), @@ -748,8 +878,8 @@ impl<'a> Codegen<'a> { }; let size = self.size_of(ty); - let loc = match ctx { - Ctx::Dest(dest) => dest.loc, + let loc = match ctx.loc() { + Some(loc) => loc, _ => Loc::Stack(self.alloc_stack(size), 0), }; @@ -878,40 +1008,11 @@ impl<'a> Codegen<'a> { } drop(values); - let size = self.size_of(fn_label.ret); - let loc = match size { - 0 => Loc::Imm(0), - ..=8 => Loc::RegRef(1), - ..=16 => match ctx { - Ctx::Dest(dest) => dest.loc, - _ => Loc::Stack(self.alloc_stack(size), 0), - }, - ..=u64::MAX => { - let val = match ctx { - Ctx::Dest(dest) => dest.loc, - _ => Loc::Stack(self.alloc_stack(size), 0), - }; - let (ptr, off) = val.ref_to_ptr(); - self.code.encode(instrs::cp(1, ptr)); - self.code.addi64(1, ptr, off); - val - } - }; + let loc = self.alloc_ret_loc(fn_label.ret, ctx); self.code.call(func); - match size { - 0 => {} - ..=8 => {} - ..=16 => { - if let Loc::Stack(ref stack, off) = loc { - self.store_stack(1, stack.offset + off, 16); - } else { - unreachable!() - } - } - ..=u64::MAX => {} - } + self.post_process_ret_loc(fn_label.ret, &loc); return Some(Value { ty: fn_label.ret, @@ -1156,11 +1257,18 @@ impl<'a> Codegen<'a> { ast => unimplemented!("{:#?}", ast), }?; - if let Ctx::Dest(dest) = ctx { - self.assign(dest.ty, dest.loc, value.loc); - Some(Value::VOID) - } else { - Some(value) + match ctx { + Ctx::Dest(dest) => { + _ = self.assert_ty(expr.pos(), dest.ty, value.ty); + self.assign(dest.ty, dest.loc, value.loc)?; + Some(Value::VOID) + } + Ctx::DestUntyped(loc, size) => { + // Wo dont check since bitcast does + self.assign_opaque(size, loc, value.loc); + Some(Value::VOID) + } + _ => Some(value), } } @@ -1269,13 +1377,11 @@ impl<'a> Codegen<'a> { } } - fn assign(&mut self, ty: Type, right: Loc, left: Loc) -> Option { + fn assign_opaque(&mut self, size: u64, right: Loc, left: Loc) -> Option { if left == right { return Some(Value::VOID); } - let size = self.size_of(ty); - match size { 0 => {} ..=8 => { @@ -1309,6 +1415,10 @@ impl<'a> Codegen<'a> { Some(Value::VOID) } + fn assign(&mut self, ty: Type, right: Loc, left: Loc) -> Option { + self.assign_opaque(self.size_of(ty), right, left) + } + fn to_ptr(&mut self, loc: Loc) -> LinReg { match loc { Loc::Deref(reg, .., off) => { @@ -1521,6 +1631,44 @@ impl<'a> Codegen<'a> { println!("{}:{}:{}: {}", self.path, line, col, msg); unreachable!(); } + + fn alloc_ret_loc(&mut self, ret: Type, ctx: Ctx) -> Loc { + let size = self.size_of(ret); + match size { + 0 => Loc::Imm(0), + ..=8 => Loc::RegRef(1), + ..=16 => match ctx { + Ctx::Dest(dest) => dest.loc, + _ => Loc::Stack(self.alloc_stack(size), 0), + }, + ..=u64::MAX => { + let val = match ctx { + Ctx::Dest(dest) => dest.loc, + _ => Loc::Stack(self.alloc_stack(size), 0), + }; + let (ptr, off) = val.ref_to_ptr(); + self.code.encode(instrs::cp(1, ptr)); + self.code.addi64(1, ptr, off); + val + } + } + } + + fn post_process_ret_loc(&mut self, ty: Type, loc: &Loc) { + let size = self.size_of(ty); + match size { + 0 => {} + ..=8 => {} + ..=16 => { + if let Loc::Stack(ref stack, off) = loc { + self.store_stack(1, stack.offset + off, size as _); + } else { + unreachable!() + } + } + ..=u64::MAX => {} + } + } } #[derive(Debug)] @@ -1704,5 +1852,6 @@ mod tests { structs => include_str!("../examples/structs.hb"); different_types => include_str!("../examples/different_types.hb"); struct_operators => include_str!("../examples/struct_operators.hb"); + directives => include_str!("../examples/directives.hb"); } } diff --git a/hblang/src/lexer.rs b/hblang/src/lexer.rs index 5e6c95972..f3a6ee7a8 100644 --- a/hblang/src/lexer.rs +++ b/hblang/src/lexer.rs @@ -83,6 +83,7 @@ gen_token_kind! { Number, Eof, Error, + Driective, #[keywords] Return = b"return", If = b"if", @@ -195,7 +196,7 @@ impl<'a> Iterator for Lexer<'a> { fn next(&mut self) -> Option { use TokenKind as T; loop { - let start = self.pos; + let mut start = self.pos; let kind = match self.advance()? { b'\n' | b'\r' | b'\t' | b' ' => continue, b'0'..=b'9' => { @@ -204,13 +205,18 @@ impl<'a> Iterator for Lexer<'a> { } T::Number } - b'a'..=b'z' | b'A'..=b'Z' | b'_' => { + c @ (b'a'..=b'z' | b'A'..=b'Z' | b'_' | b'@') => { while let Some(b'a'..=b'z' | b'A'..=b'Z' | b'0'..=b'9' | b'_') = self.peek() { self.advance(); } - let ident = &self.bytes[start as usize..self.pos as usize]; - T::from_ident(ident) + if c == b'@' { + start += 1; + T::Driective + } else { + let ident = &self.bytes[start as usize..self.pos as usize]; + T::from_ident(ident) + } } b':' if self.advance_if(b'=') => T::Decl, b':' => T::Colon, diff --git a/hblang/src/parser.rs b/hblang/src/parser.rs index 02d5fd52c..a83a436b0 100644 --- a/hblang/src/parser.rs +++ b/hblang/src/parser.rs @@ -153,6 +153,14 @@ impl<'a, 'b> Parser<'a, 'b> { let frame = self.idents.len(); let token = self.next(); let mut expr = match token.kind { + T::Driective => E::Directive { + pos: token.start, + name: self.lexer.slice(token.range()), + args: { + self.expect_advance(T::LParen); + self.collect_list(T::Comma, T::RParen, Self::expr) + }, + }, T::True => E::Bool { pos: token.start, value: true, @@ -428,6 +436,11 @@ pub enum Expr<'a> { pos: Pos, value: bool, }, + Directive { + pos: u32, + name: &'a str, + args: &'a [Self], + }, } impl<'a> Expr<'a> { @@ -436,6 +449,7 @@ impl<'a> Expr<'a> { Self::Call { func, .. } => func.pos(), Self::Ident { id, .. } => ident::pos(*id), Self::Break { pos } + | Self::Directive { pos, .. } | Self::Continue { pos } | Self::Closure { pos, .. } | Self::Block { pos, .. } @@ -459,18 +473,31 @@ impl<'a> std::fmt::Display for Expr<'a> { static INDENT: Cell = Cell::new(0); } + fn fmt_list<'a, T>( + f: &mut std::fmt::Formatter, + end: &str, + list: &'a [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, ", ")?; + } + fmt(expr, f)?; + } + write!(f, "{end}") + } + match *self { - Self::Field { target, field } => write!(f, "{}.{}", target, field), + Self::Field { target, field } => write!(f, "{target}.{field}"), + Self::Directive { name, args, .. } => { + write!(f, "@{name}(")?; + fmt_list(f, ")", args, std::fmt::Display::fmt) + } Self::Struct { fields, .. } => { write!(f, "struct {{")?; - let first = &mut true; - for (name, ty) in fields { - if !std::mem::take(first) { - write!(f, ", ")?; - } - write!(f, "{}: {}", name, ty)?; - } - write!(f, "}}") + fmt_list(f, "}", fields, |(name, val), f| write!(f, "{name}: {val}",)) } Self::Ctor { ty, fields, .. } => { let (left, rith) = if fields.iter().any(|(name, _)| name.is_some()) { @@ -482,7 +509,7 @@ impl<'a> std::fmt::Display for Expr<'a> { if let Some(ty) = ty { write!(f, "{ty}")?; } - write!(f, ".{}", left)?; + write!(f, ".{left}")?; let first = &mut true; for (name, val) in fields { if !std::mem::take(first) { @@ -495,46 +522,33 @@ impl<'a> std::fmt::Display for Expr<'a> { } write!(f, "{rith}") } - Self::UnOp { op, val, .. } => write!(f, "{}{}", op, val), + Self::UnOp { op, val, .. } => write!(f, "{op}{val}"), Self::Break { .. } => write!(f, "break;"), Self::Continue { .. } => write!(f, "continue;"), Self::If { cond, then, else_, .. } => { - write!(f, "if {} {}", cond, then)?; + write!(f, "if {cond} {then}")?; if let Some(else_) = else_ { - write!(f, " else {}", else_)?; + write!(f, " else {else_}")?; } Ok(()) } - Self::Loop { body, .. } => write!(f, "loop {}", body), + Self::Loop { body, .. } => write!(f, "loop {body}"), Self::Closure { ret, body, args, .. } => { - write!(f, "|")?; - let first = &mut true; - for arg in args { - if !std::mem::take(first) { - write!(f, ", ")?; - } - write!(f, "{}: {}", arg.name, arg.ty)?; - } - write!(f, "|: {} {}", ret, body) + write!(f, "fn(")?; + fmt_list(f, "", args, |arg, f| write!(f, "{}: {}", arg.name, arg.ty))?; + write!(f, "): {ret} {body}") } Self::Call { func, args } => { - write!(f, "{}(", func)?; - let first = &mut true; - for arg in args { - if !std::mem::take(first) { - write!(f, ", ")?; - } - write!(f, "{}", arg)?; - } - write!(f, ")") + write!(f, "{func}(")?; + fmt_list(f, ")", args, std::fmt::Display::fmt) } - Self::Return { val: Some(val), .. } => write!(f, "return {};", val), + Self::Return { val: Some(val), .. } => write!(f, "return {val};"), Self::Return { val: None, .. } => write!(f, "return;"), - Self::Ident { name, .. } => write!(f, "{}", name), + Self::Ident { name, .. } => write!(f, "{name}"), Self::Block { stmts, .. } => { writeln!(f, "{{")?; INDENT.with(|i| i.set(i.get() + 1)); @@ -543,7 +557,7 @@ impl<'a> std::fmt::Display for Expr<'a> { for _ in 0..INDENT.with(|i| i.get()) { write!(f, " ")?; } - writeln!(f, "{}", stmt)?; + writeln!(f, "{stmt}")?; } Ok(()) })(); @@ -551,21 +565,21 @@ impl<'a> std::fmt::Display for Expr<'a> { write!(f, "}}")?; res } - Self::Number { value, .. } => write!(f, "{}", value), - Self::Bool { value, .. } => write!(f, "{}", value), + Self::Number { value, .. } => write!(f, "{value}"), + Self::Bool { value, .. } => write!(f, "{value}"), Self::BinOp { left, right, op } => { let display_branch = |f: &mut std::fmt::Formatter, expr: &Self| { if let Self::BinOp { op: lop, .. } = expr && op.precedence() > lop.precedence() { - write!(f, "({})", expr) + write!(f, "({expr})") } else { - write!(f, "{}", expr) + write!(f, "{expr}") } }; display_branch(f, left)?; - write!(f, " {} ", op)?; + write!(f, " {op} ")?; display_branch(f, right) } } diff --git a/hblang/tests/codegen_tests_directives.txt b/hblang/tests/codegen_tests_directives.txt new file mode 100644 index 000000000..1322855d8 --- /dev/null +++ b/hblang/tests/codegen_tests_directives.txt @@ -0,0 +1,4 @@ +ev: Ecall +code size: 217 +ret: 0 +status: Ok(()) diff --git a/hblang/tests/parser_tests_arithmetic.txt b/hblang/tests/parser_tests_arithmetic.txt index 73a1788f3..5a9d1e67f 100644 --- a/hblang/tests/parser_tests_arithmetic.txt +++ b/hblang/tests/parser_tests_arithmetic.txt @@ -1,3 +1,3 @@ -main := ||: int { +main := fn(): int { return 10 - 20 / 2 + 4 * (2 + 2) - 4 * 4 + 1; } diff --git a/hblang/tests/parser_tests_example.txt b/hblang/tests/parser_tests_example.txt index 3b672315d..fa54eb111 100644 --- a/hblang/tests/parser_tests_example.txt +++ b/hblang/tests/parser_tests_example.txt @@ -1,3 +1,3 @@ -main := ||: int { +main := fn(): int { return 1; }