Allow ..= alongside .. as a "full range" operator; Update core:odin/parser etc

This commit is contained in:
gingerBill
2021-05-16 12:34:35 +01:00
parent 24c89b3eee
commit ce08e832f7
9 changed files with 89 additions and 19 deletions

View File

@@ -12,6 +12,10 @@ Parser :: struct {
file: ^ast.File,
tok: tokenizer.Tokenizer,
// If optional_semicolons is true, semicolons are completely as statement terminators
// different to .Insert_Semicolon in tok.flags
optional_semicolons: bool,
warn: Warning_Handler,
err: Error_Handler,
@@ -128,6 +132,10 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool {
p.line_comment = nil;
}
if p.optional_semicolons {
p.tok.flags += {.Insert_Semicolon};
}
p.file = file;
tokenizer.init(&p.tok, file.src, file.fullpath, p.err);
if p.tok.ch <= 0 {
@@ -400,6 +408,11 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool {
if node == nil {
return false;
}
if p.optional_semicolons {
return true;
}
switch n in node.derived {
case ast.Empty_Stmt, ast.Block_Stmt:
return true;
@@ -439,14 +452,34 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool {
return false;
}
expect_semicolon_newline_error :: proc(p: ^Parser, token: tokenizer.Token, s: ^ast.Node) {
if !p.optional_semicolons && .Insert_Semicolon in p.tok.flags && token.text == "\n" {
#partial switch token.kind {
case .Close_Brace:
case .Close_Paren:
case .Else:
return;
}
if is_semicolon_optional_for_node(p, s) {
return;
}
tok := token;
tok.pos.column -= 1;
error(p, tok.pos, "expected ';', got newline");
}
}
expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool {
if allow_token(p, .Semicolon) {
expect_semicolon_newline_error(p, p.prev_tok, node);
return true;
}
prev := p.prev_tok;
if prev.kind == .Semicolon {
expect_semicolon_newline_error(p, p.prev_tok, node);
return true;
}
@@ -615,7 +648,7 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt {
cond = parse_expr(p, false);
} else {
init = parse_simple_stmt(p, nil);
if allow_token(p, .Semicolon) {
if parse_control_statement_semicolon_separator(p) {
cond = parse_expr(p, false);
} else {
cond = convert_stmt_to_expr(p, init, "boolean expression");
@@ -668,6 +701,18 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt {
return if_stmt;
}
parse_control_statement_semicolon_separator :: proc(p: ^Parser) -> bool {
tok := peek_token(p);
if tok.kind != .Open_Brace {
return allow_token(p, .Semicolon);
}
if tok.text == ";" {
return allow_token(p, .Semicolon);
}
return false;
}
parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
if p.curr_proc == nil {
error(p, p.curr_tok.pos, "you cannot use a for statement in the file scope");
@@ -716,7 +761,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
}
}
if !is_range && allow_token(p, .Semicolon) {
if !is_range && parse_control_statement_semicolon_separator(p) {
init = cond;
cond = nil;
if p.curr_tok.kind != .Semicolon {
@@ -820,7 +865,7 @@ parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
tag = parse_simple_stmt(p, {Stmt_Allow_Flag.In});
if as, ok := tag.derived.(ast.Assign_Stmt); ok && as.op.kind == .In {
is_type_switch = true;
} else if allow_token(p, .Semicolon) {
} else if parse_control_statement_semicolon_separator(p) {
init = tag;
tag = nil;
if p.curr_tok.kind != .Open_Brace {
@@ -831,6 +876,7 @@ parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
}
skip_possible_newline(p);
open := expect_token(p, .Open_Brace);
for p.curr_tok.kind == .Case {
@@ -958,6 +1004,7 @@ parse_foreign_block :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Foreign_Bl
defer p.in_foreign_block = prev_in_foreign_block;
p.in_foreign_block = true;
skip_possible_newline_for_literal(p);
open := expect_token(p, .Open_Brace);
for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF {
decl := parse_foreign_block_decl(p);
@@ -1287,7 +1334,7 @@ token_precedence :: proc(p: ^Parser, kind: tokenizer.Token_Kind) -> int {
#partial switch kind {
case .Question, .If, .When:
return 1;
case .Ellipsis, .Range_Half:
case .Ellipsis, .Range_Half, .Range_Full:
if !p.allow_range {
return 0;
}
@@ -2233,6 +2280,8 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
}
body: ^ast.Stmt;
skip_possible_newline_for_literal(p);
if allow_token(p, .Undef) {
body = nil;
if where_token.kind != .Invalid {
@@ -2405,6 +2454,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
p.expr_level = where_prev_level;
}
skip_possible_newline_for_literal(p);
expect_token(p, .Open_Brace);
fields, name_count = parse_field_list(p, .Close_Brace, ast.Field_Flags_Struct);
close := expect_token(p, .Close_Brace);
@@ -2473,6 +2523,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
variants: [dynamic]^ast.Expr;
skip_possible_newline_for_literal(p);
expect_token_after(p, .Open_Brace, "union");
for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF {
@@ -2503,6 +2554,8 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
if p.curr_tok.kind != .Open_Brace {
base_type = parse_type(p);
}
skip_possible_newline_for_literal(p);
open := expect_token(p, .Open_Brace);
fields := parse_elem_list(p);
close := expect_token(p, .Close_Brace);
@@ -2601,6 +2654,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
}
}
skip_possible_newline_for_literal(p);
open := expect_token(p, .Open_Brace);
asm_string := parse_expr(p, false);
expect_token(p, .Comma);
@@ -2811,7 +2865,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
open := expect_token(p, .Open_Bracket);
#partial switch p.curr_tok.kind {
case .Colon, .Ellipsis, .Range_Half:
case .Colon, .Ellipsis, .Range_Half, .Range_Full:
// NOTE(bill): Do not err yet
break;
case:
@@ -2819,7 +2873,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
}
#partial switch p.curr_tok.kind {
case .Ellipsis, .Range_Half:
case .Ellipsis, .Range_Half, .Range_Full:
error(p, p.curr_tok.pos, "expected a colon, not a range");
fallthrough;
case .Colon:

View File

@@ -107,6 +107,7 @@ Token_Kind :: enum u32 {
Comma, // ,
Ellipsis, // ..
Range_Half, // ..<
Range_Full, // ..=
Back_Slash, // \
B_Operator_End,
@@ -233,6 +234,7 @@ tokens := [Token_Kind.COUNT]string {
",",
"..",
"..<",
"..=",
"\\",
"",

View File

@@ -623,6 +623,9 @@ scan :: proc(t: ^Tokenizer) -> Token {
if t.ch == '<' {
advance_rune(t);
kind = .Range_Half;
} else if t.ch == '=' {
advance_rune(t);
kind = .Range_Full;
}
}
}

View File

@@ -5940,8 +5940,9 @@ bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValu
TokenKind op = Token_Lt;
switch (ie->op.kind) {
case Token_Ellipsis: op = Token_LtEq; break;
case Token_RangeHalf: op = Token_Lt; break;
case Token_Ellipsis: op = Token_LtEq; break; // ..
case Token_RangeFull: op = Token_LtEq; break; // ..=
case Token_RangeHalf: op = Token_Lt; break; // ..<
default: error(ie->op, "Invalid range operator"); break;
}
bool ok = compare_exact_values(op, a, b);
@@ -5952,7 +5953,7 @@ bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValu
}
ExactValue inline_for_depth = exact_value_sub(b, a);
if (ie->op.kind == Token_Ellipsis) {
if (ie->op.kind != Token_RangeHalf) {
inline_for_depth = exact_value_increment_one(inline_for_depth);
}

View File

@@ -939,6 +939,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
TokenKind upper_op = Token_Invalid;
switch (be->op.kind) {
case Token_Ellipsis: upper_op = Token_GtEq; break;
case Token_RangeFull: upper_op = Token_GtEq; break;
case Token_RangeHalf: upper_op = Token_Gt; break;
default: GB_PANIC("Invalid range operator"); break;
}

View File

@@ -931,6 +931,7 @@ void check_bit_set_type(CheckerContext *c, Type *type, Type *named_type, Ast *no
switch (be->op.kind) {
case Token_Ellipsis:
case Token_RangeFull:
if (upper - lower >= bits) {
error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, (upper-lower+1));
}

View File

@@ -4041,6 +4041,7 @@ void lb_build_range_interval(lbProcedure *p, AstBinaryExpr *node,
TokenKind op = Token_Lt;
switch (node->op.kind) {
case Token_Ellipsis: op = Token_LtEq; break;
case Token_RangeFull: op = Token_LtEq; break;
case Token_RangeHalf: op = Token_Lt; break;
default: GB_PANIC("Invalid interval operator"); break;
}
@@ -4454,7 +4455,7 @@ void lb_build_inline_range_stmt(lbProcedure *p, AstUnrollRangeStmt *rs, Scope *s
ExactValue start = start_expr->tav.value;
ExactValue end = end_expr->tav.value;
if (op == Token_Ellipsis) { // .. [start, end]
if (op != Token_RangeHalf) { // .. [start, end] (or ..=)
ExactValue index = exact_value_i64(0);
for (ExactValue val = start;
compare_exact_values(Token_LtEq, val, end);
@@ -4465,7 +4466,7 @@ void lb_build_inline_range_stmt(lbProcedure *p, AstUnrollRangeStmt *rs, Scope *s
lb_build_stmt(p, rs->body);
}
} else if (op == Token_RangeHalf) { // ..< [start, end)
} else { // ..< [start, end)
ExactValue index = exact_value_i64(0);
for (ExactValue val = start;
compare_exact_values(Token_Lt, val, end);
@@ -4632,6 +4633,7 @@ void lb_build_switch_stmt(lbProcedure *p, AstSwitchStmt *ss, Scope *scope) {
TokenKind op = Token_Invalid;
switch (ie->op.kind) {
case Token_Ellipsis: op = Token_LtEq; break;
case Token_RangeFull: op = Token_LtEq; break;
case Token_RangeHalf: op = Token_Lt; break;
default: GB_PANIC("Invalid interval operator"); break;
}

View File

@@ -1344,6 +1344,7 @@ Token expect_token_after(AstFile *f, TokenKind kind, char const *msg) {
bool is_token_range(TokenKind kind) {
switch (kind) {
case Token_Ellipsis:
case Token_RangeFull:
case Token_RangeHalf:
return true;
}
@@ -1574,6 +1575,10 @@ void expect_semicolon(AstFile *f, Ast *s) {
return;
}
if (f->curr_token.kind == Token_EOF) {
return;
}
if (s != nullptr) {
bool insert_semi = (f->tokenizer.flags & TokenizerFlag_InsertSemicolon) != 0;
if (insert_semi) {
@@ -2315,7 +2320,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
f->expr_level = prev_level;
}
skip_possible_newline_for_literal(f);
Token open = expect_token_after(f, Token_OpenBrace, "struct");
isize name_count = 0;
@@ -2675,6 +2680,7 @@ Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) {
switch (f->curr_token.kind) {
case Token_Ellipsis:
case Token_RangeFull:
case Token_RangeHalf:
// NOTE(bill): Do not err yet
case Token_Colon:
@@ -2686,6 +2692,7 @@ Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) {
switch (f->curr_token.kind) {
case Token_Ellipsis:
case Token_RangeFull:
case Token_RangeHalf:
syntax_error(f->curr_token, "Expected a colon, not a range");
/* fallthrough */
@@ -2812,6 +2819,7 @@ i32 token_precedence(AstFile *f, TokenKind t) {
case Token_when:
return 1;
case Token_Ellipsis:
case Token_RangeFull:
case Token_RangeHalf:
if (!f->allow_range) {
return 0;
@@ -3926,12 +3934,6 @@ Ast *parse_return_stmt(AstFile *f) {
while (f->curr_token.kind != Token_Semicolon) {
Ast *arg = parse_expr(f, false);
// if (f->curr_token.kind == Token_Eq) {
// Token eq = expect_token(f, Token_Eq);
// Ast *value = parse_value(f);
// arg = ast_field_value(f, arg, value, eq);
// }
array_add(&results, arg);
if (f->curr_token.kind != Token_Comma ||
f->curr_token.kind == Token_EOF) {
@@ -4052,7 +4054,7 @@ Ast *parse_case_clause(AstFile *f, bool is_type) {
}
f->allow_range = prev_allow_range;
f->allow_in_expr = prev_allow_in_expr;
expect_token(f, Token_Colon); // TODO(bill): Is this the best syntax?
expect_token(f, Token_Colon);
Array<Ast *> stmts = parse_stmt_list(f);
return ast_case_clause(f, token, list, stmts);

View File

@@ -76,6 +76,7 @@ TOKEN_KIND(Token__ComparisonEnd, ""), \
TOKEN_KIND(Token_Period, "."), \
TOKEN_KIND(Token_Comma, ","), \
TOKEN_KIND(Token_Ellipsis, ".."), \
TOKEN_KIND(Token_RangeFull, "..="), \
TOKEN_KIND(Token_RangeHalf, "..<"), \
TOKEN_KIND(Token_BackSlash, "\\"), \
TOKEN_KIND(Token__OperatorEnd, ""), \
@@ -1204,6 +1205,9 @@ void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) {
if (t->curr_rune == '<') {
advance_to_next_rune(t);
token->kind = Token_RangeHalf;
} else if (t->curr_rune == '=') {
advance_to_next_rune(t);
token->kind = Token_RangeFull;
}
} else if ('0' <= t->curr_rune && t->curr_rune <= '9') {
scan_number_to_token(t, token, true);