parser,cgen: improve `[flag]enum Abc{ a b }`, allow `x := Abc.a | Abc.b`

pull/6990/head
Delyan Angelov 2020-11-27 23:09:14 +02:00
parent 18cf36a22c
commit ac27a3ccee
3 changed files with 31 additions and 6 deletions

View File

@ -829,10 +829,11 @@ fn (mut g Gen) stmt(node ast.Stmt) {
}
ast.EnumDecl {
enum_name := util.no_dots(node.name)
is_flag := node.is_flag
g.enum_typedefs.writeln('typedef enum {')
mut cur_enum_expr := ''
mut cur_enum_offset := 0
for field in node.fields {
for i, field in node.fields {
g.enum_typedefs.write('\t${enum_name}_$field.name')
if field.has_expr {
g.enum_typedefs.write(' = ')
@ -843,6 +844,11 @@ fn (mut g Gen) stmt(node ast.Stmt) {
g.enum_typedefs.write(expr_str)
cur_enum_expr = expr_str
cur_enum_offset = 0
} else if is_flag {
g.enum_typedefs.write(' = ')
cur_enum_expr = '1 << $i'
g.enum_typedefs.write((1 << i).str())
cur_enum_offset = 0
}
cur_value := if cur_enum_offset > 0 { '$cur_enum_expr+$cur_enum_offset' } else { cur_enum_expr }
g.enum_typedefs.writeln(', // $cur_value')

View File

@ -1869,13 +1869,19 @@ fn (mut p Parser) enum_decl() ast.EnumDecl {
if fields.len > 32 {
p.error('when an enum is used as bit field, it must have a max of 32 fields')
}
for f in fields {
if f.has_expr {
p.error_with_pos('when an enum is used as a bit field, you can not assign custom values',
f.pos)
}
}
pubfn := if p.mod == 'main' { 'fn' } else { 'pub fn' }
p.scanner.codegen('
//
$pubfn ( e &$enum_name) has(flag $enum_name) bool { return (int(*e) & (1 << int(flag))) != 0 }
$pubfn (mut e $enum_name) set(flag $enum_name) { unsafe{ *e = int(*e) | (1 << int(flag)) } }
$pubfn (mut e $enum_name) clear(flag $enum_name) { unsafe{ *e = int(*e) & ~(1 << int(flag)) } }
$pubfn (mut e $enum_name) toggle(flag $enum_name) { unsafe{ *e = int(*e) ^ (1 << int(flag)) } }
$pubfn ( e &$enum_name) has(flag $enum_name) bool { return (int(*e) & (int(flag))) != 0 }
$pubfn (mut e $enum_name) set(flag $enum_name) { unsafe{ *e = int(*e) | (int(flag)) } }
$pubfn (mut e $enum_name) clear(flag $enum_name) { unsafe{ *e = int(*e) & ~(int(flag)) } }
$pubfn (mut e $enum_name) toggle(flag $enum_name) { unsafe{ *e = int(*e) ^ (int(flag)) } }
//
')
}

View File

@ -13,6 +13,10 @@ mut:
fn test_enum_bitfield() {
mut a := BfFile{}
assert 1 == int(BfPermission.read)
assert 2 == int(BfPermission.write)
assert 4 == int(BfPermission.execute)
assert 8 == int(BfPermission.other)
a.perm.set(.read)
a.perm.set(.write)
a.perm.toggle(.execute)
@ -22,11 +26,20 @@ fn test_enum_bitfield() {
assert a.perm.has(.execute)
assert !a.perm.has(.write)
assert !a.perm.has(.other)
mut b := BfPermission.read // TODO: this does nothing currenty just sets the type
mut b := BfPermission.read | BfPermission.execute
assert int(b) == 1 + 0 + 4 + 0
assert b.has(.read)
assert b.has(.execute)
b.set(.write)
assert int(b) == 1 + 2 + 4 + 0
b.set(.other)
assert int(b) == 1 + 2 + 4 + 8
assert b.has(.write)
assert b.has(.other)
b.toggle(.read)
assert int(b) == 0 + 2 + 4 + 8
b.toggle(.execute)
assert int(b) == 0 + 2 + 0 + 8
assert !b.has(.read)
assert !b.has(.execute)
}