Browse Source

Add `#force_inline`, `#force_no_inline` and `#unroll` for the transition to deprecate and then remove the keywords `inline` and `no_inline`
`inline for` will be replaced with `#unroll for`

gingerBill 4 years ago
parent
commit
908a403d78
3 changed files with 198 additions and 172 deletions
  1. 100 92
      core/odin/parser/parser.odin
  2. 0 2
      core/odin/tokenizer/token.odin
  3. 98 78
      src/parser.cpp

+ 100 - 92
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 {
 parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 	#partial switch p.curr_tok.kind {
 	#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
 	// Operands
 	case .Context, // Also allows for 'context = '
 	case .Context, // Also allows for 'context = '
 	     .Proc,
 	     .Proc,
-	     .Inline, .No_Inline,
+	     .No_Inline,
 	     .Asm, // Inline assembly
 	     .Asm, // Inline assembly
 	     .Ident,
 	     .Ident,
 	     .Integer, .Float, .Imag,
 	     .Integer, .Float, .Imag,
@@ -1065,63 +1127,6 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 	     // Unary Expressions
 	     // Unary Expressions
 	     .Add, .Sub, .Xor, .Not, .And:
 	     .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});
 	    s := parse_simple_stmt(p, {Stmt_Allow_Flag.Label});
 	    expect_semicolon(p, s);
 	    expect_semicolon(p, s);
 		return 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 := ast.new(ast.Expr_Stmt, ce.pos, ce.end);
 			es.expr = ce;
 			es.expr = ce;
 			return es;
 			return es;
+		case "unroll":
+			return parse_unrolled_for_loop(p, tag);
 		case "include":
 		case "include":
 			error(p, tag.pos, "#include is not a valid import declaration kind. Did you meant 'import'?");
 			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));
 			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 {
 parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 	#partial switch p.curr_tok.kind {
 	#partial switch p.curr_tok.kind {
@@ -2056,14 +2097,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 		dt.type = type;
 		dt.type = type;
 		return dt;
 		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:
 	case .Hash:
 		tok := expect_token(p, .Hash);
 		tok := expect_token(p, .Hash);
 		name := expect_token(p, .Ident);
 		name := expect_token(p, .Ident);
@@ -2164,32 +2197,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 
 
 	case .Inline, .No_Inline:
 	case .Inline, .No_Inline:
 		tok := advance_token(p);
 		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:
 	case .Proc:
 		tok := expect_token(p, .Proc);
 		tok := expect_token(p, .Proc);

+ 0 - 2
core/odin/tokenizer/token.odin

@@ -142,7 +142,6 @@ Token_Kind :: enum u32 {
 		Cast,        // cast
 		Cast,        // cast
 		Transmute,   // transmute
 		Transmute,   // transmute
 		Distinct,    // distinct
 		Distinct,    // distinct
-		Opaque,      // opaque
 		Using,       // using
 		Using,       // using
 		Inline,      // inline
 		Inline,      // inline
 		No_Inline,   // no_inline
 		No_Inline,   // no_inline
@@ -270,7 +269,6 @@ tokens := [Token_Kind.COUNT]string {
 	"cast",
 	"cast",
 	"transmute",
 	"transmute",
 	"distinct",
 	"distinct",
-	"opaque",
 	"using",
 	"using",
 	"inline",
 	"inline",
 	"no_inline",
 	"no_inline",

+ 98 - 78
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);
 	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 *parse_operand(AstFile *f, bool lhs) {
 	Ast *operand = nullptr; // Operand
 	Ast *operand = nullptr; // Operand
@@ -1986,6 +2025,9 @@ Ast *parse_operand(AstFile *f, bool lhs) {
 		} else if (name.string == "opaque") {
 		} else if (name.string == "opaque") {
 			Ast *type = parse_type(f);
 			Ast *type = parse_type(f);
 			return ast_opaque_type(f, token, type);
 			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 {
 		} else {
 			operand = ast_tag_expr(f, token, name, parse_expr(f, false));
 			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:
 	case Token_no_inline:
 	{
 	{
 		Token token = advance_token(f);
 		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;
 	} break;
 
 
 	// Parse Procedure Type or Literal or Group
 	// 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<Ast *> 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 *parse_stmt(AstFile *f) {
 	Ast *s = nullptr;
 	Ast *s = nullptr;
 	Token token = f->curr_token;
 	Token token = f->curr_token;
@@ -4290,55 +4356,7 @@ Ast *parse_stmt(AstFile *f) {
 	case Token_inline:
 	case Token_inline:
 		if (peek_token_kind(f, Token_for)) {
 		if (peek_token_kind(f, Token_for)) {
 			Token inline_token = expect_token(f, Token_inline);
 			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<Ast *> 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 */
 		/* fallthrough */
 	case Token_no_inline:
 	case Token_no_inline:
@@ -4484,6 +4502,8 @@ Ast *parse_stmt(AstFile *f) {
 		} else if (tag == "panic") {
 		} else if (tag == "panic") {
 			Ast *t = ast_basic_directive(f, hash_token, tag);
 			Ast *t = ast_basic_directive(f, hash_token, tag);
 			return ast_expr_stmt(f, parse_call_expr(f, t));
 			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") {
 		} else if (tag == "include") {
 			syntax_error(token, "#include is not a valid import declaration kind. Did you mean 'import'?");
 			syntax_error(token, "#include is not a valid import declaration kind. Did you mean 'import'?");
 			s = ast_bad_stmt(f, token, f->curr_token);
 			s = ast_bad_stmt(f, token, f->curr_token);