From 3c810e1a74d529af14367a64bcaca012b2cd17b6 Mon Sep 17 00:00:00 2001 From: Charles Baker Date: Mon, 17 Apr 2023 13:08:59 +1200 Subject: [PATCH] Generate state machine from kernel items only --- src/lalr/GrammarGenerator.cpp | 82 ++++++++++++++++++++++++++++-- src/lalr/GrammarGenerator.hpp | 5 +- src/lalr/GrammarItem.cpp | 14 ++++- src/lalr/GrammarItem.hpp | 1 + src/lalr/GrammarProductionLess.cpp | 17 +++++++ src/lalr/GrammarProductionLess.hpp | 17 +++++++ src/lalr/GrammarState.cpp | 8 +++ src/lalr/GrammarState.hpp | 1 + src/lalr/GrammarSymbol.cpp | 19 +++++++ src/lalr/GrammarSymbol.hpp | 5 ++ src/lalr/lalr.forge | 1 + 11 files changed, 164 insertions(+), 6 deletions(-) create mode 100644 src/lalr/GrammarProductionLess.cpp create mode 100644 src/lalr/GrammarProductionLess.hpp diff --git a/src/lalr/GrammarGenerator.cpp b/src/lalr/GrammarGenerator.cpp index 93e61a4..b99871e 100644 --- a/src/lalr/GrammarGenerator.cpp +++ b/src/lalr/GrammarGenerator.cpp @@ -11,6 +11,7 @@ #include "GrammarSymbol.hpp" #include "GrammarAction.hpp" #include "GrammarCompiler.hpp" +#include "GrammarProductionLess.hpp" #include "ParserState.hpp" #include "ParserAction.hpp" #include "ParserSymbol.hpp" @@ -24,8 +25,10 @@ using std::set; using std::vector; +using std::multimap; using std::unique_ptr; using std::shared_ptr; +using std::make_shared; using namespace lalr; /** @@ -107,6 +110,7 @@ int GrammarGenerator::generate( Grammar& grammar, ErrorPolicy* error_policy ) calculate_first(); calculate_follow(); calculate_precedence_of_productions(); + calculate_reachable_productions(); generate_states( start_symbol_, end_symbol_, symbols_ ); } @@ -240,13 +244,26 @@ std::shared_ptr GrammarGenerator::goto_( const std::shared_ptr& items = state->items(); for ( set::const_iterator item = items.begin(); item != items.end(); ++item ) { - if ( item->next_node(symbol) ) + const GrammarSymbol* next_symbol = item->next_symbol(); + if ( next_symbol ) { - goto_state->add_item( item->production(), item->position() + 1 ); + if ( next_symbol == &symbol ) + { + goto_state->add_item( item->production(), item->position() + 1 ); + } + + const multimap& reachable_productions = next_symbol->reachable_productions_by_first_symbol(); + auto i = reachable_productions.find( &symbol ); + while ( i != reachable_productions.end() && i->first == &symbol ) + { + GrammarProduction* production = i->second; + const int position = 1; + goto_state->add_item( production, position ); + ++i; + } } } - closure( goto_state ); return goto_state; } @@ -620,6 +637,49 @@ void GrammarGenerator::calculate_follow() } } +/** +// Calculate the productions reachable by right-most derivation from each +// non-terminal. +// +// This pre-calculation is used to add items to goto states that would have +// been added for transitions from non-kernel items. This is needed because +// non-kernel items are excluded in an efficient construction of LALR(1) +// states. The pre-calculation provides a more efficient and convenient +// means of discovering items added for transitions from non-kernel items. +*/ +void GrammarGenerator::calculate_reachable_productions() +{ + for ( const auto& symbol : symbols_ ) + { + set productions; + calculate_reachable_productions_for_symbol( *symbol, &productions ); + for ( const auto& production : productions ) + { + symbol->append_reachable_production( production ); + } + } +} + +/** +// Recursively calculate productions reachable by derivation from symbol. +*/ +void GrammarGenerator::calculate_reachable_productions_for_symbol( const GrammarSymbol& symbol, std::set* productions ) +{ + LALR_ASSERT( productions ); + for ( const auto& production : symbol.productions() ) + { + const GrammarSymbol* first_symbol = production->symbol_by_position( 0 ); + if ( first_symbol ) + { + bool inserted = productions->insert( production ).second; + if ( inserted ) + { + calculate_reachable_productions_for_symbol( *first_symbol, productions ); + } + } + } +} + /** // Generate the states for a grammar with \e symbols starting with // \e start_symbol and ending when \e end_symbol is accepted. @@ -641,7 +701,7 @@ void GrammarGenerator::generate_states( const GrammarSymbol* start_symbol, const if ( !start_symbol->productions().empty() ) { - std::shared_ptr start_state( new GrammarState() ); + shared_ptr start_state = make_shared(); start_state->add_item( start_symbol->productions().front(), 0 ); closure( start_state ); states_.insert( start_state ); @@ -681,6 +741,20 @@ void GrammarGenerator::generate_states( const GrammarSymbol* start_symbol, const } } } + + vector> states; + states.reserve( states_.size() ); + for ( const auto& state : states_ ) + { + states.push_back( state ); + } + + states_.clear(); + for ( const auto& state : states ) + { + closure( state ); + states_.insert( state ); + } generate_indices_for_states(); diff --git a/src/lalr/GrammarGenerator.hpp b/src/lalr/GrammarGenerator.hpp index 93215fd..7dcda0b 100644 --- a/src/lalr/GrammarGenerator.hpp +++ b/src/lalr/GrammarGenerator.hpp @@ -4,6 +4,7 @@ #include "RegexToken.hpp" #include "GrammarSymbolLess.hpp" #include "GrammarStateLess.hpp" +#include "GrammarProductionLess.hpp" #include #include #include @@ -64,12 +65,14 @@ private: void check_for_error_symbol_on_left_hand_side_errors(); void check_for_implicit_terminal_duplicate_associativity(); void calculate_identifiers(); - void calculate_terminal_and_non_terminal_symbols(); + void calculate_terminal_and_non_terminal_symbols(); void calculate_implicit_terminal_symbols(); void calculate_precedence_of_productions(); void calculate_symbol_indices(); void calculate_first(); void calculate_follow(); + void calculate_reachable_productions(); + void calculate_reachable_productions_for_symbol( const GrammarSymbol& symbol, std::set* productions ); void generate_states( const GrammarSymbol* start_symbol, const GrammarSymbol* end_symbol, const std::vector>& symbols ); void generate_indices_for_states(); void generate_reduce_transitions(); diff --git a/src/lalr/GrammarItem.cpp b/src/lalr/GrammarItem.cpp index 2f7cf79..ed75537 100644 --- a/src/lalr/GrammarItem.cpp +++ b/src/lalr/GrammarItem.cpp @@ -94,7 +94,19 @@ bool GrammarItem::dot_at_end() const */ bool GrammarItem::next_node( const GrammarSymbol& symbol ) const { - return production_->symbol_by_position(position_) == &symbol; + return production_->symbol_by_position( position_ ) == &symbol; +} + +/** +// Get the symbol after the dot in this item. +// +// @return +// The next symbol, i.e. following the dot, in this item or null if there +// is no such symbol. +*/ +const GrammarSymbol* GrammarItem::next_symbol() const +{ + return production_->symbol_by_position( position_ ); } /** diff --git a/src/lalr/GrammarItem.hpp b/src/lalr/GrammarItem.hpp index 2f23dbb..afff49f 100644 --- a/src/lalr/GrammarItem.hpp +++ b/src/lalr/GrammarItem.hpp @@ -29,6 +29,7 @@ public: bool dot_at_beginning() const; bool dot_at_end() const; bool next_node( const GrammarSymbol& symbol ) const; + const GrammarSymbol* next_symbol() const; const std::set& lookahead_symbols() const; bool operator<( const GrammarItem& item ) const; int add_lookahead_symbols( const std::set& lookahead_symbols ) const; diff --git a/src/lalr/GrammarProductionLess.cpp b/src/lalr/GrammarProductionLess.cpp new file mode 100644 index 0000000..1d6d67b --- /dev/null +++ b/src/lalr/GrammarProductionLess.cpp @@ -0,0 +1,17 @@ +// +// GrammarProductionLess.cpp +// Copyright (c) Charles Baker. All rights reserved. +// + +#include "GrammarProductionLess.hpp" +#include "GrammarProduction.hpp" +#include "assert.hpp" + +using namespace lalr; + +bool GrammarProductionLess::operator()( const GrammarProduction* lhs, const GrammarProduction* rhs ) const +{ + LALR_ASSERT( lhs->index() >= 0 ); + LALR_ASSERT( rhs->index() >= 0 ); + return lhs->index() < rhs->index(); +} diff --git a/src/lalr/GrammarProductionLess.hpp b/src/lalr/GrammarProductionLess.hpp new file mode 100644 index 0000000..5092573 --- /dev/null +++ b/src/lalr/GrammarProductionLess.hpp @@ -0,0 +1,17 @@ +#pragma once + +namespace lalr +{ + +class GrammarProduction; + +/** +// Indirectly compare productions through pointers. +*/ +class GrammarProductionLess +{ +public: + bool operator()( const GrammarProduction* lhs, const GrammarProduction* rhs ) const; +}; + +} diff --git a/src/lalr/GrammarState.cpp b/src/lalr/GrammarState.cpp index 4975ff7..d5f131f 100644 --- a/src/lalr/GrammarState.cpp +++ b/src/lalr/GrammarState.cpp @@ -23,6 +23,14 @@ GrammarState::GrammarState() { } +GrammarState::GrammarState( const GrammarState& state ) +: items_( state.items_ ) +, transitions_( state.transitions_ ) +, processed_( state.processed_ ) +, index_( state.index_ ) +{ +} + /** // Get the items in this state. // diff --git a/src/lalr/GrammarState.hpp b/src/lalr/GrammarState.hpp index 9e7e8c1..c014fc3 100644 --- a/src/lalr/GrammarState.hpp +++ b/src/lalr/GrammarState.hpp @@ -24,6 +24,7 @@ class GrammarState public: GrammarState(); + GrammarState( const GrammarState& state ); const std::set& items() const; const GrammarTransition* find_transition_by_symbol( const GrammarSymbol* symbol ) const; diff --git a/src/lalr/GrammarSymbol.cpp b/src/lalr/GrammarSymbol.cpp index 13deb2f..446ec22 100644 --- a/src/lalr/GrammarSymbol.cpp +++ b/src/lalr/GrammarSymbol.cpp @@ -11,6 +11,7 @@ using std::set; using std::vector; using std::shared_ptr; +using std::make_pair; using namespace lalr; GrammarSymbol::GrammarSymbol( const std::string& lexeme ) @@ -95,6 +96,16 @@ const std::vector& GrammarSymbol::productions() const return productions_; } +const std::multimap& GrammarSymbol::reachable_productions_by_first_symbol() const +{ + return reachable_productions_by_first_symbol_; +} + +std::multimap::const_iterator GrammarSymbol::find_reachable_productions( const GrammarSymbol& first_symbol ) const +{ + return reachable_productions_by_first_symbol_.find( &first_symbol ); +} + /** // Get the implicit terminal for this symbol. // @@ -197,6 +208,14 @@ void GrammarSymbol::append_production( GrammarProduction* production ) productions_.push_back( production ); } +void GrammarSymbol::append_reachable_production( GrammarProduction* production ) +{ + LALR_ASSERT( production ); + const GrammarSymbol* first_symbol = production->symbol_by_position( 0 ); + LALR_ASSERT( first_symbol ); + reachable_productions_by_first_symbol_.insert( make_pair(first_symbol, production) ); +} + /** // Calculate the identifier for this symbol. */ diff --git a/src/lalr/GrammarSymbol.hpp b/src/lalr/GrammarSymbol.hpp index e1bc784..88a1492 100644 --- a/src/lalr/GrammarSymbol.hpp +++ b/src/lalr/GrammarSymbol.hpp @@ -8,6 +8,7 @@ #include #include #include +#include namespace lalr { @@ -29,6 +30,7 @@ class GrammarSymbol std::set first_; ///< The symbols that can start this symbol in a production or regular expression. std::set follow_; ///< The symbols that can follow this symbol in a production or regular expression. std::vector productions_; ///< The productions that reduce to this symbol. + std::multimap reachable_productions_by_first_symbol_; ///< The productions reachable by right-most derivation from this symbol by their first symbol. public: GrammarSymbol( const std::string& lexeme ); @@ -46,6 +48,8 @@ public: const std::set& first() const; const std::set& follow() const; const std::vector& productions() const; + const std::multimap& reachable_productions_by_first_symbol() const; + std::multimap::const_iterator find_reachable_productions( const GrammarSymbol& first_symbol ) const; GrammarSymbol* implicit_terminal() const; bool matches( const std::string& lexeme, SymbolType symbol_type ) const; @@ -60,6 +64,7 @@ public: void set_nullable( bool nullable ); void set_referenced_in_precedence_directive( bool referenced_in_precedence_directive ); void append_production( GrammarProduction* production ); + void append_reachable_production( GrammarProduction* production ); void calculate_identifier(); void replace_by_non_terminal( const GrammarSymbol* non_terminal_symbol ); int add_symbol_to_first( const GrammarSymbol* symbol ); diff --git a/src/lalr/lalr.forge b/src/lalr/lalr.forge index 78864d3..394e4ea 100644 --- a/src/lalr/lalr.forge +++ b/src/lalr/lalr.forge @@ -18,6 +18,7 @@ for _, cc in toolsets('^cc.*') do 'GrammarItem.cpp', 'GrammarParser.cpp', 'GrammarProduction.cpp', + 'GrammarProductionLess.cpp', 'GrammarState.cpp', 'GrammarStateLess.cpp', 'GrammarSymbol.cpp',