diff --git a/core/odin/parser/parser.odin b/core/odin/parser/parser.odin index 3c0be0856..d7dfbc67b 100644 --- a/core/odin/parser/parser.odin +++ b/core/odin/parser/parser.odin @@ -1050,12 +1050,74 @@ parse_foreign_decl :: proc(p: ^Parser) -> ^ast.Decl { } +parse_unrolled_for_loop :: proc(p: ^Parser, inline_tok: tokenizer.Token) -> ^ast.Stmt { + for_tok := expect_token(p, .For); + val0, val1: ^ast.Expr; + in_tok: tokenizer.Token; + expr: ^ast.Expr; + body: ^ast.Stmt; + + bad_stmt := false; + + if p.curr_tok.kind != .In { + idents := parse_ident_list(p, false); + switch len(idents) { + case 1: + val0 = idents[0]; + case 2: + val0, val1 = idents[0], idents[1]; + case: + error(p, for_tok.pos, "expected either 1 or 2 identifiers"); + bad_stmt = true; + } + } + + in_tok = expect_token(p, .In); + + prev_allow_range := p.allow_range; + prev_level := p.expr_level; + p.allow_range = true; + p.expr_level = -1; + + expr = parse_expr(p, false); + + p.expr_level = prev_level; + p.allow_range = prev_allow_range; + + if allow_token(p, .Do) { + body = convert_stmt_to_body(p, parse_stmt(p)); + } else { + body = parse_block_stmt(p, false); + } + + if bad_stmt { + return ast.new(ast.Bad_Stmt, inline_tok.pos, end_pos(p.prev_tok)); + } + + range_stmt := ast.new(ast.Inline_Range_Stmt, inline_tok.pos, body.end); + range_stmt.inline_pos = inline_tok.pos; + range_stmt.for_pos = for_tok.pos; + range_stmt.val0 = val0; + range_stmt.val1 = val1; + range_stmt.in_pos = in_tok.pos; + range_stmt.expr = expr; + range_stmt.body = body; + return range_stmt; +} + parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { #partial switch p.curr_tok.kind { + + case .Inline: + if peek_token_kind(p, .For) { + inline_tok := expect_token(p, .Inline); + return parse_unrolled_for_loop(p, inline_tok); + } + fallthrough; // Operands case .Context, // Also allows for 'context = ' .Proc, - .Inline, .No_Inline, + .No_Inline, .Asm, // Inline assembly .Ident, .Integer, .Float, .Imag, @@ -1065,63 +1127,6 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { // Unary Expressions .Add, .Sub, .Xor, .Not, .And: - if peek_token_kind(p, .For) { - inline_tok := expect_token(p, .Inline); - for_tok := expect_token(p, .For); - val0, val1: ^ast.Expr; - in_tok: tokenizer.Token; - expr: ^ast.Expr; - body: ^ast.Stmt; - - bad_stmt := false; - - if p.curr_tok.kind != .In { - idents := parse_ident_list(p, false); - switch len(idents) { - case 1: - val0 = idents[0]; - case 2: - val0, val1 = idents[0], idents[1]; - case: - error(p, for_tok.pos, "expected either 1 or 2 identifiers"); - bad_stmt = true; - } - } - - in_tok = expect_token(p, .In); - - prev_allow_range := p.allow_range; - prev_level := p.expr_level; - p.allow_range = true; - p.expr_level = -1; - - expr = parse_expr(p, false); - - p.expr_level = prev_level; - p.allow_range = prev_allow_range; - - if allow_token(p, .Do) { - body = convert_stmt_to_body(p, parse_stmt(p)); - } else { - body = parse_block_stmt(p, false); - } - - if bad_stmt { - return ast.new(ast.Bad_Stmt, inline_tok.pos, end_pos(p.prev_tok)); - } - - range_stmt := ast.new(ast.Inline_Range_Stmt, inline_tok.pos, body.end); - range_stmt.inline_pos = inline_tok.pos; - range_stmt.for_pos = for_tok.pos; - range_stmt.val0 = val0; - range_stmt.val1 = val1; - range_stmt.in_pos = in_tok.pos; - range_stmt.expr = expr; - range_stmt.body = body; - return range_stmt; - } - - s := parse_simple_stmt(p, {Stmt_Allow_Flag.Label}); expect_semicolon(p, s); return s; @@ -1261,6 +1266,8 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt { es := ast.new(ast.Expr_Stmt, ce.pos, ce.end); es.expr = ce; return es; + case "unroll": + return parse_unrolled_for_loop(p, tag); case "include": error(p, tag.pos, "#include is not a valid import declaration kind. Did you meant 'import'?"); return ast.new(ast.Bad_Stmt, tok.pos, end_pos(tag)); @@ -2004,7 +2011,41 @@ check_poly_params_for_type :: proc(p: ^Parser, poly_params: ^ast.Field_List, tok } } +parse_inlining_operand :: proc(p: ^Parser, lhs: bool, tok: tokenizer.Token) -> ^ast.Expr { + expr := parse_unary_expr(p, lhs); + pi := ast.Proc_Inlining.None; + #partial switch tok.kind { + case .Inline: + pi = .Inline; + case .No_Inline: + pi = .No_Inline; + case .Ident: + switch tok.text { + case "force_inline": + pi = .Inline; + case "force_no_inline": + pi = .No_Inline; + } + } + + switch e in &ast.unparen_expr(expr).derived { + case ast.Proc_Lit: + if e.inlining != .None && e.inlining != pi { + error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure literal"); + } + e.inlining = pi; + case ast.Call_Expr: + if e.inlining != .None && e.inlining != pi { + error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure call"); + } + e.inlining = pi; + case: + error(p, tok.pos, "'%s' must be followed by a procedure literal or call", tok.text); + return ast.new(ast.Bad_Expr, tok.pos, expr.end); + } + return expr; +} parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { #partial switch p.curr_tok.kind { @@ -2056,14 +2097,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { dt.type = type; return dt; - case .Opaque: - tok := advance_token(p); - warn(p, tok.pos, "opaque is deprecated in favour of #opaque"); - type := parse_type(p); - ot := ast.new(ast.Opaque_Type, tok.pos, type.end); - ot.type = type; - return ot; - case .Hash: tok := expect_token(p, .Hash); name := expect_token(p, .Ident); @@ -2164,32 +2197,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr { case .Inline, .No_Inline: tok := advance_token(p); - expr := parse_unary_expr(p, lhs); - - pi := ast.Proc_Inlining.None; - #partial switch tok.kind { - case .Inline: - pi = ast.Proc_Inlining.Inline; - case .No_Inline: - pi = ast.Proc_Inlining.No_Inline; - } - - switch e in &ast.unparen_expr(expr).derived { - case ast.Proc_Lit: - if e.inlining != ast.Proc_Inlining.None && e.inlining != pi { - error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure literal"); - } - e.inlining = pi; - case ast.Call_Expr: - if e.inlining != ast.Proc_Inlining.None && e.inlining != pi { - error(p, expr.pos, "both 'inline' and 'no_inline' cannot be applied to a procedure call"); - } - e.inlining = pi; - case: - error(p, tok.pos, "'%s' must be followed by a procedure literal or call", tok.text); - return ast.new(ast.Bad_Expr, tok.pos, expr.end); - } - return expr; + return parse_inlining_operand(p, lhs, tok); case .Proc: tok := expect_token(p, .Proc); diff --git a/core/odin/tokenizer/token.odin b/core/odin/tokenizer/token.odin index 3e6b51ba6..174ae0b1e 100644 --- a/core/odin/tokenizer/token.odin +++ b/core/odin/tokenizer/token.odin @@ -142,7 +142,6 @@ Token_Kind :: enum u32 { Cast, // cast Transmute, // transmute Distinct, // distinct - Opaque, // opaque Using, // using Inline, // inline No_Inline, // no_inline @@ -270,7 +269,6 @@ tokens := [Token_Kind.COUNT]string { "cast", "transmute", "distinct", - "opaque", "using", "inline", "no_inline", diff --git a/src/parser.cpp b/src/parser.cpp index 7ae1810ec..c1160f8b1 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -1865,6 +1865,45 @@ bool ast_on_same_line(Ast *x, Ast *y) { return ast_on_same_line(ast_token(x), y); } +Ast *parse_force_inlining_operand(AstFile *f, Token token) { + Ast *expr = parse_unary_expr(f, false); + Ast *e = unparen_expr(expr); + if (e->kind != Ast_ProcLit && e->kind != Ast_CallExpr) { + syntax_error(expr, "%.*s must be followed by a procedure literal or call, got %.*s", LIT(token.string), LIT(ast_strings[expr->kind])); + return ast_bad_expr(f, token, f->curr_token); + } + ProcInlining pi = ProcInlining_none; + if (token.kind == Token_inline) { + pi = ProcInlining_inline; + } else if (token.kind == Token_no_inline) { + pi = ProcInlining_no_inline; + } else if (token.kind == Token_Ident) { + if (token.string == "force_inline") { + pi = ProcInlining_inline; + } else if (token.string == "force_no_inline") { + pi = ProcInlining_no_inline; + } + } + + if (pi != ProcInlining_none) { + if (e->kind == Ast_ProcLit) { + if (expr->ProcLit.inlining != ProcInlining_none && + expr->ProcLit.inlining != pi) { + syntax_error(expr, "You cannot apply both 'inline' and 'no_inline' to a procedure literal"); + } + expr->ProcLit.inlining = pi; + } else if (e->kind == Ast_CallExpr) { + if (expr->CallExpr.inlining != ProcInlining_none && + expr->CallExpr.inlining != pi) { + syntax_error(expr, "You cannot apply both 'inline' and 'no_inline' to a procedure call"); + } + expr->CallExpr.inlining = pi; + } + } + + return expr; +} + Ast *parse_operand(AstFile *f, bool lhs) { Ast *operand = nullptr; // Operand @@ -1986,6 +2025,9 @@ Ast *parse_operand(AstFile *f, bool lhs) { } else if (name.string == "opaque") { Ast *type = parse_type(f); return ast_opaque_type(f, token, type); + } else if (name.string == "force_inline" || + name.string == "force_no_inline") { + return parse_force_inlining_operand(f, name); } else { operand = ast_tag_expr(f, token, name, parse_expr(f, false)); } @@ -1996,35 +2038,7 @@ Ast *parse_operand(AstFile *f, bool lhs) { case Token_no_inline: { Token token = advance_token(f); - Ast *expr = parse_unary_expr(f, false); - Ast *e = unparen_expr(expr); - if (e->kind != Ast_ProcLit && e->kind != Ast_CallExpr) { - syntax_error(expr, "%.*s must be followed by a procedure literal or call, got %.*s", LIT(token.string), LIT(ast_strings[expr->kind])); - return ast_bad_expr(f, token, f->curr_token); - } - ProcInlining pi = ProcInlining_none; - if (token.kind == Token_inline) { - pi = ProcInlining_inline; - } else if (token.kind == Token_no_inline) { - pi = ProcInlining_no_inline; - } - if (pi != ProcInlining_none) { - if (e->kind == Ast_ProcLit) { - if (expr->ProcLit.inlining != ProcInlining_none && - expr->ProcLit.inlining != pi) { - syntax_error(expr, "You cannot apply both 'inline' and 'no_inline' to a procedure literal"); - } - expr->ProcLit.inlining = pi; - } else if (e->kind == Ast_CallExpr) { - if (expr->CallExpr.inlining != ProcInlining_none && - expr->CallExpr.inlining != pi) { - syntax_error(expr, "You cannot apply both 'inline' and 'no_inline' to a procedure call"); - } - expr->CallExpr.inlining = pi; - } - } - - return expr; + return parse_force_inlining_operand(f, token); } break; // Parse Procedure Type or Literal or Group @@ -4282,6 +4296,58 @@ Ast *parse_attribute(AstFile *f, Token token, TokenKind open_kind, TokenKind clo } +Ast *parse_unrolled_for_loop(AstFile *f, Token inline_token) { + Token for_token = expect_token(f, Token_for); + Ast *val0 = nullptr; + Ast *val1 = nullptr; + Token in_token = {}; + Ast *expr = nullptr; + Ast *body = nullptr; + + bool bad_stmt = false; + + if (f->curr_token.kind != Token_in) { + Array idents = parse_ident_list(f, false); + switch (idents.count) { + case 1: + val0 = idents[0]; + break; + case 2: + val0 = idents[0]; + val1 = idents[1]; + break; + default: + syntax_error(for_token, "Expected either 1 or 2 identifiers"); + bad_stmt = true; + break; + } + } + in_token = expect_token(f, Token_in); + + bool prev_allow_range = f->allow_range; + isize prev_level = f->expr_level; + f->allow_range = true; + f->expr_level = -1; + expr = parse_expr(f, false); + f->expr_level = prev_level; + f->allow_range = prev_allow_range; + + if (allow_token(f, Token_do)) { + body = convert_stmt_to_body(f, parse_stmt(f)); + if (build_context.disallow_do) { + syntax_error(body, "'do' has been disallowed"); + } else if (!ast_on_same_line(for_token, body)) { + syntax_error(body, "The body of a 'do' be on the same line as the 'for' token"); + } + } else { + body = parse_block_stmt(f, false); + } + if (bad_stmt) { + return ast_bad_stmt(f, inline_token, f->curr_token); + } + return ast_inline_range_stmt(f, inline_token, for_token, val0, val1, in_token, expr, body); +} + Ast *parse_stmt(AstFile *f) { Ast *s = nullptr; Token token = f->curr_token; @@ -4290,55 +4356,7 @@ Ast *parse_stmt(AstFile *f) { case Token_inline: if (peek_token_kind(f, Token_for)) { Token inline_token = expect_token(f, Token_inline); - Token for_token = expect_token(f, Token_for); - Ast *val0 = nullptr; - Ast *val1 = nullptr; - Token in_token = {}; - Ast *expr = nullptr; - Ast *body = nullptr; - - bool bad_stmt = false; - - if (f->curr_token.kind != Token_in) { - Array idents = parse_ident_list(f, false); - switch (idents.count) { - case 1: - val0 = idents[0]; - break; - case 2: - val0 = idents[0]; - val1 = idents[1]; - break; - default: - syntax_error(for_token, "Expected either 1 or 2 identifiers"); - bad_stmt = true; - break; - } - } - in_token = expect_token(f, Token_in); - - bool prev_allow_range = f->allow_range; - isize prev_level = f->expr_level; - f->allow_range = true; - f->expr_level = -1; - expr = parse_expr(f, false); - f->expr_level = prev_level; - f->allow_range = prev_allow_range; - - if (allow_token(f, Token_do)) { - body = convert_stmt_to_body(f, parse_stmt(f)); - if (build_context.disallow_do) { - syntax_error(body, "'do' has been disallowed"); - } else if (!ast_on_same_line(for_token, body)) { - syntax_error(body, "The body of a 'do' be on the same line as the 'for' token"); - } - } else { - body = parse_block_stmt(f, false); - } - if (bad_stmt) { - return ast_bad_stmt(f, inline_token, f->curr_token); - } - return ast_inline_range_stmt(f, inline_token, for_token, val0, val1, in_token, expr, body); + return parse_unrolled_for_loop(f, inline_token); } /* fallthrough */ case Token_no_inline: @@ -4484,6 +4502,8 @@ Ast *parse_stmt(AstFile *f) { } else if (tag == "panic") { Ast *t = ast_basic_directive(f, hash_token, tag); return ast_expr_stmt(f, parse_call_expr(f, t)); + } else if (tag == "unroll" || tag == "force_inline") { + return parse_unrolled_for_loop(f, name); } else if (tag == "include") { syntax_error(token, "#include is not a valid import declaration kind. Did you mean 'import'?"); s = ast_bad_stmt(f, token, f->curr_token);