mirror of
https://github.com/cwbaker/lalr.git
synced 2026-09-26 16:33:18 +03:00
447 lines
14 KiB
C++
447 lines
14 KiB
C++
//
|
|
// RegexGenerator.cpp
|
|
// Copyright (c) Charles Baker. All rights reserved.
|
|
//
|
|
|
|
#include "RegexGenerator.hpp"
|
|
#include "ErrorCode.hpp"
|
|
#include "RegexToken.hpp"
|
|
#include "RegexCompiler.hpp"
|
|
#include "LexerState.hpp"
|
|
#include "LexerTransition.hpp"
|
|
#include "LexerAction.hpp"
|
|
#include "RegexItem.hpp"
|
|
#include "RegexState.hpp"
|
|
#include "RegexAction.hpp"
|
|
#include "RegexNode.hpp"
|
|
#include "RegexSyntaxTree.hpp"
|
|
#include "RegexParser.hpp"
|
|
#include "ErrorPolicy.hpp"
|
|
#include "assert.hpp"
|
|
#include <limits.h>
|
|
|
|
using std::set;
|
|
using std::pair;
|
|
using std::vector;
|
|
using std::make_pair;
|
|
using std::unique_ptr;
|
|
using namespace lalr;
|
|
|
|
/**
|
|
// Constructor.
|
|
*/
|
|
RegexGenerator::RegexGenerator()
|
|
: error_policy_( nullptr )
|
|
, syntax_tree_( nullptr )
|
|
, actions_()
|
|
, states_()
|
|
, start_state_( nullptr )
|
|
, ranges_()
|
|
{
|
|
syntax_tree_ = new RegexSyntaxTree;
|
|
}
|
|
|
|
RegexGenerator::~RegexGenerator()
|
|
{
|
|
delete syntax_tree_;
|
|
}
|
|
|
|
const std::vector<std::unique_ptr<RegexAction>>& RegexGenerator::actions() const
|
|
{
|
|
return actions_;
|
|
}
|
|
|
|
const std::set<std::unique_ptr<RegexState>, RegexStateLess>& RegexGenerator::states() const
|
|
{
|
|
return states_;
|
|
}
|
|
|
|
const RegexState* RegexGenerator::start_state() const
|
|
{
|
|
return start_state_;
|
|
}
|
|
|
|
/**
|
|
// Fire an error from this generator.
|
|
//
|
|
// @param line
|
|
// The line number that the error occured on.
|
|
//
|
|
// @param column
|
|
// The column number that the error occured on.
|
|
//
|
|
// @param error
|
|
// The error code that indicates the error that occured.
|
|
//
|
|
// @param format
|
|
// A printf-style format string describing the error that occured (assumed
|
|
// not null).
|
|
//
|
|
// @param ...
|
|
// Parameters as described by *format*.
|
|
*/
|
|
void RegexGenerator::fire_error( int line, int column, int error, const char* format, ... ) const
|
|
{
|
|
if ( error_policy_ )
|
|
{
|
|
va_list args;
|
|
va_start( args, format );
|
|
error_policy_->lalr_error( line, column, error, format, args );
|
|
va_end( args );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
// Fire a message to be printed from this generator.
|
|
//
|
|
// @param format
|
|
// A printf-style format string that desribes the message to print.
|
|
//
|
|
// @param ...
|
|
// Parameters as described by \e format.
|
|
*/
|
|
void RegexGenerator::fire_printf( const char* format, ... ) const
|
|
{
|
|
if ( error_policy_ )
|
|
{
|
|
LALR_ASSERT( format );
|
|
va_list args;
|
|
va_start( args, format );
|
|
error_policy_->lalr_vprintf( format, args );
|
|
va_end( args );
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
// Add a new or retrieve an existing RegexAction.
|
|
//
|
|
// If the parser already has a RegexAction whose identifier
|
|
// matches \e identifier then that RegexAction is returned. Otherwise
|
|
// a new RegexAction is created, added to this `Generator` so that it
|
|
// can be returned later if necessary, and returned from this call.
|
|
//
|
|
// @param identifier
|
|
// The identifier of the RegexAction to add or retrieve.
|
|
//
|
|
// @return
|
|
// The RegexAction whose identifier matches \e identifier or null if
|
|
// \e identifier is empty.
|
|
*/
|
|
const RegexAction* RegexGenerator::add_lexer_action( const std::string& identifier )
|
|
{
|
|
LALR_ASSERT( !identifier.empty() );
|
|
if ( !identifier.empty() )
|
|
{
|
|
vector<std::unique_ptr<RegexAction> >::const_iterator i = actions_.begin();
|
|
while ( i != actions_.end() && (*i)->identifier() != identifier )
|
|
{
|
|
++i;
|
|
}
|
|
if ( i == actions_.end() )
|
|
{
|
|
unique_ptr<RegexAction> action( new RegexAction(int(actions_.size()), identifier) );
|
|
actions_.push_back( move(action) );
|
|
i = actions_.end() - 1;
|
|
}
|
|
return i->get();
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
int RegexGenerator::generate( const std::string& regular_expression, void* symbol, ErrorPolicy* error_policy )
|
|
{
|
|
error_policy_ = error_policy;
|
|
actions_.clear();
|
|
states_.clear();
|
|
start_state_ = nullptr;
|
|
ranges_.clear();
|
|
|
|
RegexToken token( TOKEN_REGULAR_EXPRESSION, 0, 0, symbol, regular_expression );
|
|
syntax_tree_->reset( token, this );
|
|
generate_states( *syntax_tree_, &states_, &start_state_ );
|
|
error_policy_ = nullptr;
|
|
return 0;
|
|
}
|
|
|
|
int RegexGenerator::generate( const std::vector<RegexToken>& tokens, ErrorPolicy* error_policy )
|
|
{
|
|
error_policy_ = error_policy;
|
|
actions_.clear();
|
|
states_.clear();
|
|
start_state_ = nullptr;
|
|
ranges_.clear();
|
|
|
|
syntax_tree_->reset( tokens, this );
|
|
generate_states( *syntax_tree_, &states_, &start_state_ );
|
|
error_policy_ = nullptr;
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
// Generate the state that results from accepting any character in the range
|
|
// [\e begin, \e end) from \e state.
|
|
//
|
|
// @param state
|
|
// The state to generate from.
|
|
//
|
|
// @param begin
|
|
// The begin character in the range to accept to generate the goto state.
|
|
//
|
|
// @param end
|
|
// The end character in the range to accept to generate the goto state.
|
|
//
|
|
// @return
|
|
// The state generated when accepting [\e begin, \e end) from \e state.
|
|
*/
|
|
std::unique_ptr<RegexState> RegexGenerator::goto_( const RegexState* state, int begin, int end )
|
|
{
|
|
LALR_ASSERT( state );
|
|
LALR_ASSERT( begin != INVALID_BEGIN_CHARACTER && begin != INVALID_END_CHARACTER );
|
|
LALR_ASSERT( begin <= end );
|
|
|
|
std::unique_ptr<RegexState> goto_state( new RegexState );
|
|
const std::set<RegexItem>& items = state->get_items();
|
|
for ( std::set<RegexItem>::const_iterator item = items.begin(); item != items.end(); ++item )
|
|
{
|
|
std::set<RegexNode*, RegexNodeLess> next_nodes = item->next_nodes( begin, end );
|
|
if ( !next_nodes.empty() )
|
|
{
|
|
goto_state->add_item( next_nodes );
|
|
}
|
|
}
|
|
return goto_state;
|
|
}
|
|
|
|
/**
|
|
// Generate the states for a LexerStateMachine from \e syntax_tree.
|
|
//
|
|
// @param syntax_tree
|
|
// The RegexSyntaxTree to get the RegexNodes from that are then used to generate
|
|
// states.
|
|
//
|
|
// @param states
|
|
// The set of states to populate from the output of the RegexSyntaxTree (assumed
|
|
// not null).
|
|
//
|
|
// @param start_state
|
|
// A variable to receive the starting state for the lexical analyzer
|
|
// (assumed not null).
|
|
*/
|
|
void RegexGenerator::generate_states( const RegexSyntaxTree& syntax_tree, std::set<std::unique_ptr<RegexState>, RegexStateLess>* states, const RegexState** start_state )
|
|
{
|
|
LALR_ASSERT( states );
|
|
LALR_ASSERT( states->empty() );
|
|
LALR_ASSERT( start_state );
|
|
LALR_ASSERT( !*start_state );
|
|
|
|
if ( !syntax_tree.empty() && syntax_tree.errors() == 0 )
|
|
{
|
|
std::unique_ptr<RegexState> state( new RegexState() );
|
|
state->add_item( syntax_tree.node()->get_first_positions() );
|
|
generate_symbol_for_state( state.get() );
|
|
*start_state = state.get();
|
|
states->insert( move(state) );
|
|
|
|
int added = 1;
|
|
while ( added > 0 )
|
|
{
|
|
added = 0;
|
|
for ( std::set<std::unique_ptr<RegexState>, RegexStateLess>::const_iterator i = states->begin(); i != states->end(); ++i )
|
|
{
|
|
RegexState* state = i->get();
|
|
LALR_ASSERT( state );
|
|
|
|
if ( !state->is_processed() )
|
|
{
|
|
state->set_processed( true );
|
|
|
|
// Create the distinct ranges of characters that can be
|
|
// transitioned on from the current state.
|
|
clear();
|
|
const std::set<RegexItem>& items = state->get_items();
|
|
for ( std::set<RegexItem>::const_iterator item = items.begin(); item != items.end(); ++item )
|
|
{
|
|
const std::set<RegexNode*, RegexNodeLess>& next_nodes = item->next_nodes();
|
|
for ( std::set<RegexNode*, RegexNodeLess>::const_iterator j = next_nodes.begin(); j != next_nodes.end(); ++j )
|
|
{
|
|
const RegexNode* next_node = *j;
|
|
LALR_ASSERT( next_node );
|
|
if ( !next_node->is_end() )
|
|
{
|
|
insert( next_node->get_begin_character(), next_node->get_end_character() );
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create a goto state and a transition from the current
|
|
// state for each distinct range.
|
|
vector<pair<int, bool> >::const_iterator j = ranges_.begin();
|
|
while ( j != ranges_.end() )
|
|
{
|
|
int begin = (j + 0)->first;
|
|
int end = (j + 1)->first;
|
|
LALR_ASSERT( begin < end );
|
|
|
|
std::unique_ptr<RegexState> goto_state = goto_( state, begin, end );
|
|
if ( !goto_state->get_items().empty() )
|
|
{
|
|
auto existing_goto_state = states->find( goto_state );
|
|
if ( existing_goto_state == states->end() )
|
|
{
|
|
state->add_transition( begin, end, goto_state.get() );
|
|
generate_symbol_for_state( goto_state.get() );
|
|
states->insert( move(goto_state) );
|
|
added += 1;
|
|
}
|
|
else
|
|
{
|
|
state->add_transition( begin, end, existing_goto_state->get() );
|
|
}
|
|
}
|
|
|
|
++j;
|
|
if ( !j->second )
|
|
{
|
|
++j;
|
|
LALR_ASSERT( j == ranges_.end() || j->second );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
generate_indices_for_states();
|
|
}
|
|
|
|
/**
|
|
// Generate indices for the generated states.
|
|
*/
|
|
void RegexGenerator::generate_indices_for_states()
|
|
{
|
|
int index = 0;
|
|
for ( auto i = states_.begin(); i != states_.end(); ++i )
|
|
{
|
|
RegexState* state = i->get();
|
|
LALR_ASSERT( state );
|
|
state->set_index( index );
|
|
++index;
|
|
}
|
|
}
|
|
|
|
/**
|
|
// Generate the matching symbol for \e state if it has one.
|
|
//
|
|
// @param state
|
|
// The state to generate a matching symbol for.
|
|
*/
|
|
void RegexGenerator::generate_symbol_for_state( RegexState* state ) const
|
|
{
|
|
LALR_ASSERT( state );
|
|
|
|
int line = INT_MAX;
|
|
RegexTokenType type = TOKEN_NULL;
|
|
const RegexToken* token = NULL;
|
|
|
|
const std::set<RegexItem>& items = state->get_items();
|
|
for ( std::set<RegexItem>::const_iterator item = items.begin(); item != items.end(); ++item )
|
|
{
|
|
std::set<RegexNode*, RegexNodeLess>::const_iterator i = item->next_nodes().begin();
|
|
while ( i != item->next_nodes().end() )
|
|
{
|
|
const RegexNode* node = *i;
|
|
LALR_ASSERT( node );
|
|
|
|
if ( node->is_end() && node->get_token() )
|
|
{
|
|
if ( node->get_token()->type() > type )
|
|
{
|
|
line = node->get_token()->line();
|
|
type = node->get_token()->type();
|
|
token = node->get_token();
|
|
}
|
|
else if ( node->get_token()->type() == type && node->get_token()->line() < line )
|
|
{
|
|
line = node->get_token()->line();
|
|
type = node->get_token()->type();
|
|
token = node->get_token();
|
|
}
|
|
else if ( node->get_token()->type() == type && node->get_token()->line() == line )
|
|
{
|
|
LALR_ASSERT( type != TOKEN_NULL );
|
|
LALR_ASSERT( line != INT_MAX );
|
|
LALR_ASSERT( token );
|
|
if ( !token->conflicted_with(node->get_token()) )
|
|
{
|
|
fire_error( token->line(), token->column(), LEXER_ERROR_SYMBOL_CONFLICT, "'%s' and '%s' conflict but are both defined on line %d", token->lexeme().c_str(), node->get_token()->lexeme().c_str(), token->line() );
|
|
token->add_conflicted_with( node->get_token() );
|
|
}
|
|
}
|
|
}
|
|
|
|
++i;
|
|
}
|
|
}
|
|
|
|
state->set_symbol( token ? token->symbol() : NULL );
|
|
}
|
|
|
|
/**
|
|
// Clear the current distinct ranges maintained by this RegexGenerator.
|
|
*/
|
|
void RegexGenerator::clear()
|
|
{
|
|
ranges_.clear();
|
|
}
|
|
|
|
/**
|
|
// Insert the range [\e begin, \e end) into the current distinct ranges for
|
|
// this RegexGenerator.
|
|
//
|
|
// The ranges are stored as a vector of pair<int, bool>. The first element of
|
|
// the pair represents the character and the second element represents whether
|
|
// or not that character is considered in or out.
|
|
//
|
|
// This is done so that transitions can be efficiently calculated for
|
|
// independent ranges of characters. For example if a state has three next
|
|
// nodes that represent characters in the the ranges [0, 256), [0, 32), and
|
|
// [0, 64) then three goto states should be generated with transitions on
|
|
// [0, 32), [32, 64), and [64, 256) respectively.
|
|
//
|
|
// @param begin
|
|
// The begin character in the range of characters to insert.
|
|
//
|
|
// @param end
|
|
// The end character in the range of characters to insert.
|
|
*/
|
|
void RegexGenerator::insert( int begin, int end )
|
|
{
|
|
bool in = false;
|
|
|
|
vector<pair<int, bool> >::iterator i = ranges_.begin();
|
|
while ( i != ranges_.end() && i->first < begin )
|
|
{
|
|
in = i->second;
|
|
++i;
|
|
}
|
|
|
|
if ( i == ranges_.end() || i->first != begin )
|
|
{
|
|
i = ranges_.insert( i, make_pair(begin, true) );
|
|
++i;
|
|
}
|
|
|
|
while ( i != ranges_.end() && i->first < end )
|
|
{
|
|
in = i->second;
|
|
i->second = true;
|
|
++i;
|
|
}
|
|
|
|
if ( i == ranges_.end() || i->first != end )
|
|
{
|
|
ranges_.insert( i, make_pair(end, in) );
|
|
}
|
|
}
|