sstwcw created this revision. sstwcw added reviewers: HazardyKnusperkeks, MyDeveloperDay, curdeius, owenpan. Herald added a project: All. sstwcw requested review of this revision. Herald added a project: clang. Herald added a subscriber: cfe-commits.
These statements are like switch statements in C, but without the 'case' keyword in labels. How labels are parsed. In UnwrappedLineParser, the program tries to parse a statement every time it sees a colon. In TokenAnnotator, a colon that isn't part of an expression is annotated as a label. The token type `TT_GotoLabelColon` is added. We did not include Verilog in the name because we thought we would eventually have to fix the problem that case labels in C can't contain ternary conditional expressions and we would use that token type. The style is like below. Labels are on separate lines and indented by default. The linked style guide also has examples where labels and the corresponding statements are on the same lines. They are not supported for now. https://github.com/lowRISC/style-guides/blob/master/VerilogCodingStyle.md case (state_q) StIdle: state_d = StA; StA: begin state_d = StB; end endcase Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D128714 Files: clang/lib/Format/ContinuationIndenter.cpp clang/lib/Format/Format.cpp clang/lib/Format/FormatToken.h clang/lib/Format/TokenAnnotator.cpp clang/lib/Format/UnwrappedLineParser.cpp clang/lib/Format/UnwrappedLineParser.h clang/unittests/Format/FormatTestVerilog.cpp clang/unittests/Format/TokenAnnotatorTest.cpp
Index: clang/unittests/Format/TokenAnnotatorTest.cpp =================================================================== --- clang/unittests/Format/TokenAnnotatorTest.cpp +++ clang/unittests/Format/TokenAnnotatorTest.cpp @@ -875,6 +875,21 @@ Tokens = Annotate("extern function [1 : 0] x;"); ASSERT_EQ(Tokens.size(), 10u) << Tokens; EXPECT_TOKEN(Tokens[4], tok::colon, TT_BitFieldColon); + // Test case labels and ternary operators. + Tokens = Annotate("case (x)\n" + " x:\n" + " x;\n" + "endcase\n"); + ASSERT_EQ(Tokens.size(), 10u) << Tokens; + EXPECT_TOKEN(Tokens[5], tok::colon, TT_GotoLabelColon); + Tokens = Annotate("case (x)\n" + " x ? x : x:\n" + " x;\n" + "endcase\n"); + ASSERT_EQ(Tokens.size(), 14u) << Tokens; + EXPECT_TOKEN(Tokens[5], tok::question, TT_ConditionalExpr); + EXPECT_TOKEN(Tokens[7], tok::colon, TT_ConditionalExpr); + EXPECT_TOKEN(Tokens[9], tok::colon, TT_GotoLabelColon); } } // namespace Index: clang/unittests/Format/FormatTestVerilog.cpp =================================================================== --- clang/unittests/Format/FormatTestVerilog.cpp +++ clang/unittests/Format/FormatTestVerilog.cpp @@ -116,6 +116,90 @@ "x = x;"); } +TEST_F(FormatTestVerilog, Case) { + verifyFormat("case (data)\n" + "endcase"); + verifyFormat("casex (data)\n" + "endcase"); + verifyFormat("casez (data)\n" + "endcase"); + verifyFormat("case (data) inside\n" + "endcase"); + verifyFormat("case (data)\n" + " 16'd0:\n" + " result = 10'b0111111111;\n" + "endcase"); + verifyFormat("case (data)\n" + " xxxxxxxx:\n" + " result = 10'b0111111111;\n" + "endcase"); + // Test labels with multiple options. + verifyFormat("case (data)\n" + " 16'd0, 16'd1:\n" + " result = 10'b0111111111;\n" + "endcase"); + verifyFormat("case (data)\n" + " 16'd0, //\n" + " 16'd1:\n" + " result = 10'b0111111111;\n" + "endcase"); + // Test that blocks following labels are indented. + verifyFormat("case (data)\n" + " 16'd1: fork\n" + " result = 10'b1011111111;\n" + " join\n" + "endcase\n"); + verifyFormat("case (data)\n" + " 16'd1: fork : x\n" + " result = 10'b1011111111;\n" + " join : x\n" + "endcase\n"); + // Test default. + verifyFormat("case (data)\n" + " default\n" + " result = 10'b1011111111;\n" + "endcase"); + verifyFormat("case (data)\n" + " default:\n" + " result = 10'b1011111111;\n" + "endcase"); + // Test that question marks and colons don't get mistaken as labels. + verifyFormat("case (data)\n" + " 8'b1???????:\n" + " instruction1(ir);\n" + "endcase"); + verifyFormat("case (data)\n" + " x ? 8'b1??????? : 1:\n" + " instruction3(ir);\n" + "endcase"); + // Test indention options. + auto Style = getLLVMStyle(FormatStyle::LK_Verilog); + Style.IndentCaseLabels = false; + verifyFormat("case (data)\n" + "16'd0:\n" + " result = 10'b0111111111;\n" + "endcase", + Style); + verifyFormat("case (data)\n" + "16'd0: begin\n" + " result = 10'b0111111111;\n" + "end\n" + "endcase", + Style); + Style.IndentCaseLabels = true; + verifyFormat("case (data)\n" + " 16'd0:\n" + " result = 10'b0111111111;\n" + "endcase", + Style); + verifyFormat("case (data)\n" + " 16'd0: begin\n" + " result = 10'b0111111111;\n" + " end\n" + "endcase", + Style); +} + TEST_F(FormatTestVerilog, Delay) { // Delay by the default unit. verifyFormat("#0;"); Index: clang/lib/Format/UnwrappedLineParser.h =================================================================== --- clang/lib/Format/UnwrappedLineParser.h +++ clang/lib/Format/UnwrappedLineParser.h @@ -183,6 +183,7 @@ // level for a block, used for indenting case labels. unsigned parseVerilogHierHeader(); void parseVerilogTable(); + void parseVerilogCaseLabel(); // Used by addUnwrappedLine to denote whether to keep or remove a level // when resetting the line state. Index: clang/lib/Format/UnwrappedLineParser.cpp =================================================================== --- clang/lib/Format/UnwrappedLineParser.cpp +++ clang/lib/Format/UnwrappedLineParser.cpp @@ -575,9 +575,12 @@ LLVM_FALLTHROUGH; } case tok::kw_case: - if (Style.isJavaScript() && Line->MustBeDeclaration) { - // A 'case: string' style field declaration. - parseStructuralElement(); + if (Style.isVerilog() || + (Style.isJavaScript() && Line->MustBeDeclaration)) { + // Verilog: Case labels don't have this word. We handle case + // labels including default in TokenAnnotator. + // JavaScript: A 'case: string' style field declaration. + ParseDefault(); break; } if (!SwitchLabelEncountered && @@ -1533,6 +1536,9 @@ parseSwitch(); return; case tok::kw_default: + // In Verilog default along with other labels are handled in the next loop. + if (Style.isVerilog()) + break; if (Style.isJavaScript() && Line->MustBeDeclaration) { // 'default: string' field declaration. break; @@ -1545,6 +1551,12 @@ // e.g. "default void f() {}" in a Java interface. break; case tok::kw_case: + // In Verilog switch is called case. + if (Style.isVerilog()) { + parseBlock(); + addUnwrappedLine(); + return; + } if (Style.isJavaScript() && Line->MustBeDeclaration) { // 'case: string' field declaration. nextToken(); @@ -1958,7 +1970,9 @@ return End - Tok == 1; }; if (OneTokenSoFar()) { - if (FormatTok->is(tok::colon) && !Line->MustBeDeclaration) { + // In Verilog labels can be any expression, so we don't do them here. + if (!Style.isVerilog() && FormatTok->is(tok::colon) && + !Line->MustBeDeclaration) { Line->Tokens.begin()->Tok->MustBreakBefore = true; parseLabel(!Style.IndentGotoLabels); if (HasLabel) @@ -2015,6 +2029,12 @@ parseNew(); break; case tok::kw_case: + // In Verilog switch is called case. + if (Style.isVerilog()) { + parseBlock(); + addUnwrappedLine(); + return; + } if (Style.isJavaScript() && Line->MustBeDeclaration) { // 'case: string' field declaration. nextToken(); @@ -2022,6 +2042,30 @@ } parseCaseLabel(); break; + case tok::kw_default: + nextToken(); + if (Style.isVerilog()) { + if (FormatTok->is(tok::colon)) { + // The label will be handled in the next iteration. + break; + } + if (FormatTok->is(Keywords.kw_clocking)) { + // A default clocking block. + parseBlock(); + addUnwrappedLine(); + return; + } + parseVerilogCaseLabel(); + return; + } + break; + case tok::colon: + nextToken(); + if (Style.isVerilog()) { + parseVerilogCaseLabel(); + return; + } + break; default: nextToken(); break; @@ -4075,10 +4119,13 @@ } else if (FormatTok->isOneOf(tok::kw_case, Keywords.kw_casex, Keywords.kw_casez, Keywords.kw_randcase, Keywords.kw_randsequence)) { - AddLevels += Style.IndentCaseLabels; + if (Style.IndentCaseLabels) + AddLevels++; nextToken(); - if (FormatTok->is(tok::l_paren)) + if (FormatTok->is(tok::l_paren)) { + FormatTok->setFinalizedType(TT_ConditionLParen); parseParens(); + } if (FormatTok->isOneOf(Keywords.kw_inside, Keywords.kw_matches)) nextToken(); // The case header has no semicolon. @@ -4176,6 +4223,26 @@ addUnwrappedLine(); } +void UnwrappedLineParser::parseVerilogCaseLabel() { + // The label will get unindented in AnnotatingParser. If there are no leading + // spaces, indent the rest here so that things inside the block will be + // indented relative to things outside. We don't use parseLabel because we + // don't know whether this colon is a label or a ternary expression at this + // point. + auto OrigLevel = Line->Level; + auto FirstLine = CurrentLines->size(); + if (Line->Level == 0 || (Line->InPPDirective && Line->Level <= 1)) + ++Line->Level; + else if (!Style.IndentCaseBlocks && Keywords.isVerilogBegin(*FormatTok)) + --Line->Level; + parseStructuralElement(); + // Restore the indentation in both the new line and the line that has the + // label. + if (CurrentLines->size() > FirstLine) + (*CurrentLines)[FirstLine].Level = OrigLevel; + Line->Level = OrigLevel; +} + LLVM_ATTRIBUTE_UNUSED static void printDebugInfo(const UnwrappedLine &Line, StringRef Prefix = "") { llvm::dbgs() << Prefix << "Line(" << Line.Level Index: clang/lib/Format/TokenAnnotator.cpp =================================================================== --- clang/lib/Format/TokenAnnotator.cpp +++ clang/lib/Format/TokenAnnotator.cpp @@ -955,11 +955,22 @@ break; } } else if (Style.isVerilog() && Tok->isNot(TT_BinaryOperator)) { + // The distribution weight operators are labeled + // TT_BinaryOperator by the lexer. if (Keywords.isVerilogEnd(*Tok->Previous) || Keywords.isVerilogBegin(*Tok->Previous)) { Tok->setType(TT_VerilogBlockLabelColon); } else if (Contexts.back().ContextKind == tok::l_square) { Tok->setType(TT_BitFieldColon); + } else if (Contexts.back().ColonIsDictLiteral) { + Tok->setType(TT_DictLiteral); + } else if (Contexts.size() == 1) { + // In Verilog a case label doesn't have the case keyword. We + // assume a colon following an expression is a case label. + // Colons from ?: are annotated in parseConditional(). + Tok->setType(TT_GotoLabelColon); + if (Line.Level > 1 || (!Line.InPPDirective && Line.Level > 0)) + --Line.Level; } break; } @@ -1229,6 +1240,13 @@ if (Contexts.back().ContextType == Context::ForEachMacro) Contexts.back().IsExpression = true; break; + case tok::kw_default: + // Unindent case labels. + if (Style.isVerilog() && Keywords.isVerilogEndOfLabel(*Tok) && + (Line.Level > 1 || (!Line.InPPDirective && Line.Level > 0))) { + --Line.Level; + } + break; case tok::identifier: if (Tok->isOneOf(Keywords.kw___has_include, Keywords.kw___has_include_next)) { @@ -2575,6 +2593,10 @@ Keywords.kw_throws)) { return 0; } + // In Verilog case labels are not on separate lines straight out of + // UnwrappedLineParser. The colon is not part of an expression. + if (Style.isVerilog() && Current->is(tok::colon)) + return 0; } return -1; } @@ -3575,7 +3597,8 @@ return true; if (Left.isOneOf(tok::pp_elif, tok::kw_for, tok::kw_while, tok::kw_switch, tok::kw_case, TT_ForEachMacro, TT_ObjCForIn) || - Left.isIf(Line.Type != LT_PreprocessorDirective)) { + Left.isIf(Line.Type != LT_PreprocessorDirective) || + Right.is(TT_ConditionLParen)) { return Style.SpaceBeforeParensOptions.AfterControlStatements || spaceRequiredBeforeParens(Right); } @@ -4046,6 +4069,11 @@ Style.BitFieldColonSpacing == FormatStyle::BFCS_After; } if (Right.is(tok::colon)) { + if (Right.is(TT_GotoLabelColon) || + (!Style.isVerilog() && + Line.First->isOneOf(tok::kw_default, tok::kw_case))) { + return Style.SpaceBeforeCaseColon; + } if (Line.First->isOneOf(tok::kw_default, tok::kw_case)) return Style.SpaceBeforeCaseColon; const FormatToken *Next = Right.getNextNonComment(); @@ -4308,6 +4336,11 @@ Right.Next->is(tok::string_literal)) { return true; } + } else if (Style.isVerilog()) { + // Break after labels. In Verilog labels don't have the 'case' keyword, so + // it is hard to identify them in UnwrappedLineParser. + if (!Keywords.isVerilogBegin(Right) && Keywords.isVerilogEndOfLabel(Left)) + return true; } else if (Style.Language == FormatStyle::LK_Cpp || Style.Language == FormatStyle::LK_ObjC || Style.Language == FormatStyle::LK_Proto || Index: clang/lib/Format/FormatToken.h =================================================================== --- clang/lib/Format/FormatToken.h +++ clang/lib/Format/FormatToken.h @@ -39,7 +39,10 @@ TYPE(CastRParen) \ TYPE(ClassLBrace) \ TYPE(CompoundRequirementLBrace) \ + /* ternary ?: expression */ \ TYPE(ConditionalExpr) \ + /* the condition in an if statement */ \ + TYPE(ConditionLParen) \ TYPE(ConflictAlternative) \ TYPE(ConflictEnd) \ TYPE(ConflictStart) \ @@ -67,6 +70,9 @@ TYPE(FunctionLBrace) \ TYPE(FunctionLikeOrFreestandingMacro) \ TYPE(FunctionTypeLParen) \ + /* The colon at the end of a goto label or a case label. Currently only used \ + * for Verilog. */ \ + TYPE(GotoLabelColon) \ TYPE(IfMacro) \ TYPE(ImplicitStringLiteral) \ TYPE(InheritanceColon) \ @@ -1756,6 +1762,15 @@ kw_task); } + bool isVerilogEndOfLabel(const FormatToken &Tok) const { + const FormatToken *Next = Tok.getNextNonComment(); + // In Verilog the colon in a default label is optional. + return Tok.is(TT_GotoLabelColon) || + (Tok.is(tok::kw_default) && + !(Next && Next->isOneOf(tok::colon, tok::semi, kw_clocking, kw_iff, + kw_input, kw_output, kw_sequence))); + } + /// Whether the token begins a block. bool isBlockBegin(const FormatToken &Tok, const FormatStyle &Style) const { return Tok.is(TT_MacroBlockBegin) || Index: clang/lib/Format/Format.cpp =================================================================== --- clang/lib/Format/Format.cpp +++ clang/lib/Format/Format.cpp @@ -1262,7 +1262,7 @@ LLVMStyle.IncludeStyle.IncludeIsMainRegex = "(Test)?$"; LLVMStyle.IncludeStyle.IncludeBlocks = tooling::IncludeStyle::IBS_Preserve; LLVMStyle.IndentAccessModifiers = false; - LLVMStyle.IndentCaseLabels = false; + LLVMStyle.IndentCaseLabels = LLVMStyle.isVerilog() ? true : false; LLVMStyle.IndentCaseBlocks = false; LLVMStyle.IndentGotoLabels = true; LLVMStyle.IndentPPDirectives = FormatStyle::PPDIS_None; Index: clang/lib/Format/ContinuationIndenter.cpp =================================================================== --- clang/lib/Format/ContinuationIndenter.cpp +++ clang/lib/Format/ContinuationIndenter.cpp @@ -1087,8 +1087,12 @@ CurrentState.Indent + Style.ContinuationIndentWidth); } - if (Style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths && - State.Line->First->is(tok::kw_enum)) { + // After a goto label. Usually labels are on separate lines. However + // for Verilog the labels may be only recognized by the annotator and + // thus are on the same line as the current token. + if ((Style.isVerilog() && Keywords.isVerilogEndOfLabel(Previous)) || + (Style.BreakBeforeBraces == FormatStyle::BS_Whitesmiths && + State.Line->First->is(tok::kw_enum))) { return (Style.IndentWidth * State.Line->First->IndentLevel) + Style.IndentWidth; }
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits