Commit 2bb2e61ee2

Andrew Kelley <superjoe30@gmail.com>
2016-01-29 02:58:28
parser: allow missing fn name and missing param names
now these problems are caught in analyzer this is for purpose of function type, see #14
1 parent ff02852
Changed files (4)
doc/langref.md
@@ -3,153 +3,153 @@
 ## Grammar
 
 ```
-Root : many(TopLevelDecl) "EOF"
+Root = many(TopLevelDecl) "EOF"
 
-TopLevelDecl : many(Directive) option(VisibleMod) (FnDef | ExternDecl | RootExportDecl | Import | ContainerDecl | GlobalVarDecl | ErrorValueDecl | CImportDecl)
+TopLevelDecl = many(Directive) option(VisibleMod) (FnDef | ExternDecl | RootExportDecl | Import | ContainerDecl | GlobalVarDecl | ErrorValueDecl | CImportDecl)
 
-CImportDecl : "c_import" Block
+CImportDecl = "c_import" Block
 
-ErrorValueDecl : "error" "Symbol" ";"
+ErrorValueDecl = "error" "Symbol" ";"
 
-GlobalVarDecl : VariableDeclaration ";"
+GlobalVarDecl = VariableDeclaration ";"
 
-VariableDeclaration : ("var" | "const") "Symbol" option(":" PrefixOpExpression) "=" Expression
+VariableDeclaration = ("var" | "const") "Symbol" option(":" PrefixOpExpression) "=" Expression
 
-ContainerDecl : ("struct" | "enum") "Symbol" "{" many(StructMember) "}"
+ContainerDecl = ("struct" | "enum") "Symbol" "{" many(StructMember) "}"
 
-StructMember: many(Directive) option(VisibleMod) (StructField | FnDef)
+StructMember = many(Directive) option(VisibleMod) (StructField | FnDef)
 
-StructField : "Symbol" option(":" Expression) ",")
+StructField = "Symbol" option(":" Expression) ",")
 
-Import : "import" "String" ";"
+Import = "import" "String" ";"
 
-RootExportDecl : "export" "Symbol" "String" ";"
+RootExportDecl = "export" "Symbol" "String" ";"
 
-ExternDecl : "extern" FnProto ";"
+ExternDecl = "extern" FnProto ";"
 
-FnProto : "fn" "Symbol" ParamDeclList option("->" PrefixOpExpression)
+FnProto = "fn" option("Symbol") ParamDeclList option("->" PrefixOpExpression)
 
-Directive : "#" "Symbol" "(" "String" ")"
+Directive = "#" "Symbol" "(" "String" ")"
 
-VisibleMod : "pub" | "export"
+VisibleMod = "pub" | "export"
 
-FnDef : FnProto Block
+FnDef = FnProto Block
 
-ParamDeclList : "(" list(ParamDecl, ",") ")"
+ParamDeclList = "(" list(ParamDecl, ",") ")"
 
-ParamDecl : option("noalias") "Symbol" ":" PrefixOpExpression | "..."
+ParamDecl = option("noalias") option("Symbol" ":") PrefixOpExpression | "..."
 
-Block : "{" list(option(Statement), ";") "}"
+Block = "{" list(option(Statement), ";") "}"
 
-Statement : Label | VariableDeclaration ";" | NonBlockExpression ";" | BlockExpression
+Statement = Label | VariableDeclaration ";" | NonBlockExpression ";" | BlockExpression
 
-Label: "Symbol" ":"
+Label = "Symbol" ":"
 
-Expression : BlockExpression | NonBlockExpression
+Expression = BlockExpression | NonBlockExpression
 
-NonBlockExpression : ReturnExpression | AssignmentExpression
+NonBlockExpression = ReturnExpression | AssignmentExpression
 
-AsmExpression : "asm" option("volatile") "(" "String" option(AsmOutput) ")"
+AsmExpression = "asm" option("volatile") "(" "String" option(AsmOutput) ")"
 
-AsmOutput : ":" list(AsmOutputItem, ",") option(AsmInput)
+AsmOutput = ":" list(AsmOutputItem, ",") option(AsmInput)
 
-AsmInput : ":" list(AsmInputItem, ",") option(AsmClobbers)
+AsmInput = ":" list(AsmInputItem, ",") option(AsmClobbers)
 
-AsmOutputItem : "[" "Symbol" "]" "String" "(" ("Symbol" | "->" PrefixOpExpression) ")"
+AsmOutputItem = "[" "Symbol" "]" "String" "(" ("Symbol" | "->" PrefixOpExpression) ")"
 
-AsmInputItem : "[" "Symbol" "]" "String" "(" Expression ")"
+AsmInputItem = "[" "Symbol" "]" "String" "(" Expression ")"
 
-AsmClobbers: ":" list("String", ",")
+AsmClobbers= ":" list("String", ",")
 
-UnwrapExpression : BoolOrExpression (UnwrapMaybe | UnwrapError) | BoolOrExpression
+UnwrapExpression = BoolOrExpression (UnwrapMaybe | UnwrapError) | BoolOrExpression
 
-UnwrapMaybe : "??" BoolOrExpression
+UnwrapMaybe = "??" BoolOrExpression
 
-UnwrapError : "%%" option("|" "Symbol" "|") BoolOrExpression
+UnwrapError = "%%" option("|" "Symbol" "|") BoolOrExpression
 
-AssignmentExpression : UnwrapExpression AssignmentOperator UnwrapExpression | UnwrapExpression
+AssignmentExpression = UnwrapExpression AssignmentOperator UnwrapExpression | UnwrapExpression
 
-AssignmentOperator : "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&=" | "^=" | "|=" | "&&=" | "||="
+AssignmentOperator = "=" | "*=" | "/=" | "%=" | "+=" | "-=" | "<<=" | ">>=" | "&=" | "^=" | "|=" | "&&=" | "||="
 
-BlockExpression : IfExpression | Block | WhileExpression | ForExpression | SwitchExpression
+BlockExpression = IfExpression | Block | WhileExpression | ForExpression | SwitchExpression
 
-SwitchExpression : "switch" "(" Expression ")" "{" many(SwitchProng) "}"
+SwitchExpression = "switch" "(" Expression ")" "{" many(SwitchProng) "}"
 
-SwitchProng : (list(SwitchItem, ",") | "else") option(":" "(" "Symbol" ")") "=>" Expression ","
+SwitchProng = (list(SwitchItem, ",") | "else") option(":" "(" "Symbol" ")") "=>" Expression ","
 
-SwitchItem : Expression | (Expression "..." Expression)
+SwitchItem = Expression | (Expression "..." Expression)
 
-WhileExpression : "while" "(" Expression ")" Expression
+WhileExpression = "while" "(" Expression ")" Expression
 
-ForExpression : "for" "(" "Symbol" "," Expression option("," "Symbol") ")" Expression
+ForExpression = "for" "(" "Symbol" "," Expression option("," "Symbol") ")" Expression
 
-BoolOrExpression : BoolAndExpression "||" BoolOrExpression | BoolAndExpression
+BoolOrExpression = BoolAndExpression "||" BoolOrExpression | BoolAndExpression
 
-ReturnExpression : option("%" | "?") "return" option(Expression)
+ReturnExpression = option("%" | "?") "return" option(Expression)
 
-IfExpression : IfVarExpression | IfBoolExpression
+IfExpression = IfVarExpression | IfBoolExpression
 
-IfBoolExpression : "if" "(" Expression ")" Expression option(Else)
+IfBoolExpression = "if" "(" Expression ")" Expression option(Else)
 
-IfVarExpression : "if" "(" ("const" | "var") "Symbol" option(":" PrefixOpExpression) "?=" Expression ")" Expression Option(Else)
+IfVarExpression = "if" "(" ("const" | "var") "Symbol" option(":" PrefixOpExpression) "?=" Expression ")" Expression Option(Else)
 
-Else : "else" Expression
+Else = "else" Expression
 
-BoolAndExpression : ComparisonExpression "&&" BoolAndExpression | ComparisonExpression
+BoolAndExpression = ComparisonExpression "&&" BoolAndExpression | ComparisonExpression
 
-ComparisonExpression : BinaryOrExpression ComparisonOperator BinaryOrExpression | BinaryOrExpression
+ComparisonExpression = BinaryOrExpression ComparisonOperator BinaryOrExpression | BinaryOrExpression
 
-ComparisonOperator : "==" | "!=" | "<" | ">" | "<=" | ">="
+ComparisonOperator = "==" | "!=" | "<" | ">" | "<=" | ">="
 
-BinaryOrExpression : BinaryXorExpression "|" BinaryOrExpression | BinaryXorExpression
+BinaryOrExpression = BinaryXorExpression "|" BinaryOrExpression | BinaryXorExpression
 
-BinaryXorExpression : BinaryAndExpression "^" BinaryXorExpression | BinaryAndExpression
+BinaryXorExpression = BinaryAndExpression "^" BinaryXorExpression | BinaryAndExpression
 
-BinaryAndExpression : BitShiftExpression "&" BinaryAndExpression | BitShiftExpression
+BinaryAndExpression = BitShiftExpression "&" BinaryAndExpression | BitShiftExpression
 
-BitShiftExpression : AdditionExpression BitShiftOperator BitShiftExpression | AdditionExpression
+BitShiftExpression = AdditionExpression BitShiftOperator BitShiftExpression | AdditionExpression
 
-BitShiftOperator : "<<" | ">>"
+BitShiftOperator = "<<" | ">>"
 
-AdditionExpression : MultiplyExpression AdditionOperator AdditionExpression | MultiplyExpression
+AdditionExpression = MultiplyExpression AdditionOperator AdditionExpression | MultiplyExpression
 
-AdditionOperator : "+" | "-" | "++"
+AdditionOperator = "+" | "-" | "++"
 
-MultiplyExpression : CurlySuffixExpression MultiplyOperator MultiplyExpression | CurlySuffixExpression
+MultiplyExpression = CurlySuffixExpression MultiplyOperator MultiplyExpression | CurlySuffixExpression
 
-CurlySuffixExpression : PrefixOpExpression option(ContainerInitExpression)
+CurlySuffixExpression = PrefixOpExpression option(ContainerInitExpression)
 
-MultiplyOperator : "*" | "/" | "%"
+MultiplyOperator = "*" | "/" | "%"
 
-PrefixOpExpression : PrefixOp PrefixOpExpression | SuffixOpExpression
+PrefixOpExpression = PrefixOp PrefixOpExpression | SuffixOpExpression
 
-SuffixOpExpression : PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
+SuffixOpExpression = PrimaryExpression option(FnCallExpression | ArrayAccessExpression | FieldAccessExpression | SliceExpression)
 
-FieldAccessExpression : "." "Symbol"
+FieldAccessExpression = "." "Symbol"
 
-FnCallExpression : "(" list(Expression, ",") ")"
+FnCallExpression = "(" list(Expression, ",") ")"
 
-ArrayAccessExpression : "[" Expression "]"
+ArrayAccessExpression = "[" Expression "]"
 
-SliceExpression : "[" Expression "..." option(Expression) "]" option("const")
+SliceExpression = "[" Expression "..." option(Expression) "]" option("const")
 
-ContainerInitExpression : "{" ContainerInitBody "}"
+ContainerInitExpression = "{" ContainerInitBody "}"
 
-ContainerInitBody : list(StructLiteralField, ",") | list(Expression, ",")
+ContainerInitBody = list(StructLiteralField, ",") | list(Expression, ",")
 
-StructLiteralField : "." "Symbol" "=" Expression
+StructLiteralField = "." "Symbol" "=" Expression
 
-PrefixOp : "!" | "-" | "~" | "*" | ("&" option("const")) | "?" | "%" | "%%"
+PrefixOp = "!" | "-" | "~" | "*" | ("&" option("const")) | "?" | "%" | "%%"
 
-PrimaryExpression : "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | AsmExpression | ("error" "." "Symbol")
+PrimaryExpression = "Number" | "String" | "CharLiteral" | KeywordLiteral | GroupedExpression | GotoExpression | BlockExpression | "Symbol" | ("@" "Symbol" FnCallExpression) | ArrayType | FnProto | AsmExpression | ("error" "." "Symbol")
 
-ArrayType : "[" option(Expression) "]" option("const") PrefixOpExpression
+ArrayType = "[" option(Expression) "]" option("const") PrefixOpExpression
 
-GotoExpression: "goto" "Symbol"
+GotoExpression = "goto" "Symbol"
 
-GroupedExpression : "(" Expression ")"
+GroupedExpression = "(" Expression ")"
 
-KeywordLiteral : "true" | "false" | "null" | "break" | "continue" | "undefined" | "error"
+KeywordLiteral = "true" | "false" | "null" | "break" | "continue" | "undefined" | "error"
 ```
 
 ## Operator Precedence
