Browse Source

Add `-strict-style` flag: Enforces code style stricter whilst parsing, requiring such things as trailing commas

gingerBill 4 years ago
parent
commit
b5c0c68615
3 changed files with 90 additions and 11 deletions
  1. 1 0
      src/build_settings.cpp
  2. 16 0
      src/main.cpp
  3. 73 11
      src/parser.cpp

+ 1 - 0
src/build_settings.cpp

@@ -196,6 +196,7 @@ struct BuildContext {
 	bool   keep_object_files;
 	bool   disallow_do;
 	bool   insert_semicolon;
+	bool   strict_style;
 
 	bool   ignore_warnings;
 	bool   warnings_as_errors;

+ 16 - 0
src/main.cpp

@@ -602,6 +602,7 @@ enum BuildFlagKind {
 	BuildFlag_DisallowDo,
 	BuildFlag_DefaultToNilAllocator,
 	BuildFlag_InsertSemicolon,
+	BuildFlag_StrictStyle,
 
 	BuildFlag_Compact,
 	BuildFlag_GlobalDefinitions,
@@ -716,6 +717,7 @@ bool parse_build_flags(Array<String> args) {
 	add_flag(&build_flags, BuildFlag_DisallowDo,            str_lit("disallow-do"),              BuildFlagParam_None, Command__does_check);
 	add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None, Command__does_check);
 	add_flag(&build_flags, BuildFlag_InsertSemicolon,       str_lit("insert-semicolon"),         BuildFlagParam_None, Command__does_check);
+	add_flag(&build_flags, BuildFlag_StrictStyle,           str_lit("strict-style"),             BuildFlagParam_None, Command__does_check);
 	add_flag(&build_flags, BuildFlag_Compact,           str_lit("compact"),            BuildFlagParam_None, Command_query);
 	add_flag(&build_flags, BuildFlag_GlobalDefinitions, str_lit("global-definitions"), BuildFlagParam_None, Command_query);
 	add_flag(&build_flags, BuildFlag_GoToDefinitions,   str_lit("go-to-definitions"),  BuildFlagParam_None, Command_query);
@@ -1183,6 +1185,12 @@ bool parse_build_flags(Array<String> args) {
 							build_context.insert_semicolon = true;
 							break;
 
+						case BuildFlag_StrictStyle:
+							build_context.insert_semicolon = true;
+							build_context.strict_style = true;
+							break;
+
+
 						case BuildFlag_Compact:
 							if (!build_context.query_data_set_settings.ok) {
 								gb_printf_err("Invalid use of -compact flag, only allowed with 'odin query'\n");
@@ -1733,6 +1741,14 @@ void print_show_help(String const arg0, String const &command) {
 		print_usage_line(2, "Sets the default allocator to be the nil_allocator, an allocator which does nothing");
 		print_usage_line(0, "");
 
+		print_usage_line(1, "-insert-semicolon");
+		print_usage_line(2, "Inserts semicolons on newlines during tokenization using a basic rule");
+		print_usage_line(0, "");
+
+		print_usage_line(1, "-strict-style");
+		print_usage_line(2, "Enforces code style stricter whilst parsing, requiring such things as trailing commas");
+		print_usage_line(0, "");
+
 		print_usage_line(1, "-ignore-warnings");
 		print_usage_line(2, "Ignores warning messages");
 		print_usage_line(0, "");

+ 73 - 11
src/parser.cpp

@@ -106,6 +106,16 @@ Token ast_token(Ast *node) {
 	return empty_token;
 }
 
+Token token_end_of_line(AstFile *f, Token tok) {
+	u8 const *start = f->tokenizer.start + tok.pos.offset;
+	u8 const *s = start;
+	while (*s && *s != '\n' && s < f->tokenizer.end) {
+		s += 1;
+	}
+	tok.pos.column += cast(i32)(s - start) - 1;
+	return tok;
+}
+
 
 isize ast_node_size(AstKind kind) {
 	return align_formula_isize(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind], gb_align_of(void *));
@@ -1379,6 +1389,17 @@ bool allow_token(AstFile *f, TokenKind kind) {
 	return false;
 }
 
+Token expect_closing_brace_of_field_list(AstFile *f) {
+	Token token = f->curr_token;
+	if (allow_token(f, Token_CloseBrace)) {
+		return token;
+	}
+	if (allow_token(f, Token_Semicolon)) {
+		String p = token_to_string(token);
+		syntax_error(token_end_of_line(f, f->prev_token), "Expected a comma, got a %.*s", LIT(p));
+	}
+	return expect_token(f, Token_CloseBrace);
+}
 
 bool is_blank_ident(String str) {
 	if (str.len == 1) {
@@ -1451,7 +1472,9 @@ Token expect_closing(AstFile *f, TokenKind kind, String context) {
 	if (f->curr_token.kind != kind &&
 	    f->curr_token.kind == Token_Semicolon &&
 	    f->curr_token.string == "\n") {
-		syntax_error(f->curr_token, "Missing ',' before newline in %.*s", LIT(context));
+		Token tok = f->prev_token;
+		tok.pos.column += cast(i32)tok.string.len;
+		syntax_error(tok, "Missing ',' before newline in %.*s", LIT(context));
 		advance_token(f);
 	}
 	return expect_token(f, kind);
@@ -1512,16 +1535,42 @@ bool is_semicolon_optional_for_node(AstFile *f, Ast *s) {
 	return false;
 }
 
+void expect_semicolon_newline_error(AstFile *f, Token const &token, Ast *s) {
+	if (build_context.strict_style && false) {
+		if (f->curr_proc != nullptr && token.string == "\n") {
+			switch (token.kind) {
+			case Token_CloseBrace:
+			case Token_CloseParen:
+			case Token_else:
+			case Token_EOF:
+				return;
+			}
+			if (s != nullptr) {
+				if (is_semicolon_optional_for_node(f, s)) {
+					return;
+				}
+			}
+
+			Token tok = token;
+			tok.pos.column -= 1;
+			syntax_error(tok, "Expected ';', got newline");
+		}
+	}
+}
+
 void expect_semicolon(AstFile *f, Ast *s) {
+	Token prev_token = {};
+
 	if (allow_token(f, Token_Semicolon)) {
+		expect_semicolon_newline_error(f, f->prev_token, s);
 		return;
 	}
-	Token prev_token = f->prev_token;
+	prev_token = f->prev_token;
 	if (prev_token.kind == Token_Semicolon) {
+		expect_semicolon_newline_error(f, f->prev_token, s);
 		return;
 	}
 
-
 	if (s != nullptr) {
 		bool insert_semi = (f->tokenizer.flags & TokenizerFlag_InsertSemicolon) != 0;
 		if (insert_semi) {
@@ -2241,9 +2290,9 @@ Ast *parse_operand(AstFile *f, bool lhs) {
 
 		Token open = expect_token_after(f, Token_OpenBrace, "struct");
 
-		isize    name_count = 0;
+		isize name_count = 0;
 		Ast *fields = parse_struct_field_list(f, &name_count);
-		Token    close  = expect_token(f, Token_CloseBrace);
+		Token close = expect_closing_brace_of_field_list(f);
 
 		Slice<Ast *> decls = {};
 		if (fields != nullptr) {
@@ -2328,7 +2377,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
 			}
 		}
 
-		Token close = expect_token(f, Token_CloseBrace);
+		Token close = expect_closing_brace_of_field_list(f);
 
 		return ast_union_type(f, token, variants, polymorphic_params, align, no_nil, maybe, where_token, where_clauses);
 	} break;
@@ -2342,7 +2391,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
 		Token open = expect_token(f, Token_OpenBrace);
 
 		Array<Ast *> values = parse_element_list(f);
-		Token close = expect_token(f, Token_CloseBrace);
+		Token close = expect_closing_brace_of_field_list(f);
 
 		return ast_enum_type(f, token, base_type, values);
 	} break;
@@ -2435,7 +2484,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
 		expect_token(f, Token_Comma);
 		Ast *constraints_string = parse_expr(f, false);
 		allow_token(f, Token_Comma);
-		Token close = expect_token(f, Token_CloseBrace);
+		Token close = expect_closing_brace_of_field_list(f);
 
 		return ast_inline_asm_expr(f, token, open, close, param_types, return_type, asm_string, constraints_string, has_side_effects, is_align_stack, dialect);
 	}
@@ -3370,7 +3419,7 @@ bool parse_expect_field_separator(AstFile *f, Ast *param) {
 	}
 	if (token.kind == Token_Semicolon) {
 		String p = token_to_string(token);
-		syntax_error(f->curr_token, "Expected a comma, got a %.*s", LIT(p));
+		syntax_error(token_end_of_line(f, f->prev_token), "Expected a comma, got a %.*s", LIT(p));
 		advance_token(f);
 		return true;
 	}
@@ -3648,6 +3697,18 @@ Ast *parse_body(AstFile *f) {
 	return ast_block_stmt(f, stmts, open, close);
 }
 
+bool parse_control_statement_semicolon_separator(AstFile *f) {
+	Token tok = peek_token(f);
+	if (tok.kind != Token_OpenBrace) {
+		return allow_token(f, Token_Semicolon);
+	}
+	if (f->curr_token.string == ";") {
+		return allow_token(f, Token_Semicolon);
+	}
+	return false;
+}
+
+
 Ast *parse_if_stmt(AstFile *f) {
 	if (f->curr_proc == nullptr) {
 		syntax_error(f->curr_token, "You cannot use an if statement in the file scope");
@@ -3669,7 +3730,7 @@ Ast *parse_if_stmt(AstFile *f) {
 		cond = parse_expr(f, false);
 	} else {
 		init = parse_simple_stmt(f, StmtAllowFlag_None);
-		if (allow_token(f, Token_Semicolon)) {
+		if (parse_control_statement_semicolon_separator(f)) {
 			cond = parse_expr(f, false);
 		} else {
 			cond = convert_stmt_to_expr(f, init, str_lit("boolean expression"));
@@ -3977,7 +4038,7 @@ Ast *parse_switch_stmt(AstFile *f) {
 			tag = parse_simple_stmt(f, StmtAllowFlag_In);
 			if (tag->kind == Ast_AssignStmt && tag->AssignStmt.op.kind == Token_in) {
 				is_type_switch = true;
-			} else if (allow_token(f, Token_Semicolon)) {
+			} else if (parse_control_statement_semicolon_separator(f)) {
 				init = tag;
 				tag = nullptr;
 				if (f->curr_token.kind != Token_OpenBrace) {
@@ -3986,6 +4047,7 @@ Ast *parse_switch_stmt(AstFile *f) {
 			}
 		}
 	}
+	skip_possible_newline(f);
 	open = expect_token(f, Token_OpenBrace);
 
 	while (f->curr_token.kind == Token_case) {