Browse Source

Improve core:odin/parser

gingerBill 4 years ago
parent
commit
57a17a708b
2 changed files with 108 additions and 20 deletions
  1. 106 18
      core/odin/parser/parser.odin
  2. 2 2
      src/parser.cpp

+ 106 - 18
core/odin/parser/parser.odin

@@ -44,8 +44,13 @@ Parser :: struct {
 	curr_proc: ^ast.Node,
 
 	error_count: int,
+
+	fix_count: int,
+	fix_prev_pos: tokenizer.Pos,
 }
 
+MAX_FIX_COUNT :: 10;
+
 Stmt_Allow_Flag :: enum {
 	In,
 	Label,
@@ -140,9 +145,7 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool {
 		p.line_comment     = nil;
 	}
 
-	if .Optional_Semicolons in p.flags {
-		p.tok.flags += {.Insert_Semicolon};
-	}
+	p.tok.flags += {.Insert_Semicolon};
 
 	p.file = file;
 	tokenizer.init(&p.tok, file.src, file.fullpath, p.err);
@@ -229,7 +232,7 @@ peek_token :: proc(p: ^Parser, lookahead := 0) -> (tok: tokenizer.Token) {
 	return;
 }
 skip_possible_newline :: proc(p: ^Parser) -> bool {
-	if .Insert_Semicolon not_in p.tok.flags {
+	if .Optional_Semicolons not_in p.flags {
 		return false;
 	}
 
@@ -242,7 +245,7 @@ skip_possible_newline :: proc(p: ^Parser) -> bool {
 }
 
 skip_possible_newline_for_literal :: proc(p: ^Parser) -> bool {
-	if .Insert_Semicolon not_in p.tok.flags {
+	if .Optional_Semicolons not_in p.flags {
 		return false;
 	}
 
@@ -411,6 +414,33 @@ is_blank_ident_node :: proc(node: ^ast.Node) -> bool {
 	return true;
 }
 
+fix_advance_to_next_stmt :: proc(p: ^Parser) {
+	for {
+		#partial switch t := p.curr_tok; t.kind {
+		case .EOF, .Semicolon:
+			return;
+
+		case .Package, .Foreign, .Import,
+		     .If, .For, .When, .Return, .Switch,
+		     .Defer, .Using,
+		     .Break, .Continue, .Fallthrough,
+		     .Hash:
+
+
+			if t.pos == p.fix_prev_pos && p.fix_count < MAX_FIX_COUNT {
+				p.fix_count += 1;
+				return;
+			}
+			if t.pos.offset < p.fix_prev_pos.offset {
+				p.fix_prev_pos = t.pos;
+				p.fix_count = 0;
+				return;
+			}
+		}
+		advance_token(p);
+	}
+}
+
 
 is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool {
 	if node == nil {
@@ -526,6 +556,7 @@ expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool {
 	}
 
 	error(p, prev.pos, "expected ';', got %s", tokenizer.token_to_string(p.curr_tok));
+	fix_advance_to_next_stmt(p);
 	return false;
 }
 
@@ -597,12 +628,16 @@ parse_when_stmt :: proc(p: ^Parser) -> ^ast.When_Stmt {
 	}
 	if allow_token(p, .Do) {
 		body = convert_stmt_to_body(p, parse_stmt(p));
+		if cond.pos.line != body.pos.line {
+			error(p, body.pos, "the body of a 'do' must be on the same line as when statement");
+		}
 	} else {
 		body = parse_block_stmt(p, true);
 	}
 
 	skip_possible_newline_for_literal(p);
-	if allow_token(p, .Else) {
+	if p.curr_tok.kind == .Else {
+		else_tok := expect_token(p, .Else);
 		#partial switch p.curr_tok.kind {
 		case .When:
 			else_stmt = parse_when_stmt(p);
@@ -611,6 +646,9 @@ parse_when_stmt :: proc(p: ^Parser) -> ^ast.When_Stmt {
 		case .Do:
 			expect_token(p, .Do);
 			else_stmt = convert_stmt_to_body(p, parse_stmt(p));
+			if else_tok.pos.line != else_stmt.pos.line {
+				error(p, else_stmt.pos, "the body of a 'do' must be on the same line as 'else'");
+			}
 		case:
 			error(p, p.curr_tok.pos, "expected when statement block statement");
 			else_stmt = ast.new(ast.Bad_Stmt, p.curr_tok.pos, end_pos(p.curr_tok));
@@ -673,6 +711,9 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt {
 	}
 	if allow_token(p, .Do) {
 		body = convert_stmt_to_body(p, parse_stmt(p));
+		if cond.pos.line != body.pos.line {
+			error(p, body.pos, "the body of a 'do' must be on the same line as the if condition");
+		}
 	} else {
 		body = parse_block_stmt(p, false);
 	}
@@ -680,7 +721,8 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt {
 	else_tok := p.curr_tok.pos;
 
 	skip_possible_newline_for_literal(p);
-	if allow_token(p, .Else) {
+	if p.curr_tok.kind == .Else {
+		else_tok := expect_token(p, .Else);
 		#partial switch p.curr_tok.kind {
 		case .If:
 			else_stmt = parse_if_stmt(p);
@@ -689,6 +731,9 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt {
 		case .Do:
 			expect_token(p, .Do);
 			else_stmt = convert_stmt_to_body(p, parse_stmt(p));
+			if else_tok.pos.line != else_stmt.pos.line {
+				error(p, body.pos, "the body of a 'do' must be on the same line as 'else'");
+			}
 		case:
 			error(p, p.curr_tok.pos, "expected if statement block statement");
 			else_stmt = ast.new(ast.Bad_Stmt, p.curr_tok.pos, end_pos(p.curr_tok));
@@ -750,6 +795,10 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 
 			if allow_token(p, .Do) {
 				body = convert_stmt_to_body(p, parse_stmt(p));
+				if tok.pos.line != body.pos.line {
+					error(p, body.pos, "the body of a 'do' must be on the same line as 'else'");
+				}
+
 			} else {
 				body = parse_body(p);
 			}
@@ -784,6 +833,9 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 
 	if allow_token(p, .Do) {
 		body = convert_stmt_to_body(p, parse_stmt(p));
+		if tok.pos.line != body.pos.line {
+			error(p, body.pos, "the body of a 'do' must be on the same line as the 'for' token");
+		}
 	} else {
 		body = parse_body(p);
 	}
@@ -1129,6 +1181,9 @@ parse_unrolled_for_loop :: proc(p: ^Parser, inline_tok: tokenizer.Token) -> ^ast
 
 	if allow_token(p, .Do) {
 		body = convert_stmt_to_body(p, parse_stmt(p));
+		if for_tok.pos.line != body.pos.line {
+			error(p, body.pos, "the body of a 'do' must be on the same line as the 'for' token");
+		}
 	} else {
 		body = parse_block_stmt(p, false);
 	}
@@ -1150,7 +1205,6 @@ parse_unrolled_for_loop :: proc(p: ^Parser, inline_tok: tokenizer.Token) -> ^ast
 
 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);
@@ -1158,15 +1212,15 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 		}
 		fallthrough;
 	// Operands
-	case .Context, // Also allows for 'context = '
+	case .No_Inline,
+	     .Context, // Also allows for 'context = '
 	     .Proc,
-	     .No_Inline,
-	     .Asm, // Inline assembly
 	     .Ident,
 	     .Integer, .Float, .Imag,
 	     .Rune, .String,
 	     .Open_Paren,
 	     .Pointer,
+	     .Asm, // Inline assembly
 	     // Unary Expressions
 	     .Add, .Sub, .Xor, .Not, .And:
 
@@ -1175,8 +1229,8 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 		return s;
 
 
-	case .Import:  return parse_import_decl(p);
 	case .Foreign: return parse_foreign_decl(p);
+	case .Import:  return parse_import_decl(p);
 	case .If:      return parse_if_stmt(p);
 	case .When:    return parse_when_stmt(p);
 	case .For:     return parse_for_stmt(p);
@@ -1309,6 +1363,12 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 			es := ast.new(ast.Expr_Stmt, ce.pos, ce.end);
 			es.expr = ce;
 			return es;
+
+		case "force_inline", "force_no_inline":
+			expr := parse_inlining_operand(p, true, tok);
+			es := ast.new(ast.Expr_Stmt, expr.pos, expr.end);
+			es.expr = expr;
+			return es;
 		case "unroll":
 			return parse_unrolled_for_loop(p, tag);
 		case "include":
@@ -1320,6 +1380,8 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 			te.op   = tok;
 			te.name = name;
 			te.stmt = stmt;
+
+			fix_advance_to_next_stmt(p);
 			return te;
 		}
 	case .Open_Brace:
@@ -1331,8 +1393,31 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
 		return s;
 	}
 
+
+	#partial switch p.curr_tok.kind {
+	case .Else:
+		token := expect_token(p, .Else);
+		error(p, token.pos, "'else' unattached to an 'if' statement");
+		#partial switch p.curr_tok.kind {
+		case .If:
+			return parse_if_stmt(p);
+		case .When:
+			return parse_when_stmt(p);
+		case .Open_Brace:
+			return parse_block_stmt(p, true);
+		case .Do:
+			expect_token(p, .Do);
+			return convert_stmt_to_body(p, parse_stmt(p));
+		case:
+			fix_advance_to_next_stmt(p);
+			return ast.new(ast.Bad_Stmt, token.pos, end_pos(p.curr_tok));
+		}
+	}
+
+
 	tok := advance_token(p);
 	error(p, tok.pos, "expected a statement, got %s", tokenizer.token_to_string(tok));
+	fix_advance_to_next_stmt(p);
 	s := ast.new(ast.Bad_Stmt, tok.pos, end_pos(tok));
 	return s;
 }
@@ -2232,9 +2317,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 		}
 
 		type := parse_proc_type(p, tok);
-		tags := parse_proc_tags(p);
-		type.tags = tags;
-
+		tags: ast.Proc_Tags;
 		where_token: tokenizer.Token;
 		where_clauses: []^ast.Expr;
 
@@ -2246,8 +2329,10 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 			p.expr_level = -1;
 			where_clauses = parse_rhs_expr_list(p);
 			p.expr_level = prev_level;
-			tags = parse_proc_tags(p);
 		}
+		tags = parse_proc_tags(p);
+		type.tags = tags;
+
 		if p.allow_type && p.expr_level < 0 {
 			if where_token.kind != .Invalid {
 				error(p, where_token.pos, "'where' clauses are not allowed on procedure types");
@@ -2273,6 +2358,9 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 			p.curr_proc = type;
 			body = convert_stmt_to_body(p, parse_stmt(p));
 			p.curr_proc = prev_proc;
+			if type.pos.line != body.pos.line {
+				error(p, body.pos, "the body of a 'do' must be on the same line as the signature");
+			}
 		} else {
 			return type;
 		}
@@ -2495,11 +2583,11 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
 			p.expr_level = where_prev_level;
 		}
 
-		variants: [dynamic]^ast.Expr;
 
 		skip_possible_newline_for_literal(p);
 		expect_token_after(p, .Open_Brace, "union");
 
+		variants: [dynamic]^ast.Expr;
 		for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF {
 			type := parse_type(p);
 			if _, ok := type.derived.(ast.Bad_Expr); !ok {
@@ -2811,8 +2899,8 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
 			return nil;
 		}
 		error(p, p.curr_tok.pos, "expected an operand");
+		fix_advance_to_next_stmt(p);
 		be := ast.new(ast.Bad_Expr, p.curr_tok.pos, end_pos(p.curr_tok));
-		advance_token(p);
 		operand = be;
 	}
 

+ 2 - 2
src/parser.cpp

@@ -2169,7 +2169,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
 			if (build_context.disallow_do) {
 				syntax_error(body, "'do' has been disallowed");
 			} else if (!ast_on_same_line(type, body)) {
-				syntax_error(body, "The body of a 'do' be on the same line as the signature");
+				syntax_error(body, "The body of a 'do' must be on the same line as the signature");
 			}
 
 			return ast_proc_lit(f, type, body, tags, where_token, where_clauses);
@@ -3840,7 +3840,7 @@ Ast *parse_if_stmt(AstFile *f) {
 			else_stmt = convert_stmt_to_body(f, parse_stmt(f));
 			if (build_context.disallow_do) {
 				syntax_error(else_stmt, "'do' has been disallowed");
-			} else if (!ast_on_same_line(else_stmt, else_stmt)) {
+			} else if (!ast_on_same_line(else_token, else_stmt)) {
 				syntax_error(else_stmt, "The body of a 'do' be on the same line as 'else'");
 			}
 		} break;