@@ -204,9 +204,11 @@ c_ulonglong     unsigned long long  for ABI compatibility with C
 ```
 
 ### Boolean Type
+
 The boolean type has the name `bool` and represents either true or false.
 
 ### Function Type
+
 TODO
 
 ### Fixed-Size Array Type
@@ -222,6 +224,7 @@ A slice can be obtained with the slicing syntax: `array[start...end]`
 Example: `"aoeu"[0...2]` has type `[]u8`.
 
 ### Struct Type
+
 TODO
 
 ### Enum Type
@@ -296,34 +299,44 @@ Hex floating point  TODO         TODO
 ```
 
 ### Identifiers
+
 TODO
 
 ### Declarations
+
 Declarations have type `void`.
 
 #### Function Declarations
+
 TODO
 
 #### Variable Declarations
+
 TODO
 
 #### Struct Declarations
+
 TODO
 
 #### Enum Declarations
+
 TODO
 
 
 ## Built-in Functions
+
 Built-in functions are prefixed with `@`.
 
 ### Typeof
+
 TODO
 
 ### Sizeof
+
 TODO
 
 ### Overflow Arithmetic
+
 Overflow arithmetic functions have defined behavior on overflow or underflow. TODO what is that behaviour?
 
 The functions take an integer (TODO float?) type, two variables of the specified type, and a pointer to a variable of the specified type where the result is stored. The functions return a boolean value: true of overflow/underflow occurred, false otherwise.
