/* Copyright (C) 1995 Bjoern Beutel. */
/* Description. =============================================================*/
/* This is the interactive program for morphological and syntactic analysis. */
/* Includes. ================================================================*/
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <setjmp.h>
#include <time.h>
#include <glib.h>
#include "basic.h"
#include "pools.h"
#include "values.h"
#include "symbols.h"
#include "files.h"
#include "rule_type.h"
#include "rules.h"
#include "analysis.h"
#include "input.h"
#include "commands.h"
#include "commands_interactive.h"
#include "options.h"
#include "display.h"
#include "malaga_lib.h"
#include "generation.h"
#include "debugger.h"
#include "breakpoints.h"
#include "cache.h"
#include "transmit.h"
#include "hangul.h"
/* Macros. ==================================================================*/
#define SAFE_STRING(s) ((s) != NULL ? (s) : (string_t) "")
/* Return an empty string if S == NULL. */
/* Variables. ===============================================================*/
static char_t *analysis_input; /* Input line for interactive analysis. */
static int_t debug_state_index; /* Index of state to debug or -1. */
static debug_mode_t state_debug_mode;
/* Debug mode to use for states to debug. */
static string_t analysis_file_name; /* Name of input file for analysis. */
/* Analysis result output. ==================================================*/
static void
display_result( void )
/* Generate result file and start program to display result. */
{
char_t *input;
string_t value_string;
string_t count_string;
value_t feat;
int_t result_count;
if (! analysis_has_nodes())
complain( "No analysis started." );
/* Print analysis result header. */
input = new_string_readable( analysis_input, NULL );
decode_hangul( &input );
if (use_display)
{
start_display_process();
fprintf( display_stream, "result\n" );
fprintf( display_stream, "%s\n", input );
}
else
{
if (! analysis_has_results())
printf( "No analyses of %s.\n", input );
else
printf( "Analyses of %s:\n", input );
}
free_mem( &input );
/* Print analysis results. */
result_count = 0;
for (feat = first_analysis_result();
feat != NULL;
feat = next_analysis_result())
{
result_count++;
if (use_display)
{
value_string = value_to_readable( feat, FALSE, -1 );
fprintf( display_stream, "%d {%s}\n", result_count, value_string );
free_mem( &value_string );
}
else
{
count_string = int_to_string( result_count );
value_string = value_to_readable(
feat, FALSE, g_utf8_strlen( count_string, -1 ) + 2 );
printf( "%s: %s\n", count_string, value_string );
free_mem( &value_string );
free_mem( &count_string );
}
}
/* Print analysis results footer. */
if (use_display)
{
fprintf( display_stream, "end\n" );
fflush( display_stream );
}
}
/*---------------------------------------------------------------------------*/
static void
display_tree( void )
/* Display analysis tree. */
{
analysis_node_t *node;
string_t node_type = NULL, value_string;
char_t *input;
char_t *surf_string;
if (! analysis_has_nodes())
complain( "No analysis started." );
start_display_process();
fprintf( display_stream, "tree\n" );
/* Print sentence that has been analysed. */
input = new_string_readable( analysis_input, NULL );
decode_hangul( &input );
fprintf( display_stream, "%s\n", input );
free_mem( &input );
/* Print nodes. */
for (node = get_first_analysis_node();
node != NULL;
node = get_next_analysis_node())
{
/* Print node index and type, parent index and rule name. */
switch (node->type)
{
case INTER_NODE: node_type = "inter"; break;
case BREAK_NODE: node_type = "break"; break;
case FINAL_NODE: node_type = "final"; break;
case UNFINAL_NODE: node_type = "unfinal"; break;
case PRUNED_NODE: node_type = "pruned"; break;
}
fprintf( display_stream, "%d %s %d \"%s\" ",
node->index, node_type, node->parent_index,
SAFE_STRING( node->rule_name ) );
/* Print link's surface and feature structure. */
if (node->link_surf != NULL)
{
surf_string = new_string_readable( node->link_surf, NULL );
decode_hangul( &surf_string );
}
else
surf_string = new_string( "", NULL );
value_string = value_to_readable( node->link_feat, FALSE, -1 );
fprintf( display_stream, "{%s} {%s} ", surf_string, value_string );
free_mem( &value_string );
free_mem( &surf_string );
/* Print result surface and feature structure. */
if (node->result_surf != NULL)
{
surf_string = new_string_readable( node->result_surf, NULL );
decode_hangul( &surf_string );
}
else
surf_string = new_string( "", NULL );
value_string = value_to_readable( node->result_feat, FALSE, -1 );
fprintf( display_stream, "{%s} {%s} ", surf_string, value_string );
free_mem( &value_string );
free_mem( &surf_string );
/* Print rule set. */
fprintf( display_stream, "\"%s\"\n", SAFE_STRING( node->rule_set ) );
free_analysis_node( &node );
}
fprintf( display_stream, "end\n" );
fflush( display_stream );
}
/*---------------------------------------------------------------------------*/
static void
display_after_analysis( void )
/* Display result in the modes that have been switched on after analysis. */
{
if (auto_result)
display_result();
if (auto_tree && use_display)
display_tree();
}
/*---------------------------------------------------------------------------*/
static void
do_result( string_t arguments )
/* Show result of last analysis. */
{
parse_end( &arguments );
if (! analysis_has_nodes())
complain( "No previous analysis." );
display_result();
}
static command_t result_command =
{
"result res", do_result,
"Show result of last analysis.\n"
"Usage: result\n"
};
/*---------------------------------------------------------------------------*/
static void
do_tree( string_t arguments )
/* Generate analysis tree file and start program to display tree. */
{
parse_end( &arguments );
if (! analysis_has_nodes())
complain( "No analysis started." );
if (! use_display)
complain( "Can only show a tree when using a display process." );
display_tree();
}
static command_t tree_command =
{
"tree t", do_tree,
"Display the analysis tree.\n"
"Usage: tree\n"
"In debug mode or after a rule execution error, the tree may be "
"incomplete.\n"
};
/* Analysis functions. ======================================================*/
static void
analyse_argument( grammar_t grammar, string_t arguments )
/* Analyse ARGUMENTS (or last analysis, if *ARGUMENTS == EOS).
* Use GRAMMAR (SYNTAX or MORPHOLOGY). */
{
if (*arguments == EOS)
{
if (analysis_input == NULL)
complain( "No previous analysis." );
}
else
{
free_mem( &analysis_input );
analysis_input = new_string( arguments, NULL );
preprocess_input( analysis_input, FALSE );
encode_hangul( &analysis_input );
}
debug_state = NULL;
analyse( grammar, analysis_input, TRUE, TRUE );
}
/*---------------------------------------------------------------------------*/
static void
analyse_line( grammar_t grammar, string_t arguments )
/* Analyse a word or a sentence in file FILE, line LINE_NO.
* ARGUMENTS must be of format "FILE LINE_NO".
* Use GRAMMAR. */
{
int_t line_number, current_line_number;
FILE *input_stream;
char_t *input_line;
input_stream = NULL;
TRY
{
/* Read arguments. */
line_number = parse_cardinal( &arguments );
if (*arguments != EOS)
{
free_mem( &analysis_file_name );
analysis_file_name = parse_absolute_path( &arguments, NULL );
}
parse_end( &arguments );
if (analysis_file_name == NULL)
complain( "Missing input file name." );
/* Read the line from input. */
input_stream = open_stream( analysis_file_name, "r" );
current_line_number = 0;
input_line = NULL;
TRY
{
do
{
check_user_break();
free_mem( &input_line );
current_line_number++;
input_line = read_line( input_stream );
} while (current_line_number < line_number && input_line != NULL);
}
IF_ERROR
print_text( error_text, " (line %d)", current_line_number );
END_TRY;
if (input_line == NULL)
{
complain( "\"%s\" contains only %d lines.",
name_in_path( analysis_file_name ), current_line_number - 1 );
}
preprocess_input( input_line, FALSE );
if (*input_line == EOS)
complain( "Line %d is empty.", line_number );
free_mem( &analysis_input );
analysis_input = input_line;
encode_hangul( &analysis_input );
}
FINALLY
close_stream( &input_stream, NULL );
END_TRY;
debug_state = NULL;
analyse( grammar, analysis_input, TRUE, TRUE );
}
/*---------------------------------------------------------------------------*/
static void
do_ma( string_t arguments )
/* Analyse ARGUMENTS morphologically. */
{
assert_not_in_debug_mode();
set_debug_mode( RUN_MODE, NULL );
analyse_argument( MORPHOLOGY, arguments );
display_after_analysis();
}
static command_t ma_command =
{
"ma", do_ma,
"Analyse the argument morphologically.\n"
"Usage:\n"
" ma INPUT -- Analyse INPUT.\n"
" ma -- Re-analyse last input.\n"
"\"ma\" can't be used in debug mode.\n"
};
/*---------------------------------------------------------------------------*/
static void
do_sa( string_t arguments )
/* Analyse ARGUMENTS syntactically. */
{
assert_not_in_debug_mode();
set_debug_mode( RUN_MODE, NULL );
analyse_argument( SYNTAX, arguments );
display_after_analysis();
}
static command_t sa_command =
{
"sa", do_sa,
"Analyse the argument syntactically.\n"
"Usage:\n"
" sa INPUT -- Analyse INPUT.\n"
" sa -- Re-analyse last input.\n"
"\"sa\" can't be used in debug mode.\n"
};
/*---------------------------------------------------------------------------*/
static void
do_ma_line( string_t arguments )
/* Analyse ARGUMENTS morphologically. */
{
assert_not_in_debug_mode();
set_debug_mode( RUN_MODE, NULL );
analyse_line( MORPHOLOGY, arguments );
display_after_analysis();
}
static command_t ma_line_command =
{
"ma-line mal", do_ma_line,
"Analyse a line in a file morphologically.\n"
"Usage:\n"
" ma-line LINE FILE -- Analyse LINE in FILE.\n"
"If FILE is omitted, the previous file name is used.\n"
"\"ma-line\" can't be used in debug mode.\n"
};
/*---------------------------------------------------------------------------*/
static void
do_sa_line( string_t arguments )
/* Analyse ARGUMENTS syntactically. */
{
assert_not_in_debug_mode();
set_debug_mode( RUN_MODE, NULL );
analyse_line( SYNTAX, arguments );
display_after_analysis();
}
static command_t sa_line_command =
{
"sa-line sal", do_sa_line,
"Analyse a line in a file syntactically.\n"
"Usage:\n"
" sa-line LINE [FILE] -- Analyse LINE in FILE.\n"
"If FILE is omitted, the previous file name is used.\n"
"\"sa-line\" can't be used in debug mode.\n"
};
/*---------------------------------------------------------------------------*/
static void analyse_input( grammar_t grammar, string_t input )
{
value_t value;
int_t i;
string_t string, count;
char_t *input_readable;
analysis_input = new_string( input, NULL );
input_readable = new_string_readable( input, NULL );
preprocess_input( analysis_input, FALSE );
encode_hangul( &analysis_input );
analyse( grammar, analysis_input, TRUE, TRUE );
if (analysis_has_results())
{
printf( "Results for %s:\n", input_readable );
i = 0;
for (value = first_analysis_result();
value != NULL;
value = next_analysis_result())
{
i++;
count = int_to_string( i );
string = value_to_readable( value, FALSE,
g_utf8_strlen( count, -1 ) + 2) ;
printf( "\n%s: %s\n", count, string );
free_mem( &string );
free_mem( &count );
}
}
else
printf( "No results for %s.\n", input_readable );
free( input_readable );
}
/* Debug support. ===========================================================*/
static void
display_where( void )
/* Print rule name, left and right surface. */
{
char_t *surf;
string_t file, rule;
int_t line;
source_of_instr( executed_rule_sys, pc, &line, &file, &rule );
printf( "At \"%s\", line %d, rule \"%s\".\n",
name_in_path( file ), line, rule );
/* Print state number and state's surface. */
surf = get_surface( STATE_SURFACE );
decode_hangul( &surf );
if (current_state != -1)
printf( "State: %d, surf: %s", current_state, surf );
else
printf( "Surf: %s", surf );
free_mem( &surf );
/* Print link's surface. */
surf = get_surface( LINK_SURFACE );
if (surf != NULL)
{
decode_hangul( &surf );
printf( ", link: %s", surf );
free_mem( &surf );
}
printf( ".\n" );
if (in_emacs_malaga_mode)
printf( "SHOW \"%s\":%d:0\n", file, line );
}
/*---------------------------------------------------------------------------*/
static void
malaga_debug_state( int_t state, bool_t enter )
/* Callback function for "analyse".
* This is called with ENTER == TRUE when successor rules for
* state with analysis node INDEX will be executed.
* It is called with ENTER == FALSE when successor rules for state with
* analysis node INDEX have been executed. */
{
if (state != debug_state_index)
return;
if (enter)
set_debug_mode( state_debug_mode, rule_system[ top_grammar ] );
else
{
state_debug_mode = get_debug_mode();
set_debug_mode( RUN_MODE, NULL );
}
}
/*---------------------------------------------------------------------------*/
static void
check_interactive_analysis( void )
{
if (analysis_input == NULL || analysis_input != last_analysis_input)
complain( "No interactive analysis." );
}
/*---------------------------------------------------------------------------*/
static void
do_debug_state( string_t arguments )
/* Analyse the last argument again and stop before executing the rules for a
* state whose tree node index is specified in ARGUMENTS. */
{
assert_not_in_debug_mode();
check_interactive_analysis();
debug_state_index = parse_int( &arguments );
parse_end( &arguments );
if (debug_state_index < 0 || debug_state_index >= state_count)
complain( "State not found." );
state_debug_mode = WALK_MODE;
debug_state = malaga_debug_state;
/* Debug mode is set by "malaga_debug_state" before and after
* rule application. */
analyse( top_grammar, analysis_input, TRUE, TRUE );
}
static command_t debug_state_command =
{
"debug-state debug-node dn", do_debug_state,
"Re-analyse the last analysis input.\n"
"Execute successor rule for given state in debug mode.\n"
"Usage: debug-state STATE_INDEX\n"
"Analysis is restarted for last input and switches to debug mode\n"
"when executing successor rules for state STATE_INDEX.\n"
"\"debug-state\" can't be used in debug mode.\n"
};
/*---------------------------------------------------------------------------*/
static void
do_debug_ma( string_t arguments )
/* Analyse ARGUMENTS morphologically.
* Execute morphology combination rules in debug mode. */
{
assert_not_in_debug_mode();
set_debug_mode( WALK_MODE, rule_system[ MORPHOLOGY ] );
analyse_argument( MORPHOLOGY, arguments );
}
static command_t debug_ma_command =
{
"debug-ma dma debug-mor ma-debug mad", do_debug_ma,
"Analyse morphologically. "
"Execute morphology combination rules in debug mode.\n"
"Usage:\n"
" debug-ma INPUT -- Analyse INPUT.\n"
" debug-ma -- Re-analyse the last analysis argument.\n"
"Rule execution stops at the first statement.\n"
"\"debug-ma\" can't be used in debug mode.\n"
};
/*---------------------------------------------------------------------------*/
static void
do_debug_sa( string_t arguments )
/* Analyse ARGUMENTS syntactically.
* Execute syntax combination rules in debug mode. */
{
assert_not_in_debug_mode();
set_debug_mode( WALK_MODE, rule_system[ SYNTAX ] );
analyse_argument( SYNTAX, arguments );
}
static command_t debug_sa_command =
{
"debug-sa dsa debug-syn sa-debug sad", do_debug_sa,
"Analyse syntactically. Execute syntax combination rules in debug mode.\n"
"Usage:\n"
" debug-sa INPUT -- Analyse INPUT.\n"
" debug-sa -- Re-analyse the last analysis argument.\n"
"Rule execution stops at the first statement.\n"
"\"debug-sa\" can't be used in debug mode.\n"
};
/*---------------------------------------------------------------------------*/
static void
do_debug_ma_line( string_t arguments )
/* Analyse a word in file FILE, line LINE_NO.
* ARGUMENTS must be of format "FILE LINE_NO".
* Execute morphology combination rules in debug mode. */
{
assert_not_in_debug_mode();
set_debug_mode( WALK_MODE, rule_system[ MORPHOLOGY ] );
analyse_line( MORPHOLOGY, arguments );
}
static command_t debug_ma_line_command =
{
"debug-ma-line dmal", do_debug_ma_line,
"Analyse a line in a file morphologically.\n"
"Execute morphology combination rules in debug mode.\n"
"Usage:\n"
" debug-ma-line LINE [FILE] -- Analyse LINE in FILE.\n"
"If FILE is omitted, the previous file name is used.\n"
"Rule execution stops at the first statement.\n"
"\"debug-ma-line\" can't be used in debug mode.\n"
};
/*---------------------------------------------------------------------------*/
static void
do_debug_sa_line( string_t arguments )
/* Analyse a sentence in file FILE, line LINE_NO.
* ARGUMENTS must be of format "FILE LINE_NO".
* Execute syntax combination rules in debug mode. */
{
assert_not_in_debug_mode();
set_debug_mode( WALK_MODE, rule_system[ SYNTAX ] );
analyse_line( SYNTAX, arguments );
}
static command_t debug_sa_line_command =
{
"debug-sa-line dsal", do_debug_sa_line,
"Analyse a line in a file syntactically.\n"
"Execute syntax combination rules in debug mode.\n"
"Usage:\n"
" debug-sa-line LINE [FILE] -- Analyse LINE in FILE.\n"
"If FILE is omitted, the previous file name is used.\n"
"Rule execution stops at the first statement.\n"
"\"debug-sa-line\" can't be used in debug mode.\n"
};
/* File analysis. ===========================================================*/
static void
write_output( string_t input, int_t line_number, string_t error_message,
FILE *output )
/* Write the result of the last analysis on OUTPUT. */
{
string_t line_number_string, state_count_string, buffer;
string_t value_string, result_number_string;
char_t *input_string;
int_t result_count;
value_t feat;
input_string = new_string_readable( input, NULL );
decode_hangul( &input_string );
line_number_string = int_to_string( line_number );
state_count_string = int_to_string( state_count );
if (error_message != NULL)
{
/* Print error result. */
if (*error_format != EOS)
{
buffer = replace_arguments( error_format, "slne",
input_string, line_number_string,
state_count_string, error_message );
fprintf( output, "%s\n", buffer );
free_mem( &buffer );
}
}
else if (! analysis_has_results())
{
/* Print unknown result. */
if (*unknown_format != EOS)
{
buffer = replace_arguments( unknown_format, "sln",
input_string, line_number_string,
state_count_string );
fprintf( output, "%s\n", buffer );
free_mem( &buffer );
}
}
else
{
if (result_as_list)
{
result_count = 0;
for (feat = first_analysis_result();
feat != NULL;
feat = next_analysis_result())
{
result_count++;
push_value( feat );
}
build_list( result_count );
value_string = value_to_readable( value_stack[ --top], FALSE, -1 );
buffer = replace_arguments( result_format, "slrfn",
input_string, line_number_string,
"0", value_string, state_count_string );
fprintf( output, "%s\n", buffer );
free_mem( &buffer );
free_mem( &value_string );
}
else
{
/* Print real results. */
result_count = 0;
for (feat = first_analysis_result();
feat != NULL;
feat = next_analysis_result())
{
result_count++;
if (*result_format != EOS)
{
result_number_string = int_to_string( result_count );
value_string = value_to_readable( feat, FALSE, -1 );
buffer = replace_arguments( result_format, "slrfn",
input_string, line_number_string,
result_number_string, value_string,
state_count_string );
fprintf( output, "%s\n", buffer );
free_mem( &buffer );
free_mem( &value_string );
free_mem( &result_number_string );
}
}
}
}
free_mem( &input_string );
free_mem( &line_number_string );
free_mem( &state_count_string );
if (ferror( output ))
complain( "Can't write result: %s.", strerror( errno ) );
}
/*---------------------------------------------------------------------------*/
static void
analyse_stream( grammar_t grammar,
FILE *input,
FILE *output,
FILE *statistics,
bool_t expect_quotes )
/* Analyse words or sentences in INPUT, write result to OUTPUT.
* Use GRAMMAR for analysis.
* If EXPECT_QUOTES == TRUE, expect quoted input lines and remove the quotes.
* Write statistic information to STATISTICS. */
{
volatile int_t analyses, recognised, results; /* Statistic information. */
volatile int_t combi_recognised, robust_recognised, errors, line_number;
volatile string_t item, error_message;
time_t start_time, stop_time;
char_t *input_line;
bool_t old_in_emacs_malaga_mode;
value_t feat;
double time_diff;
item = (grammar == MORPHOLOGY ? "wordform" : "sentence");
set_debug_mode( RUN_MODE, NULL );
debug_state = NULL;
robust_recognised = recognised = results = analyses = errors = 0;
combi_recognised = line_number = 0;
cache_hits = cache_accesses = 0;
time( &start_time );
input_line = NULL;
TRY
{
while (TRUE)
{
check_user_break();
free_mem( &input_line );
input_line = read_line( input );
if (input_line == NULL)
break;
line_number++;
preprocess_input( input_line, expect_quotes );
if (*input_line != EOS)
{
/* Analyse a non-empty line. */
old_in_emacs_malaga_mode = in_emacs_malaga_mode;
in_emacs_malaga_mode = FALSE;
error_message = NULL;
TRY
{
analyses++;
encode_hangul( &input_line );
analyse( grammar, input_line, FALSE, TRUE );
if (recognised_by_combi_rules)
combi_recognised++;
if (recognised_by_robust_rule)
robust_recognised++;
if (analysis_has_results())
recognised++;
for (feat = first_analysis_result();
feat != NULL;
feat = next_analysis_result())
{
results++;
}
}
IF_ERROR
{
error_message = error_text->buffer;
errors++;
RESUME;
}
FINALLY
in_emacs_malaga_mode = old_in_emacs_malaga_mode;
END_TRY;
write_output( input_line, line_number, error_message, output );
fflush( output );
}
}
}
IF_ERROR
{
printf( "%s (line %d)\n", error_text->buffer, line_number );
RESUME;
}
FINALLY
free_mem( &input_line );
END_TRY;
time( &stop_time );
if (analyses == 0)
fprintf( statistics, "No %ss analysed.\n", item );
else
{
time_diff = difftime( stop_time, start_time );
fprintf( statistics, "Analysed %ss: %d\n", item, analyses );
fprintf( statistics, "Recognised: %d (%.2f%%)\n",
recognised, (100.0 * recognised) / analyses );
if (combi_recognised > 0)
{
fprintf( statistics, "Recognised by combi rules: %d (%.2f%%)\n",
combi_recognised, (100.0 * combi_recognised) / analyses );
}
if (robust_recognised > 0)
{
fprintf( statistics, "Recognised by robust rule: %d (%.2f%%)\n",
robust_recognised, (100.0 * robust_recognised) / analyses );
}
if (errors > 0)
{
fprintf( statistics, "Error-creating %ss: %d (%.2f%%)\n",
item, errors, (100.0 * errors) / analyses );
}
if (results > 0)
{
fprintf( statistics, "Results per %s: %.4G\n",
item, ((double) results / (double) recognised) );
}
if (time_diff > 0)
{
fprintf( statistics, "Analysis run time: %d sec\n",
(int_t) time_diff );
fprintf( statistics, "Avg. %ss per second: %d\n",
item, (int_t) (analyses / time_diff) );
}
if (cache_accesses > 0)
{
fprintf( statistics, "Cache accesses: %d\n",
cache_accesses );
fprintf( statistics, "Cache hits: %d (%.2f%%)\n",
cache_hits, (100.0 * cache_hits) / cache_accesses );
}
}
}
/*---------------------------------------------------------------------------*/
static void
analyse_file( string_t arguments, grammar_t grammar )
/* Open the file with name in ARGUMENTS, which must contain a word list
* or sentence list, analyse all its lines according to GRAMMAR,
* and write the results to a file with extension ".out". */
{
string_t result_file_name;
FILE *input_stream, *output_stream;
input_stream = output_stream = NULL;
result_file_name = NULL;
TRY
{
if (*arguments != EOS)
{
free_mem( &analysis_file_name );
analysis_file_name = parse_absolute_path( &arguments, NULL );
}
if (*analysis_file_name == NULL)
complain( "Missing input file name." );
if (*arguments != EOS)
result_file_name = parse_absolute_path( &arguments, NULL );
else
result_file_name = concat_strings( analysis_file_name, ".out", NULL );
parse_end( &arguments );
input_stream = open_stream( analysis_file_name, "r" );
output_stream = open_stream( result_file_name, "w" );
analyse_stream( grammar, input_stream, output_stream, stdout, FALSE );
}
FINALLY
{
close_stream( &input_stream, analysis_file_name );
close_stream( &output_stream, result_file_name );
free_mem( &result_file_name );
}
END_TRY;
}
/*---------------------------------------------------------------------------*/
static void
do_ma_file( string_t arguments )
/* Analyse file in ARGUMENTS morphologically. */
{
assert_not_in_debug_mode();
analyse_file( arguments, MORPHOLOGY );
}
static command_t ma_file_command =
{
"ma-file maf", do_ma_file,
"Analyse a word list file.\n"
"Usage: ma-file [INPUT_FILE [OUTPUT_FILE]]\n"
"INPUT_FILE must contain one word form on each line.\n"
"The results are written to \"OUTPUT_FILE\".\n"
"If INPUT_FILE is missing, the previous file name is used.\n"
"If OUTPUT_FILE is missing, they are written to \"INPUT_FILE.out\".\n"
"\"ma-file\" can't be used in debug mode.\n"
};
/*---------------------------------------------------------------------------*/
static void
do_sa_file( string_t arguments )
/* Analyse file in ARGUMENTS syntactically. */
{
assert_not_in_debug_mode();
if (rule_system[ SYNTAX ] == NULL)
complain( "Syntax rule file not loaded." );
analyse_file( arguments, SYNTAX );
}
static command_t sa_file_command =
{
"sa-file saf", do_sa_file,
"Analyse a sentence list file.\n"
"Usage: sa-file [INPUT_FILE [OUTPUT_FILE]]\n"
"INPUT_FILE must contain one sentence on each line.\n"
"The results are written to \"OUTPUT_FILE\".\n"
"If INPUT_FILE is missing, the previous file name is used.\n"
"If OUTPUT_FILE is missing, they are written to \"INPUT_FILE.out\".\n"
"\"sa-file\" can't be used in debug mode.\n"
};
/*===========================================================================*/
static void
do_clear_cache( string_t arguments )
/* Clear the wordform analysis cache. */
{
parse_end( &arguments );
clear_cache();
}
static command_t clear_cache_command =
{
"clear-cache", do_clear_cache,
"Clear the wordform analysis cache.\n"
"Usage: clear-cache\n"
};
/*---------------------------------------------------------------------------*/
static void
do_info( string_t arguments )
/* Show information about morphology and syntax. */
{
parse_end( &arguments );
printf( "%s", grammar_info->buffer );
}
static command_t info_command =
{
"info", do_info,
"Show information about current grammar.\n"
"Usage: info\n"
};
/*---------------------------------------------------------------------------*/
/* The commands that can be called interactively, in alphabetical order. */
static command_t *malaga_commands[] =
{
&backtrace_command, &break_command, &clear_cache_command, &continue_command,
&debug_ma_command, &debug_ma_line_command, &debug_sa_command,
&debug_sa_line_command, &debug_state_command, &delete_command, &down_command,
&finish_command, &frame_command, &get_command, &help_command, &info_command,
&list_command, &ma_command, &ma_file_command, &ma_line_command, &mg_command,
&next_command, &print_command, &quit_command, &result_command, &run_command,
&sa_command, &sa_file_command, &sa_line_command, &set_command, &sg_command,
&step_command, &transmit_command, &tree_command, &up_command,
&variables_command, &walk_command, &where_command,
NULL
};
/*---------------------------------------------------------------------------*/
int
main( int argc, char *argv[] )
/* The main function of "malaga". */
{
enum {INTERACTIVE_MODE, MORPHOLOGY_MODE, SYNTAX_MODE} malaga_mode;
int_t i;
string_t project_file, input;
rule_sys_name_t rule_systems[2]; /* Rule systems for debugger. */
grammar_t grammar; /* Grammar for batch mode. */
bool_t expect_quotes;
expect_quotes = FALSE;
malaga_mode = INTERACTIVE_MODE;
input = NULL;
init_basic( "malaga" );
/* Parse arguments. */
if (argc == 2)
{
if (strcmp_no_case( argv[1], "--version" ) == 0
|| strcmp_no_case( argv[1], "-version" ) == 0
|| strcmp_no_case( argv[1], "-v" ) == 0)
{
program_message();
exit(0);
}
else if (strcmp_no_case( argv[1], "--help" ) == 0
|| strcmp_no_case( argv[1], "-help" ) == 0
|| strcmp_no_case( argv[1], "-h" ) == 0)
{
printf( "Analyse words and/or sentences according to a Malaga grammar.\n"
"\n"
"Usage:\n"
"malaga PROJECT-FILE "
"-- Start interactive malaga.\n"
"malaga PROJECT-FILE -m[orphology] "
"-- Run as a morphology filter.\n"
"malaga PROJECT-FILE -s[yntax] "
"-- Run as a syntax filter.\n"
"malaga -v[ersion] "
"-- Print version information.\n"
"malaga -h[elp] "
"-- Print this help.\n\n"
"Option \"-i[nput] STRING\" makes malaga analyse STRING.\n"
"Option \"-q[uoted]\" expects quoted lines in filter mode.\n"
"PROJECT_FILE must end on \".pro\".\n" );
exit(0);
}
}
project_file = NULL;
for (i = 1; i < argc; i++)
{
if (has_extension( argv[i], "pro" ))
set_file_name( &project_file, argv[i] );
else if (strcmp_no_case( argv[i], "-morphology" ) == 0
|| strcmp_no_case( argv[i], "-m" ) == 0)
{
malaga_mode = MORPHOLOGY_MODE;
}
else if (strcmp_no_case( argv[i], "-syntax" ) == 0
|| strcmp_no_case( argv[i], "-s" ) == 0)
{
malaga_mode = SYNTAX_MODE;
}
else if (strcmp_no_case( argv[i], "-input" ) == 0
|| strcmp_no_case( argv[i], "-i" ) == 0)
{
if (argv[ ++i ] == NULL)
complain( "Missing string after \"-input\"." );
if (input != NULL)
complain( "Redundant \"-input\"." );
input = argv[i];
if (! g_utf8_validate( input, -1, NULL ))
complain( "Illegal UTF-8 character." );
}
else if (strcmp_no_case( argv[i], "-quoted" ) == 0
|| strcmp_no_case( argv[i], "-q" ) == 0)
{
expect_quotes = TRUE;
}
else
complain( "Illegal argument \"%s\".", argv[i] );
}
if (project_file == NULL)
complain( "Missing project file name." );
init_malaga( project_file );
if (malaga_mode == INTERACTIVE_MODE)
{
if (input != NULL)
complain( "Need \"-morphology\" or \"-syntax\"." );
init_debugger( display_where, malaga_commands );
rule_systems[0].rule_sys = rule_system[ MORPHOLOGY ];
rule_systems[0].name = "mor";
rule_systems[1].rule_sys = rule_system[ SYNTAX ];
rule_systems[1].name = "syn";
init_breakpoints( 2, rule_systems );
init_generation();
program_message();
command_loop( program_name, malaga_commands );
terminate_generation();
terminate_breakpoints();
terminate_debugger();
}
else
{
grammar = (malaga_mode == MORPHOLOGY_MODE) ? MORPHOLOGY : SYNTAX;
if (rule_system[ grammar ] == NULL)
complain( "Rule file not loaded." );
if (input != NULL)
analyse_input( grammar, input );
else
analyse_stream( grammar, stdin, stdout, stderr, expect_quotes );
}
stop_display_process();
terminate_malaga();
free_mem( &analysis_input );
free_mem( &analysis_file_name );
free_mem( &project_file );
terminate_basic();
return 0;
}
/* End of file. =============================================================*/