forked from AbleOS/holey-bytes
adding minimal enums
This commit is contained in:
parent
397b2a4b1b
commit
12b9d43754
153
lang/README.md
153
lang/README.md
|
@ -51,6 +51,7 @@ main := fn(): uint {
|
|||
}
|
||||
```
|
||||
|
||||
|
||||
#### floating_point_arithmetic
|
||||
```hb
|
||||
main := fn(): f32 {
|
||||
|
@ -113,6 +114,21 @@ main := fn(): uint {
|
|||
}
|
||||
```
|
||||
|
||||
#### hex_octal_binary_literals
|
||||
```hb
|
||||
main := fn(): uint {
|
||||
hex := 0xFF
|
||||
decimal := 255
|
||||
octal := 0o377
|
||||
binary := 0b11111111
|
||||
|
||||
if hex == decimal & octal == decimal & binary == decimal {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
```
|
||||
|
||||
#### loops
|
||||
```hb
|
||||
main := fn(): uint {
|
||||
|
@ -158,6 +174,60 @@ drop := fn(a: uint): void {
|
|||
}
|
||||
```
|
||||
|
||||
#### structs
|
||||
```hb
|
||||
Ty := struct {
|
||||
// comment
|
||||
|
||||
a: uint,
|
||||
}
|
||||
|
||||
Ty2 := struct {
|
||||
ty: Ty,
|
||||
c: uint,
|
||||
}
|
||||
|
||||
useless := struct {}
|
||||
|
||||
main := fn(): uint {
|
||||
// `packed` structs have no padding (all fields are alighred to 1)
|
||||
if @sizeof(packed struct {a: u8, b: u16}) != 3 {
|
||||
return 9001
|
||||
}
|
||||
|
||||
finst := Ty2.{ty: .{a: 4}, c: 3}
|
||||
inst := odher_pass(finst)
|
||||
if inst.c == 3 {
|
||||
return pass(&inst.ty)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
pass := fn(t: ^Ty): uint {
|
||||
return t.a
|
||||
}
|
||||
|
||||
odher_pass := fn(t: Ty2): Ty2 {
|
||||
return t
|
||||
}
|
||||
```
|
||||
|
||||
#### enums
|
||||
```hb
|
||||
Enum := enum {A, B, C}
|
||||
|
||||
some_enum := fn(): Enum return .A
|
||||
|
||||
main := fn(): uint {
|
||||
e := some_enum()
|
||||
|
||||
match e {
|
||||
.A => return 0,
|
||||
_ => return 100,
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### nullable_types
|
||||
```hb
|
||||
main := fn(): uint {
|
||||
|
@ -215,74 +285,6 @@ new_bar := fn(a: ?^uint): ?Bar return .(a, 1)
|
|||
decide := fn(): bool return !false
|
||||
```
|
||||
|
||||
#### structs
|
||||
```hb
|
||||
Ty := struct {
|
||||
// comment
|
||||
|
||||
a: uint,
|
||||
}
|
||||
|
||||
Ty2 := struct {
|
||||
ty: Ty,
|
||||
c: uint,
|
||||
}
|
||||
|
||||
useless := struct {}
|
||||
|
||||
main := fn(): uint {
|
||||
// `packed` structs have no padding (all fields are alighred to 1)
|
||||
if @sizeof(packed struct {a: u8, b: u16}) != 3 {
|
||||
return 9001
|
||||
}
|
||||
|
||||
finst := Ty2.{ty: .{a: 4}, c: 3}
|
||||
inst := odher_pass(finst)
|
||||
if inst.c == 3 {
|
||||
return pass(&inst.ty)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
pass := fn(t: ^Ty): uint {
|
||||
return t.a
|
||||
}
|
||||
|
||||
odher_pass := fn(t: Ty2): Ty2 {
|
||||
return t
|
||||
}
|
||||
```
|
||||
|
||||
#### hex_octal_binary_literals
|
||||
```hb
|
||||
main := fn(): uint {
|
||||
hex := 0xFF
|
||||
decimal := 255
|
||||
octal := 0o377
|
||||
binary := 0b11111111
|
||||
|
||||
if hex == decimal & octal == decimal & binary == decimal {
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
}
|
||||
```
|
||||
|
||||
#### wrong_dead_code_elimination
|
||||
```hb
|
||||
Color := struct {b: u8}
|
||||
main := fn(): void {
|
||||
color := Color.(0)
|
||||
n := @as(u8, 1)
|
||||
loop {
|
||||
if color.b == 255 | color.b == 0 {
|
||||
n = -n
|
||||
}
|
||||
color.b += n
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### inline_return_stack
|
||||
```hb
|
||||
$fun := fn(): [uint; 3] {
|
||||
|
@ -649,6 +651,21 @@ main := fn(): uint {
|
|||
|
||||
### Purely Testing Examples
|
||||
|
||||
#### wrong_dead_code_elimination
|
||||
```hb
|
||||
Color := struct {b: u8}
|
||||
main := fn(): void {
|
||||
color := Color.(0)
|
||||
n := @as(u8, 1)
|
||||
loop {
|
||||
if color.b == 255 | color.b == 0 {
|
||||
n = -n
|
||||
}
|
||||
color.b += n
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### different_function_destinations
|
||||
```hb
|
||||
Stru := struct {a: uint, b: uint}
|
||||
|
|
103
lang/src/fmt.rs
103
lang/src/fmt.rs
|
@ -1,7 +1,7 @@
|
|||
use {
|
||||
crate::{
|
||||
lexer::{self, Lexer, TokenKind},
|
||||
parser::{self, CommentOr, CtorField, Expr, Poser, Radix, StructField},
|
||||
parser::{self, CommentOr, CtorField, EnumField, Expr, Poser, Radix, StructField},
|
||||
},
|
||||
core::fmt::{self},
|
||||
};
|
||||
|
@ -28,50 +28,44 @@ pub fn display_radix(radix: Radix, mut value: u64, buf: &mut [u8; 64]) -> &str {
|
|||
|
||||
#[repr(u8)]
|
||||
enum TokenGroup {
|
||||
Blank = 0,
|
||||
Comment = 1,
|
||||
Keyword = 2,
|
||||
Identifier = 3,
|
||||
Directive = 4,
|
||||
Number = 5,
|
||||
String = 6,
|
||||
Op = 7,
|
||||
Assign = 8,
|
||||
Paren = 9,
|
||||
Bracket = 10,
|
||||
Colon = 11,
|
||||
Comma = 12,
|
||||
Dot = 13,
|
||||
Ctor = 14,
|
||||
Blank,
|
||||
Comment,
|
||||
Keyword,
|
||||
Identifier,
|
||||
Directive,
|
||||
Number,
|
||||
String,
|
||||
Op,
|
||||
Assign,
|
||||
Paren,
|
||||
Bracket,
|
||||
Colon,
|
||||
Comma,
|
||||
Dot,
|
||||
Ctor,
|
||||
}
|
||||
|
||||
fn token_group(kind: TokenKind) -> TokenGroup {
|
||||
use crate::lexer::TokenKind::*;
|
||||
use {crate::lexer::TokenKind::*, TokenGroup as TG};
|
||||
match kind {
|
||||
// unused/unimplemented
|
||||
| BSlash | Pound | Eof | Ct => TokenGroup::Blank,
|
||||
| Comment => TokenGroup::Comment,
|
||||
| Directive => TokenGroup::Directive,
|
||||
| Colon => TokenGroup::Colon,
|
||||
| Semi | Comma => TokenGroup::Comma,
|
||||
| Dot => TokenGroup::Dot,
|
||||
| Ctor | Tupl => TokenGroup::Ctor,
|
||||
| LParen | RParen => TokenGroup::Paren,
|
||||
| LBrace | RBrace | LBrack | RBrack => TokenGroup::Bracket,
|
||||
| Number | Float => TokenGroup::Number,
|
||||
| Under | CtIdent | Ident => TokenGroup::Identifier,
|
||||
| Tick | Tilde | Que
|
||||
| Not | Mod | Band | Bor | Xor
|
||||
| Mul | Add | Sub | Div
|
||||
| Shl | Shr | Or | And
|
||||
| Lt | Gt | Eq | Le | Ge | Ne => TokenGroup::Op,
|
||||
| Decl | Assign
|
||||
| BorAss | XorAss | BandAss
|
||||
| AddAss | SubAss | MulAss | DivAss | ModAss
|
||||
| ShrAss | ShlAss => TokenGroup::Assign,
|
||||
| DQuote | Quote => TokenGroup::String,
|
||||
| Return | If | Else | Loop | Break | Continue | Fn | Idk | Die
|
||||
| Struct | Packed | True | False | Null => TokenGroup::Keyword,
|
||||
BSlash | Pound | Eof | Ct => TG::Blank,
|
||||
Comment => TG::Comment,
|
||||
Directive => TG::Directive,
|
||||
Colon => TG::Colon,
|
||||
Semi | Comma => TG::Comma,
|
||||
Dot => TG::Dot,
|
||||
Ctor | Tupl | TArrow => TG::Ctor,
|
||||
LParen | RParen => TG::Paren,
|
||||
LBrace | RBrace | LBrack | RBrack => TG::Bracket,
|
||||
Number | Float => TG::Number,
|
||||
Under | CtIdent | Ident => TG::Identifier,
|
||||
Tick | Tilde | Que | Not | Mod | Band | Bor | Xor | Mul | Add | Sub | Div | Shl | Shr
|
||||
| Or | And | Lt | Gt | Eq | Le | Ge | Ne => TG::Op,
|
||||
Decl | Assign | BorAss | XorAss | BandAss | AddAss | SubAss | MulAss | DivAss | ModAss
|
||||
| ShrAss | ShlAss => TG::Assign,
|
||||
DQuote | Quote => TG::String,
|
||||
Return | If | Else | Loop | Break | Continue | Fn | Idk | Die | Struct | Packed | True
|
||||
| False | Null | Match | Enum => TG::Keyword,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -295,7 +289,7 @@ impl<'a> Formatter<'a> {
|
|||
f.write_str("packed ")?;
|
||||
}
|
||||
|
||||
write!(f, "struct {{")?;
|
||||
f.write_str("struct {")?;
|
||||
self.fmt_list_low(f, trailing_comma, "}", ",", fields, |s, field, f| {
|
||||
match field {
|
||||
CommentOr::Or(StructField { name, ty, .. }) => {
|
||||
|
@ -311,6 +305,21 @@ impl<'a> Formatter<'a> {
|
|||
Ok(field.or().is_some())
|
||||
})
|
||||
}
|
||||
Expr::Enum { variants, trailing_comma, .. } => {
|
||||
f.write_str("enum {")?;
|
||||
self.fmt_list_low(f, trailing_comma, "}", ",", variants, |_, var, f| {
|
||||
match var {
|
||||
CommentOr::Or(EnumField { name, .. }) => {
|
||||
f.write_str(name)?;
|
||||
}
|
||||
CommentOr::Comment { literal, .. } => {
|
||||
f.write_str(literal)?;
|
||||
f.write_str("\n")?;
|
||||
}
|
||||
}
|
||||
Ok(var.or().is_some())
|
||||
})
|
||||
}
|
||||
Expr::Ctor { ty, fields, trailing_comma, .. } => {
|
||||
if let Some(ty) = ty {
|
||||
self.fmt_paren(ty, f, unary)?;
|
||||
|
@ -385,6 +394,16 @@ impl<'a> Formatter<'a> {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
Expr::Match { value, branches, .. } => {
|
||||
f.write_str("match ")?;
|
||||
self.fmt(value, f)?;
|
||||
f.write_str(" {")?;
|
||||
self.fmt_list(f, true, "}", ",", branches, |s, br, f| {
|
||||
s.fmt(&br.pat, f)?;
|
||||
f.write_str(" => ")?;
|
||||
s.fmt(&br.body, f)
|
||||
})
|
||||
}
|
||||
Expr::Loop { body, .. } => {
|
||||
f.write_str("loop ")?;
|
||||
self.fmt(body, f)
|
||||
|
|
|
@ -121,23 +121,9 @@ pub enum TokenKind {
|
|||
|
||||
Ct,
|
||||
|
||||
Return,
|
||||
If,
|
||||
Else,
|
||||
Loop,
|
||||
Break,
|
||||
Continue,
|
||||
Fn,
|
||||
Struct,
|
||||
Packed,
|
||||
True,
|
||||
False,
|
||||
Null,
|
||||
Idk,
|
||||
Die,
|
||||
|
||||
Ctor,
|
||||
Tupl,
|
||||
TArrow,
|
||||
|
||||
Or,
|
||||
And,
|
||||
|
@ -147,8 +133,26 @@ pub enum TokenKind {
|
|||
BSlash = b'\\',
|
||||
RBrack = b']',
|
||||
Xor = b'^',
|
||||
Tick = b'`',
|
||||
Under = b'_',
|
||||
Tick = b'`',
|
||||
|
||||
Return,
|
||||
If,
|
||||
Match,
|
||||
Else,
|
||||
Loop,
|
||||
Break,
|
||||
Continue,
|
||||
Fn,
|
||||
Struct,
|
||||
Packed,
|
||||
Enum,
|
||||
True,
|
||||
False,
|
||||
Null,
|
||||
Idk,
|
||||
Die,
|
||||
|
||||
// Unused = a-z
|
||||
LBrace = b'{',
|
||||
Bor = b'|',
|
||||
|
@ -300,6 +304,7 @@ gen_token_kind! {
|
|||
#[keywords]
|
||||
Return = b"return",
|
||||
If = b"if",
|
||||
Match = b"match",
|
||||
Else = b"else",
|
||||
Loop = b"loop",
|
||||
Break = b"break",
|
||||
|
@ -307,6 +312,7 @@ gen_token_kind! {
|
|||
Fn = b"fn",
|
||||
Struct = b"struct",
|
||||
Packed = b"packed",
|
||||
Enum = b"enum",
|
||||
True = b"true",
|
||||
False = b"false",
|
||||
Null = b"null",
|
||||
|
@ -316,6 +322,7 @@ gen_token_kind! {
|
|||
#[punkt]
|
||||
Ctor = ".{",
|
||||
Tupl = ".(",
|
||||
TArrow = "=>",
|
||||
// #define OP: each `#[prec]` delimeters a level of precedence from lowest to highest
|
||||
#[ops]
|
||||
#[prec]
|
||||
|
@ -514,6 +521,7 @@ impl<'a> Lexer<'a> {
|
|||
}
|
||||
b'.' if self.advance_if(b'{') => T::Ctor,
|
||||
b'.' if self.advance_if(b'(') => T::Tupl,
|
||||
b'=' if self.advance_if(b'>') => T::TArrow,
|
||||
b'&' if self.advance_if(b'&') => T::And,
|
||||
b'|' if self.advance_if(b'|') => T::Or,
|
||||
b'$' if self.advance_if(b':') => T::Ct,
|
||||
|
|
123
lang/src/lib.rs
123
lang/src/lib.rs
|
@ -352,6 +352,11 @@ pub mod ty {
|
|||
debug_assert_ne!(st.pos, Pos::MAX);
|
||||
crate::SymKey::Struct(st.file, st.pos, st.captures)
|
||||
}
|
||||
Kind::Enum(e) => {
|
||||
let en = &ctx.enums[e];
|
||||
debug_assert_ne!(en.pos, Pos::MAX);
|
||||
crate::SymKey::Enum(en.file, en.pos)
|
||||
}
|
||||
Kind::Ptr(p) => crate::SymKey::Pointer(&ctx.ptrs[p]),
|
||||
Kind::Opt(p) => crate::SymKey::Optional(&ctx.opts[p]),
|
||||
Kind::Func(f) => {
|
||||
|
@ -485,7 +490,7 @@ pub mod ty {
|
|||
{
|
||||
Loc::Reg
|
||||
}
|
||||
Kind::Ptr(_) | Kind::Builtin(_) => Loc::Reg,
|
||||
Kind::Ptr(_) | Kind::Enum(_) | Kind::Builtin(_) => Loc::Reg,
|
||||
Kind::Struct(_) if tys.size_of(*self) == 0 => Loc::Reg,
|
||||
Kind::Struct(_) | Kind::Slice(_) | Kind::Opt(_) => Loc::Stack,
|
||||
Kind::Func(_) | Kind::Global(_) | Kind::Module(_) | Kind::Const(_) => {
|
||||
|
@ -640,6 +645,7 @@ pub mod ty {
|
|||
pub enum Kind {
|
||||
Builtin,
|
||||
Struct,
|
||||
Enum,
|
||||
Ptr,
|
||||
Slice,
|
||||
Opt,
|
||||
|
@ -720,7 +726,7 @@ pub mod ty {
|
|||
f.write_str("[")?;
|
||||
idx.fmt(f)?;
|
||||
f.write_str("]{")?;
|
||||
for (i, &super::Field { name, ty }) in
|
||||
for (i, &super::StructField { name, ty }) in
|
||||
self.tys.struct_fields(idx).iter().enumerate()
|
||||
{
|
||||
if i != 0 {
|
||||
|
@ -736,6 +742,12 @@ pub mod ty {
|
|||
f.write_str(file.ident_str(record.name))
|
||||
}
|
||||
}
|
||||
TK::Enum(idx) => {
|
||||
let enm = &self.tys.ins.enums[idx];
|
||||
debug_assert!(!enm.name.is_null());
|
||||
let file = &self.files[enm.file.index()];
|
||||
f.write_str(file.ident_str(enm.name))
|
||||
}
|
||||
TK::Func(idx) => {
|
||||
f.write_str("fn")?;
|
||||
idx.fmt(f)
|
||||
|
@ -773,6 +785,7 @@ pub enum SymKey<'a> {
|
|||
Pointer(&'a Ptr),
|
||||
Optional(&'a Opt),
|
||||
Struct(Module, Pos, ty::Tuple),
|
||||
Enum(Module, Pos),
|
||||
FuncInst(ty::Func, ty::Tuple),
|
||||
Decl(Module, Ident),
|
||||
Array(&'a Array),
|
||||
|
@ -852,7 +865,19 @@ impl Reloc {
|
|||
}
|
||||
}
|
||||
|
||||
struct Field {
|
||||
struct EnumField {
|
||||
name: Ident,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Enum {
|
||||
name: Ident,
|
||||
pos: Pos,
|
||||
file: Module,
|
||||
field_start: u32,
|
||||
}
|
||||
|
||||
struct StructField {
|
||||
name: Ident,
|
||||
ty: ty::Id,
|
||||
}
|
||||
|
@ -955,18 +980,21 @@ impl IdentInterner {
|
|||
|
||||
#[derive(Default)]
|
||||
struct TypesTmp {
|
||||
fields: Vec<Field>,
|
||||
struct_fields: Vec<StructField>,
|
||||
enum_fields: Vec<EnumField>,
|
||||
args: Vec<ty::Id>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct TypeIns {
|
||||
args: Vec<ty::Id>,
|
||||
fields: Vec<Field>,
|
||||
struct_fields: Vec<StructField>,
|
||||
enum_fields: Vec<EnumField>,
|
||||
funcs: EntVec<ty::Func, Func>,
|
||||
globals: EntVec<ty::Global, Global>,
|
||||
consts: EntVec<ty::Const, Const>,
|
||||
structs: EntVec<ty::Struct, Struct>,
|
||||
enums: EntVec<ty::Enum, Enum>,
|
||||
ptrs: EntVec<ty::Ptr, Ptr>,
|
||||
opts: EntVec<ty::Opt, Opt>,
|
||||
slices: EntVec<ty::Slice, Array>,
|
||||
|
@ -1149,11 +1177,11 @@ trait TypeParser {
|
|||
return ty;
|
||||
}
|
||||
|
||||
let prev_tmp = self.tys().tmp.fields.len();
|
||||
let prev_tmp = self.tys().tmp.struct_fields.len();
|
||||
for field in fields.iter().filter_map(CommentOr::or) {
|
||||
let ty = self.parse_ty(file, &field.ty, None, files);
|
||||
let field = Field { name: self.tys().names.intern(field.name), ty };
|
||||
self.tys().tmp.fields.push(field);
|
||||
let field = StructField { name: self.tys().names.intern(field.name), ty };
|
||||
self.tys().tmp.struct_fields.push(field);
|
||||
}
|
||||
|
||||
let tys = self.tys();
|
||||
|
@ -1164,13 +1192,43 @@ trait TypeParser {
|
|||
file,
|
||||
pos,
|
||||
name: name.unwrap_or_default(),
|
||||
field_start: tys.ins.fields.len() as _,
|
||||
field_start: tys.ins.struct_fields.len() as _,
|
||||
explicit_alignment: packed.then_some(1),
|
||||
..Default::default()
|
||||
})
|
||||
.into();
|
||||
|
||||
tys.ins.fields.extend(tys.tmp.fields.drain(prev_tmp..));
|
||||
tys.ins.struct_fields.extend(tys.tmp.struct_fields.drain(prev_tmp..));
|
||||
|
||||
tys.syms.insert(sym, ty, &tys.ins);
|
||||
ty
|
||||
}
|
||||
Expr::Enum { pos, variants, .. } => {
|
||||
let sym = SymKey::Enum(file, pos);
|
||||
let tys = self.tys();
|
||||
if let Some(&ty) = tys.syms.get(sym, &tys.ins) {
|
||||
return ty;
|
||||
}
|
||||
|
||||
let prev_tmp = self.tys().tmp.enum_fields.len();
|
||||
for field in variants.iter().filter_map(CommentOr::or) {
|
||||
let field = EnumField { name: self.tys().names.intern(field.name) };
|
||||
self.tys().tmp.enum_fields.push(field);
|
||||
}
|
||||
|
||||
let tys = self.tys();
|
||||
let ty = tys
|
||||
.ins
|
||||
.enums
|
||||
.push(Enum {
|
||||
file,
|
||||
pos,
|
||||
name: name.unwrap_or_default(),
|
||||
field_start: tys.ins.enum_fields.len() as _,
|
||||
})
|
||||
.into();
|
||||
|
||||
tys.ins.enum_fields.extend(tys.tmp.enum_fields.drain(prev_tmp..));
|
||||
|
||||
tys.syms.insert(sym, ty, &tys.ins);
|
||||
ty
|
||||
|
@ -1211,13 +1269,6 @@ trait TypeParser {
|
|||
}
|
||||
|
||||
impl Types {
|
||||
fn struct_field_range(&self, strct: ty::Struct) -> Range<usize> {
|
||||
let start = self.ins.structs[strct].field_start as usize;
|
||||
let end =
|
||||
self.ins.structs.next(strct).map_or(self.ins.fields.len(), |s| s.field_start as usize);
|
||||
start..end
|
||||
}
|
||||
|
||||
fn pack_args(&mut self, arg_base: usize) -> Option<ty::Tuple> {
|
||||
let base = self.ins.args.len();
|
||||
self.ins.args.extend(self.tmp.args.drain(arg_base..));
|
||||
|
@ -1233,8 +1284,29 @@ impl Types {
|
|||
ty::Tuple::new(sp, len)
|
||||
}
|
||||
|
||||
fn struct_fields(&self, strct: ty::Struct) -> &[Field] {
|
||||
&self.ins.fields[self.struct_field_range(strct)]
|
||||
fn struct_fields(&self, strct: ty::Struct) -> &[StructField] {
|
||||
&self.ins.struct_fields[self.struct_field_range(strct)]
|
||||
}
|
||||
|
||||
fn struct_field_range(&self, strct: ty::Struct) -> Range<usize> {
|
||||
let start = self.ins.structs[strct].field_start as usize;
|
||||
let end = self
|
||||
.ins
|
||||
.structs
|
||||
.next(strct)
|
||||
.map_or(self.ins.struct_fields.len(), |s| s.field_start as usize);
|
||||
start..end
|
||||
}
|
||||
|
||||
fn enum_fields(&self, enm: ty::Enum) -> &[EnumField] {
|
||||
&self.ins.enum_fields[self.enum_field_range(enm)]
|
||||
}
|
||||
|
||||
fn enum_field_range(&self, enm: ty::Enum) -> Range<usize> {
|
||||
let start = self.ins.enums[enm].field_start as usize;
|
||||
let end =
|
||||
self.ins.enums.next(enm).map_or(self.ins.enum_fields.len(), |s| s.field_start as usize);
|
||||
start..end
|
||||
}
|
||||
|
||||
fn parama(&self, ret: impl Into<ty::Id>) -> (Option<PLoc>, ParamAlloc) {
|
||||
|
@ -1285,6 +1357,7 @@ impl Types {
|
|||
self.ins.structs[stru].size.set(oiter.offset);
|
||||
oiter.offset
|
||||
}
|
||||
ty::Kind::Enum(enm) => (self.enum_field_range(enm).len().ilog2() + 7) / 8,
|
||||
ty::Kind::Opt(opt) => {
|
||||
let base = self.ins.opts[opt].base;
|
||||
if self.nieche_of(base).is_some() {
|
||||
|
@ -1308,7 +1381,7 @@ impl Types {
|
|||
|| {
|
||||
self.struct_fields(stru)
|
||||
.iter()
|
||||
.map(|&Field { ty, .. }| self.align_of(ty))
|
||||
.map(|&StructField { ty, .. }| self.align_of(ty))
|
||||
.max()
|
||||
.unwrap_or(1)
|
||||
},
|
||||
|
@ -1380,11 +1453,11 @@ impl Types {
|
|||
self.ins.args.clear();
|
||||
self.ins.globals.clear();
|
||||
self.ins.structs.clear();
|
||||
self.ins.fields.clear();
|
||||
self.ins.struct_fields.clear();
|
||||
self.ins.ptrs.clear();
|
||||
self.ins.slices.clear();
|
||||
|
||||
debug_assert_eq!(self.tmp.fields.len(), 0);
|
||||
debug_assert_eq!(self.tmp.struct_fields.len(), 0);
|
||||
debug_assert_eq!(self.tmp.args.len(), 0);
|
||||
|
||||
debug_assert_eq!(self.tasks.len(), 0);
|
||||
|
@ -1416,9 +1489,9 @@ impl OffsetIter {
|
|||
.map(|(f, off)| (off, f.ty))
|
||||
}
|
||||
|
||||
fn next<'a>(&mut self, tys: &'a Types) -> Option<(&'a Field, Offset)> {
|
||||
fn next<'a>(&mut self, tys: &'a Types) -> Option<(&'a StructField, Offset)> {
|
||||
let stru = &tys.ins.structs[self.strct];
|
||||
let field = &tys.ins.fields[self.fields.next()?];
|
||||
let field = &tys.ins.struct_fields[self.fields.next()?];
|
||||
|
||||
let align = stru.explicit_alignment.map_or_else(|| tys.align_of(field.ty), |a| a as u32);
|
||||
self.offset = (self.offset + align - 1) & !(align - 1);
|
||||
|
@ -1433,7 +1506,7 @@ impl OffsetIter {
|
|||
Some((field.ty, off))
|
||||
}
|
||||
|
||||
fn into_iter(mut self, tys: &Types) -> impl Iterator<Item = (&Field, Offset)> {
|
||||
fn into_iter(mut self, tys: &Types) -> impl Iterator<Item = (&StructField, Offset)> {
|
||||
core::iter::from_fn(move || self.next(tys))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -397,6 +397,22 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
},
|
||||
trailing_comma: core::mem::take(&mut self.trailing_sep),
|
||||
},
|
||||
T::Enum => E::Enum {
|
||||
pos,
|
||||
variants: {
|
||||
self.expect_advance(T::LBrace)?;
|
||||
self.collect_list(T::Comma, T::RBrace, |s| {
|
||||
let tok = s.token;
|
||||
Some(if s.advance_if(T::Comment) {
|
||||
CommentOr::Comment { literal: s.tok_str(tok), pos: tok.start }
|
||||
} else {
|
||||
let name = s.expect_advance(T::Ident)?;
|
||||
CommentOr::Or(EnumField { pos: name.start, name: s.tok_str(name) })
|
||||
})
|
||||
})
|
||||
},
|
||||
trailing_comma: core::mem::take(&mut self.trailing_sep),
|
||||
},
|
||||
T::Ident | T::CtIdent => {
|
||||
let (id, is_first) = self.resolve_ident(token);
|
||||
E::Ident {
|
||||
|
@ -413,6 +429,20 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
then: self.ptr_expr()?,
|
||||
else_: self.advance_if(T::Else).then(|| self.ptr_expr()).trans()?,
|
||||
},
|
||||
T::Match => E::Match {
|
||||
pos,
|
||||
value: self.ptr_expr()?,
|
||||
branches: {
|
||||
self.expect_advance(T::LBrace)?;
|
||||
self.collect_list(T::Comma, T::RBrace, |s| {
|
||||
Some(MatchBranch {
|
||||
pat: s.expr()?,
|
||||
pos: s.expect_advance(T::TArrow)?.start,
|
||||
body: s.expr()?,
|
||||
})
|
||||
})
|
||||
},
|
||||
},
|
||||
T::Loop => E::Loop { pos, body: self.ptr_expr()? },
|
||||
T::Break => E::Break { pos },
|
||||
T::Continue => E::Continue { pos },
|
||||
|
@ -459,14 +489,18 @@ impl<'a, 'b> Parser<'a, 'b> {
|
|||
pos
|
||||
},
|
||||
},
|
||||
T::Band | T::Mul | T::Xor | T::Sub | T::Que | T::Not => E::UnOp {
|
||||
T::Band | T::Mul | T::Xor | T::Sub | T::Que | T::Not | T::Dot => E::UnOp {
|
||||
pos,
|
||||
op: token.kind,
|
||||
val: {
|
||||
let prev_ident_stack = self.ctx.idents.len();
|
||||
let expr = self.ptr_unit_expr()?;
|
||||
if token.kind == T::Band {
|
||||
self.flag_idents(*expr, idfl::REFERENCED);
|
||||
}
|
||||
if token.kind == T::Dot {
|
||||
self.ctx.idents.truncate(prev_ident_stack);
|
||||
}
|
||||
expr
|
||||
},
|
||||
},
|
||||
|
@ -858,6 +892,11 @@ generate_expr! {
|
|||
then: &'a Self,
|
||||
else_: Option<&'a Self>,
|
||||
},
|
||||
Match {
|
||||
pos: Pos,
|
||||
value: &'a Self,
|
||||
branches: &'a [MatchBranch<'a>],
|
||||
},
|
||||
/// `'loop' Expr`
|
||||
Loop {
|
||||
pos: Pos,
|
||||
|
@ -877,6 +916,12 @@ generate_expr! {
|
|||
trailing_comma: bool,
|
||||
packed: bool,
|
||||
},
|
||||
/// `'enum' LIST('{', ',', '}', Ident)`
|
||||
Enum {
|
||||
pos: Pos,
|
||||
variants: &'a [CommentOr<'a, EnumField<'a>>],
|
||||
trailing_comma: bool,
|
||||
},
|
||||
/// `[Expr] LIST('.{', ',', '}', Ident [':' Expr])`
|
||||
Ctor {
|
||||
pos: Pos,
|
||||
|
@ -992,6 +1037,31 @@ impl Expr<'_> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub struct MatchBranch<'a> {
|
||||
pub pat: Expr<'a>,
|
||||
pub pos: Pos,
|
||||
pub body: Expr<'a>,
|
||||
}
|
||||
|
||||
impl Poser for MatchBranch<'_> {
|
||||
fn posi(&self) -> Pos {
|
||||
self.pat.pos()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub struct EnumField<'a> {
|
||||
pub pos: Pos,
|
||||
pub name: &'a str,
|
||||
}
|
||||
|
||||
impl Poser for EnumField<'_> {
|
||||
fn posi(&self) -> Pos {
|
||||
self.pos
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub struct StructField<'a> {
|
||||
pub pos: Pos,
|
||||
|
|
166
lang/src/son.rs
166
lang/src/son.rs
|
@ -10,7 +10,7 @@ use {
|
|||
parser::{
|
||||
self,
|
||||
idfl::{self},
|
||||
CtorField, Expr, Pos,
|
||||
CtorField, Expr, MatchBranch, Pos,
|
||||
},
|
||||
ty::{self, Arg, ArrayLen, Loc, Module, Tuple},
|
||||
utils::{BitSet, Ent, Vc},
|
||||
|
@ -2944,6 +2944,41 @@ impl<'a> Codegen<'a> {
|
|||
vl.ty = base;
|
||||
Some(vl)
|
||||
}
|
||||
Expr::UnOp { pos, op: TokenKind::Dot, val: &Expr::Ident { id, .. } } => {
|
||||
inference!(ty, ctx, self, pos, "enum type", "<EnumTy>.Variant");
|
||||
|
||||
let ty::Kind::Enum(e) = ty.expand() else {
|
||||
self.report(
|
||||
pos,
|
||||
fa!("expected inferred type to be enum but got '{}'", self.ty_display(ty)),
|
||||
);
|
||||
return Value::NEVER;
|
||||
};
|
||||
|
||||
let intrnd = self.tys.names.project(self.file().ident_str(id));
|
||||
let Some(index) =
|
||||
self.tys.enum_fields(e).iter().position(|f| Some(f.name) == intrnd)
|
||||
else {
|
||||
let field_list = self
|
||||
.tys
|
||||
.enum_fields(e)
|
||||
.iter()
|
||||
.map(|f| self.tys.names.ident_str(f.name))
|
||||
.intersperse("', '")
|
||||
.collect::<String>();
|
||||
self.report(
|
||||
pos,
|
||||
fa!(
|
||||
"the '{}' does not have this variant, \
|
||||
but it does have '{field_list}'",
|
||||
self.ty_display(ty)
|
||||
),
|
||||
);
|
||||
return Value::NEVER;
|
||||
};
|
||||
|
||||
Some(self.ci.nodes.new_const_lit(ty, index as i64))
|
||||
}
|
||||
Expr::UnOp { pos, op: op @ TokenKind::Sub, val } => {
|
||||
let val =
|
||||
self.expr_ctx(val, Ctx::default().with_ty(ctx.ty.unwrap_or(ty::Id::INT)))?;
|
||||
|
@ -3569,6 +3604,17 @@ impl<'a> Codegen<'a> {
|
|||
for stmt in stmts {
|
||||
ret = ret.and(self.expr(stmt));
|
||||
if let Some(mut id) = ret {
|
||||
if id.ty != ty::Id::VOID {
|
||||
self.report(
|
||||
stmt.pos(),
|
||||
fa!(
|
||||
"statements need to evaluate to 'void', \
|
||||
but this statements evaluates '{}', \
|
||||
use '_ = <stmt>' to discard the value if its intentional",
|
||||
self.ty_display(id.ty)
|
||||
),
|
||||
);
|
||||
}
|
||||
self.assert_ty(stmt.pos(), &mut id, ty::Id::VOID, "statement");
|
||||
} else {
|
||||
break;
|
||||
|
@ -3885,6 +3931,119 @@ impl<'a> Codegen<'a> {
|
|||
|
||||
Some(Value::VOID)
|
||||
}
|
||||
Expr::Match { pos, value, branches } => {
|
||||
let value = self.expr(value)?;
|
||||
|
||||
let ty::Kind::Enum(e) = value.ty.expand() else {
|
||||
self.report(
|
||||
pos,
|
||||
fa!(
|
||||
"match operates on enums (for now), '{}' is not an enum",
|
||||
self.ty_display(value.ty)
|
||||
),
|
||||
);
|
||||
return Value::NEVER;
|
||||
};
|
||||
|
||||
let mut covered_values = vec![Pos::MAX; self.tys.enum_field_range(e).len()];
|
||||
let mut scopes = vec![];
|
||||
let mut else_branch = None;
|
||||
for &MatchBranch { pat, pos: bp, body } in branches {
|
||||
if let Expr::Wildcard { .. } = pat {
|
||||
else_branch = Some(body);
|
||||
continue;
|
||||
}
|
||||
let pat_val = self.eval_const(self.ci.file, &pat, value.ty);
|
||||
if covered_values[pat_val as usize] != Pos::MAX {
|
||||
self.report(bp, "duplicate branch");
|
||||
self.report(covered_values[pat_val as usize], "first branch declared here");
|
||||
continue;
|
||||
}
|
||||
covered_values[pat_val as usize] = bp;
|
||||
|
||||
let pat_val = self.ci.nodes.new_const(value.ty, pat_val as i64);
|
||||
let cnd = self.ci.nodes.new_node(
|
||||
ty::Id::BOOL,
|
||||
Kind::BinOp { op: TokenKind::Eq },
|
||||
[VOID, value.id, pat_val],
|
||||
self.tys,
|
||||
);
|
||||
|
||||
let if_node = self.ci.nodes.new_node(
|
||||
ty::Id::VOID,
|
||||
Kind::If,
|
||||
[self.ci.ctrl.get(), cnd],
|
||||
self.tys,
|
||||
);
|
||||
|
||||
let cached_scope = self.ci.scope.dup(&mut self.ci.nodes);
|
||||
self.ci.ctrl.set(
|
||||
self.ci.nodes.new_node(ty::Id::VOID, Kind::Then, [if_node], self.tys),
|
||||
&mut self.ci.nodes,
|
||||
);
|
||||
let ctrl = self.expr(&body).map_or(Nid::MAX, |_| self.ci.ctrl.get());
|
||||
scopes.push((ctrl, mem::replace(&mut self.ci.scope, cached_scope)));
|
||||
|
||||
self.ci.ctrl.set(
|
||||
self.ci.nodes.new_node(ty::Id::VOID, Kind::Else, [if_node], self.tys),
|
||||
&mut self.ci.nodes,
|
||||
);
|
||||
}
|
||||
|
||||
let mut rcntrl = if let Some(ebr) = else_branch {
|
||||
self.expr(&ebr).map_or(Nid::MAX, |_| self.ci.ctrl.get())
|
||||
} else {
|
||||
let missing_branches = covered_values
|
||||
.into_iter()
|
||||
.zip(self.tys.enum_fields(e))
|
||||
.filter(|&(f, _)| f == Pos::MAX)
|
||||
.map(|(_, f)| self.tys.names.ident_str(f.name))
|
||||
.intersperse("', '")
|
||||
.collect::<String>();
|
||||
|
||||
if !missing_branches.is_empty() {
|
||||
self.report(
|
||||
pos,
|
||||
fa!("not all cases covered, missing '{missing_branches}'"),
|
||||
);
|
||||
}
|
||||
self.ci.ctrl.get()
|
||||
};
|
||||
|
||||
for (lcntrl, mut then_scope) in scopes.into_iter().rev() {
|
||||
if lcntrl == Nid::MAX && rcntrl == Nid::MAX {
|
||||
then_scope.clear(&mut self.ci.nodes);
|
||||
return None;
|
||||
} else if lcntrl == Nid::MAX {
|
||||
then_scope.clear(&mut self.ci.nodes);
|
||||
return Some(Value::VOID);
|
||||
} else if rcntrl == Nid::MAX {
|
||||
self.ci.scope.clear(&mut self.ci.nodes);
|
||||
self.ci.scope = then_scope;
|
||||
self.ci.ctrl.set(lcntrl, &mut self.ci.nodes);
|
||||
return Some(Value::VOID);
|
||||
}
|
||||
|
||||
rcntrl = self.ci.nodes.new_node(
|
||||
ty::Id::VOID,
|
||||
Kind::Region,
|
||||
[lcntrl, rcntrl],
|
||||
self.tys,
|
||||
);
|
||||
self.ci.ctrl.set(rcntrl, &mut self.ci.nodes);
|
||||
|
||||
self.ci.nodes.merge_scopes(
|
||||
&mut self.ci.loops,
|
||||
&self.ci.ctrl,
|
||||
&mut self.ci.scope,
|
||||
&mut then_scope,
|
||||
self.tys,
|
||||
);
|
||||
then_scope.clear(&mut self.ci.nodes);
|
||||
}
|
||||
|
||||
Some(Value::VOID)
|
||||
}
|
||||
ref e => {
|
||||
self.report_unhandled_ast(e, "bruh");
|
||||
Value::NEVER
|
||||
|
@ -4953,11 +5112,12 @@ mod tests {
|
|||
comments;
|
||||
if_statements;
|
||||
variables;
|
||||
hex_octal_binary_literals;
|
||||
loops;
|
||||
pointers;
|
||||
nullable_types;
|
||||
structs;
|
||||
hex_octal_binary_literals;
|
||||
enums;
|
||||
nullable_types;
|
||||
struct_operators;
|
||||
global_variables;
|
||||
constants;
|
||||
|
|
20
lang/tests/son_tests_enums.txt
Normal file
20
lang/tests/son_tests_enums.txt
Normal file
|
@ -0,0 +1,20 @@
|
|||
main:
|
||||
ADDI64 r254, r254, -16d
|
||||
ST r31, r254, 0a, 16h
|
||||
JAL r31, r0, :some_enum
|
||||
CP r32, r1
|
||||
ANDI r32, r32, 255d
|
||||
JNE r32, r0, :0
|
||||
CP r1, r0
|
||||
JMP :1
|
||||
0: LI64 r32, 100d
|
||||
CP r1, r32
|
||||
1: LD r31, r254, 0a, 16h
|
||||
ADDI64 r254, r254, 16d
|
||||
JALA r0, r31, 0a
|
||||
some_enum:
|
||||
CP r1, r0
|
||||
JALA r0, r31, 0a
|
||||
code size: 128
|
||||
ret: 0
|
||||
status: Ok(())
|
Loading…
Reference in a new issue