@@ -336,10 +349,13 @@ bool mul_with_overflow(type, a: type, b: type, x: &type)  *x = a * b
 ```
 
 ### Memory Operations
+
 TODO memset and memcpy
 
 ### Value Count
+
 TODO
 
 ### Max and Min Value
+
 TODO
src/analyze.cpp
@@ -457,6 +457,10 @@ static void resolve_function_proto(CodeGen *g, AstNode *node, FnTableEntry *fn_t
     assert(node->type == NodeTypeFnProto);
     AstNodeFnProto *fn_proto = &node->data.fn_proto;
 
+    if (fn_proto->skip) {
+        return;
+    }
+
     TypeTableEntry *fn_type = new_type_table_entry(TypeTableEntryIdFn);
     fn_table_entry->type_entry = fn_type;
     fn_type->data.fn.calling_convention = fn_table_entry->internal_linkage ? LLVMFastCallConv : LLVMCCallConv;
@@ -932,6 +936,9 @@ static void resolve_struct_type(CodeGen *g, ImportTableEntry *import, TypeTableE
 static void preview_fn_proto(CodeGen *g, ImportTableEntry *import,
         AstNode *proto_node)
 {
+    if (proto_node->data.fn_proto.skip) {
+        return;
+    }
     AstNode *fn_def_node = proto_node->data.fn_proto.fn_def_node;
     AstNode *struct_node = proto_node->data.fn_proto.struct_node;
     bool is_extern = proto_node->data.fn_proto.is_extern;
@@ -4307,6 +4314,10 @@ static void analyze_top_level_fn_def(CodeGen *g, ImportTableEntry *import, AstNo
                 buf_sprintf("byvalue struct parameters not yet supported on exported functions"));
         }
 
+        if (buf_len(&param_decl->name) == 0) {
+            add_node_error(g, param_decl_node, buf_sprintf("missing parameter name"));
+        }
+
         VariableTableEntry *var = add_local_var(g, param_decl_node, context, &param_decl->name, type, true);
         var->src_arg_index = i;
         param_decl_node->data.param_decl.variable = var;
@@ -4682,6 +4693,15 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
             }
         case NodeTypeFnProto:
             {
+                // if the name is missing, we immediately announce an error
+                Buf *name = &node->data.fn_proto.name;
+                if (buf_len(name) == 0) {
+                    node->data.fn_proto.skip = true;
+                    add_node_error(g, node, buf_sprintf("missing function name"));
+                    break;
+                }
+
+
                 // determine which other top level declarations this function prototype depends on.
                 TopLevelDecl *decl_node = &node->data.fn_proto.top_level_decl;
                 decl_node->deps.init(1);
@@ -4692,7 +4712,6 @@ static void detect_top_level_decl_deps(CodeGen *g, ImportTableEntry *import, Ast
                 }
                 collect_expr_decl_deps(g, import, node->data.fn_proto.return_type, decl_node);
 
-                Buf *name = &node->data.fn_proto.name;
                 decl_node->name = name;
                 decl_node->import = import;
                 if (decl_node->deps.size() > 0) {
src/parser.cpp
@@ -569,35 +569,33 @@ static void ast_parse_directives(ParseContext *pc, int *token_index,
 }
 
 /*
-ParamDecl : option("noalias") "Symbol" ":" PrefixOpExpression | "..."
+ParamDecl = option("noalias") option("Symbol" ":") PrefixOpExpression | "..."
 */
 static AstNode *ast_parse_param_decl(ParseContext *pc, int *token_index) {
-    Token *first_token = &pc->tokens->at(*token_index);
+    Token *token = &pc->tokens->at(*token_index);
 
-    if (first_token->id == TokenIdEllipsis) {
+    if (token->id == TokenIdEllipsis) {
         *token_index += 1;
         return nullptr;
     }
 
-    AstNode *node = ast_create_node(pc, NodeTypeParamDecl, first_token);
-    Token *name_token;
+    AstNode *node = ast_create_node(pc, NodeTypeParamDecl, token);
 
-    if (first_token->id == TokenIdKeywordNoAlias) {
+    if (token->id == TokenIdKeywordNoAlias) {
         node->data.param_decl.is_noalias = true;
         *token_index += 1;
-        name_token = ast_eat_token(pc, token_index, TokenIdSymbol);
-    } else if (first_token->id == TokenIdSymbol) {
-        name_token = first_token;
-        *token_index += 1;
-    } else {
-        ast_invalid_token_error(pc, first_token);
+        token = &pc->tokens->at(*token_index);
     }
 
-    ast_buf_from_token(pc, name_token, &node->data.param_decl.name);
+    buf_resize(&node->data.param_decl.name, 0);
 
-    Token *colon = &pc->tokens->at(*token_index);
-    *token_index += 1;
-    ast_expect_token(pc, colon, TokenIdColon);
+    if (token->id == TokenIdSymbol) {
+        Token *next_token = &pc->tokens->at(*token_index + 1);
+        if (next_token->id == TokenIdColon) {
+            ast_buf_from_token(pc, token, &node->data.param_decl.name);
+            *token_index += 2;
+        }
+    }
 
     node->data.param_decl.type = ast_parse_prefix_op_expr(pc, token_index, true);
 
@@ -2179,7 +2177,7 @@ static AstNode *ast_parse_block(ParseContext *pc, int *token_index, bool mandato
 }
 
 /*
-FnProto : "fn" "Symbol" ParamDeclList option("->" PrefixOpExpression)
+FnProto : "fn" option("Symbol") ParamDeclList option("->" PrefixOpExpression)
 */
 static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mandatory,
         ZigList<AstNode*> *directives, VisibMod visib_mod)
@@ -2200,11 +2198,12 @@ static AstNode *ast_parse_fn_proto(ParseContext *pc, int *token_index, bool mand
     node->data.fn_proto.directives = directives;
 
     Token *fn_name = &pc->tokens->at(*token_index);
-    *token_index += 1;
-    ast_expect_token(pc, fn_name, TokenIdSymbol);
-
-    ast_buf_from_token(pc, fn_name, &node->data.fn_proto.name);
-
+    if (fn_name->id == TokenIdSymbol) {
+        *token_index += 1;
+        ast_buf_from_token(pc, fn_name, &node->data.fn_proto.name);
+    } else {
+        buf_resize(&node->data.fn_proto.name, 0);
+    }
 
     ast_parse_param_decl_list(pc, token_index, &node->data.fn_proto.params, &node->data.fn_proto.is_var_args);
 
test/run_tests.cpp
@@ -1859,6 +1859,13 @@ fn f(foo: Foo, index: i32) {
     const result = members[index]();
 }
     )SOURCE", 1, ".tmp_source.zig:21:34: error: expected 1 arguments, got 0");
+
+    add_compile_fail_case("missing function name and param name", R"SOURCE(
+fn () {}
+fn f(i32) {}
+    )SOURCE", 2,
+            ".tmp_source.zig:2:1: error: missing function name",
+            ".tmp_source.zig:3:6: error: missing parameter name");
 }
 
 //////////////////////////////////////////////////////////////////////////////