/* Copyright (C) 1995 Bjoern Beutel. */
/* Description. =============================================================*/
/* This module manages the symbol tree for constants, variables and rules. */
/* Includes. ================================================================*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <setjmp.h>
#include <glib.h>
#include "basic.h"
#include "pools.h"
#include "files.h"
#include "values.h"
#include "rule_type.h"
#include "rule_code.h"
#include "avl_trees.h"
#include "rule_symbols.h"
/* Types. ===================================================================*/
typedef struct /* A definition of a variable and its scope. */
{
list_node_t *next;
int_t first_instr; /* First instruction where variable is accessible. */
int_t last_instr; /* Last instruction where variable is accessible.
* -1 means "scope not closed yet". */
int_t stack_index;
} comp_scope_t;
typedef struct /* A symbol table node. */
{
avln_node_t node; /* A symbol node is an AVLN node. */
int_t name_index; /* Index of NODE.NAME in CODE.STRING_POOL. */
/* The associated rule. */
int_t rule_number; /* Rule number in CODE.RULE_POOL or -1. */
rule_t *rule_p; /* Rule pointer or NULL. */
int_t rule_line; /* The source line of the rule definition. */
string_t rule_file; /* The source file of the rule definition. */
/* The associated constant. */
int_t const_index; /* Index in CODE.VALUE_POOL or -1. */
bool_t const_fixed; /* TRUE if constant is already fixed. */
/* The associated variable scopes. */
list_t scopes;
} symbol_node_t;
/* Variables. ===============================================================*/
static avln_node_t *symbol_tree; /* Root node of the symbol tree.
* Actually points to symbol_node_t. */
/* Basic functions. =========================================================*/
static symbol_node_t *
find_symbol_node( string_t name )
/* Find and return a symbol node with NAME.
* If that symbol node does not exist, create a new one. */
{
symbol_node_t *node;
/* Look for existing node. */
node = (symbol_node_t *) find_avln_node( name, symbol_tree );
if (node == NULL)
{
/* Node not found - create a new one. */
node = new_mem( sizeof( symbol_node_t ) );
node->node.name = copy_string_to_pool( code.string_pool, name,
&node->name_index );
node->rule_number = -1;
node->rule_p = NULL;
node->const_index = -1;
clear_list( &node->scopes );
insert_avln_node( (avln_node_t *) node, &symbol_tree );
}
return node;
}
/*---------------------------------------------------------------------------*/
static void
free_symbol_tree( avln_node_t **node_p )
/* Free all memory used by *NODE_P (including the node itself). */
{
comp_scope_t *scope;
symbol_node_t *node = (symbol_node_t *) *node_p;
if (*node_p == NULL)
return;
free_symbol_tree( &(*node_p)->left );
free_symbol_tree( &(*node_p)->right );
FOREACH_FREE( scope, node->scopes )
; /* empty */
free_mem( node_p );
}
/*---------------------------------------------------------------------------*/
void
free_symbols( void )
/* Free all memory used by the symbol table. */
{
free_symbol_tree( &symbol_tree );
}
/* Functions that use the symbol table for rules. ===========================*/
static void
create_rule_node( symbol_node_t *node )
/* Create an empty rule entry for NODE. */
{
rule_t rule;
rule.name = node->name_index;
rule.first_instr = -1;
rule.type = -1;
rule.param_count = -1;
node->rule_p = (rule_t *) copy_to_pool( code.rule_pool, &rule, 1,
&node->rule_number );
}
/*---------------------------------------------------------------------------*/
static string_t
rule_type_name( rule_type_t type )
/* Return TYPE as a readable string. */
{
switch (type)
{
case ALLO_RULE:
return "allo-rule";
case COMBI_RULE:
return "combi-rule";
case END_RULE:
return "end-rule";
case FILTER_RULE:
return "filter-rule";
case PRUNING_RULE:
return "pruning-rule";
case ROBUST_RULE:
return "robust-rule";
case SUBRULE:
return "subrule";
default:
complain( "Internal error." );
}
}
/*---------------------------------------------------------------------------*/
string_t
get_rule_file( string_t name )
/* Return file where rule NAME is defined. */
{
symbol_node_t *node;
node = find_symbol_node( name );
return node->rule_file;
}
/*---------------------------------------------------------------------------*/
int_t
get_rule_line( string_t name )
/* Return line where rule NAME is defined. */
{
symbol_node_t *node;
node = find_symbol_node( name );
return node->rule_line;
}
/*---------------------------------------------------------------------------*/
void
enter_function( string_t name, int_t index )
/* Associate standard function NAME with INDEX. */
{
symbol_node_t *node;
node = find_symbol_node( name );
if (node->rule_p != NULL || node->rule_number != -1)
complain( "Function \"%s\" already defined.", name );
node->rule_number = index;
}
/*---------------------------------------------------------------------------*/
int_t
enter_rule( string_t name, int_t first_instr, rule_type_t type,
int_t param_count, int rule_line, string_t rule_file )
/* Enter into the symbol table the rule NAME of TYPE that starts at FIRST_INSTR
* and takes PARAM_COUNT parameters. The source is at RULE_LINE in RULE_FILE.
* The rule number will be returned. */
{
symbol_node_t *node;
node = find_symbol_node( name );
if (node->rule_p != NULL)
{
if (node->rule_p->first_instr != -1)
{
complain( "Rule \"%s\" already defined in \"%s\", line %d.",
name, name_in_path( node->rule_file ), node->rule_line );
}
else if (type == SUBRULE)
{
if (node->rule_p->type == COMBI_RULE)
{
complain( "Rule \"%s\" already used as a combi rule or end rule "
"in \"%s\", line %d.", name,
name_in_path( node->rule_file ), node->rule_line );
}
if (node->rule_p->param_count != param_count)
{
complain( "\"%s\" has been called with %d parameters "
"in \"%s\", line %d.", name, node->rule_p->param_count,
name_in_path( node->rule_file ), node->rule_line );
}
}
else if (node->rule_p->type == SUBRULE)
{
complain( "Rule \"%s\" already used as a subrule in \"%s\", line %d.",
name, name_in_path( node->rule_file ), node->rule_line );
}
}
else
{
if (node->rule_number != -1)
complain( "\"%s\" is a standard function." );
create_rule_node( node );
}
node->rule_line = rule_line;
node->rule_file = rule_file;
node->rule_p->first_instr = first_instr;
node->rule_p->type = type;
node->rule_p->param_count = param_count;
return node->rule_number;
}
/*---------------------------------------------------------------------------*/
rule_t *
find_subrule( string_t name, int_t *rule_number, int line, string_t file )
/* Find subrule or standard function NAME and return its rule descriptor.
* If the rule descriptor is NULL, NAME denotes a standard function.
* If RULE_NUMBER != NULL, save the rule index in *RULE_NUMBER. */
{
symbol_node_t *node;
node = find_symbol_node( name );
if (node->rule_p == NULL)
{
if (node->rule_number == -1)
{
create_rule_node( node );
node->rule_p->type = SUBRULE;
node->rule_line = line;
node->rule_file = file;
}
}
else if (node->rule_p->type != SUBRULE)
{
complain( "\"%s\" %s as a %s in \"%s\", line %d.", name,
(node->rule_p->first_instr == -1 ? "used" : "defined"),
rule_type_name( node->rule_p->type ),
name_in_path( node->rule_file ), node->rule_line );
}
if (rule_number != NULL)
*rule_number = node->rule_number;
return node->rule_p;
}
/*---------------------------------------------------------------------------*/
rule_t *
find_combi_rule( string_t name, int_t *rule_number, int line, string_t file )
/* Find combi-rule or end-rule NAME and return its rule descriptor.
* If the rule descriptor is NULL, NAME describes a standard function.
* If RULE_NUMBER != NULL, save the rule index in *RULE_NUMBER. */
{
symbol_node_t *node;
node = find_symbol_node( name );
if (node->rule_p == NULL)
{
if (node->rule_number != -1)
complain( "\"%s\" is a function.", name );
create_rule_node( node );
node->rule_p->type = COMBI_RULE;
node->rule_line = line;
node->rule_file = file;
}
else if (node->rule_p->type != COMBI_RULE && node->rule_p->type != END_RULE)
{
complain( "\"%s\" %s as a %s in \"%s\", line %d.", name,
(node->rule_p->first_instr == -1 ? "used" : "defined"),
rule_type_name( node->rule_p->type ),
name_in_path( node->rule_file ), node->rule_line );
}
if (rule_number != NULL)
*rule_number = node->rule_number;
return node->rule_p;
}
/*---------------------------------------------------------------------------*/
static void
check_rules_defined_local( avln_node_t *node )
/* If NODE is associated with a rule, check if it is defined. */
{
symbol_node_t *sym_node = (symbol_node_t *) node;
if (node == NULL)
return;
check_rules_defined_local( node->left );
if (sym_node->rule_p != NULL && sym_node->rule_p->first_instr == -1)
{
if (in_emacs_malaga_mode)
printf( "SHOW \"%s\":%d:0\n", sym_node->rule_file, sym_node->rule_line );
complain( "Undefined rule \"%s\". (\"%s\", line %d)", node->name,
name_in_path( sym_node->rule_file ), sym_node->rule_line );
}
check_rules_defined_local( node->right );
}
/*---------------------------------------------------------------------------*/
void
check_rules_defined( void )
/* Check if all rules in the symbol tree are defined. */
{
check_rules_defined_local( symbol_tree );
}
/* Functions that use the symbol table for variables. =======================*/
int_t
find_variable( string_t name )
/* Find variable NAME in the symbol table and return its stack index. */
{
comp_scope_t *scope;
scope = (comp_scope_t *) find_symbol_node( name )->scopes.last;
if (scope == NULL || scope->last_instr != -1)
complain( "Variable \"$%s\" is not defined.", name );
return scope->stack_index;
}
/*---------------------------------------------------------------------------*/
string_t
define_variable( string_t name, int_t stack_index )
/* Define the value on index STACK_INDEX to be a new variable NAME.
* Copy its name to the string pool and return the index of this copy. */
{
symbol_node_t *node;
comp_scope_t *scope;
node = find_symbol_node( name );
scope = (comp_scope_t *) node->scopes.last;
if (scope != NULL && scope->last_instr == -1)
complain( "Variable \"$%s\" is defined twice.", name );
/* Allocate a new scope and initialise it. */
scope = new_node( &node->scopes, sizeof( comp_scope_t ), LIST_END );
scope->first_instr = code.instr_count;
scope->last_instr = -1; /* Scope is not yet closed. */
scope->stack_index = stack_index;
return node->node.name;
}
/*---------------------------------------------------------------------------*/
void
undefine_variable( string_t name )
/* Close the scope of variable NAME. */
{
comp_scope_t *scope;
scope = (comp_scope_t *) find_symbol_node( name )->scopes.last;
scope->last_instr = code.instr_count;
}
/*---------------------------------------------------------------------------*/
static void
dump_variables_and_constants_local( avln_node_t *node )
/* Dump all variables and constants in NODE and its descendants. */
{
comp_scope_t *scope; /* A scope in the symbol table. */
var_t var; /* A new entry in CODE.VAR_POOL. */
var_scope_t var_scope; /* A new entry in CODE.VAR_SCOPE_POOL. */
constant_t constant;
symbol_node_t *sym_node = (symbol_node_t *) node;
if (node == NULL)
return;
dump_variables_and_constants_local( node->left );
/* Dump variables. */
if (sym_node->scopes.first != NULL)
{
var.name = sym_node->name_index;
var.first_scope = pool_item_count( code.var_scope_pool );
var.scope_count = 0;
/* Copy all scopes to the var-scope pool. */
FOREACH( scope, sym_node->scopes )
{
var_scope.first_instr = scope->first_instr;
var_scope.last_instr = scope->last_instr;
var_scope.stack_index = scope->stack_index;
copy_to_pool( code.var_scope_pool, (void *) &var_scope, 1, NULL );
var.scope_count++;
}
copy_to_pool( code.var_pool, (void *) &var, 1, NULL );
}
/* Dump constant. */
if (sym_node->const_index != -1)
{
constant.name = sym_node->name_index;
constant.value = sym_node->const_index;
copy_to_pool( code.constant_pool, (void *) &constant, 1, NULL );
}
dump_variables_and_constants_local( node->right );
}
/*---------------------------------------------------------------------------*/
void
dump_variables_and_constants( void )
/* Dump all variables and constants in the table. */
{
/* Emit all variables and constants in alphabetical order. */
dump_variables_and_constants_local( symbol_tree );
}
/* Functions that use the symbol table for constants. =======================*/
int_t
find_constant( string_t name )
/* Find constant NAME in the symbol table and return its value pool index. */
{
symbol_node_t *node;
node = find_symbol_node( name );
if (node->const_index == -1)
complain( "Constant \"@%s\" is not defined.", name );
node->const_fixed = TRUE;
return node->const_index;
}
/*---------------------------------------------------------------------------*/
void
define_constant( string_t name, int_t const_index, bool_t fixed )
/* Define constant NAME with value at CONST_INDEX in the value pool.
* If FIXED is FALSE, the given value is only a default value. */
{
symbol_node_t *node;
node = find_symbol_node( name );
if (node->const_fixed)
complain( "Constant \"@%s\" is already defined.", name );
if (! fixed && node->const_index != -1)
complain( "Constant \"@%s\" already has a default value.", name );
node->const_index = const_index;
node->const_fixed = fixed;
}
/* End of file. =============================================================*/