diff --git a/lib/Dialect/FIRRTL/Import/FIRLexer.cpp b/lib/Dialect/FIRRTL/Import/FIRLexer.cpp index 5a60d682fad1..6da18d2d5bd0 100644 --- a/lib/Dialect/FIRRTL/Import/FIRLexer.cpp +++ b/lib/Dialect/FIRRTL/Import/FIRLexer.cpp @@ -419,6 +419,19 @@ FIRToken FIRLexer::lexIdentifierOrKeyword(const char *tokStart) { } } + // Check to see if this is a keyword followed by '<' character. + if (*curPtr == '<') { + FIRToken::Kind kind = llvm::StringSwitch(spelling) +#define TOK_LESSKEYWORD(SPELLING) .Case(#SPELLING, FIRToken::langle_##SPELLING) +#include "FIRTokenKinds.def" + .Default(FIRToken::identifier); +#undef TOK_LESSKEYWORD + if (kind != FIRToken::identifier) { + ++curPtr; + return formToken(kind, tokStart); + } + } + // See if the identifier is a keyword. By default, it is an identifier. FIRToken::Kind kind = llvm::StringSwitch(spelling) #define TOK_KEYWORD(SPELLING) .Case(#SPELLING, FIRToken::kw_##SPELLING) diff --git a/lib/Dialect/FIRRTL/Import/FIRLexer.h b/lib/Dialect/FIRRTL/Import/FIRLexer.h index 08fe16c85d69..d3f254992432 100644 --- a/lib/Dialect/FIRRTL/Import/FIRLexer.h +++ b/lib/Dialect/FIRRTL/Import/FIRLexer.h @@ -35,6 +35,7 @@ class FIRToken { #define TOK_PUNCTUATION(NAME, SPELLING) NAME, #define TOK_KEYWORD(SPELLING) kw_##SPELLING, #define TOK_LPKEYWORD(SPELLING) lp_##SPELLING, +#define TOK_LESSKEYWORD(SPELLING) langle_##SPELLING, #include "FIRTokenKinds.def" }; diff --git a/lib/Dialect/FIRRTL/Import/FIRParser.cpp b/lib/Dialect/FIRRTL/Import/FIRParser.cpp index 6f604e61afd3..50b17c0fbba9 100644 --- a/lib/Dialect/FIRRTL/Import/FIRParser.cpp +++ b/lib/Dialect/FIRRTL/Import/FIRParser.cpp @@ -271,9 +271,8 @@ struct FIRParser { // Parse 'verLit' into specified value ParseResult parseVersionLit(const Twine &message); - // Parse ('<' intLit '>')? setting result to -1 if not present. - template - ParseResult parseOptionalWidth(T &result); + // Parse 'intLit' '>' assuming '<' was already consumed. + ParseResult parseWidth(int32_t &result); // Parse the 'id' grammar, which is an identifier or an allowed keyword. ParseResult parseId(StringRef &result, const Twine &message); @@ -682,23 +681,15 @@ ParseResult FIRParser::parseVersionLit(const Twine &message) { return success(); } -// optional-width ::= ('<' intLit '>')? -// -// This returns with result equal to -1 if not present. -template -ParseResult FIRParser::parseOptionalWidth(T &result) { - if (!consumeIf(FIRToken::less)) - return result = -1, success(); - - // Parse a width specifier if present. +/// Parse a width specifier: intLit '>' +/// This is used when the '<' has already been consumed. +ParseResult FIRParser::parseWidth(int32_t &result) { auto widthLoc = getToken().getLoc(); if (parseIntLit(result, "expected width") || - parseToken(FIRToken::greater, "expected >")) + parseToken(FIRToken::greater, "expected '>'")) return failure(); - if (result < 0) return emitError(widthLoc, "invalid width specifier"), failure(); - return success(); } @@ -1016,22 +1007,41 @@ ParseResult FIRParser::parseType(FIRRTLType &result, const Twine &message) { break; case FIRToken::kw_UInt: + consumeToken(FIRToken::kw_UInt); + // Width is not present since langle_UInt would have been lexed instead. + result = UIntType::get(getContext(), -1); + break; + case FIRToken::kw_SInt: - case FIRToken::kw_Analog: { + consumeToken(FIRToken::kw_SInt); + // Width is not present since langle_SInt would have been lexed instead. + result = SIntType::get(getContext(), -1); + break; + + case FIRToken::kw_Analog: + consumeToken(FIRToken::kw_Analog); + // Width is not present since langle_Analog would have been lexed instead. + result = AnalogType::get(getContext(), -1); + break; + + case FIRToken::langle_UInt: + case FIRToken::langle_SInt: + case FIRToken::langle_Analog: { + // The '<' has already been consumed by the lexer, so we need to parse + // the mandatory width and the trailing '>'. auto kind = getToken().getKind(); consumeToken(); - // Parse a width specifier if present. int32_t width; - if (parseOptionalWidth(width)) + if (parseWidth(width)) return failure(); - if (kind == FIRToken::kw_SInt) + if (kind == FIRToken::langle_SInt) result = SIntType::get(getContext(), width); - else if (kind == FIRToken::kw_UInt) + else if (kind == FIRToken::langle_UInt) result = UIntType::get(getContext(), width); else { - assert(kind == FIRToken::kw_Analog); + assert(kind == FIRToken::langle_Analog); result = AnalogType::get(getContext(), width); } break; @@ -1219,6 +1229,22 @@ ParseResult FIRParser::parseType(FIRRTLType &result, const Twine &message) { if (requireFeature({4, 0, 0}, "Lists") || parseListType(result)) return failure(); break; + + case FIRToken::langle_List: { + // The '<' has already been consumed by the lexer, so we need to parse + // the element type and the trailing '>'. + if (requireFeature({4, 0, 0}, "Lists")) + return failure(); + consumeToken(); + + PropertyType elementType; + if (parsePropertyType(elementType, "expected List element type") || + parseToken(FIRToken::greater, "expected '>' in List type")) + return failure(); + + result = ListType::get(getContext(), elementType); + break; + } } // Handle postfix vector sizes. @@ -1959,7 +1985,9 @@ struct FIRStmtParser : public FIRParser { ParseResult parsePostFixFieldId(Value &result); ParseResult parsePostFixIntSubscript(Value &result); ParseResult parsePostFixDynamicSubscript(Value &result); - ParseResult parseIntegerLiteralExp(Value &result); + ParseResult + parseIntegerLiteralExp(Value &result, bool isSigned, + std::optional allocatedWidth = {}); ParseResult parseListExp(Value &result); ParseResult parseListConcatExp(Value &result); ParseResult parseCatExp(Value &result); @@ -2227,19 +2255,37 @@ ParseResult FIRStmtParser::parseExpImpl(Value &result, const Twine &message, return failure(); break; - case FIRToken::kw_UInt: - case FIRToken::kw_SInt: - if (parseIntegerLiteralExp(result)) + case FIRToken::langle_UInt: + case FIRToken::langle_SInt: { + // The '<' has already been consumed by the lexer, so we need to parse + // the mandatory width and '>'. + bool isSigned = getToken().is(FIRToken::langle_SInt); + consumeToken(); + int32_t width; + if (parseWidth(width)) + return failure(); + + // Now parse the '(' intLit ')' part. + if (parseIntegerLiteralExp(result, isSigned, width)) + return failure(); + break; + } + + case FIRToken::lp_UInt: + if (parseIntegerLiteralExp(result, /*isSigned=*/false)) return failure(); break; - case FIRToken::kw_String: { + case FIRToken::lp_SInt: + if (parseIntegerLiteralExp(result, /*isSigned=*/true)) + return failure(); + break; + case FIRToken::lp_String: { if (requireFeature({3, 1, 0}, "Strings")) return failure(); locationProcessor.setLoc(getToken().getLoc()); - consumeToken(FIRToken::kw_String); + consumeToken(FIRToken::lp_String); StringRef spelling; - if (parseToken(FIRToken::l_paren, "expected '(' in String expression") || - parseGetSpelling(spelling) || + if (parseGetSpelling(spelling) || parseToken(FIRToken::string, "expected string literal in String expression") || parseToken(FIRToken::r_paren, "expected ')' in String expression")) @@ -2249,14 +2295,13 @@ ParseResult FIRStmtParser::parseExpImpl(Value &result, const Twine &message, builder, attr, builder.getType(), attr); break; } - case FIRToken::kw_Integer: { + case FIRToken::lp_Integer: { if (requireFeature({3, 1, 0}, "Integers")) return failure(); locationProcessor.setLoc(getToken().getLoc()); - consumeToken(FIRToken::kw_Integer); + consumeToken(FIRToken::lp_Integer); APInt value; - if (parseToken(FIRToken::l_paren, "expected '(' in Integer expression") || - parseIntLit(value, "expected integer literal in Integer expression") || + if (parseIntLit(value, "expected integer literal in Integer expression") || parseToken(FIRToken::r_paren, "expected ')' in Integer expression")) return failure(); APSInt apint(value, /*isUnsigned=*/false); @@ -2265,13 +2310,11 @@ ParseResult FIRStmtParser::parseExpImpl(Value &result, const Twine &message, builder.getType(), apint); break; } - case FIRToken::kw_Bool: { + case FIRToken::lp_Bool: { if (requireFeature(missingSpecFIRVersion, "Bools")) return failure(); locationProcessor.setLoc(getToken().getLoc()); - consumeToken(FIRToken::kw_Bool); - if (parseToken(FIRToken::l_paren, "expected '(' in Bool expression")) - return failure(); + consumeToken(FIRToken::lp_Bool); bool value; if (consumeIf(FIRToken::kw_true)) value = true; @@ -2286,13 +2329,11 @@ ParseResult FIRStmtParser::parseExpImpl(Value &result, const Twine &message, builder, attr, builder.getType(), value); break; } - case FIRToken::kw_Double: { + case FIRToken::lp_Double: { if (requireFeature(missingSpecFIRVersion, "Doubles")) return failure(); locationProcessor.setLoc(getToken().getLoc()); - consumeToken(FIRToken::kw_Double); - if (parseToken(FIRToken::l_paren, "expected '(' in Double expression")) - return failure(); + consumeToken(FIRToken::lp_Double); auto spelling = getTokenSpelling(); if (parseToken(FIRToken::floatingpoint, "expected floating point in Double expression") || @@ -2308,7 +2349,8 @@ ParseResult FIRStmtParser::parseExpImpl(Value &result, const Twine &message, builder, attr, builder.getType(), attr); break; } - case FIRToken::kw_List: { + case FIRToken::lp_List: + case FIRToken::langle_List: { if (requireFeature({4, 0, 0}, "Lists")) return failure(); if (isLeadingStmt) @@ -2359,6 +2401,13 @@ ParseResult FIRStmtParser::parseExpImpl(Value &result, const Twine &message, // try them. case FIRToken::identifier: // exp ::= id case FIRToken::literal_identifier: + case FIRToken::kw_UInt: + case FIRToken::kw_SInt: + case FIRToken::kw_String: + case FIRToken::kw_Integer: + case FIRToken::kw_Bool: + case FIRToken::kw_Double: + case FIRToken::kw_List: default: { StringRef name; auto loc = getToken().getLoc(); @@ -2596,17 +2645,31 @@ ParseResult FIRStmtParser::parsePostFixDynamicSubscript(Value &result) { /// integer-literal-exp ::= 'UInt' optional-width '(' intLit ')' /// ::= 'SInt' optional-width '(' intLit ')' -ParseResult FIRStmtParser::parseIntegerLiteralExp(Value &result) { - bool isSigned = getToken().is(FIRToken::kw_SInt); +/// +/// If allocatedWidth is provided, it means the width was already parsed +/// (e.g., from a langle_UInt token) and should be used instead of parsing +/// it from the token stream. +ParseResult +FIRStmtParser::parseIntegerLiteralExp(Value &result, bool isSigned, + std::optional allocatedWidth) { auto loc = getToken().getLoc(); - consumeToken(); - // Parse a width specifier if present. - int32_t width; + // Determine if '(' was already consumed by the lexer. + bool hasLParen = getToken().isAny(FIRToken::lp_UInt, FIRToken::lp_SInt); + if (hasLParen) + consumeToken(); + + // Parse a width specifier if not already provided. + int32_t width = allocatedWidth.value_or(-1); APInt value; - if (parseOptionalWidth(width) || - parseToken(FIRToken::l_paren, "expected '(' in integer expression") || - parseIntLit(value, "expected integer value") || + + // If we consumed an lp_ token, the '(' was already consumed by the lexer. + // Otherwise, we need to parse it. + if (!hasLParen && + parseToken(FIRToken::l_paren, "expected '(' in integer expression")) + return failure(); + + if (parseIntLit(value, "expected integer value") || parseToken(FIRToken::r_paren, "expected ')' in integer expression")) return failure(); @@ -2640,13 +2703,24 @@ ParseResult FIRStmtParser::parseIntegerLiteralExp(Value &result) { /// list-exp ::= list-type '(' exp* ')' ParseResult FIRStmtParser::parseListExp(Value &result) { auto loc = getToken().getLoc(); - FIRRTLType type; - if (parseListType(type)) + bool hasLAngle = getToken().is(FIRToken::langle_List); + bool hasLParen = getToken().is(FIRToken::lp_List); + consumeToken(); + + PropertyType elementType; + // If we consumed a langle_ token, the '<' was already consumed by the lexer. + if (!hasLAngle && parseToken(FIRToken::less, "expected '<' in List type")) return failure(); - auto listType = type_cast(type); - auto elementType = listType.getElementType(); - if (parseToken(FIRToken::l_paren, "expected '(' in List expression")) + if (parsePropertyType(elementType, "expected List element type") || + parseToken(FIRToken::greater, "expected '>' in List type")) + return failure(); + + auto listType = ListType::get(getContext(), elementType); + + // If we consumed an lp_ token, the '(' was already consumed by the lexer. + if (!hasLParen && + parseToken(FIRToken::l_paren, "expected '(' in List expression")) return failure(); SmallVector operands; diff --git a/lib/Dialect/FIRRTL/Import/FIRTokenKinds.def b/lib/Dialect/FIRRTL/Import/FIRTokenKinds.def index 28485ce50ae5..9d4fc3943143 100644 --- a/lib/Dialect/FIRRTL/Import/FIRTokenKinds.def +++ b/lib/Dialect/FIRRTL/Import/FIRTokenKinds.def @@ -14,7 +14,7 @@ #if !defined(TOK_MARKER) && !defined(TOK_IDENTIFIER) && \ !defined(TOK_LITERAL) && !defined(TOK_PUNCTUATION) && \ !defined(TOK_KEYWORD) && !defined(TOK_LPKEYWORD) && \ - !defined(TOK_LPKEYWORD_PRIM) + !defined(TOK_LESSKEYWORD) && !defined(TOK_LPKEYWORD_PRIM) #error Must define one of the TOK_ macros. #endif @@ -41,6 +41,9 @@ VERSION, FEATURE) \ TOK_LPKEYWORD(SPELLING) #endif +#ifndef TOK_LESSKEYWORD +#define TOK_LESSKEYWORD(SPELLING) +#endif // Markers TOK_MARKER(eof) @@ -199,6 +202,21 @@ TOK_LPKEYWORD(intrinsic) TOK_LPKEYWORD(cat) TOK_LPKEYWORD(unsafe_domain_cast) +TOK_LPKEYWORD(UInt) +TOK_LPKEYWORD(SInt) +TOK_LPKEYWORD(String) +TOK_LPKEYWORD(Integer) +TOK_LPKEYWORD(Bool) +TOK_LPKEYWORD(Double) +TOK_LPKEYWORD(List) + +// Keywords when followed by a '<'. These turn "foo" into +// FIRToken::langle_foo enums. +TOK_LESSKEYWORD(UInt) +TOK_LESSKEYWORD(SInt) +TOK_LESSKEYWORD(Analog) +TOK_LESSKEYWORD(List) + // These are for LPKEYWORD cases that correspond to a primitive operation. TOK_LPKEYWORD_PRIM(add, AddPrimOp, 2, 0, FIRVersion(0, 0, 0), "Base") TOK_LPKEYWORD_PRIM(and, AndPrimOp, 2, 0, FIRVersion(0, 0, 0), "Base") @@ -253,4 +271,5 @@ TOK_LPKEYWORD_PRIM(integer_shl, IntegerShlOp, 2, 0, FIRVersion(3, 1, 0), #undef TOK_PUNCTUATION #undef TOK_KEYWORD #undef TOK_LPKEYWORD +#undef TOK_LESSKEYWORD #undef TOK_LPKEYWORD_PRIM diff --git a/test/Dialect/FIRRTL/parse-basic.fir b/test/Dialect/FIRRTL/parse-basic.fir index 473f4fbfbef3..de688244095c 100644 --- a/test/Dialect/FIRRTL/parse-basic.fir +++ b/test/Dialect/FIRRTL/parse-basic.fir @@ -383,8 +383,8 @@ circuit MyModule : ; CHECK: firrtl.circuit "MyModule" ; CHECK: firrtl.attach %a8, %a8, %a8 : attach (a8, a8, a8) - wire pred: UInt <1> - wire en: UInt <1> + wire pred: UInt<1> + wire en: UInt<1> connect pred, eq(i8, i8) connect en, not(reset) ; CHECK: firrtl.assert %clock, %pred, %en, "X equals Y when Z is valid" : !firrtl.clock, !firrtl.uint<1>, !firrtl.uint<1> {eventControl = 0 : i32, isConcurrent = false} @@ -2298,3 +2298,25 @@ circuit UnknownProperties: ; CHECK: %[[#INSTANCE:]] = firrtl.unknown : !firrtl.class<@Foo()> ; CHECK-NEXT: firrtl.propassign %c, %[[#INSTANCE]] : !firrtl.class<@Foo()> propassign c, Unknown: Inst + +; // ----- + +; Test that keywords can be used as identifiers when not followed by '(' or '<' +; and as expressions when followed by '(' or '<' + +FIRRTL version 4.0.0 +circuit KeywordIdentifiers : + public module KeywordIdentifiers : + ; CHECK-LABEL: firrtl.module @KeywordIdentifiers + + ; Test UInt and SInt as identifiers + ; CHECK: %UInt = firrtl.wire + ; CHECK: %SInt = firrtl.wire + wire UInt : UInt<8> + wire SInt : SInt<8> + + ; Test UInt<>() and SInt<>() as expressions + ; CHECK: firrtl.constant 42 + ; CHECK: firrtl.constant -10 + connect UInt, UInt<8>(42) + connect SInt, SInt<8>(-10)