parser: allow enums to be used as bitfield flags
parent
0650d58818
commit
6d5e9f88f8
|
@ -258,4 +258,6 @@ const (
|
|||
|
||||
and_or_error = 'use `()` to make the boolean expression clear\n' +
|
||||
'for example: `(a && b) || c` instead of `a && b || c`'
|
||||
|
||||
err_modify_bitfield = 'to modify a bitfield flag use the methods: set, clear, toggle. and to check for flag use: has'
|
||||
)
|
||||
|
|
|
@ -469,3 +469,26 @@ fn (p mut Parser) comptime_if_block(name string) {
|
|||
p.genln('#endif')
|
||||
}
|
||||
}
|
||||
|
||||
fn (p mut Parser) gen_enum_flag_methods(typ mut Type) {
|
||||
for method in ['set', 'clear', 'toggle', 'has'] {
|
||||
typ.methods << Fn{
|
||||
name: method,
|
||||
typ: if method == 'has' { 'bool' } else { 'void' }
|
||||
args: [Var{typ: typ.name, is_mut: true, is_arg:true}, Var{typ: typ.name, is_arg: true}]
|
||||
is_method: true
|
||||
is_public: true
|
||||
receiver_typ: typ.name
|
||||
}
|
||||
}
|
||||
p.v.vgen_buf.writeln('
|
||||
pub fn (e mut $typ.name) set(flag $typ.name) { *e = int(*e) | (1 << int(flag)) }
|
||||
pub fn (e mut $typ.name) clear(flag $typ.name) { *e = int(*e) &~ (1 << int(flag)) }
|
||||
pub fn (e mut $typ.name) toggle(flag $typ.name) { *e = int(*e) ^ (1 << int(flag)) }
|
||||
pub fn (e &$typ.name) has(flag $typ.name) bool { return int(*e)&(1 << int(flag)) != 0 }'
|
||||
)
|
||||
p.cgen.fns << 'void ${typ.name}_set($typ.name *e, $typ.name flag);'
|
||||
p.cgen.fns << 'void ${typ.name}_clear($typ.name *e, $typ.name flag);'
|
||||
p.cgen.fns << 'void ${typ.name}_toggle($typ.name *e, $typ.name flag);'
|
||||
p.cgen.fns << 'bool ${typ.name}_has($typ.name *e, $typ.name flag);'
|
||||
}
|
||||
|
|
|
@ -57,14 +57,23 @@ fn (p mut Parser) enum_decl(no_name bool) {
|
|||
}
|
||||
val++
|
||||
}
|
||||
p.table.register_type(Type {
|
||||
is_flag := p.attr == 'flag'
|
||||
if is_flag && fields.len > 32 {
|
||||
p.error('when an enum is used as bit field, it must have a max of 32 fields')
|
||||
}
|
||||
mut T := Type {
|
||||
name: enum_name
|
||||
mod: p.mod
|
||||
parent: 'int'
|
||||
cat: .enum_
|
||||
enum_vals: fields.clone()
|
||||
is_public: is_pub
|
||||
})
|
||||
is_flag: is_flag
|
||||
}
|
||||
if is_flag && !p.first_pass() {
|
||||
p.gen_enum_flag_methods(mut T)
|
||||
}
|
||||
p.table.register_type(T)
|
||||
p.check(.rcbr)
|
||||
p.fgenln('\n')
|
||||
}
|
||||
|
|
|
@ -1449,6 +1449,10 @@ fn ($v.name mut $v.typ) $p.cur_fn.name (...) {
|
|||
}
|
||||
}
|
||||
else if !p.builtin_mod && !p.check_types_no_throw(expr_type, p.assigned_type) {
|
||||
t := p.table.find_type( p.assigned_type)
|
||||
if t.cat == .enum_ && t.is_flag {
|
||||
p.error_with_token_index(err_modify_bitfield, errtok)
|
||||
}
|
||||
p.error_with_token_index( 'cannot use type `$expr_type` as type `$p.assigned_type` in assignment', errtok)
|
||||
}
|
||||
if (is_str || is_ustr) && tok == .plus_assign && !p.is_js {
|
||||
|
@ -2792,6 +2796,11 @@ fn (p mut Parser) attribute() {
|
|||
p.attr = ''
|
||||
return
|
||||
}
|
||||
else if p.tok == .key_enum {
|
||||
p.enum_decl(false)
|
||||
p.attr = ''
|
||||
return
|
||||
}
|
||||
p.error_with_token_index('bad attribute usage', attr_token_idx)
|
||||
}
|
||||
|
||||
|
|
|
@ -285,6 +285,10 @@ fn (p mut Parser) struct_init(typ string) string {
|
|||
p.error('no such field: "$field" in type $typ')
|
||||
break
|
||||
}
|
||||
tt := p.table.find_type(f.typ)
|
||||
if tt.is_flag {
|
||||
p.error(err_modify_bitfield)
|
||||
}
|
||||
inited_fields << field
|
||||
p.gen_struct_field_init(field)
|
||||
p.check(.colon)
|
||||
|
@ -361,6 +365,10 @@ fn (p mut Parser) struct_init(typ string) string {
|
|||
if !p.check_types_no_throw(expr_typ, ffield.typ) {
|
||||
p.error('field value #${i+1} `$ffield.name` has type `$ffield.typ`, got `$expr_typ` ')
|
||||
}
|
||||
tt := p.table.find_type(ffield.typ)
|
||||
if tt.is_flag {
|
||||
p.error(err_modify_bitfield)
|
||||
}
|
||||
if i < T.fields.len - 1 {
|
||||
if p.tok != .comma {
|
||||
p.error('too few values in `$typ` literal (${i+1} instead of $T.fields.len)')
|
||||
|
|
|
@ -111,7 +111,7 @@ mut:
|
|||
// This information is needed in the first pass.
|
||||
is_placeholder bool
|
||||
gen_str bool // needs `.str()` method generation
|
||||
|
||||
is_flag bool // enum bitfield flag
|
||||
}
|
||||
|
||||
struct TypeNode {
|
||||
|
@ -502,7 +502,7 @@ fn (p mut Parser) add_method(type_name string, f Fn) {
|
|||
}
|
||||
// TODO table.typesmap[type_name].methods << f
|
||||
mut t := p.table.typesmap[type_name]
|
||||
if f.name != 'str' && f in t.methods {
|
||||
if f.name != 'str' && f in t.methods {
|
||||
p.error('redefinition of method `${type_name}.$f.name`')
|
||||
}
|
||||
t.methods << f
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
[flag]
|
||||
enum BfPermission {
|
||||
read
|
||||
write
|
||||
execute
|
||||
other
|
||||
}
|
||||
|
||||
struct BfFile {
|
||||
mut:
|
||||
perm BfPermission
|
||||
}
|
||||
|
||||
fn test_enum_bitfield() {
|
||||
mut a := BfFile{}
|
||||
a.perm.set(.read)
|
||||
a.perm.set(.write)
|
||||
a.perm.toggle(.execute)
|
||||
a.perm.clear(.write)
|
||||
//a.perm.set(.other)
|
||||
|
||||
assert a.perm.has(.read)
|
||||
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
|
||||
b.set(.write)
|
||||
b.set(.other)
|
||||
assert b.has(.write)
|
||||
assert b.has(.other)
|
||||
assert !b.has(.read)
|
||||
assert !b.has(.execute)
|
||||
}
|
Loading…
Reference in New Issue