#
############################################################################
# File : parser.pl #
# Author : Supun Ruwanpura #
# Date : 19-10-2000 #
############################################################################
use strict;
############################################################################
# Global variables
use vars qw( %table_columns );
use vars qw( %table_relationships );
############################################################################
# Modular level variables
### Following variables used to test this parser only ###
my $dataFile = "data1.txt"; ### Input Data file (for testing) ###
my $outputFile = "output.txt"; ### Output results file (for testing) ###
my $debug_on = 0; ### Enable testing of this parser ###
my $trace_on = 0; ### Enable tracing the parser output ###
my $grammar; ### SQ-HAL parser grammar ###
my $parser_file; ### name of the file which contains parser object ###
############################################################################
# combine table names into parser accepted string
sub load_parser
############################################################################
{
$parser_file = "sq_hal_${db_type}_${db_source}_${user}";
### replace invalid names in the file name ###
$parser_file =~ s/[\\\/:*?<>|]//g;
### if parser file not found then load grammar ###
if (!eval{require "${parser_file}.pm"})
{
print "Creating the parser...\n";
### Create the grammar ###
$grammar = do "grammar.pl" or warn "Bad Grammar!";
### Replace table_names ###
my $tables_str = table_names_to_parser_str();
$grammar =~ s/TABLES/$tables_str/;
### Replace column names ###
my $columns_str = column_names_to_parser_str();
$grammar =~ s/FIELDS/$columns_str/;
}
else
{
print "Loading the parser from the file '${parser_file}.pm'...\n";
do "grammar_func.pl";
}
### initialize variables such as database structure, etc. ###
initialize_vars();
use Parse::RecDescent;
## Enable tracing of the parser ###
if ($trace_on) { $RD_TRACE = 1; }
### Load the parser from the file or ###
### create the parser if the file is not available ###
$parser = eval{require "${parser_file}.pm"}
? $parser_file->new()
: Parse::RecDescent->new($grammar)
or warn "Bad Parser!";
### if the parser file is not found, then the parser need to be saved ###
if (!eval{require "${parser_file}.pm"})
{
$save_parser = 1;
}
### if testing this parser only then do the following code ###
if ($debug_on)
{
### Open the input data file
open(DATA, "< $dataFile") || die $!;
open(OUT, "> $outputFile") || die $!;
$| = 1;
### Parse each line of data ###
while ()
{
if (!/^#/ && !/^[\s]*\n/) ### Ignore commented or empty lines ###
{
print "> ";
#sleep 1;
print;
### Translate the grammar ###
my $SQL = $parser->translate("\L$_");
### Print the translated output to the screen and output file ###
print "$_$SQL\n";
print OUT "> $_$SQL\n";
}
else
{
print OUT "$_";
}
}
### Close files ##
close(DATA);
close(OUT);
### Exit to the system ###
exit;
}
}
############################################################################
# save the parser to a file
sub save_parser()
############################################################################
{
print "Saving the parser to the file '${parser_file}.pm'...";
### if exist, then delete the parser file ###
if (eval{require "${parser_file}.pm"})
{
eval { unlink "${parser_file}.pm"; };
}
### save the parser to file ###
eval { $parser->Save($parser_file); };
### do not need to save the parser in the neer future again ###
$save_parser = 0;
}
############################################################################
# learn new rule by the parser
sub extend_parser
############################################################################
{
my ($rule, $str) = ($_[0], $_[1]); ### parser rule and the new learn string ###
my $grammar = qq{ $rule : $str }; ### new grammar to be learn ###
$parser->Extend($grammar); ### extend the parser grammar ###
#$save_parser = 1; ### parser has been changed and therefore need to save ###
### print the newly learn grammar ###
print "Learn grammar:\n$grammar\n";
}
############################################################################
# initialize table relationships and table-column relationships
sub initialize_vars
############################################################################
{
@table_columns{get_table_names()} = ();
foreach my $table (keys(%table_columns))
{
my %tmp;
@tmp{get_column_names($table)} = ();
foreach (keys(%tmp))
{
$tmp{$_} = "1";
}
$table_columns{$table} = { %tmp };
}
### load the table relationships from the file ###
if (open(DB, "${parser_file}.db"))
{
### read each of the table relationship ###
while ()
{
chomp($_);
next unless s/^(.*?):\s*//;
my $tbl1 = qq{$1};
for my $field ( split /;/ )
{
my ($tbl2, $val) = split(",", $field);
$table_relationships{ qq{$tbl1} }{ qq{$tbl2} } = qq{$val};
}
}
close(DB); ### close the file ###
}
}
############################################################################
# combine table names into parser accepted string
sub save_db_info()
############################################################################
{
open(DB, "> ${parser_file}.db");
### save each table relationship to a file
foreach my $tbl1 (keys(%table_relationships))
{
print DB "$tbl1:";
foreach (keys(%{$table_relationships{$tbl1}}))
{
print DB "$_,", $table_relationships{$tbl1}{$_},";" ;
}
print DB "\n";
}
close(DB); ### close the file ###
}
############################################################################
# combine table names into parser accepted string
sub table_names_to_parser_str()
############################################################################
{
print "Reading table names...\n";
### Get table names for the current database ###
%table_columns = ();
@table_columns{get_table_names()} = ();
print " Table names: ", join(", ", keys(%table_columns)), "\n\n";
### Create the parser recognised string ###
my $tables_str = "";
foreach my $table ( keys(%table_columns) )
{
### table words need to be lower case ###
my $table_words = "\L${table}?";
$tables_str .= "/${table_words}/{'${table}'}|";
}
chop($tables_str); ### Remove the last '|' character ###
return $tables_str;
}
############################################################################
# combine all table columns into parser accepted string
sub column_names_to_parser_str()
############################################################################
{
use Lingua::EN::Inflect ':ALL';
### read column names for each table ###
my $columns_str = "";
foreach ( keys(%table_columns) )
{
if ( $_ )
{
print "Read column names for '$_'...\n";
my $current_table = $_;
### Get the column names for the given table ###
my @columns = get_column_names($_);
print " Column names: ", join(", ", @columns), "\n\n";
### Create parser recognised string ###
foreach my $column (@columns)
{
### column words need to be lower case ###
my $column_words = "\L" . PL_N($column) . "|$column";
$columns_str .= "/${column_words}/{'${column}'}|";
}
}
}
chop($columns_str); ### Remove the last "|" character ###
return $columns_str;
}
1; ### so the 'do' command succeeds ###