Commit 8e2c441b2e

Andrew Kelley <andrew@ziglang.org>
2019-10-06 22:39:27
stage1 parser supports doc comments
1 parent 86171af
doc/langref.html.in
@@ -10097,7 +10097,7 @@ TopLevelComptime &lt;- KEYWORD_comptime BlockExpr
 TopLevelDecl
     &lt;- (KEYWORD_export / KEYWORD_extern STRINGLITERAL? / KEYWORD_inline)? FnProto (SEMICOLON / Block)
      / (KEYWORD_export / KEYWORD_extern STRINGLITERAL?)? KEYWORD_threadlocal? VarDecl
-     / KEYWORD_use Expr SEMICOLON
+     / KEYWORD_usingnamespace Expr SEMICOLON
 
 FnProto &lt;- FnCC? KEYWORD_fn IDENTIFIER? LPAREN ParamDeclList RPAREN ByteAlign? LinkSection? EXCLAMATIONMARK? (KEYWORD_var / TypeExpr)
 
src/all_types.hpp
@@ -589,6 +589,7 @@ enum NodeType {
     NodeTypeIfErrorExpr,
     NodeTypeIfOptional,
     NodeTypeErrorSetDecl,
+    NodeTypeErrorSetField,
     NodeTypeResume,
     NodeTypeAwaitExpr,
     NodeTypeSuspend,
@@ -612,16 +613,10 @@ enum FnInline {
 };
 
 struct AstNodeFnProto {
-    VisibMod visib_mod;
     Buf *name;
     ZigList<AstNode *> params;
     AstNode *return_type;
     Token *return_var_token;
-    bool is_var_args;
-    bool is_extern;
-    bool is_export;
-    FnInline fn_inline;
-    CallingConvention cc;
     AstNode *fn_def_node;
     // populated if this is an extern declaration
     Buf *lib_name;
@@ -629,8 +624,16 @@ struct AstNodeFnProto {
     AstNode *align_expr;
     // populated if the "section(S)" is present
     AstNode *section_expr;
+    Buf doc_comments;
 
+    FnInline fn_inline;
+    CallingConvention cc;
+
+    VisibMod visib_mod;
     bool auto_err_set;
+    bool is_var_args;
+    bool is_extern;
+    bool is_export;
 };
 
 struct AstNodeFnDef {
@@ -642,6 +645,7 @@ struct AstNodeParamDecl {
     Buf *name;
     AstNode *type;
     Token *var_token;
+    Buf doc_comments;
     bool is_noalias;
     bool is_comptime;
     bool is_var_args;
@@ -684,6 +688,7 @@ struct AstNodeVariableDeclaration {
     // populated if the "section(S)" is present
     AstNode *section_expr;
     Token *threadlocal_tok;
+    Buf doc_comments;
 
     VisibMod visib_mod;
     bool is_const;
@@ -957,25 +962,35 @@ enum ContainerLayout {
 };
 
 struct AstNodeContainerDecl {
-    ContainerKind kind;
+    AstNode *init_arg_expr; // enum(T), struct(endianness), or union(T), or union(enum(T))
     ZigList<AstNode *> fields;
     ZigList<AstNode *> decls;
+
+    ContainerKind kind;
     ContainerLayout layout;
-    AstNode *init_arg_expr; // enum(T), struct(endianness), or union(T), or union(enum(T))
+
     bool auto_enum, is_root; // union(enum)
 };
 
+struct AstNodeErrorSetField {
+    Buf doc_comments;
+    AstNode *field_name;
+};
+
 struct AstNodeErrorSetDecl {
+    // Each AstNode could be AstNodeErrorSetField or just AstNodeSymbolExpr to save memory
     ZigList<AstNode *> decls;
 };
 
 struct AstNodeStructField {
-    VisibMod visib_mod;
     Buf *name;
     AstNode *type;
     AstNode *value;
     // populated if the "align(A)" is present
     AstNode *align_expr;
+    Buf doc_comments;
+
+    VisibMod visib_mod;
 };
 
 struct AstNodeStringLiteral {
@@ -1126,6 +1141,7 @@ struct AstNode {
         AstNodeInferredArrayType inferred_array_type;
         AstNodeErrorType error_type;
         AstNodeErrorSetDecl err_set_decl;
+        AstNodeErrorSetField err_set_field;
         AstNodeResumeExpr resume_expr;
         AstNodeAwaitExpr await_expr;
         AstNodeSuspend suspend;
src/analyze.cpp
@@ -3572,6 +3572,7 @@ void scan_decls(CodeGen *g, ScopeDecls *decls_scope, AstNode *node) {
         case NodeTypeSuspend:
         case NodeTypeEnumLiteral:
         case NodeTypeAnyFrameType:
+        case NodeTypeErrorSetField:
             zig_unreachable();
     }
 }
src/ast_render.cpp
@@ -266,6 +266,8 @@ static const char *node_type_str(NodeType node_type) {
             return "AnyFrameType";
         case NodeTypeEnumLiteral:
             return "EnumLiteral";
+        case NodeTypeErrorSetField:
+            return "ErrorSetField";
     }
     zig_unreachable();
 }
@@ -1177,6 +1179,7 @@ static void render_node_extra(AstRender *ar, AstNode *node, bool grouped) {
         case NodeTypeTestDecl:
         case NodeTypeStructField:
         case NodeTypeUsingNamespace:
+        case NodeTypeErrorSetField:
             zig_panic("TODO more ast rendering");
     }
 }
src/ir.cpp
@@ -7945,7 +7945,15 @@ static IrInstruction *ir_gen_err_set_decl(IrBuilder *irb, Scope *parent_scope, A
     ErrorTableEntry **errors = allocate<ErrorTableEntry *>(irb->codegen->errors_by_index.length + err_count);
 
     for (uint32_t i = 0; i < err_count; i += 1) {
-        AstNode *symbol_node = node->data.err_set_decl.decls.at(i);
+        AstNode *field_node = node->data.err_set_decl.decls.at(i);
+        AstNode *symbol_node;
+        if (field_node->type == NodeTypeSymbol) {
+            symbol_node = field_node;
+        } else if (field_node->type == NodeTypeErrorSetField) {
+            symbol_node = field_node->data.err_set_field.field_name;
+        } else {
+            zig_unreachable();
+        }
         assert(symbol_node->type == NodeTypeSymbol);
         Buf *err_name = symbol_node->data.symbol_expr.symbol;
         ErrorTableEntry *err = allocate<ErrorTableEntry>(1);
@@ -8116,6 +8124,7 @@ static IrInstruction *ir_gen_node_raw(IrBuilder *irb, AstNode *node, Scope *scop
         case NodeTypeSwitchProng:
         case NodeTypeSwitchRange:
         case NodeTypeStructField:
+        case NodeTypeErrorSetField:
         case NodeTypeFnDef:
         case NodeTypeTestDecl:
             zig_unreachable();
src/parser.cpp
@@ -37,7 +37,7 @@ static AstNode *ast_parse_root(ParseContext *pc);
 static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc);
 static AstNode *ast_parse_test_decl(ParseContext *pc);
 static AstNode *ast_parse_top_level_comptime(ParseContext *pc);
-static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod);
+static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, Buf *doc_comments);
 static AstNode *ast_parse_fn_proto(ParseContext *pc);
 static AstNode *ast_parse_var_decl(ParseContext *pc);
 static AstNode *ast_parse_container_field(ParseContext *pc);
@@ -497,6 +497,19 @@ static AstNode *ast_parse_root(ParseContext *pc) {
     return node;
 }
 
+static Token *ast_parse_doc_comments(ParseContext *pc, Buf *buf) {
+    Token *doc_token = nullptr;
+    while ((doc_token = eat_token_if(pc, TokenIdDocComment))) {
+        if (buf->list.length == 0) {
+            buf_resize(buf, 0);
+        }
+        // chops off '///' and '\n'
+        buf_append_mem(buf, buf_ptr(pc->buf) + doc_token->start_pos + 3,
+                doc_token->end_pos - doc_token->start_pos - 4);
+    }
+    return doc_token;
+}
+
 // ContainerMembers
 //     <- TestDecl ContainerMembers
 //      / TopLevelComptime ContainerMembers
@@ -519,10 +532,13 @@ static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) {
             continue;
         }
 
+        Buf doc_comment_buf = BUF_INIT;
+        ast_parse_doc_comments(pc, &doc_comment_buf);
+
         Token *visib_token = eat_token_if(pc, TokenIdKeywordPub);
         VisibMod visib_mod = visib_token != nullptr ? VisibModPub : VisibModPrivate;
 
-        AstNode *top_level_decl = ast_parse_top_level_decl(pc, visib_mod);
+        AstNode *top_level_decl = ast_parse_top_level_decl(pc, visib_mod, &doc_comment_buf);
         if (top_level_decl != nullptr) {
             res.decls.append(top_level_decl);
             continue;
@@ -532,6 +548,7 @@ static AstNodeContainerDecl ast_parse_container_members(ParseContext *pc) {
         if (container_field != nullptr) {
             assert(container_field->type == NodeTypeStructField);
             container_field->data.struct_field.visib_mod = visib_mod;
+            container_field->data.struct_field.doc_comments = doc_comment_buf;
             res.fields.append(container_field);
             if (eat_token_if(pc, TokenIdComma) != nullptr) {
                 continue;
@@ -581,7 +598,7 @@ static AstNode *ast_parse_top_level_comptime(ParseContext *pc) {
 //     <- (KEYWORD_export / KEYWORD_extern STRINGLITERAL? / (KEYWORD_inline / KEYWORD_noinline))? FnProto (SEMICOLON / Block)
 //      / (KEYWORD_export / KEYWORD_extern STRINGLITERAL?)? KEYWORD_threadlocal? VarDecl
 //      / KEYWORD_use Expr SEMICOLON
-static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod) {
+static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod, Buf *doc_comments) {
     Token *first = eat_token_if(pc, TokenIdKeywordExport);
     if (first == nullptr)
         first = eat_token_if(pc, TokenIdKeywordExtern);
@@ -603,6 +620,7 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod) {
                 var_decl->column = first->start_column;
                 var_decl->data.variable_declaration.threadlocal_tok = thread_local_kw;
                 var_decl->data.variable_declaration.visib_mod = visib_mod;
+                var_decl->data.variable_declaration.doc_comments = *doc_comments;
                 var_decl->data.variable_declaration.is_extern = first->id == TokenIdKeywordExtern;
                 var_decl->data.variable_declaration.is_export = first->id == TokenIdKeywordExport;
                 var_decl->data.variable_declaration.lib_name = token_buf(lib_name);
@@ -623,6 +641,7 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod) {
             fn_proto->line = first->start_line;
             fn_proto->column = first->start_column;
             fn_proto->data.fn_proto.visib_mod = visib_mod;
+            fn_proto->data.fn_proto.doc_comments = *doc_comments;
             fn_proto->data.fn_proto.is_extern = first->id == TokenIdKeywordExtern;
             fn_proto->data.fn_proto.is_export = first->id == TokenIdKeywordExport;
             switch (first->id) {
@@ -657,6 +676,7 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod) {
     if (var_decl != nullptr) {
         assert(var_decl->type == NodeTypeVariableDeclaration);
         var_decl->data.variable_declaration.visib_mod = visib_mod;
+        var_decl->data.variable_declaration.doc_comments = *doc_comments;
         var_decl->data.variable_declaration.threadlocal_tok = thread_local_kw;
         return var_decl;
     }
@@ -672,6 +692,7 @@ static AstNode *ast_parse_top_level_decl(ParseContext *pc, VisibMod visib_mod) {
 
         assert(fn_proto->type == NodeTypeFnProto);
         fn_proto->data.fn_proto.visib_mod = visib_mod;
+        fn_proto->data.fn_proto.doc_comments = *doc_comments;
         AstNode *res = fn_proto;
         if (body != nullptr) {
             res = ast_create_node_copy_line_info(pc, NodeTypeFnDef, fn_proto);
@@ -1719,11 +1740,20 @@ static AstNode *ast_parse_error_set_decl(ParseContext *pc) {
     }
 
     ZigList<AstNode *> decls = ast_parse_list<AstNode>(pc, TokenIdComma, [](ParseContext *context) {
+        Buf doc_comment_buf = BUF_INIT;
+        Token *doc_token = ast_parse_doc_comments(context, &doc_comment_buf);
         Token *ident = eat_token_if(context, TokenIdSymbol);
         if (ident == nullptr)
             return (AstNode*)nullptr;
 
-        return token_symbol(context, ident);
+        AstNode *symbol_node = token_symbol(context, ident);
+        if (doc_token == nullptr)
+            return symbol_node;
+
+        AstNode *field_node = ast_create_node(context, NodeTypeErrorSetField, doc_token);
+        field_node->data.err_set_field.field_name = symbol_node;
+        field_node->data.err_set_field.doc_comments = doc_comment_buf;
+        return field_node;
     });
     expect_token(pc, TokenIdRBrace);
 
@@ -2057,6 +2087,9 @@ static Optional<AstNodeFnProto> ast_parse_fn_cc(ParseContext *pc) {
 
 // ParamDecl <- (KEYWORD_noalias / KEYWORD_comptime)? (IDENTIFIER COLON)? ParamType
 static AstNode *ast_parse_param_decl(ParseContext *pc) {
+    Buf doc_comments = BUF_INIT;
+    ast_parse_doc_comments(pc, &doc_comments);
+
     Token *first = eat_token_if(pc, TokenIdKeywordNoAlias);
     if (first == nullptr)
         first = eat_token_if(pc, TokenIdKeywordCompTime);
@@ -2089,6 +2122,7 @@ static AstNode *ast_parse_param_decl(ParseContext *pc) {
     res->line = first->start_line;
     res->column = first->start_column;
     res->data.param_decl.name = token_buf(name);
+    res->data.param_decl.doc_comments = doc_comments;
     res->data.param_decl.is_noalias = first->id == TokenIdKeywordNoAlias;
     res->data.param_decl.is_comptime = first->id == TokenIdKeywordCompTime;
     return res;
@@ -3029,6 +3063,9 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
         case NodeTypeErrorSetDecl:
             visit_node_list(&node->data.err_set_decl.decls, visit, context);
             break;
+        case NodeTypeErrorSetField:
+            visit_field(&node->data.err_set_field.field_name, visit, context);
+            break;
         case NodeTypeResume:
             visit_field(&node->data.resume_expr.expr, visit, context);
             break;
src/tokenizer.cpp
@@ -196,6 +196,8 @@ enum TokenizeState {
     TokenizeStateSawStar,
     TokenizeStateSawStarPercent,
     TokenizeStateSawSlash,
+    TokenizeStateSawSlash2,
+    TokenizeStateSawSlash3,
     TokenizeStateSawBackslash,
     TokenizeStateSawPercent,
     TokenizeStateSawPlus,
@@ -206,6 +208,7 @@ enum TokenizeState {
     TokenizeStateSawCaret,
     TokenizeStateSawBar,
     TokenizeStateSawBarBar,
+    TokenizeStateDocComment,
     TokenizeStateLineComment,
     TokenizeStateLineString,
     TokenizeStateLineStringEnd,
@@ -910,8 +913,7 @@ void tokenize(Buf *buf, Tokenization *out) {
             case TokenizeStateSawSlash:
                 switch (c) {
                     case '/':
-                        cancel_token(&t);
-                        t.state = TokenizeStateLineComment;
+                        t.state = TokenizeStateSawSlash2;
                         break;
                     case '=':
                         set_token_id(&t, t.cur_tok, TokenIdDivEq);
@@ -925,6 +927,38 @@ void tokenize(Buf *buf, Tokenization *out) {
                         continue;
                 }
                 break;
+            case TokenizeStateSawSlash2:
+                switch (c) {
+                    case '/':
+                        t.state = TokenizeStateSawSlash3;
+                        break;
+                    case '\n':
+                        cancel_token(&t);
+                        t.state = TokenizeStateStart;
+                        break;
+                    default:
+                        cancel_token(&t);
+                        t.state = TokenizeStateLineComment;
+                        break;
+                }
+                break;
+            case TokenizeStateSawSlash3:
+                switch (c) {
+                    case '/':
+                        cancel_token(&t);
+                        t.state = TokenizeStateLineComment;
+                        break;
+                    case '\n':
+                        set_token_id(&t, t.cur_tok, TokenIdDocComment);
+                        end_token(&t);
+                        t.state = TokenizeStateStart;
+                        break;
+                    default:
+                        set_token_id(&t, t.cur_tok, TokenIdDocComment);
+                        t.state = TokenizeStateDocComment;
+                        break;
+                }
+                break;
             case TokenizeStateSawBackslash:
                 switch (c) {
                     case '\\':
@@ -1004,6 +1038,17 @@ void tokenize(Buf *buf, Tokenization *out) {
                         break;
                 }
                 break;
+            case TokenizeStateDocComment:
+                switch (c) {
+                    case '\n':
+                        end_token(&t);
+                        t.state = TokenizeStateStart;
+                        break;
+                    default:
+                        // do nothing
+                        break;
+                }
+                break;
             case TokenizeStateSymbolFirstC:
                 switch (c) {
                     case '"':
@@ -1466,6 +1511,7 @@ void tokenize(Buf *buf, Tokenization *out) {
         case TokenizeStateLineStringEnd:
         case TokenizeStateSawBarBar:
         case TokenizeStateLBracket:
+        case TokenizeStateDocComment:
             end_token(&t);
             break;
         case TokenizeStateSawDotDot:
@@ -1478,6 +1524,8 @@ void tokenize(Buf *buf, Tokenization *out) {
             tokenize_error(&t, "unexpected EOF");
             break;
         case TokenizeStateLineComment:
+        case TokenizeStateSawSlash2:
+        case TokenizeStateSawSlash3:
             break;
     }
     if (t.state != TokenizeStateError) {
@@ -1524,6 +1572,7 @@ const char * token_name(TokenId id) {
         case TokenIdComma: return ",";
         case TokenIdDash: return "-";
         case TokenIdDivEq: return "/=";
+        case TokenIdDocComment: return "DocComment";
         case TokenIdDot: return ".";
         case TokenIdEllipsis2: return "..";
         case TokenIdEllipsis3: return "...";
src/tokenizer.hpp
@@ -42,6 +42,7 @@ enum TokenId {
     TokenIdComma,
     TokenIdDash,
     TokenIdDivEq,
+    TokenIdDocComment,
     TokenIdDot,
     TokenIdEllipsis2,
     TokenIdEllipsis3,