enums
This commit is contained in:
@@ -588,6 +588,21 @@ parse_primary :: proc(parser: ^Parser, nesting: int) -> ast.Expr_Id {
|
||||
})
|
||||
case .Left_Bracket:
|
||||
return parse_array_literal(parser, nesting)
|
||||
case .Dot:
|
||||
start := advance(parser)
|
||||
member := current(parser)
|
||||
if member.kind != .Identifier {
|
||||
return invalid_expr(parser, member.span, "expected an enum member after '.'")
|
||||
}
|
||||
advance(parser)
|
||||
return add_expr(parser, ast.Expr{
|
||||
kind=.Enum_Literal,
|
||||
span=span_from(start.span, member.span),
|
||||
name=member.symbol,
|
||||
left=ast.INVALID_EXPR,
|
||||
right=ast.INVALID_EXPR,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Identifier:
|
||||
first := advance(parser)
|
||||
name := first
|
||||
@@ -1490,6 +1505,118 @@ parse_distinct :: proc(parser: ^Parser, name: token.Token) {
|
||||
_ = finish_statement(parser)
|
||||
}
|
||||
|
||||
parse_enum :: proc(parser: ^Parser, name: token.Token) {
|
||||
start := advance(parser)
|
||||
explicit_backing := false
|
||||
backing := types.INVALID
|
||||
if _, ok := allow(parser, .Left_Paren); ok {
|
||||
explicit_backing = true
|
||||
backing = parse_type(parser)
|
||||
if _, close_ok := allow(parser, .Right_Paren); !close_ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected ')' after enum backing type")
|
||||
}
|
||||
}
|
||||
skip_newlines(parser)
|
||||
if _, ok := allow(parser, .Left_Brace); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected '{' after enum declaration")
|
||||
_ = finish_statement(parser)
|
||||
return
|
||||
}
|
||||
members: [dynamic]types.Enum_Member
|
||||
members.allocator = parser.module.allocator
|
||||
defer delete(members)
|
||||
next_value: i128
|
||||
previous_value: i128
|
||||
has_previous := false
|
||||
skip_newlines(parser)
|
||||
for current(parser).kind != .Right_Brace && current(parser).kind != .Eof {
|
||||
if current(parser).kind != .Identifier {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected an enum member name")
|
||||
for current(parser).kind != .Newline &&
|
||||
current(parser).kind != .Right_Brace &&
|
||||
current(parser).kind != .Eof {
|
||||
advance(parser)
|
||||
}
|
||||
skip_newlines(parser)
|
||||
continue
|
||||
}
|
||||
member := advance(parser)
|
||||
duplicate := false
|
||||
for existing in members {
|
||||
if existing.name == u32(member.symbol) {
|
||||
duplicate = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if duplicate {
|
||||
source.addf(parser.diagnostics, member.span, "duplicate enum member '%s'", token_text(parser, member))
|
||||
}
|
||||
value := next_value
|
||||
if _, ok := allow(parser, .Equal); ok {
|
||||
if !explicit_backing {
|
||||
source.add(parser.diagnostics, member.span, "explicit enum values require a backing type")
|
||||
}
|
||||
negative := false
|
||||
if _, minus_ok := allow(parser, .Minus); minus_ok {
|
||||
negative = true
|
||||
}
|
||||
literal := current(parser)
|
||||
if literal.kind != .Integer {
|
||||
source.add(parser.diagnostics, literal.span, "expected a decimal integer literal for enum value")
|
||||
} else {
|
||||
advance(parser)
|
||||
magnitude, magnitude_ok := parse_integer_magnitude(token_text(parser, literal))
|
||||
if !magnitude_ok {
|
||||
source.add(parser.diagnostics, literal.span, "enum value magnitude does not fit in u64")
|
||||
} else {
|
||||
value = i128(magnitude)
|
||||
if negative {
|
||||
value = -value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if has_previous && value <= previous_value {
|
||||
source.add(parser.diagnostics, member.span, "enum values must be strictly increasing")
|
||||
}
|
||||
if !duplicate {
|
||||
append(&members, types.Enum_Member{name=u32(member.symbol), value=value})
|
||||
}
|
||||
previous_value = value
|
||||
has_previous = true
|
||||
next_value = value+1
|
||||
if _, ok := allow(parser, .Comma); ok {
|
||||
skip_newlines(parser)
|
||||
} else {
|
||||
_ = finish_statement(parser, true)
|
||||
}
|
||||
}
|
||||
if _, ok := allow(parser, .Right_Brace); !ok {
|
||||
source.add(parser.diagnostics, current(parser).span, "expected '}' after enum members")
|
||||
}
|
||||
if len(members) == 0 {
|
||||
source.add(parser.diagnostics, start.span, "enum declarations require at least one member")
|
||||
}
|
||||
if !explicit_backing {
|
||||
max_value := u64(max(len(members)-1, 0))
|
||||
backing = types.U8
|
||||
if max_value > 0xff {
|
||||
backing = types.U16
|
||||
}
|
||||
if max_value > 0xffff {
|
||||
backing = types.U32
|
||||
}
|
||||
if max_value > 0xffff_ffff {
|
||||
backing = types.U64
|
||||
}
|
||||
}
|
||||
id := types.named(&parser.module.type_store, u32(parser.pkg), u32(name.symbol))
|
||||
if !types.define_enum(&parser.module.type_store, id, backing, members[:], explicit_backing) {
|
||||
source.addf(parser.diagnostics, name.span, "duplicate type declaration '%s'", token_text(parser, name))
|
||||
}
|
||||
_ = finish_statement(parser)
|
||||
}
|
||||
|
||||
decode_import_path :: proc(parser: ^Parser, tok: token.Token) -> string {
|
||||
text := token_text(parser, tok)
|
||||
if len(text) < 2 {
|
||||
@@ -1601,6 +1728,10 @@ parse_top_level :: proc(parser: ^Parser) {
|
||||
parse_struct(parser, name, current(parser).kind == .Keyword_C_Struct)
|
||||
return
|
||||
}
|
||||
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Enum {
|
||||
parse_enum(parser, name)
|
||||
return
|
||||
}
|
||||
if operator.kind == .Colon_Colon && current(parser).kind == .Keyword_Distinct {
|
||||
parse_distinct(parser, name)
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user