| // Copyright 2017-2020 The Verible Authors. | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| #ifndef VERIBLE_VERILOG_PARSER_VERILOG_LEXICAL_CONTEXT_H_ | |
| #define VERIBLE_VERILOG_PARSER_VERILOG_LEXICAL_CONTEXT_H_ | |
| #include <iosfwd> | |
| #include <iterator> | |
| #include <stack> | |
| #include <vector> | |
| #include "common/text/token_info.h" | |
| #include "common/text/token_stream_view.h" | |
| #include "common/util/with_reason.h" | |
| namespace verilog { | |
| // TODO(fangism): move all of these _classes into an internal namespace. | |
| // Helper state machine to parse optional labels after certain keywords. | |
| class _KeywordLabelStateMachine { | |
| public: | |
| // Updates the state machine, by looking ahead at the next token's enum. | |
| void UpdateState(int); | |
| // Returns true if a statement or item could start in this state. | |
| bool ItemMayStart() const { | |
| return state_ == kItemStart || state_ == kGotLabelableKeyword; | |
| } | |
| private: | |
| enum State { | |
| kItemStart, // Could be the start of an item. | |
| kItemMiddle, // After the start of an item. | |
| kGotLabelableKeyword, // Seen a keyword that can accept a label. | |
| kGotColonExpectingLabel, | |
| }; | |
| State state_ = kItemStart; | |
| }; | |
| // Helper state machine for tracking constraint_block and constraint_set in the | |
| // grammar. | |
| class _ConstraintBlockStateMachine { | |
| public: | |
| _ConstraintBlockStateMachine() = default; | |
| bool IsActive() const { return !states_.empty(); } | |
| // Updates the state machine, by looking ahead at the next token's enum. | |
| void UpdateState(int); | |
| // Returns disambiguated enum for '->' token. | |
| int InterpretToken(int token_enum) const; | |
| // Show representation (for debugging). | |
| std::ostream& Dump(std::ostream&) const; | |
| private: | |
| void DeferInvalidToken(int token_enum); | |
| // See grammar for constraint_block_item and constraint_expression. | |
| enum State { | |
| kBeginningOfBlockItemOrExpression, // list item (home state) | |
| // kIgnoreUntilSemicolon is applicable to: | |
| // "soft ...;" | |
| // "unique { ... };" | |
| // "disable soft ...;" | |
| // "solve ... before ...;" (from constraint_block_item) | |
| kIgnoreUntilSemicolon, | |
| // constraint_expression | |
| // : expression_or_dist ; | |
| // | expression -> constraint_set | |
| kExpectingExpressionOrImplication, | |
| kGotIf, // if ... | |
| kGotForeach, // foreach ... | |
| // constraint_set | |
| // : constraint_expression | |
| // | '{' { constraint_expression , }** '}' | |
| // | |
| // This is the final nonterminal for: if-clause, else-clause, foreach-body, | |
| // and RHS of expression -> constraint_set (constraint-implication) | |
| kExpectingConstraintSet, | |
| kInParenExpression, // balance until ')' | |
| kInBraceExpression, // balance until '}' | |
| }; | |
| // Constraint sets are nestable, so we need a stack. | |
| // Each level of this stack represents a level of constraint block or | |
| // constraint set, both of which are wrapped in { }. | |
| std::stack<State> states_; | |
| }; | |
| inline std::ostream& operator<<(std::ostream& os, | |
| const _ConstraintBlockStateMachine& s) { | |
| return s.Dump(os); | |
| } | |
| // Helper state machine to parse randomize calls. | |
| class _RandomizeCallStateMachine { | |
| public: | |
| bool IsActive() const { return state_ != kNone; } | |
| // Updates the state machine, by looking ahead at the next token's enum. | |
| void UpdateState(int); | |
| int InterpretToken(int) const; | |
| private: | |
| enum State { | |
| kNone, // Not in a andomize call. | |
| kGotRandomizeKeyword, | |
| kOpenedVariableList, | |
| kClosedVariableList, | |
| kGotWithKeyword, | |
| kInsideWithIdentifierList, | |
| kExpectConstraintBlock, | |
| kInsideConstraintBlock, | |
| }; | |
| // TODO(fangism): do we need a stack? can randomize appear inside a | |
| // randomize_call? | |
| State state_ = kNone; | |
| // Nested state machine. | |
| _ConstraintBlockStateMachine constraint_block_tracker_; | |
| }; | |
| // Helper state machine to parse (non-extern) constraint declarations. | |
| class _ConstraintDeclarationStateMachine { | |
| public: | |
| bool IsActive() const { return state_ != kNone; } | |
| // Updates the state machine, by looking ahead at the next token's enum. | |
| void UpdateState(int); | |
| int InterpretToken(int) const; | |
| private: | |
| enum State { | |
| kNone, | |
| kGotConstraintKeyword, | |
| kGotConstraintIdentifier, | |
| // TODO(fangism): handle out-of-line definitions: constraint foo::bar ... | |
| kInsideConstraintBlock, | |
| }; | |
| State state_ = kNone; | |
| // Nested state machine. | |
| _ConstraintBlockStateMachine constraint_block_tracker_; | |
| }; | |
| // This state machine keeps track of semicolons in a range enclosed by | |
| // a pair of (keyword) tokens. This is useful in disambiguating between | |
| // grammatic constructs that can conflict due to optionality of a former | |
| // list. See the definition bodies of property_declaration and | |
| // sequence_declaration for examples. | |
| // For additional fun, both declarations accept an optional ';' right before | |
| // the terminating keyword, but that one should *not* count as the 'last'. | |
| class _LastSemicolonStateMachine { | |
| public: | |
| _LastSemicolonStateMachine(int trigger, int stop, int replacement) | |
| : trigger_token_enum_(trigger), | |
| finish_token_enum_(stop), | |
| semicolon_replacement_(replacement) {} | |
| void UpdateState(verible::TokenInfo*); | |
| protected: | |
| enum State { | |
| kNone, | |
| kActive, // in betwen two keywords | |
| }; | |
| // This is the token_enum that activates this state machine. | |
| const int trigger_token_enum_; | |
| // This is the token_enum that de-activates this state machine. | |
| const int finish_token_enum_; | |
| // This is the token_enum that should replace the last ';'. | |
| const int semicolon_replacement_; | |
| State state_ = kNone; | |
| // Keeps track of the last semicolons. Upon de-activation, the last | |
| // semicolon will be replaced. Technically, we only need a two-slot queue, | |
| // but a CircularBuffer is overkill. | |
| std::stack<verible::TokenInfo*> semicolons_; | |
| // One token look-back. | |
| verible::TokenInfo* previous_token_ = nullptr; | |
| }; | |
| // A structure for tracking context needed to disambiguate tokens. | |
| // The main input is a token stream coming from a lexer, and the main consumer | |
| // is a parser that accepts a token stream. | |
| // The vast majority of tokens should pass through unchanged. | |
| // The ones that are changed are those that require context-based | |
| // disambiguation. | |
| // This should be designed in a manner that is forgiving of invalid inputs, | |
| // i.e. improperly balanced code should never cause fatal errors. | |
| // This class should maintain just enough state to correctly | |
| // transform token enums on *valid* lexical streams. | |
| // | |
| // Design philosophy: This class itself is a state machine while employing | |
| // smaller, simpler, concurrent state machines. | |
| // The constituent state machines also scan the input token stream and | |
| // update their states accordingly. | |
| // The smaller state machines will be inactive most of the time, and activated | |
| // on certain keywords in certain states. | |
| class LexicalContext { | |
| public: | |
| LexicalContext(); | |
| ~LexicalContext() = default; | |
| // Not copy-able. | |
| LexicalContext(const LexicalContext&) = delete; | |
| LexicalContext& operator=(const LexicalContext&) = delete; | |
| // Re-writes some token enums in-place using context-sensitivity. | |
| // This function must re-tag tokens enumerated (_TK_*), see verilog.y and | |
| // verilog.lex for all such enumerations. | |
| // This function must accept both valid and invalid inputs, but is only | |
| // required to operate correctly on valid inputs. | |
| // Postcondition: tokens_view's tokens must not be tagged with (_TK_*) | |
| // enumerations. | |
| void TransformVerilogSymbols( | |
| const verible::TokenStreamReferenceView& tokens_view) { | |
| // TODO(fangism): Using a stream interface would further decouple the input | |
| // iteration from output iteration. | |
| for (auto iter : tokens_view) { | |
| _AdvanceToken(&*iter); | |
| } | |
| } | |
| protected: // Allow direct testing of some methods. | |
| // Reads a single token, and may alter it depending on internal state. | |
| void _AdvanceToken(verible::TokenInfo*); | |
| // Changes the enum of a token where disambiguation is needed. | |
| int _InterpretToken(int token_enum) const; | |
| // Changes the enum of a token (in-place) without changing internal state. | |
| void _MutateToken(verible::TokenInfo* token) const { | |
| token->set_token_enum(_InterpretToken(token->token_enum())); | |
| } | |
| // Updates the internally tracked state without touching the token. | |
| void _UpdateState(const verible::TokenInfo& token); | |
| // State functions: | |
| bool ExpectingStatement() const; | |
| verible::WithReason<bool> ExpectingBodyItemStart() const; | |
| bool InFlowControlHeader() const; | |
| bool InModuleDeclarationHeader() const { | |
| return in_module_declaration_ && !in_module_body_; | |
| } | |
| bool InFunctionDeclarationHeader() const { | |
| return in_function_declaration_ && !in_function_body_; | |
| } | |
| bool InTaskDeclarationHeader() const { | |
| return in_task_declaration_ && !in_task_body_; | |
| } | |
| bool InAnyDeclaration() const; | |
| bool InAnyDeclarationHeader() const; | |
| bool InStatementContext() const { | |
| return in_function_body_ || in_task_body_ || | |
| in_initial_always_final_construct_; | |
| } | |
| const verible::TokenInfo* previous_token_ = nullptr; | |
| // Non-nestable states can be represented without a stack. | |
| // Do not bother trying to accommodate malformed input token sequences. | |
| bool in_module_declaration_ = false; | |
| bool in_module_body_ = false; | |
| bool in_initial_always_final_construct_ = false; | |
| bool in_function_declaration_ = false; | |
| bool in_function_body_ = false; | |
| bool in_task_declaration_ = false; | |
| bool in_task_body_ = false; | |
| // TODO(fangism): class_declaration, interface_declaration, udp_declaration... | |
| // Extern declarations cannot be nested, so a single bool suffices. | |
| bool in_extern_declaration_ = false; | |
| bool previous_token_finished_header_ = true; | |
| // Nestable states need to be tracked with a stack. | |
| // Tracks if, for, case blocks. | |
| struct FlowControlState { | |
| const verible::TokenInfo* start; | |
| // When this is false, the state is still in the header, which is: | |
| // if (...) | |
| // for (...) | |
| // case (...) (including other case-variants) | |
| bool in_body = false; // starts in header state | |
| explicit FlowControlState(const verible::TokenInfo* token) : start(token) {} | |
| }; | |
| std::vector<FlowControlState> flow_control_stack_; | |
| // Tracks optional labels after certain keywords. | |
| _KeywordLabelStateMachine keyword_label_tracker_; | |
| // Tracks parsing state inside randomize_call. | |
| _RandomizeCallStateMachine randomize_call_tracker_; | |
| // Tracks parsing state inside randomize_call. | |
| _ConstraintDeclarationStateMachine constraint_declaration_tracker_; | |
| // Tracks last semicolon in property_declarations so that it can be | |
| // re-enumerated to help disambiguate. | |
| _LastSemicolonStateMachine property_declaration_tracker_; | |
| // Tracks last semicolon in sequence_declarations so that it can be | |
| // re-enumerated to help disambiguate. | |
| _LastSemicolonStateMachine sequence_declaration_tracker_; | |
| // Tracks begin-end paired sequence blocks in all contexts (generate blocks, | |
| // function/task statements, flow-control constructs...). | |
| // Every 'begin' token will be pushed onto this stack. | |
| // Every 'end' token will pop the stack (safely). | |
| // Accepts invalid input, which does not guarantee begin-end balancing. | |
| // Does not care about optional labels after these keywords. | |
| // | |
| // e.g. | |
| // ... // stack initially empty | |
| // begin // pushes onto this stack | |
| // ... | |
| // begin // pushes onto this stack | |
| // ... | |
| // end // pops off of this stack | |
| // ... | |
| // end // pops off of this stack | |
| // | |
| std::vector<const verible::TokenInfo*> block_stack_; | |
| // Tracks open-close paired tokens like parentheses and brackets and braces. | |
| std::vector<const verible::TokenInfo*> balance_stack_; | |
| }; | |
| } // namespace verilog | |
| #endif // VERIBLE_VERILOG_PARSER_VERILOG_LEXICAL_CONTEXT_H_ |