/* Copyright (C) 1996 Bjoern Beutel. */
/* Description. =============================================================*/
/* The generation commands for malaga. */
/* Includes. ================================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <glib.h>
#include "basic.h"
#include "pools.h"
#include "values.h"
#include "input.h"
#include "commands.h"
#include "rule_type.h"
#include "rules.h"
#include "lexicon.h"
#include "analysis.h"
#include "debugger.h"
#include "hangul.h"
#include "generation.h"
/* Types. ===================================================================*/
typedef struct /* A running generation LAG state. */
{
list_node_t *next; /* Next segment state. */
value_t feat; /* Feature structure of this state. */
int_t rule_set; /* Rule set of this state. */
} segment_state_t;
typedef struct /* A segment of generation output. */
{
list_node_t *next; /* Next segment. */
string_t surface; /* Surface of this segment. */
list_t states; /* List of running states after combination. */
} segment_t;
typedef struct /* A feature structure node for an item. */
{
list_node_t *next; /* Next item_feat. */
value_t value; /* Feature structure. */
} item_feat_t;
typedef struct /* Segment of which a word or sentence may consist. */
{
list_node_t *next; /* Next item. */
string_t surf; /* Surface of this item. */
list_t feat_list; /* Feature structures for this item. */
} item_t;
/* Variables. ===============================================================*/
static grammar_t grammar; /* Grammar used for generation. */
static int_t result_count; /* Index of the current word form. */
static int_t segment_count;
static int_t max_segment_count; /* User limit on segments in a word form. */
static list_t segments;
static rule_type_t current_rule_type;
static list_t items;
/* Functions. ===============================================================*/
static void
combine_surface( text_t *text, segment_t *segment )
{
segment_t *next_segment;
if (segment != NULL)
{
next_segment = (segment_t *) segment->next;
combine_surface( text, next_segment );
if (grammar == SYNTAX && next_segment != NULL)
add_char_to_text( text, ' ' );
add_to_text( text, segment->surface );
}
}
/*---------------------------------------------------------------------------*/
static char_t *
get_surface_local( surface_t surface_type )
/* Return surface SURFACE_TYPE for currently executed rule.
* The result must be freed after use. */
{
text_t *text;
char_t *string;
text = new_text();
switch (surface_type)
{
case RESULT_SURFACE:
combine_surface( text, (segment_t *) segments.first );
break;
case STATE_SURFACE:
if (current_rule_type == COMBI_RULE)
combine_surface( text, (segment_t *) segments.first->next );
else
combine_surface( text, (segment_t *) segments.first );
break;
case LINK_SURFACE:
if (current_rule_type == COMBI_RULE)
add_to_text( text, ((segment_t *) segments.first)->surface );
break;
default:
complain( "Internal error." );
}
string = new_string_readable( text->buffer, NULL );
free_text( &text );
return string;
}
/*---------------------------------------------------------------------------*/
static void
add_end_state_local( value_t feat )
/* Print end state, consisting of FEAT. */
{
char_t *surf;
/* Print index of word form. */
surf = get_surface_local( RESULT_SURFACE );
result_count++;
decode_hangul( &surf );
printf( "%d: %s\n", result_count, surf );
free_mem( &surf );
}
/*---------------------------------------------------------------------------*/
static void
add_running_state_local( value_t feat, int_t rule_set )
/* Add running state, consisting of FEAT and RULE_SET. */
{
segment_t *segment;
segment_state_t *state;
segment = (segment_t *) segments.first;
state = new_node( &segment->states, sizeof( segment_state_t ), LIST_END );
state->feat = new_value( feat );
state->rule_set = rule_set;
}
/*---------------------------------------------------------------------------*/
static void
push_segment( string_t surface )
/* Push a new segment with name SURFACE. */
{
segment_t *segment;
segment = new_node( &segments, sizeof( segment_t ), LIST_START );
segment->surface = surface;
clear_list( &segment->states );
segment_count++;
}
/*---------------------------------------------------------------------------*/
static void
pop_segment( void )
/* Pop the topmost segment. */
{
segment_state_t *state;
segment_t *segment;
segment = (segment_t *) segments.first;
segment_count--;
FOREACH_FREE( state, segment->states )
free_mem( &state->feat );
free_first_node( &segments );
}
/*---------------------------------------------------------------------------*/
static void
execute_rules( value_t state_feat, int_t rule_set, item_t *item )
/* Add ITEM to LAG state (STATE_FEAT, RULE_SET).
* Save the resulting states in SEGMENTS.FIRST->STATES or print them
* if they are end states. */
{
rule_sys_t *rule_sys;
int_t *rule_p;
item_feat_t *link_feat;
rule_t *rule;
rule_sys = rule_system[ grammar ];
FOREACH( link_feat, item->feat_list )
{
for (rule_p = rule_sys->rule_sets + rule_set; *rule_p >= 0; rule_p++)
{
rule = rule_sys->rules + *rule_p;
if (rule->type == COMBI_RULE)
{
current_rule_type = COMBI_RULE;
top = 0;
push_value( state_feat );
push_value( link_feat->value);
if (rule->param_count >= 3)
push_string_value( item->surf, NULL );
if (rule->param_count >= 4)
push_number_value( segment_count );
execute_rule( rule_sys, *rule_p );
}
}
}
}
/*---------------------------------------------------------------------------*/
static void
generate_local( void )
/* Generate all word forms or sentences (according to GRAMMAR)
* that are successors of STATES and print them immediately. */
{
item_t *item;
segment_t *segment;
segment_state_t *state;
int_t *rule_p;
rule_sys_t *rule_sys;
rule_t *rule;
rule_sys = rule_system[ grammar ];
segment = (segment_t *) segments.first;
check_user_break();
/* Execute end rules first. */
FOREACH( state, segment->states )
{
for (rule_p = rule_sys->rule_sets + state->rule_set;
*rule_p >= 0;
rule_p++)
{
rule = rule_sys->rules + *rule_p;
if (rule->type == END_RULE)
{
current_rule_type = END_RULE;
top = 0;
push_value( state->feat );
if (rule->param_count >= 2)
push_string_value( "", NULL );
execute_rule( rule_sys, *rule_p );
}
}
}
/* Don't execute combi_rules if too many segments are to be combined. */
if (segment_count >= max_segment_count)
return;
/* Execute rules with all ITEMS. */
FOREACH( item, items )
{
push_segment( item->surf );
FOREACH( state, segment->states )
execute_rules( state->feat, state->rule_set, item );
if (((segment_t *) segments.first)->states.first != NULL)
generate_local();
pop_segment();
}
}
/*---------------------------------------------------------------------------*/
static void
generate( void )
/* Generate a sentence or a word form */
{
item_t *item;
rule_sys_t *rule_sys;
rule_sys = rule_system[ grammar ];
while (segments.first != NULL)
pop_segment();
segment_count = result_count = 0;
add_running_state = add_running_state_local;
add_end_state = add_end_state_local;
get_surface = get_surface_local;
set_debug_mode( RUN_MODE, NULL );
/* Execute all rules that add the first item to the empty start. */
FOREACH( item, items )
{
push_segment( item->surf );
execute_rules( rule_sys->values + rule_sys->initial_feat,
rule_sys->initial_rule_set, item );
if (((segment_t *) segments.first)->states.first != NULL)
generate_local();
pop_segment();
}
}
/*---------------------------------------------------------------------------*/
static void
free_item_feat_list( item_t *item )
/* Free the feature structures in ITEM. */
{
item_feat_t *feat;
FOREACH_FREE( feat, item->feat_list )
free_mem( &feat->value );
}
/*---------------------------------------------------------------------------*/
static void
free_items( void )
/* Free the item list. */
{
item_t *item;
FOREACH_FREE( item, items )
{
free_item_feat_list( item );
free_mem( &item->surf );
}
}
/*---------------------------------------------------------------------------*/
static void
generate_command( string_t arguments )
/* Generate sentences or words from items, depending on GRAMMAR. */
{
item_t *item;
item_feat_t *feat;
value_t value;
string_t surf_end;
char_t *surf;
assert_not_in_debug_mode();
if (rule_system[ grammar ] == NULL)
{
complain( "%s rule file not loaded.",
grammar == SYNTAX ? "Syntax": "Morphology" );
}
max_segment_count = parse_cardinal( &arguments );
if (*arguments != EOS)
{
/* Read new items. */
free_items();
while (*arguments != EOS)
{
surf = parse_word( &arguments );
encode_hangul( &surf );
item = new_node( &items, sizeof( item_t ), LIST_END );
item->surf = surf;
clear_list( &item->feat_list );
}
}
/* Create feature structures for items. */
FOREACH( item, items )
{
free_item_feat_list( item );
if (grammar == MORPHOLOGY)
{
search_for_prefix( item->surf );
while (get_next_prefix( &surf_end, &value ))
{
if (*surf_end == EOS) /* Found prefix that covers the whole string. */
{
feat = new_node( &item->feat_list, sizeof( item_feat_t ), LIST_END );
feat->value = new_value( value );
}
}
}
else
{
analyse( MORPHOLOGY, item->surf, FALSE, TRUE );
for (value = first_analysis_result();
value != NULL;
value = next_analysis_result())
{
feat = new_node( &item->feat_list, sizeof( item_feat_t ), LIST_END );
feat->value = new_value( value );
}
}
}
generate();
FOREACH( item, items )
free_item_feat_list( item );
}
/*---------------------------------------------------------------------------*/
static void
do_mg( string_t arguments )
/* Generate morphologically. */
{
grammar = MORPHOLOGY;
generate_command( arguments );
}
command_t mg_command =
{
"mg", do_mg,
"Generate all word forms that consist only of the given allomorphs.\n"
"Usage:\n"
" mg MAX_ALLO_COUNT ALLOMORPHS -- Use ALLOMORPHS\n"
" mg MAX_ALLO_COUNT -- Use allomorphs of last generation command.\n"
"\"mg\" can't be used in debug mode.\n"
};
/*---------------------------------------------------------------------------*/
static void
do_sg( string_t arguments )
/* Generate syntactically. */
{
grammar = SYNTAX;
generate_command( arguments );
}
command_t sg_command =
{
"sg", do_sg,
"Generate all sentences that consist only of the given word forms.\n"
"Usage:\n"
" sg MAX_WORD_COUNT WORDS -- use WORDS\n"
" sg MAX_WORD_COUNT -- Use words of last generation command.\n"
"\"sg\" can't be used in debug mode.\n"
};
/*---------------------------------------------------------------------------*/
void
init_generation( void )
/* Initialise this module. */
{}
/*---------------------------------------------------------------------------*/
void
terminate_generation( void )
/* Terminate this module. */
{
while (segments.first != NULL)
pop_segment();
free_items();
}
/* End of file. =============================================================*/