adding directives
This commit is contained in:
parent
d8a922df26
commit
70955c1792
9
hblang/examples/directives.hb
Normal file
9
hblang/examples/directives.hb
Normal file
|
@ -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);
|
||||
}
|
|
@ -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<Loc> {
|
||||
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<Value> {
|
||||
fn expr_ctx(&mut self, expr: &'a parser::Expr<'a>, mut ctx: Ctx) -> Option<Value> {
|
||||
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(<type>, <expr>)`",
|
||||
);
|
||||
};
|
||||
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(<type>, <expr>)`",
|
||||
);
|
||||
};
|
||||
|
||||
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<Value> {
|
||||
fn assign_opaque(&mut self, size: u64, right: Loc, left: Loc) -> Option<Value> {
|
||||
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<Value> {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Self::Item> {
|
||||
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,
|
||||
|
|
|
@ -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<usize> = 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)
|
||||
}
|
||||
}
|
||||
|
|
4
hblang/tests/codegen_tests_directives.txt
Normal file
4
hblang/tests/codegen_tests_directives.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
ev: Ecall
|
||||
code size: 217
|
||||
ret: 0
|
||||
status: Ok(())
|
|
@ -1,3 +1,3 @@
|
|||
main := ||: int {
|
||||
main := fn(): int {
|
||||
return 10 - 20 / 2 + 4 * (2 + 2) - 4 * 4 + 1;
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
main := ||: int {
|
||||
main := fn(): int {
|
||||
return 1;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue