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
|
#### floating_point_arithmetic
|
||||||
```hb
|
```hb
|
||||||
main := fn(): f32 {
|
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
|
#### loops
|
||||||
```hb
|
```hb
|
||||||
main := fn(): uint {
|
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
|
#### nullable_types
|
||||||
```hb
|
```hb
|
||||||
main := fn(): uint {
|
main := fn(): uint {
|
||||||
|
@ -215,74 +285,6 @@ new_bar := fn(a: ?^uint): ?Bar return .(a, 1)
|
||||||
decide := fn(): bool return !false
|
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
|
#### inline_return_stack
|
||||||
```hb
|
```hb
|
||||||
$fun := fn(): [uint; 3] {
|
$fun := fn(): [uint; 3] {
|
||||||
|
@ -649,6 +651,21 @@ main := fn(): uint {
|
||||||
|
|
||||||
### Purely Testing Examples
|
### 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
|
#### different_function_destinations
|
||||||
```hb
|
```hb
|
||||||
Stru := struct {a: uint, b: uint}
|
Stru := struct {a: uint, b: uint}
|
||||||
|
|
103
lang/src/fmt.rs
103
lang/src/fmt.rs
|
@ -1,7 +1,7 @@
|
||||||
use {
|
use {
|
||||||
crate::{
|
crate::{
|
||||||
lexer::{self, Lexer, TokenKind},
|
lexer::{self, Lexer, TokenKind},
|
||||||
parser::{self, CommentOr, CtorField, Expr, Poser, Radix, StructField},
|
parser::{self, CommentOr, CtorField, EnumField, Expr, Poser, Radix, StructField},
|
||||||
},
|
},
|
||||||
core::fmt::{self},
|
core::fmt::{self},
|
||||||
};
|
};
|
||||||
|
@ -28,50 +28,44 @@ pub fn display_radix(radix: Radix, mut value: u64, buf: &mut [u8; 64]) -> &str {
|
||||||
|
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
enum TokenGroup {
|
enum TokenGroup {
|
||||||
Blank = 0,
|
Blank,
|
||||||
Comment = 1,
|
Comment,
|
||||||
Keyword = 2,
|
Keyword,
|
||||||
Identifier = 3,
|
Identifier,
|
||||||
Directive = 4,
|
Directive,
|
||||||
Number = 5,
|
Number,
|
||||||
String = 6,
|
String,
|
||||||
Op = 7,
|
Op,
|
||||||
Assign = 8,
|
Assign,
|
||||||
Paren = 9,
|
Paren,
|
||||||
Bracket = 10,
|
Bracket,
|
||||||
Colon = 11,
|
Colon,
|
||||||
Comma = 12,
|
Comma,
|
||||||
Dot = 13,
|
Dot,
|
||||||
Ctor = 14,
|
Ctor,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn token_group(kind: TokenKind) -> TokenGroup {
|
fn token_group(kind: TokenKind) -> TokenGroup {
|
||||||
use crate::lexer::TokenKind::*;
|
use {crate::lexer::TokenKind::*, TokenGroup as TG};
|
||||||
match kind {
|
match kind {
|
||||||
// unused/unimplemented
|
BSlash | Pound | Eof | Ct => TG::Blank,
|
||||||
| BSlash | Pound | Eof | Ct => TokenGroup::Blank,
|
Comment => TG::Comment,
|
||||||
| Comment => TokenGroup::Comment,
|
Directive => TG::Directive,
|
||||||
| Directive => TokenGroup::Directive,
|
Colon => TG::Colon,
|
||||||
| Colon => TokenGroup::Colon,
|
Semi | Comma => TG::Comma,
|
||||||
| Semi | Comma => TokenGroup::Comma,
|
Dot => TG::Dot,
|
||||||
| Dot => TokenGroup::Dot,
|
Ctor | Tupl | TArrow => TG::Ctor,
|
||||||
| Ctor | Tupl => TokenGroup::Ctor,
|
LParen | RParen => TG::Paren,
|
||||||
| LParen | RParen => TokenGroup::Paren,
|
LBrace | RBrace | LBrack | RBrack => TG::Bracket,
|
||||||
| LBrace | RBrace | LBrack | RBrack => TokenGroup::Bracket,
|
Number | Float => TG::Number,
|
||||||
| Number | Float => TokenGroup::Number,
|
Under | CtIdent | Ident => TG::Identifier,
|
||||||
| Under | CtIdent | Ident => TokenGroup::Identifier,
|
Tick | Tilde | Que | Not | Mod | Band | Bor | Xor | Mul | Add | Sub | Div | Shl | Shr
|
||||||
| Tick | Tilde | Que
|
| Or | And | Lt | Gt | Eq | Le | Ge | Ne => TG::Op,
|
||||||
| Not | Mod | Band | Bor | Xor
|
Decl | Assign | BorAss | XorAss | BandAss | AddAss | SubAss | MulAss | DivAss | ModAss
|
||||||
| Mul | Add | Sub | Div
|
| ShrAss | ShlAss => TG::Assign,
|
||||||
| Shl | Shr | Or | And
|
DQuote | Quote => TG::String,
|
||||||
| Lt | Gt | Eq | Le | Ge | Ne => TokenGroup::Op,
|
Return | If | Else | Loop | Break | Continue | Fn | Idk | Die | Struct | Packed | True
|
||||||
| Decl | Assign
|
| False | Null | Match | Enum => TG::Keyword,
|
||||||
| 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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,7 +289,7 @@ impl<'a> Formatter<'a> {
|
||||||
f.write_str("packed ")?;
|
f.write_str("packed ")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
write!(f, "struct {{")?;
|
f.write_str("struct {")?;
|
||||||
self.fmt_list_low(f, trailing_comma, "}", ",", fields, |s, field, f| {
|
self.fmt_list_low(f, trailing_comma, "}", ",", fields, |s, field, f| {
|
||||||
match field {
|
match field {
|
||||||
CommentOr::Or(StructField { name, ty, .. }) => {
|
CommentOr::Or(StructField { name, ty, .. }) => {
|
||||||
|
@ -311,6 +305,21 @@ impl<'a> Formatter<'a> {
|
||||||
Ok(field.or().is_some())
|
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, .. } => {
|
Expr::Ctor { ty, fields, trailing_comma, .. } => {
|
||||||
if let Some(ty) = ty {
|
if let Some(ty) = ty {
|
||||||
self.fmt_paren(ty, f, unary)?;
|
self.fmt_paren(ty, f, unary)?;
|
||||||
|
@ -385,6 +394,16 @@ impl<'a> Formatter<'a> {
|
||||||
}
|
}
|
||||||
Ok(())
|
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, .. } => {
|
Expr::Loop { body, .. } => {
|
||||||
f.write_str("loop ")?;
|
f.write_str("loop ")?;
|
||||||
self.fmt(body, f)
|
self.fmt(body, f)
|
||||||
|
|
|
@ -121,23 +121,9 @@ pub enum TokenKind {
|
||||||
|
|
||||||
Ct,
|
Ct,
|
||||||
|
|
||||||
Return,
|
|
||||||
If,
|
|
||||||
Else,
|
|
||||||
Loop,
|
|
||||||
Break,
|
|
||||||
Continue,
|
|
||||||
Fn,
|
|
||||||
Struct,
|
|
||||||
Packed,
|
|
||||||
True,
|
|
||||||
False,
|
|
||||||
Null,
|
|
||||||
Idk,
|
|
||||||
Die,
|
|
||||||
|
|
||||||
Ctor,
|
Ctor,
|
||||||
Tupl,
|
Tupl,
|
||||||
|
TArrow,
|
||||||
|
|
||||||
Or,
|
Or,
|
||||||
And,
|
And,
|
||||||
|
@ -147,8 +133,26 @@ pub enum TokenKind {
|
||||||
BSlash = b'\\',
|
BSlash = b'\\',
|
||||||
RBrack = b']',
|
RBrack = b']',
|
||||||
Xor = b'^',
|
Xor = b'^',
|
||||||
Tick = b'`',
|
|
||||||
Under = b'_',
|
Under = b'_',
|
||||||
|
Tick = b'`',
|
||||||
|
|
||||||
|
Return,
|
||||||
|
If,
|
||||||
|
Match,
|
||||||
|
Else,
|
||||||
|
Loop,
|
||||||
|
Break,
|
||||||
|
Continue,
|
||||||
|
Fn,
|
||||||
|
Struct,
|
||||||
|
Packed,
|
||||||
|
Enum,
|
||||||
|
True,
|
||||||
|
False,
|
||||||
|
Null,
|
||||||
|
Idk,
|
||||||
|
Die,
|
||||||
|
|
||||||
// Unused = a-z
|
// Unused = a-z
|
||||||
LBrace = b'{',
|
LBrace = b'{',
|
||||||
Bor = b'|',
|
Bor = b'|',
|
||||||
|
@ -300,6 +304,7 @@ gen_token_kind! {
|
||||||
#[keywords]
|
#[keywords]
|
||||||
Return = b"return",
|
Return = b"return",
|
||||||
If = b"if",
|
If = b"if",
|
||||||
|
Match = b"match",
|
||||||
Else = b"else",
|
Else = b"else",
|
||||||
Loop = b"loop",
|
Loop = b"loop",
|
||||||
Break = b"break",
|
Break = b"break",
|
||||||
|
@ -307,6 +312,7 @@ gen_token_kind! {
|
||||||
Fn = b"fn",
|
Fn = b"fn",
|
||||||
Struct = b"struct",
|
Struct = b"struct",
|
||||||
Packed = b"packed",
|
Packed = b"packed",
|
||||||
|
Enum = b"enum",
|
||||||
True = b"true",
|
True = b"true",
|
||||||
False = b"false",
|
False = b"false",
|
||||||
Null = b"null",
|
Null = b"null",
|
||||||
|
@ -316,6 +322,7 @@ gen_token_kind! {
|
||||||
#[punkt]
|
#[punkt]
|
||||||
Ctor = ".{",
|
Ctor = ".{",
|
||||||
Tupl = ".(",
|
Tupl = ".(",
|
||||||
|
TArrow = "=>",
|
||||||
// #define OP: each `#[prec]` delimeters a level of precedence from lowest to highest
|
// #define OP: each `#[prec]` delimeters a level of precedence from lowest to highest
|
||||||
#[ops]
|
#[ops]
|
||||||
#[prec]
|
#[prec]
|
||||||
|
@ -514,6 +521,7 @@ impl<'a> Lexer<'a> {
|
||||||
}
|
}
|
||||||
b'.' if self.advance_if(b'{') => T::Ctor,
|
b'.' if self.advance_if(b'{') => T::Ctor,
|
||||||
b'.' if self.advance_if(b'(') => T::Tupl,
|
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::And,
|
||||||
b'|' if self.advance_if(b'|') => T::Or,
|
b'|' if self.advance_if(b'|') => T::Or,
|
||||||
b'$' if self.advance_if(b':') => T::Ct,
|
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);
|
debug_assert_ne!(st.pos, Pos::MAX);
|
||||||
crate::SymKey::Struct(st.file, st.pos, st.captures)
|
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::Ptr(p) => crate::SymKey::Pointer(&ctx.ptrs[p]),
|
||||||
Kind::Opt(p) => crate::SymKey::Optional(&ctx.opts[p]),
|
Kind::Opt(p) => crate::SymKey::Optional(&ctx.opts[p]),
|
||||||
Kind::Func(f) => {
|
Kind::Func(f) => {
|
||||||
|
@ -485,7 +490,7 @@ pub mod ty {
|
||||||
{
|
{
|
||||||
Loc::Reg
|
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(_) if tys.size_of(*self) == 0 => Loc::Reg,
|
||||||
Kind::Struct(_) | Kind::Slice(_) | Kind::Opt(_) => Loc::Stack,
|
Kind::Struct(_) | Kind::Slice(_) | Kind::Opt(_) => Loc::Stack,
|
||||||
Kind::Func(_) | Kind::Global(_) | Kind::Module(_) | Kind::Const(_) => {
|
Kind::Func(_) | Kind::Global(_) | Kind::Module(_) | Kind::Const(_) => {
|
||||||
|
@ -640,6 +645,7 @@ pub mod ty {
|
||||||
pub enum Kind {
|
pub enum Kind {
|
||||||
Builtin,
|
Builtin,
|
||||||
Struct,
|
Struct,
|
||||||
|
Enum,
|
||||||
Ptr,
|
Ptr,
|
||||||
Slice,
|
Slice,
|
||||||
Opt,
|
Opt,
|
||||||
|
@ -720,7 +726,7 @@ pub mod ty {
|
||||||
f.write_str("[")?;
|
f.write_str("[")?;
|
||||||
idx.fmt(f)?;
|
idx.fmt(f)?;
|
||||||
f.write_str("]{")?;
|
f.write_str("]{")?;
|
||||||
for (i, &super::Field { name, ty }) in
|
for (i, &super::StructField { name, ty }) in
|
||||||
self.tys.struct_fields(idx).iter().enumerate()
|
self.tys.struct_fields(idx).iter().enumerate()
|
||||||
{
|
{
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
|
@ -736,6 +742,12 @@ pub mod ty {
|
||||||
f.write_str(file.ident_str(record.name))
|
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) => {
|
TK::Func(idx) => {
|
||||||
f.write_str("fn")?;
|
f.write_str("fn")?;
|
||||||
idx.fmt(f)
|
idx.fmt(f)
|
||||||
|
@ -773,6 +785,7 @@ pub enum SymKey<'a> {
|
||||||
Pointer(&'a Ptr),
|
Pointer(&'a Ptr),
|
||||||
Optional(&'a Opt),
|
Optional(&'a Opt),
|
||||||
Struct(Module, Pos, ty::Tuple),
|
Struct(Module, Pos, ty::Tuple),
|
||||||
|
Enum(Module, Pos),
|
||||||
FuncInst(ty::Func, ty::Tuple),
|
FuncInst(ty::Func, ty::Tuple),
|
||||||
Decl(Module, Ident),
|
Decl(Module, Ident),
|
||||||
Array(&'a Array),
|
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,
|
name: Ident,
|
||||||
ty: ty::Id,
|
ty: ty::Id,
|
||||||
}
|
}
|
||||||
|
@ -955,18 +980,21 @@ impl IdentInterner {
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
struct TypesTmp {
|
struct TypesTmp {
|
||||||
fields: Vec<Field>,
|
struct_fields: Vec<StructField>,
|
||||||
|
enum_fields: Vec<EnumField>,
|
||||||
args: Vec<ty::Id>,
|
args: Vec<ty::Id>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct TypeIns {
|
pub struct TypeIns {
|
||||||
args: Vec<ty::Id>,
|
args: Vec<ty::Id>,
|
||||||
fields: Vec<Field>,
|
struct_fields: Vec<StructField>,
|
||||||
|
enum_fields: Vec<EnumField>,
|
||||||
funcs: EntVec<ty::Func, Func>,
|
funcs: EntVec<ty::Func, Func>,
|
||||||
globals: EntVec<ty::Global, Global>,
|
globals: EntVec<ty::Global, Global>,
|
||||||
consts: EntVec<ty::Const, Const>,
|
consts: EntVec<ty::Const, Const>,
|
||||||
structs: EntVec<ty::Struct, Struct>,
|
structs: EntVec<ty::Struct, Struct>,
|
||||||
|
enums: EntVec<ty::Enum, Enum>,
|
||||||
ptrs: EntVec<ty::Ptr, Ptr>,
|
ptrs: EntVec<ty::Ptr, Ptr>,
|
||||||
opts: EntVec<ty::Opt, Opt>,
|
opts: EntVec<ty::Opt, Opt>,
|
||||||
slices: EntVec<ty::Slice, Array>,
|
slices: EntVec<ty::Slice, Array>,
|
||||||
|
@ -1149,11 +1177,11 @@ trait TypeParser {
|
||||||
return ty;
|
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) {
|
for field in fields.iter().filter_map(CommentOr::or) {
|
||||||
let ty = self.parse_ty(file, &field.ty, None, files);
|
let ty = self.parse_ty(file, &field.ty, None, files);
|
||||||
let field = Field { name: self.tys().names.intern(field.name), ty };
|
let field = StructField { name: self.tys().names.intern(field.name), ty };
|
||||||
self.tys().tmp.fields.push(field);
|
self.tys().tmp.struct_fields.push(field);
|
||||||
}
|
}
|
||||||
|
|
||||||
let tys = self.tys();
|
let tys = self.tys();
|
||||||
|
@ -1164,13 +1192,43 @@ trait TypeParser {
|
||||||
file,
|
file,
|
||||||
pos,
|
pos,
|
||||||
name: name.unwrap_or_default(),
|
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),
|
explicit_alignment: packed.then_some(1),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
})
|
})
|
||||||
.into();
|
.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);
|
tys.syms.insert(sym, ty, &tys.ins);
|
||||||
ty
|
ty
|
||||||
|
@ -1211,13 +1269,6 @@ trait TypeParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Types {
|
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> {
|
fn pack_args(&mut self, arg_base: usize) -> Option<ty::Tuple> {
|
||||||
let base = self.ins.args.len();
|
let base = self.ins.args.len();
|
||||||
self.ins.args.extend(self.tmp.args.drain(arg_base..));
|
self.ins.args.extend(self.tmp.args.drain(arg_base..));
|
||||||
|
@ -1233,8 +1284,29 @@ impl Types {
|
||||||
ty::Tuple::new(sp, len)
|
ty::Tuple::new(sp, len)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn struct_fields(&self, strct: ty::Struct) -> &[Field] {
|
fn struct_fields(&self, strct: ty::Struct) -> &[StructField] {
|
||||||
&self.ins.fields[self.struct_field_range(strct)]
|
&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) {
|
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);
|
self.ins.structs[stru].size.set(oiter.offset);
|
||||||
oiter.offset
|
oiter.offset
|
||||||
}
|
}
|
||||||
|
ty::Kind::Enum(enm) => (self.enum_field_range(enm).len().ilog2() + 7) / 8,
|
||||||
ty::Kind::Opt(opt) => {
|
ty::Kind::Opt(opt) => {
|
||||||
let base = self.ins.opts[opt].base;
|
let base = self.ins.opts[opt].base;
|
||||||
if self.nieche_of(base).is_some() {
|
if self.nieche_of(base).is_some() {
|
||||||
|
@ -1308,7 +1381,7 @@ impl Types {
|
||||||
|| {
|
|| {
|
||||||
self.struct_fields(stru)
|
self.struct_fields(stru)
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&Field { ty, .. }| self.align_of(ty))
|
.map(|&StructField { ty, .. }| self.align_of(ty))
|
||||||
.max()
|
.max()
|
||||||
.unwrap_or(1)
|
.unwrap_or(1)
|
||||||
},
|
},
|
||||||
|
@ -1380,11 +1453,11 @@ impl Types {
|
||||||
self.ins.args.clear();
|
self.ins.args.clear();
|
||||||
self.ins.globals.clear();
|
self.ins.globals.clear();
|
||||||
self.ins.structs.clear();
|
self.ins.structs.clear();
|
||||||
self.ins.fields.clear();
|
self.ins.struct_fields.clear();
|
||||||
self.ins.ptrs.clear();
|
self.ins.ptrs.clear();
|
||||||
self.ins.slices.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.tmp.args.len(), 0);
|
||||||
|
|
||||||
debug_assert_eq!(self.tasks.len(), 0);
|
debug_assert_eq!(self.tasks.len(), 0);
|
||||||
|
@ -1416,9 +1489,9 @@ impl OffsetIter {
|
||||||
.map(|(f, off)| (off, f.ty))
|
.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 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);
|
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);
|
self.offset = (self.offset + align - 1) & !(align - 1);
|
||||||
|
@ -1433,7 +1506,7 @@ impl OffsetIter {
|
||||||
Some((field.ty, off))
|
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))
|
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),
|
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 => {
|
T::Ident | T::CtIdent => {
|
||||||
let (id, is_first) = self.resolve_ident(token);
|
let (id, is_first) = self.resolve_ident(token);
|
||||||
E::Ident {
|
E::Ident {
|
||||||
|
@ -413,6 +429,20 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
then: self.ptr_expr()?,
|
then: self.ptr_expr()?,
|
||||||
else_: self.advance_if(T::Else).then(|| self.ptr_expr()).trans()?,
|
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::Loop => E::Loop { pos, body: self.ptr_expr()? },
|
||||||
T::Break => E::Break { pos },
|
T::Break => E::Break { pos },
|
||||||
T::Continue => E::Continue { pos },
|
T::Continue => E::Continue { pos },
|
||||||
|
@ -459,14 +489,18 @@ impl<'a, 'b> Parser<'a, 'b> {
|
||||||
pos
|
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,
|
pos,
|
||||||
op: token.kind,
|
op: token.kind,
|
||||||
val: {
|
val: {
|
||||||
|
let prev_ident_stack = self.ctx.idents.len();
|
||||||
let expr = self.ptr_unit_expr()?;
|
let expr = self.ptr_unit_expr()?;
|
||||||
if token.kind == T::Band {
|
if token.kind == T::Band {
|
||||||
self.flag_idents(*expr, idfl::REFERENCED);
|
self.flag_idents(*expr, idfl::REFERENCED);
|
||||||
}
|
}
|
||||||
|
if token.kind == T::Dot {
|
||||||
|
self.ctx.idents.truncate(prev_ident_stack);
|
||||||
|
}
|
||||||
expr
|
expr
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -858,6 +892,11 @@ generate_expr! {
|
||||||
then: &'a Self,
|
then: &'a Self,
|
||||||
else_: Option<&'a Self>,
|
else_: Option<&'a Self>,
|
||||||
},
|
},
|
||||||
|
Match {
|
||||||
|
pos: Pos,
|
||||||
|
value: &'a Self,
|
||||||
|
branches: &'a [MatchBranch<'a>],
|
||||||
|
},
|
||||||
/// `'loop' Expr`
|
/// `'loop' Expr`
|
||||||
Loop {
|
Loop {
|
||||||
pos: Pos,
|
pos: Pos,
|
||||||
|
@ -877,6 +916,12 @@ generate_expr! {
|
||||||
trailing_comma: bool,
|
trailing_comma: bool,
|
||||||
packed: bool,
|
packed: bool,
|
||||||
},
|
},
|
||||||
|
/// `'enum' LIST('{', ',', '}', Ident)`
|
||||||
|
Enum {
|
||||||
|
pos: Pos,
|
||||||
|
variants: &'a [CommentOr<'a, EnumField<'a>>],
|
||||||
|
trailing_comma: bool,
|
||||||
|
},
|
||||||
/// `[Expr] LIST('.{', ',', '}', Ident [':' Expr])`
|
/// `[Expr] LIST('.{', ',', '}', Ident [':' Expr])`
|
||||||
Ctor {
|
Ctor {
|
||||||
pos: Pos,
|
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)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||||
pub struct StructField<'a> {
|
pub struct StructField<'a> {
|
||||||
pub pos: Pos,
|
pub pos: Pos,
|
||||||
|
|
166
lang/src/son.rs
166
lang/src/son.rs
|
@ -10,7 +10,7 @@ use {
|
||||||
parser::{
|
parser::{
|
||||||
self,
|
self,
|
||||||
idfl::{self},
|
idfl::{self},
|
||||||
CtorField, Expr, Pos,
|
CtorField, Expr, MatchBranch, Pos,
|
||||||
},
|
},
|
||||||
ty::{self, Arg, ArrayLen, Loc, Module, Tuple},
|
ty::{self, Arg, ArrayLen, Loc, Module, Tuple},
|
||||||
utils::{BitSet, Ent, Vc},
|
utils::{BitSet, Ent, Vc},
|
||||||
|
@ -2944,6 +2944,41 @@ impl<'a> Codegen<'a> {
|
||||||
vl.ty = base;
|
vl.ty = base;
|
||||||
Some(vl)
|
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 } => {
|
Expr::UnOp { pos, op: op @ TokenKind::Sub, val } => {
|
||||||
let val =
|
let val =
|
||||||
self.expr_ctx(val, Ctx::default().with_ty(ctx.ty.unwrap_or(ty::Id::INT)))?;
|
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 {
|
for stmt in stmts {
|
||||||
ret = ret.and(self.expr(stmt));
|
ret = ret.and(self.expr(stmt));
|
||||||
if let Some(mut id) = ret {
|
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");
|
self.assert_ty(stmt.pos(), &mut id, ty::Id::VOID, "statement");
|
||||||
} else {
|
} else {
|
||||||
break;
|
break;
|
||||||
|
@ -3885,6 +3931,119 @@ impl<'a> Codegen<'a> {
|
||||||
|
|
||||||
Some(Value::VOID)
|
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 => {
|
ref e => {
|
||||||
self.report_unhandled_ast(e, "bruh");
|
self.report_unhandled_ast(e, "bruh");
|
||||||
Value::NEVER
|
Value::NEVER
|
||||||
|
@ -4953,11 +5112,12 @@ mod tests {
|
||||||
comments;
|
comments;
|
||||||
if_statements;
|
if_statements;
|
||||||
variables;
|
variables;
|
||||||
|
hex_octal_binary_literals;
|
||||||
loops;
|
loops;
|
||||||
pointers;
|
pointers;
|
||||||
nullable_types;
|
|
||||||
structs;
|
structs;
|
||||||
hex_octal_binary_literals;
|
enums;
|
||||||
|
nullable_types;
|
||||||
struct_operators;
|
struct_operators;
|
||||||
global_variables;
|
global_variables;
|
||||||
constants;
|
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