Implement #complete switch by default, replace with #partial switch #511

This commit is contained in:
gingerBill
2019-12-22 12:03:48 +00:00
parent 4593730632
commit d1c9fd4e01
19 changed files with 263 additions and 227 deletions

View File

@@ -106,11 +106,11 @@ create_from_tokenizer :: proc(t: ^Tokenizer) -> (^Parser, bool) {
p := new(Parser);
for {
tok := scan(t);
if tok.kind == Kind.Illegal {
if tok.kind == .Illegal {
return p, false;
}
append(&p.tokens, tok);
if tok.kind == Kind.EOF {
if tok.kind == .EOF {
break;
}
}
@@ -120,7 +120,7 @@ create_from_tokenizer :: proc(t: ^Tokenizer) -> (^Parser, bool) {
}
if len(p.tokens) == 0 {
tok := Token{kind = Kind.EOF};
tok := Token{kind = .EOF};
tok.line, tok.column = 1, 1;
append(&p.tokens, tok);
return p, true;
@@ -134,8 +134,8 @@ create_from_tokenizer :: proc(t: ^Tokenizer) -> (^Parser, bool) {
p.dict_stack = make([dynamic]^Dict, 0, 4);
append(&p.dict_stack, &p.root);
for p.curr_token.kind != Kind.EOF &&
p.curr_token.kind != Kind.Illegal &&
for p.curr_token.kind != .EOF &&
p.curr_token.kind != .Illegal &&
p.curr_token_index < len(p.tokens) {
if !parse_assignment(p) {
break;
@@ -147,7 +147,7 @@ create_from_tokenizer :: proc(t: ^Tokenizer) -> (^Parser, bool) {
destroy :: proc(p: ^Parser) {
destroy_value :: proc(value: Value) {
switch v in value {
#partial switch v in value {
case Array:
for elem in v do destroy_value(elem);
delete(v);
@@ -287,7 +287,7 @@ unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool
unquote_string :: proc(p: ^Parser, t: Token) -> (string, bool) {
if t.kind != Kind.String {
if t.kind != .String {
return t.lit, true;
}
s := t.lit;
@@ -368,8 +368,8 @@ expect_operator :: proc(p: ^Parser) -> Token {
fix_advance :: proc(p: ^Parser) {
for {
switch t := p.curr_token; t.kind {
case Kind.EOF, Kind.Semicolon:
#partial switch t := p.curr_token; t.kind {
case .EOF, .Semicolon:
return;
}
next_token(p);
@@ -377,7 +377,7 @@ fix_advance :: proc(p: ^Parser) {
}
copy_value :: proc(value: Value) -> Value {
switch v in value {
#partial switch v in value {
case Array:
a := make(Array, len(v));
for elem, idx in v {
@@ -407,79 +407,79 @@ lookup_value :: proc(p: ^Parser, name: string) -> (Value, bool) {
parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
tok := p.curr_token;
switch p.curr_token.kind {
case Kind.Ident:
#partial switch p.curr_token.kind {
case .Ident:
next_token(p);
v, ok := lookup_value(p, tok.lit);
if !ok do error(p, tok.pos, "Undeclared identifier %s", tok.lit);
return v, tok.pos;
case Kind.True:
case .True:
next_token(p);
return true, tok.pos;
case Kind.False:
case .False:
next_token(p);
return false, tok.pos;
case Kind.Nil:
case .Nil:
next_token(p);
return Nil_Value{}, tok.pos;
case Kind.Integer:
case .Integer:
next_token(p);
return strconv.parse_i64(tok.lit), tok.pos;
case Kind.Float:
case .Float:
next_token(p);
return strconv.parse_f64(tok.lit), tok.pos;
case Kind.String:
case .String:
next_token(p);
str, ok := unquote_string(p, tok);
if !ok do error(p, tok.pos, "Unable to unquote string");
return string(str), tok.pos;
case Kind.Open_Paren:
expect_token(p, Kind.Open_Paren);
case .Open_Paren:
expect_token(p, .Open_Paren);
expr, _ := parse_expr(p);
expect_token(p, Kind.Close_Paren);
expect_token(p, .Close_Paren);
return expr, tok.pos;
case Kind.Open_Bracket:
expect_token(p, Kind.Open_Bracket);
case .Open_Bracket:
expect_token(p, .Open_Bracket);
elems := make([dynamic]Value, 0, 4);
for p.curr_token.kind != Kind.Close_Bracket &&
p.curr_token.kind != Kind.EOF {
for p.curr_token.kind != .Close_Bracket &&
p.curr_token.kind != .EOF {
elem, _ := parse_expr(p);
append(&elems, elem);
if p.curr_token.kind == Kind.Semicolon && p.curr_token.lit == "\n" {
if p.curr_token.kind == .Semicolon && p.curr_token.lit == "\n" {
next_token(p);
} else if !allow_token(p, Kind.Comma) {
} else if !allow_token(p, .Comma) {
break;
}
}
expect_token(p, Kind.Close_Bracket);
expect_token(p, .Close_Bracket);
return Array(elems[:]), tok.pos;
case Kind.Open_Brace:
expect_token(p, Kind.Open_Brace);
case .Open_Brace:
expect_token(p, .Open_Brace);
dict := Dict{};
append(&p.dict_stack, &dict);
defer pop(&p.dict_stack);
for p.curr_token.kind != Kind.Close_Brace &&
p.curr_token.kind != Kind.EOF {
for p.curr_token.kind != .Close_Brace &&
p.curr_token.kind != .EOF {
name_tok := p.curr_token;
if !allow_token(p, Kind.Ident) && !allow_token(p, Kind.String) {
name_tok = expect_token(p, Kind.Ident);
if !allow_token(p, .Ident) && !allow_token(p, .String) {
name_tok = expect_token(p, .Ident);
}
name, ok := unquote_string(p, name_tok);
if !ok do error(p, tok.pos, "Unable to unquote string");
expect_token(p, Kind.Assign);
expect_token(p, .Assign);
elem, _ := parse_expr(p);
if _, ok2 := dict[name]; ok2 {
@@ -488,13 +488,13 @@ parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
dict[name] = elem;
}
if p.curr_token.kind == Kind.Semicolon && p.curr_token.lit == "\n" {
if p.curr_token.kind == .Semicolon && p.curr_token.lit == "\n" {
next_token(p);
} else if !allow_token(p, Kind.Comma) {
} else if !allow_token(p, .Comma) {
break;
}
}
expect_token(p, Kind.Close_Brace);
expect_token(p, .Close_Brace);
return dict, tok.pos;
}
@@ -504,13 +504,13 @@ parse_operand :: proc(p: ^Parser) -> (Value, Pos) {
parse_atom_expr :: proc(p: ^Parser, operand: Value, pos: Pos) -> (Value, Pos) {
loop := true;
for operand := operand; loop; {
switch p.curr_token.kind {
case Kind.Period:
#partial switch p.curr_token.kind {
case .Period:
next_token(p);
tok := next_token(p);
switch tok.kind {
case Kind.Ident:
#partial switch tok.kind {
case .Ident:
d, ok := operand.(Dict);
if !ok || d == nil {
error(p, tok.pos, "Expected a dictionary");
@@ -531,13 +531,13 @@ parse_atom_expr :: proc(p: ^Parser, operand: Value, pos: Pos) -> (Value, Pos) {
operand = nil;
}
case Kind.Open_Bracket:
expect_token(p, Kind.Open_Bracket);
case .Open_Bracket:
expect_token(p, .Open_Bracket);
index, index_pos := parse_expr(p);
expect_token(p, Kind.Close_Bracket);
expect_token(p, .Close_Bracket);
switch a in operand {
#partial switch a in operand {
case Array:
i, ok := index.(i64);
if !ok {
@@ -587,22 +587,22 @@ parse_atom_expr :: proc(p: ^Parser, operand: Value, pos: Pos) -> (Value, Pos) {
parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
op := p.curr_token;
switch p.curr_token.kind {
case Kind.At:
#partial switch p.curr_token.kind {
case .At:
next_token(p);
tok := expect_token(p, Kind.String);
tok := expect_token(p, .String);
v, ok := lookup_value(p, tok.lit);
if !ok do error(p, tok.pos, "Undeclared identifier %s", tok.lit);
return parse_atom_expr(p, v, tok.pos);
case Kind.Add, Kind.Sub:
case .Add, .Sub:
next_token(p);
// TODO(bill): Calcuate values as you go!
expr, pos := parse_unary_expr(p);
switch e in expr {
case i64: if op.kind == Kind.Sub do return -e, pos;
case f64: if op.kind == Kind.Sub do return -e, pos;
#partial switch e in expr {
case i64: if op.kind == .Sub do return -e, pos;
case f64: if op.kind == .Sub do return -e, pos;
case:
error(p, op.pos, "Unary operator %s can only be used on integers or floats", op.lit);
return nil, op.pos;
@@ -610,7 +610,7 @@ parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
return expr, op.pos;
case Kind.Not:
case .Not:
next_token(p);
expr, _ := parse_unary_expr(p);
if v, ok := expr.(bool); ok {
@@ -625,7 +625,7 @@ parse_unary_expr :: proc(p: ^Parser) -> (Value, Pos) {
value_order :: proc(v: Value) -> int {
switch _ in v {
#partial switch _ in v {
case bool, string:
return 1;
case i64:
@@ -641,13 +641,13 @@ match_values :: proc(left, right: ^Value) -> bool {
return match_values(right, left);
}
switch x in left^ {
#partial switch x in left^ {
case:
right^ = left^;
case bool, string:
return true;
case i64:
switch y in right^ {
#partial switch y in right^ {
case i64:
return true;
case f64:
@@ -656,7 +656,7 @@ match_values :: proc(left, right: ^Value) -> bool {
}
case f64:
switch y in right {
#partial switch y in right {
case f64:
return true;
}
@@ -671,59 +671,59 @@ calculate_binary_value :: proc(p: ^Parser, op: Kind, a, b: Value) -> (Value, boo
match_values(&x, &y);
switch a in x {
#partial switch a in x {
case: return x, true;
case bool:
b, ok := y.(bool);
if !ok do return nil, false;
switch op {
case Kind.Eq: return a == b, true;
case Kind.NotEq: return a != b, true;
case Kind.And: return a && b, true;
case Kind.Or: return a || b, true;
#partial switch op {
case .Eq: return a == b, true;
case .NotEq: return a != b, true;
case .And: return a && b, true;
case .Or: return a || b, true;
}
case i64:
b, ok := y.(i64);
if !ok do return nil, false;
switch op {
case Kind.Add: return a + b, true;
case Kind.Sub: return a - b, true;
case Kind.Mul: return a * b, true;
case Kind.Quo: return a / b, true;
case Kind.Rem: return a % b, true;
case Kind.Eq: return a == b, true;
case Kind.NotEq: return a != b, true;
case Kind.Lt: return a < b, true;
case Kind.Gt: return a > b, true;
case Kind.LtEq: return a <= b, true;
case Kind.GtEq: return a >= b, true;
#partial switch op {
case .Add: return a + b, true;
case .Sub: return a - b, true;
case .Mul: return a * b, true;
case .Quo: return a / b, true;
case .Rem: return a % b, true;
case .Eq: return a == b, true;
case .NotEq: return a != b, true;
case .Lt: return a < b, true;
case .Gt: return a > b, true;
case .LtEq: return a <= b, true;
case .GtEq: return a >= b, true;
}
case f64:
b, ok := y.(f64);
if !ok do return nil, false;
switch op {
case Kind.Add: return a + b, true;
case Kind.Sub: return a - b, true;
case Kind.Mul: return a * b, true;
case Kind.Quo: return a / b, true;
case Kind.Eq: return a == b, true;
case Kind.NotEq: return a != b, true;
case Kind.Lt: return a < b, true;
case Kind.Gt: return a > b, true;
case Kind.LtEq: return a <= b, true;
case Kind.GtEq: return a >= b, true;
#partial switch op {
case .Add: return a + b, true;
case .Sub: return a - b, true;
case .Mul: return a * b, true;
case .Quo: return a / b, true;
case .Eq: return a == b, true;
case .NotEq: return a != b, true;
case .Lt: return a < b, true;
case .Gt: return a > b, true;
case .LtEq: return a <= b, true;
case .GtEq: return a >= b, true;
}
case string:
b, ok := y.(string);
if !ok do return nil, false;
switch op {
case Kind.Add:
#partial switch op {
case .Add:
n := len(a) + len(b);
data := make([]byte, n);
copy(data[:], a);
@@ -732,12 +732,12 @@ calculate_binary_value :: proc(p: ^Parser, op: Kind, a, b: Value) -> (Value, boo
append(&p.allocated_strings, s);
return s, true;
case Kind.Eq: return a == b, true;
case Kind.NotEq: return a != b, true;
case Kind.Lt: return a < b, true;
case Kind.Gt: return a > b, true;
case Kind.LtEq: return a <= b, true;
case Kind.GtEq: return a >= b, true;
case .Eq: return a == b, true;
case .NotEq: return a != b, true;
case .Lt: return a < b, true;
case .Gt: return a > b, true;
case .LtEq: return a <= b, true;
case .GtEq: return a >= b, true;
}
}
@@ -755,10 +755,10 @@ parse_binary_expr :: proc(p: ^Parser, prec_in: int) -> (Value, Pos) {
}
expect_operator(p);
if op.kind == Kind.Question {
if op.kind == .Question {
cond := expr;
x, _ := parse_expr(p);
expect_token(p, Kind.Colon);
expect_token(p, .Colon);
y, _ := parse_expr(p);
if t, ok := cond.(bool); ok {
@@ -791,13 +791,13 @@ parse_expr :: proc(p: ^Parser) -> (Value, Pos) {
expect_semicolon :: proc(p: ^Parser) {
kind := p.curr_token.kind;
switch kind {
case Kind.Comma:
#partial switch kind {
case .Comma:
error(p, p.curr_token.pos, "Expected ';', got ','");
next_token(p);
case Kind.Semicolon:
case .Semicolon:
next_token(p);
case Kind.EOF:
case .EOF:
// okay
case:
error(p, p.curr_token.pos, "Expected ';', got %s", p.curr_token.lit);
@@ -811,17 +811,17 @@ parse_assignment :: proc(p: ^Parser) -> bool {
return p.dict_stack[len(p.dict_stack)-1];
}
if p.curr_token.kind == Kind.Semicolon {
if p.curr_token.kind == .Semicolon {
next_token(p);
return true;
}
if p.curr_token.kind == Kind.EOF {
if p.curr_token.kind == .EOF {
return false;
}
tok := p.curr_token;
if allow_token(p, Kind.Ident) || allow_token(p, Kind.String) {
expect_token(p, Kind.Assign);
if allow_token(p, .Ident) || allow_token(p, .String) {
expect_token(p, .Assign);
name, ok := unquote_string(p, tok);
if !ok do error(p, tok.pos, "Unable to unquote string");
expr, _ := parse_expr(p);

View File

@@ -137,7 +137,7 @@ kind_to_string := [len(Kind)]string{
};
precedence :: proc(op: Kind) -> int {
switch op {
#partial switch op {
case Question:
return 1;
case Or:

View File

@@ -40,7 +40,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
ti := type_info_base(type_info_of(v.id));
a := any{v.data, ti.id};
switch info in ti.variant {
#partial switch info in ti.variant {
case Type_Info_Named:
panic("Unreachable");
@@ -282,7 +282,7 @@ marshal_arg :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
return false;
}
t := runtime.type_info_base(ti);
switch info in t.variant {
#partial switch info in t.variant {
case runtime.Type_Info_Integer:
switch info.endianness {
case .Platform: return false;

View File

@@ -70,7 +70,7 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
defer value.end = token_end_pos(p.prev_token);
token := p.curr_token;
switch token.kind {
#partial switch token.kind {
case Kind.Null:
value.value = Null{};
advance_token(p);
@@ -105,7 +105,7 @@ parse_value :: proc(p: ^Parser) -> (value: Value, err: Error) {
case:
if p.spec == Specification.JSON5 {
switch token.kind {
#partial switch token.kind {
case Kind.Infinity:
inf: u64 = 0x7ff0000000000000;
if token.text[0] == '-' {

View File

@@ -56,7 +56,7 @@ Error :: enum {
destroy_value :: proc(value: Value) {
switch v in value.value {
#partial switch v in value.value {
case Object:
for key, elem in v {
delete(key);

View File

@@ -91,28 +91,27 @@ validate_array :: proc(p: ^Parser) -> bool {
validate_value :: proc(p: ^Parser) -> bool {
token := p.curr_token;
using Kind;
switch token.kind {
case Null, False, True:
#partial switch token.kind {
case .Null, .False, .True:
advance_token(p);
return true;
case Integer, Float:
case .Integer, .Float:
advance_token(p);
return true;
case String:
case .String:
advance_token(p);
return is_valid_string_literal(token.text, p.spec);
case Open_Brace:
case .Open_Brace:
return validate_object(p);
case Open_Bracket:
case .Open_Bracket:
return validate_array(p);
case:
if p.spec == Specification.JSON5 {
switch token.kind {
case Infinity, NaN:
#partial switch token.kind {
case .Infinity, .NaN:
advance_token(p);
return true;
}

View File

@@ -794,7 +794,7 @@ enum_value_to_string :: proc(val: any) -> (string, bool) {
v.id = runtime.typeid_base(v.id);
type_info := type_info_of(v.id);
switch e in type_info.variant {
#partial switch e in type_info.variant {
case: return "", false;
case runtime.Type_Info_Enum:
get_str :: proc(i: $T, e: runtime.Type_Info_Enum) -> (string, bool) {
@@ -857,7 +857,7 @@ fmt_enum :: proc(fi: ^Info, v: any, verb: rune) {
}
type_info := type_info_of(v.id);
switch e in type_info.variant {
#partial switch e in type_info.variant {
case: fmt_bad_verb(fi, verb);
case runtime.Type_Info_Enum:
switch verb {
@@ -898,7 +898,7 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
return false;
}
t := runtime.type_info_base(ti);
switch info in t.variant {
#partial switch info in t.variant {
case runtime.Type_Info_Integer:
switch info.endianness {
case .Platform: return false;
@@ -912,7 +912,7 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
byte_swap :: bits.byte_swap;
type_info := type_info_of(v.id);
switch info in type_info.variant {
#partial switch info in type_info.variant {
case runtime.Type_Info_Named:
val := v;
val.id = info.base.id;
@@ -982,7 +982,7 @@ fmt_bit_set :: proc(fi: ^Info, v: any, name: string = "") {
}
fmt_bit_field :: proc(fi: ^Info, v: any, bit_field_name: string = "") {
type_info := type_info_of(v.id);
switch info in type_info.variant {
#partial switch info in type_info.variant {
case runtime.Type_Info_Named:
val := v;
val.id = info.base.id;
@@ -1052,7 +1052,7 @@ fmt_opaque :: proc(fi: ^Info, v: any) {
strings.write_byte(fi.buf, '{');
defer strings.write_byte(fi.buf, '}');
switch in elem.variant {
#partial switch in elem.variant {
case rt.Type_Info_Integer, rt.Type_Info_Pointer, rt.Type_Info_Float:
fmt_value(fi, any{v.data, elem.id}, 'v');
case:
@@ -1073,8 +1073,11 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
type_info := type_info_of(v.id);
switch info in type_info.variant {
case runtime.Type_Info_Any: // Ignore
case runtime.Type_Info_Tuple: // Ignore
case runtime.Type_Info_Named:
switch b in info.base.variant {
#partial switch b in info.base.variant {
case runtime.Type_Info_Struct:
if verb != 'v' {
fmt_bad_verb(fi, verb);
@@ -1193,7 +1196,7 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
a := any{ptr, info.elem.id};
elem := runtime.type_info_base(info.elem);
if elem != nil do switch e in elem.variant {
if elem != nil do #partial switch e in elem.variant {
case runtime.Type_Info_Array,
runtime.Type_Info_Slice,
runtime.Type_Info_Dynamic_Array,

View File

@@ -331,7 +331,7 @@ Switch_Stmt :: struct {
init: ^Stmt,
cond: ^Expr,
body: ^Stmt,
complete: bool,
partial: bool,
}
Type_Switch_Stmt :: struct {
@@ -341,7 +341,7 @@ Type_Switch_Stmt :: struct {
tag: ^Stmt,
expr: ^Expr,
body: ^Stmt,
complete: bool,
partial: bool,
}
Branch_Stmt :: struct {

View File

@@ -390,7 +390,7 @@ expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool {
return true;
}
} else {
switch p.curr_tok.kind {
#partial switch p.curr_tok.kind {
case .Close_Brace:
case .Close_Paren:
case .Else:
@@ -475,7 +475,7 @@ parse_when_stmt :: proc(p: ^Parser) -> ^ast.When_Stmt {
}
if allow_token(p, .Else) {
switch p.curr_tok.kind {
#partial switch p.curr_tok.kind {
case .When:
else_stmt = parse_when_stmt(p);
case .Open_Brace:
@@ -550,7 +550,7 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt {
}
if allow_token(p, .Else) {
switch p.curr_tok.kind {
#partial switch p.curr_tok.kind {
case .If:
else_stmt = parse_if_stmt(p);
case .Open_Brace:
@@ -859,7 +859,7 @@ parse_foreign_block :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Foreign_Bl
docs := p.lead_comment;
foreign_library: ^ast.Expr;
switch p.curr_tok.kind {
#partial switch p.curr_tok.kind {
case .Open_Brace:
i := ast.new(ast.Ident, tok.pos, end_pos(tok));
i.name = "_";
@@ -901,7 +901,7 @@ parse_foreign_decl :: proc(p: ^Parser) -> ^ast.Decl {
docs := p.lead_comment;
tok := expect_token(p, .Foreign);
switch p.curr_tok.kind {
#partial switch p.curr_tok.kind {
case .Ident, .Open_Brace:
return parse_foreign_block(p, tok);
@@ -955,7 +955,7 @@ parse_foreign_decl :: proc(p: ^Parser) -> ^ast.Decl {
parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
switch p.curr_tok.kind {
#partial switch p.curr_tok.kind {
// Operands
case .Context, // Also allows for 'context = '
.Proc,
@@ -1086,12 +1086,12 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
stmt.state_flags |= {.No_Bounds_Check};
}
return stmt;
case "complete":
case "partial":
stmt := parse_stmt(p);
switch s in &stmt.derived {
case ast.Switch_Stmt: s.complete = true;
case ast.Type_Switch_Stmt: s.complete = true;
case: error(p, stmt.pos, "#complete can only be applied to a switch statement");
case ast.Switch_Stmt: s.partial = true;
case ast.Type_Switch_Stmt: s.partial = true;
case: error(p, stmt.pos, "#partial can only be applied to a switch statement");
}
return stmt;
case "assert", "panic":
@@ -1130,7 +1130,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
token_precedence :: proc(p: ^Parser, kind: tokenizer.Token_Kind) -> int {
switch kind {
#partial switch kind {
case .Question:
return 1;
case .Ellipsis, .Range_Half:
@@ -1308,7 +1308,7 @@ convert_to_ident_list :: proc(p: ^Parser, list: []Expr_And_Flags, ignore_flags,
is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix {
using Field_Prefix;
switch p.curr_tok.kind {
#partial switch p.curr_tok.kind {
case .EOF:
return Invalid;
case .Using:
@@ -1323,7 +1323,7 @@ is_token_field_prefix :: proc(p: ^Parser) -> Field_Prefix {
case .Hash:
advance_token(p);
defer advance_token(p);
switch p.curr_tok.kind {
#partial switch p.curr_tok.kind {
case .Ident:
switch p.curr_tok.text {
case "no_alias":
@@ -1359,7 +1359,7 @@ parse_field_prefixes :: proc(p: ^Parser) -> ast.Field_Flags {
for kind in Field_Prefix {
count := counts[kind];
using Field_Prefix;
#complete switch kind {
switch kind {
case Invalid, Unknown: // Ignore
case Using:
if count > 1 do error(p, p.curr_tok.pos, "multiple 'using' in this field list");
@@ -1391,7 +1391,7 @@ check_field_flag_prefixes :: proc(p: ^Parser, name_count: int, allowed_flags, se
for flag in ast.Field_Flag {
if flag notin allowed_flags && flag in flags {
#complete switch flag {
switch flag {
case .Using:
error(p, p.curr_tok.pos, "'using' is not allowed within this field list");
case .No_Alias:
@@ -1832,7 +1832,7 @@ check_poly_params_for_type :: proc(p: ^Parser, poly_params: ^ast.Field_List, tok
parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
switch p.curr_tok.kind {
#partial switch p.curr_tok.kind {
case .Ident:
return parse_ident(p);
@@ -1945,7 +1945,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
expr := parse_unary_expr(p, lhs);
pi := ast.Proc_Inlining.None;
switch tok.kind {
#partial switch tok.kind {
case .Inline:
pi = ast.Proc_Inlining.Inline;
case .No_Inline:
@@ -2537,7 +2537,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
loop := true;
is_lhs := lhs;
for loop {
switch p.curr_tok.kind {
#partial switch p.curr_tok.kind {
case:
loop = false;
@@ -2556,7 +2556,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
p.expr_level += 1;
open := expect_token(p, .Open_Bracket);
switch p.curr_tok.kind {
#partial switch p.curr_tok.kind {
case .Colon, .Ellipsis, .Range_Half:
// NOTE(bill): Do not err yet
break;
@@ -2564,7 +2564,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
indicies[0] = parse_expr(p, false);
}
switch p.curr_tok.kind {
#partial switch p.curr_tok.kind {
case .Ellipsis, .Range_Half:
error(p, p.curr_tok.pos, "expected a colon, not a range");
fallthrough;
@@ -2602,7 +2602,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
case .Period:
tok := expect_token(p, .Period);
switch p.curr_tok.kind {
#partial switch p.curr_tok.kind {
case .Ident:
field := parse_ident(p);
@@ -2659,7 +2659,7 @@ parse_expr :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
return parse_binary_expr(p, lhs, 0+1);
}
parse_unary_expr :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
switch p.curr_tok.kind {
#partial switch p.curr_tok.kind {
case .Transmute, .Cast:
tok := advance_token(p);
open := expect_token(p, .Open_Paren);
@@ -2812,7 +2812,7 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt {
case op.kind == .Colon:
expect_token_after(p, .Colon, "identifier list");
if .Label in flags && len(lhs) == 1 {
switch p.curr_tok.kind {
#partial switch p.curr_tok.kind {
case .Open_Brace, .If, .For, .Switch:
label := lhs[0];
stmt := parse_stmt(p);
@@ -2847,7 +2847,7 @@ parse_value_decl :: proc(p: ^Parser, names: []^ast.Expr, docs: ^ast.Comment_Grou
values: []^ast.Expr;
type := parse_type_or_ident(p);
switch p.curr_tok.kind {
#partial switch p.curr_tok.kind {
case .Eq, .Colon:
sep := advance_token(p);
is_mutable = sep.kind != .Colon;
@@ -2910,7 +2910,7 @@ parse_import_decl :: proc(p: ^Parser, kind := Import_Decl_Kind.Standard) -> ^ast
import_name: tokenizer.Token;
is_using := kind != Import_Decl_Kind.Standard;
switch p.curr_tok.kind {
#partial switch p.curr_tok.kind {
case .Ident:
import_name = advance_token(p);
case:

View File

@@ -313,7 +313,7 @@ is_literal :: proc(kind: Token_Kind) -> bool {
return Token_Kind.B_Literal_Begin < kind && kind < Token_Kind.B_Literal_End;
}
is_operator :: proc(kind: Token_Kind) -> bool {
switch kind {
#partial switch kind {
case .B_Operator_Begin .. .B_Operator_End:
return true;
case .In, .Notin:

View File

@@ -37,7 +37,7 @@ Type_Kind :: enum {
type_kind :: proc(T: typeid) -> Type_Kind {
ti := type_info_of(T);
if ti != nil {
#complete switch _ in ti.variant {
switch _ in ti.variant {
case runtime.Type_Info_Named: return .Named;
case runtime.Type_Info_Integer: return .Integer;
case runtime.Type_Info_Rune: return .Rune;

View File

@@ -40,6 +40,14 @@ are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool {
_, ok := b.variant.(rt.Type_Info_Complex);
return ok;
case rt.Type_Info_Quaternion:
_, ok := b.variant.(rt.Type_Info_Quaternion);
return ok;
case rt.Type_Info_Type_Id:
_, ok := b.variant.(rt.Type_Info_Type_Id);
return ok;
case rt.Type_Info_String:
_, ok := b.variant.(rt.Type_Info_String);
return ok;
@@ -174,7 +182,7 @@ are_types_identical :: proc(a, b: ^rt.Type_Info) -> bool {
is_signed :: proc(info: ^rt.Type_Info) -> bool {
if info == nil do return false;
switch i in rt.type_info_base(info).variant {
#partial switch i in rt.type_info_base(info).variant {
case rt.Type_Info_Integer: return i.signed;
case rt.Type_Info_Float: return true;
}
@@ -309,6 +317,7 @@ write_type :: proc(buf: ^strings.Builder, ti: ^rt.Type_Info) {
write_byte(buf, info.signed ? 'i' : 'u');
write_i64(buf, i64(8*ti.size), 10);
switch info.endianness {
case .Platform: // Okay
case .Little: write_string(buf, "le");
case .Big: write_string(buf, "be");
}
@@ -321,6 +330,9 @@ write_type :: proc(buf: ^strings.Builder, ti: ^rt.Type_Info) {
case rt.Type_Info_Complex:
write_string(buf, "complex");
write_i64(buf, i64(8*ti.size), 10);
case rt.Type_Info_Quaternion:
write_string(buf, "quaternion");
write_i64(buf, i64(8*ti.size), 10);
case rt.Type_Info_String:
if info.is_cstring {
write_string(buf, "cstring");
@@ -399,7 +411,7 @@ write_type :: proc(buf: ^strings.Builder, ti: ^rt.Type_Info) {
write_type(buf, info.value);
case rt.Type_Info_Struct:
#complete switch info.soa_kind {
switch info.soa_kind {
case .None: // Ignore
case .Fixed:
write_string(buf, "#soa[");

View File

@@ -305,7 +305,7 @@ type_info_base :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
base := info;
loop: for {
switch i in base.variant {
#partial switch i in base.variant {
case Type_Info_Named: base = i.base;
case: break loop;
}
@@ -319,7 +319,7 @@ type_info_core :: proc "contextless" (info: ^Type_Info) -> ^Type_Info {
base := info;
loop: for {
switch i in base.variant {
#partial switch i in base.variant {
case Type_Info_Named: base = i.base;
case Type_Info_Enum: base = i.base;
case Type_Info_Opaque: base = i.elem;

View File

@@ -110,6 +110,9 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
case Type_Info_Complex:
os.write_string(fd, "complex");
print_u64(fd, u64(8*ti.size));
case Type_Info_Quaternion:
os.write_string(fd, "quaternion");
print_u64(fd, u64(8*ti.size));
case Type_Info_String:
os.write_string(fd, "string");
case Type_Info_Boolean:
@@ -183,7 +186,7 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
print_type(fd, info.value);
case Type_Info_Struct:
#complete switch info.soa_kind {
switch info.soa_kind {
case .None: // Ignore
case .Fixed:
os.write_string(fd, "#soa[");
@@ -263,7 +266,7 @@ print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
case Type_Info_Bit_Set:
os.write_string(fd, "bit_set[");
switch elem in type_info_base(info.elem).variant {
#partial switch elem in type_info_base(info.elem).variant {
case Type_Info_Enum:
print_type(fd, info.elem);
case Type_Info_Rune:

View File

@@ -11,7 +11,7 @@ Ordering :: enum {
}
strongest_failure_ordering :: inline proc "contextless" (order: Ordering) -> Ordering {
#complete switch order {
switch order {
case .Relaxed: return .Relaxed;
case .Release: return .Relaxed;
case .Acquire: return .Acquire;
@@ -22,7 +22,7 @@ strongest_failure_ordering :: inline proc "contextless" (order: Ordering) -> Ord
}
fence :: inline proc "contextless" ($order: Ordering) {
#complete switch order {
switch order {
case .Relaxed: panic("there is no such thing as a relaxed fence");
case .Release: intrinsics.atomic_fence_rel();
case .Acquire: intrinsics.atomic_fence_acq();
@@ -34,7 +34,7 @@ fence :: inline proc "contextless" ($order: Ordering) {
atomic_store :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) {
#complete switch order {
switch order {
case .Relaxed: intrinsics.atomic_store_relaxed(dst, val);
case .Release: intrinsics.atomic_store_rel(dst, val);
case .Sequentially_Consistent: intrinsics.atomic_store(dst, val);
@@ -45,7 +45,7 @@ atomic_store :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) {
}
atomic_load :: inline proc "contextless" (dst: ^$T, $order: Ordering) -> T {
#complete switch order {
switch order {
case .Relaxed: return intrinsics.atomic_load_relaxed(dst);
case .Acquire: return intrinsics.atomic_load_acq(dst);
case .Sequentially_Consistent: return intrinsics.atomic_load(dst);
@@ -57,7 +57,7 @@ atomic_load :: inline proc "contextless" (dst: ^$T, $order: Ordering) -> T {
}
atomic_swap :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
#complete switch order {
switch order {
case .Relaxed: return intrinsics.atomic_xchg_relaxed(dst, val);
case .Release: return intrinsics.atomic_xchg_rel(dst, val);
case .Acquire: return intrinsics.atomic_xchg_acq(dst, val);
@@ -138,7 +138,7 @@ atomic_compare_exchange_weak :: inline proc "contextless" (dst: ^$T, old, new: T
atomic_add :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
#complete switch order {
switch order {
case .Relaxed: return intrinsics.atomic_add_relaxed(dst, val);
case .Release: return intrinsics.atomic_add_rel(dst, val);
case .Acquire: return intrinsics.atomic_add_acq(dst, val);
@@ -150,7 +150,7 @@ atomic_add :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) ->
}
atomic_sub :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
#complete switch order {
switch order {
case .Relaxed: return intrinsics.atomic_sub_relaxed(dst, val);
case .Release: return intrinsics.atomic_sub_rel(dst, val);
case .Acquire: return intrinsics.atomic_sub_acq(dst, val);
@@ -162,7 +162,7 @@ atomic_sub :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) ->
}
atomic_and :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
#complete switch order {
switch order {
case .Relaxed: return intrinsics.atomic_and_relaxed(dst, val);
case .Release: return intrinsics.atomic_and_rel(dst, val);
case .Acquire: return intrinsics.atomic_and_acq(dst, val);
@@ -174,7 +174,7 @@ atomic_and :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) ->
}
atomic_nand :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
#complete switch order {
switch order {
case .Relaxed: return intrinsics.atomic_nand_relaxed(dst, val);
case .Release: return intrinsics.atomic_nand_rel(dst, val);
case .Acquire: return intrinsics.atomic_nand_acq(dst, val);
@@ -186,7 +186,7 @@ atomic_nand :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) ->
}
atomic_or :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
#complete switch order {
switch order {
case .Relaxed: return intrinsics.atomic_or_relaxed(dst, val);
case .Release: return intrinsics.atomic_or_rel(dst, val);
case .Acquire: return intrinsics.atomic_or_acq(dst, val);
@@ -198,7 +198,7 @@ atomic_or :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T
}
atomic_xor :: inline proc "contextless" (dst: ^$T, val: T, $order: Ordering) -> T {
#complete switch order {
switch order {
case .Relaxed: return intrinsics.atomic_xor_relaxed(dst, val);
case .Release: return intrinsics.atomic_xor_rel(dst, val);
case .Acquire: return intrinsics.atomic_xor_acq(dst, val);

View File

@@ -7,6 +7,7 @@ import "core:thread"
import "core:reflect"
import "intrinsics"
/*
The Odin programming language is fast, concise, readable, pragmatic and open sourced.
It is designed with the intent of replacing C with the following goals:
@@ -1233,8 +1234,8 @@ implicit_selector_expression :: proc() {
}
complete_switch :: proc() {
fmt.println("\n# complete_switch");
partial_switch :: proc() {
fmt.println("\n# partial_switch");
{ // enum
Foo :: enum {
A,
@@ -1244,22 +1245,31 @@ complete_switch :: proc() {
};
f := Foo.A;
#complete switch f {
switch f {
case .A: fmt.println("A");
case .B: fmt.println("B");
case .C: fmt.println("C");
case .D: fmt.println("D");
case: fmt.println("?");
}
#partial switch f {
case .A: fmt.println("A");
case .D: fmt.println("D");
}
}
{ // union
Foo :: union {int, bool};
f: Foo = 123;
#complete switch in f {
switch in f {
case int: fmt.println("int");
case bool: fmt.println("bool");
case:
}
#partial switch in f {
case bool: fmt.println("bool");
}
}
}
@@ -1820,7 +1830,7 @@ main :: proc() {
array_programming();
map_type();
implicit_selector_expression();
complete_switch();
partial_switch();
cstring_example();
bit_set_type();
deferred_procedure_associations();

View File

@@ -807,12 +807,11 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
}
}
bool complete = ss->complete;
bool is_partial = ss->partial;
if (complete) {
if (is_partial) {
if (!is_type_enum(x.type)) {
error(x.expr, "#complete switch statement can be only used with an enum type");
complete = false;
error(x.expr, "#partial switch statement can be only used with an enum type");
}
}
@@ -877,9 +876,6 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
Operand a1 = lhs;
Operand b1 = rhs;
check_comparison(ctx, &a1, &b1, Token_LtEq);
if (complete) {
error(lhs.expr, "#complete switch statement does not allow ranges");
}
add_constant_switch_case(ctx, &seen, lhs);
if (upper_op == Token_GtEq) {
@@ -926,9 +922,6 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
continue;
}
if (y.mode != Addressing_Constant) {
if (complete) {
error(y.expr, "#complete switch statement only allows constant case clauses");
}
continue;
}
@@ -942,7 +935,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
check_close_scope(ctx);
}
if (complete) {
if (!is_partial && is_type_enum(x.type)) {
Type *et = base_type(x.type);
GB_ASSERT(is_type_enum(et));
auto fields = et->Enum.fields;
@@ -968,18 +961,17 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
defer (begin_error_block());
if (unhandled.count == 1) {
error_no_newline(node, "Unhandled switch case: ");
error_no_newline(node, "Unhandled switch case: %.*s", LIT(unhandled[0]->token.string));
} else {
error_no_newline(node, "Unhandled switch cases: ");
}
for_array(i, unhandled) {
Entity *f = unhandled[i];
if (i > 0) {
error_line(", ");
for_array(i, unhandled) {
Entity *f = unhandled[i];
error_line("\t%.*s\n", LIT(f->token.string));
}
error_line("%.*s", LIT(f->token.string));
}
error_line("\n");
error_line("\tSuggestion: Was '#partial switch' wanted? This replaces the previous '#complete switch'.\n");
}
}
}
@@ -1042,11 +1034,10 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
return;
}
bool complete = ss->complete;
if (complete) {
bool is_partial = ss->partial;
if (is_partial) {
if (switch_kind != TypeSwitch_Union) {
error(node, "#complete switch statement may only be used with a union");
complete = false;
error(node, "#partial switch statement may only be used with a union");
}
}
@@ -1174,7 +1165,7 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
check_close_scope(ctx);
}
if (complete) {
if (!is_partial && is_type_union(type_deref(x.type))) {
Type *ut = base_type(type_deref(x.type));
GB_ASSERT(is_type_union(ut));
auto variants = ut->Union.variants;
@@ -1191,20 +1182,20 @@ void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
if (unhandled.count > 0) {
if (unhandled.count == 1) {
error_no_newline(node, "Unhandled switch case: ");
} else {
error_no_newline(node, "Unhandled switch cases: ");
}
for_array(i, unhandled) {
Type *t = unhandled[i];
if (i > 0) {
error_line(", ");
}
gbString s = type_to_string(t);
error_line("%s", s);
gbString s = type_to_string(unhandled[0]);
error_no_newline(node, "Unhandled switch case: %s", s);
gb_string_free(s);
} else {
error_no_newline(node, "Unhandled switch cases:\n");
for_array(i, unhandled) {
Type *t = unhandled[i];
gbString s = type_to_string(t);
error_line("\t%s\n", s);
gb_string_free(s);
}
}
error_line("\n");
error_line("\tSuggestion: Was '#partial switch' wanted? This replaces the previous '#complete switch'.\n");
}
}
}

View File

@@ -766,6 +766,7 @@ Ast *ast_switch_stmt(AstFile *f, Token token, Ast *init, Ast *tag, Ast *body) {
result->SwitchStmt.init = init;
result->SwitchStmt.tag = tag;
result->SwitchStmt.body = body;
result->SwitchStmt.partial = false;
return result;
}
@@ -775,6 +776,7 @@ Ast *ast_type_switch_stmt(AstFile *f, Token token, Ast *tag, Ast *body) {
result->TypeSwitchStmt.token = token;
result->TypeSwitchStmt.tag = tag;
result->TypeSwitchStmt.body = body;
result->TypeSwitchStmt.partial = false;
return result;
}
@@ -4060,16 +4062,32 @@ Ast *parse_stmt(AstFile *f) {
s = parse_stmt(f);
switch (s->kind) {
case Ast_SwitchStmt:
s->SwitchStmt.complete = true;
s->SwitchStmt.partial = false;
syntax_warning(token, "#complete is now the default and has been replaced with its opposite: #partial");
break;
case Ast_TypeSwitchStmt:
s->TypeSwitchStmt.complete = true;
s->TypeSwitchStmt.partial = false;
syntax_warning(token, "#complete is now the default and has been replaced with its opposite: #partial");
break;
default:
syntax_error(token, "#complete can only be applied to a switch statement");
break;
}
return s;
} else if (tag == "partial") {
s = parse_stmt(f);
switch (s->kind) {
case Ast_SwitchStmt:
s->SwitchStmt.partial = true;
break;
case Ast_TypeSwitchStmt:
s->TypeSwitchStmt.partial = true;
break;
default:
syntax_error(token, "#partial can only be applied to a switch statement");
break;
}
return s;
} else if (tag == "assert") {
Ast *t = ast_basic_directive(f, hash_token, tag);
return ast_expr_stmt(f, parse_call_expr(f, t));

View File

@@ -363,20 +363,20 @@ AST_KIND(_ComplexStmtBegin, "", bool) \
Entity *implicit_entity; \
}) \
AST_KIND(SwitchStmt, "switch statement", struct { \
Token token; \
Ast *label; \
Ast *init; \
Ast *tag; \
Ast *body; \
bool complete; \
Token token; \
Ast *label; \
Ast *init; \
Ast *tag; \
Ast *body; \
bool partial; \
}) \
AST_KIND(TypeSwitchStmt, "type switch statement", struct { \
Token token; \
Ast *label; \
Ast *tag; \
Ast *body; \
bool complete; \
}) \
Token token; \
Ast *label; \
Ast *tag; \
Ast *body; \
bool partial; \
}) \
AST_KIND(DeferStmt, "defer statement", struct { Token token; Ast *stmt; }) \
AST_KIND(BranchStmt, "branch statement", struct { Token token; Ast *label; }) \
AST_KIND(UsingStmt, "using statement", struct { \