/* Copyright (C) 1995 Bjoern Beutel. */
/* Description. =============================================================*/
/* This module manages the emission of instructions and keeps track of the
* stack index.
* It supports constant folding.
* It also holds buffers for the compiled code. */
/* Includes. ================================================================*/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <setjmp.h>
#include <glib.h>
#include "basic.h"
#include "pools.h"
#include "values.h"
#include "symbols.h"
#include "rule_type.h"
#include "files.h"
#include "malaga_files.h"
#include "rule_code.h"
/* Global variables. ========================================================*/
code_t code;
/* Variables. ===============================================================*/
static instr_t *instr_stack;
/* The instruction stack. The associated constant values are stored in
* VALUES_STACK. */
static int_t instr_stack_size; /* The size of INSTR_STACK. */
/* Functions. ===============================================================*/
void
init_rule_code( int_t file_type )
/* Initialise this module.
* CODE will contain compilation data for a file of FILE_TYPE.
* FILE_TYPE may be ALLO_RULE_FILE, MORPHO_RULE_FILE, or SYNTAX_RULE_FILE. */
{
code.file_type = file_type;
code.stack_index = 0;
code.instr_count = 0;
code.rule_pool = new_pool( sizeof( rule_t ) );
code.rule_set_pool = new_pool( sizeof( int_t ) );
code.instr_pool = new_pool( sizeof( instr_t ) );
code.value_pool = new_pool( sizeof( cell_t ) );
code.string_pool = new_pool( sizeof( char_t ) );
code.src_line_pool = new_pool( sizeof( src_line_t ) );
code.var_pool = new_pool( sizeof( var_t ) );
code.var_scope_pool = new_pool( sizeof( var_scope_t ) );
code.constant_pool = new_pool( sizeof( constant_t ) );
instr_stack_size = 50;
instr_stack = new_vector( sizeof( instr_t ), instr_stack_size );
top = 0;
}
/*---------------------------------------------------------------------------*/
void
terminate_rule_code( void )
/* Terminate this module. */
{
free_pool( &code.rule_pool );
free_pool( &code.rule_set_pool );
free_pool( &code.instr_pool );
free_pool( &code.value_pool );
free_pool( &code.src_line_pool );
free_pool( &code.var_pool );
free_pool( &code.var_scope_pool );
free_pool( &code.constant_pool );
free_pool( &code.string_pool );
free_mem( &instr_stack );
}
/*---------------------------------------------------------------------------*/
void
write_code( string_t file_name )
/* Write CODE to FILE_NAME. */
{
FILE *stream;
rule_header_t rule_header;
stream = open_stream( file_name, "wb" );
/* Set rule file header data. */
set_header( &rule_header.common_header, RULE_FILE, RULE_CODE_VERSION );
rule_header.initial_rule_set = code.initial_rule_set;
rule_header.initial_feat = code.initial_feat;
rule_header.robust_rule = code.robust_rule;
rule_header.pruning_rule = code.pruning_rule;
rule_header.allo_rule = code.allo_rule;
rule_header.input_filter = code.input_filter;
rule_header.output_filter = code.output_filter;
rule_header.rule_count = pool_item_count( code.rule_pool );
rule_header.rule_sets_size = pool_item_count( code.rule_set_pool );
rule_header.instr_count = pool_item_count( code.instr_pool );
rule_header.values_size = pool_item_count( code.value_pool );
rule_header.src_line_count = pool_item_count( code.src_line_pool );
rule_header.var_count = pool_item_count( code.var_pool );
rule_header.var_scope_count = pool_item_count( code.var_scope_pool );
rule_header.constant_count = pool_item_count( code.constant_pool );
rule_header.strings_size = pool_item_count( code.string_pool );
/* Write the header. */
write_vector( &rule_header, sizeof( rule_header ), 1, stream, file_name );
/* Write the tables. */
write_pool( code.rule_pool, stream, file_name );
write_pool( code.rule_set_pool, stream, file_name );
write_pool( code.instr_pool, stream, file_name );
write_pool( code.value_pool, stream, file_name );
write_pool( code.src_line_pool, stream, file_name );
write_pool( code.var_pool, stream, file_name );
write_pool( code.var_scope_pool, stream, file_name );
write_pool( code.constant_pool, stream, file_name );
write_pool( code.string_pool, stream, file_name );
close_stream( &stream, file_name );
}
/*---------------------------------------------------------------------------*/
static void
set_stack_index( int_t opcode, int_t info )
/* Set the stack index according to the given instruction. */
{
/* The stack index is not always correct, because "parse_choose"
* and "parse_foreach" in "rule_parser.c" patch an INS_PUSH_NULL that they
* have previously emitted. They will also correct the stack index. */
switch (opcode)
{
case INS_SYSTEM_ERROR:
case INS_TERMINATE:
case INS_NOP:
case INS_UNARY_MINUS_OP:
case INS_GET_ATTRIBUTE:
case INS_REMOVE_ATTRIBUTE:
case INS_MATCH:
case INS_GET_1ST_ELEMENT:
case INS_ITERATE:
case INS_JUMP:
case INS_JUMP_NOW:
case INS_JUMP_LATER:
/* These instructions leave the stack size unchanged. */
return;
case INS_PUSH_VAR:
case INS_PUSH_CONST:
case INS_PUSH_SYMBOL:
case INS_PUSH_PATTERN_VAR:
case INS_JUMP_SUBRULE: /* This has to decrement the STACK_INDEX by the number
* of parameters manually. */
code.stack_index++;
return;
case INS_PUSH_NULL:
code.stack_index += info;
return;
case INS_ERROR:
case INS_ADD_END_STATE:
case INS_ADD_STATE:
case INS_TERMINATE_IF_NULL:
case INS_DOT_OPERATION:
case INS_PLUS_OPERATION:
case INS_MINUS_OPERATION:
case INS_ASTERISK_OPERATION:
case INS_SLASH_OPERATION:
case INS_SET_VAR:
case INS_PLUS_VAR:
case INS_MINUS_VAR:
case INS_ASTERISK_VAR:
case INS_SLASH_VAR:
case INS_JUMP_IF_NULL:
case INS_JUMP_IF_NOT_NULL:
case INS_JUMP_IF_YES:
case INS_JUMP_IF_NO:
case INS_ACCEPT: /* No instruction after this instruction is executed. */
case INS_RETURN: /* No instruction after this instruction is executed. */
code.stack_index--;
return;
case INS_ADD_ALLO:
case INS_SET_VAR_PATH:
case INS_PLUS_VAR_PATH:
case INS_MINUS_VAR_PATH:
case INS_ASTERISK_VAR_PATH:
case INS_SLASH_VAR_PATH:
case INS_JUMP_IF_EQUAL:
case INS_JUMP_IF_NOT_EQUAL:
case INS_JUMP_IF_CONGR:
case INS_JUMP_IF_NOT_CONGR:
case INS_JUMP_IF_IN:
case INS_JUMP_IF_NOT_IN:
case INS_JUMP_IF_LESS:
case INS_JUMP_IF_NOT_LESS:
case INS_JUMP_IF_GREATER:
case INS_JUMP_IF_NOT_GREATER:
code.stack_index -= 2;
return;
case INS_POP:
code.stack_index -= info;
return;
case INS_POP_TO:
code.stack_index = info;
return;
case INS_BUILD_LIST:
case INS_BUILD_PATH:
code.stack_index -= (info - 1);
return;
case INS_DECOMPOSE_LIST:
code.stack_index += (info - 1);
return;
case INS_BUILD_RECORD:
code.stack_index -= (2*info - 1);
return;
case INS_STD_FUNCTION:
switch (info)
{
case FUNC_TO_ATOMS:
case FUNC_IS_CAPITAL:
case FUNC_GET_LENGTH:
case FUNC_TO_MULTI:
case FUNC_TO_SET:
case FUNC_GET_SWITCH:
case FUNC_GET_VALUE_TYPE:
case FUNC_GET_VALUE_STRING:
case FUNC_TRANSMIT:
case FUNC_FLOOR:
/* These functions leave the stack size unchanged. */
return;
case FUNC_SUBSTRING:
code.stack_index -= 2;
return;
}
}
complain( "Internal error." );
}
/*---------------------------------------------------------------------------*/
static instr_t *
emit_instr_local( int_t opcode, int_t info )
/* Emit an instruction to the instruction pool. Do not flush the buffer!
* Return the address of the instruction in the pool. */
{
instr_t instr;
instr_t *instr_p;
if (info < INSTR_INFO_MIN || info > INSTR_INFO_MAX)
complain( "Internal error." );
/* Create the next instruction. */
instr = INSTR( opcode, info );
/* Generate instruction. */
instr_p = (instr_t *) copy_to_pool( code.instr_pool, &instr, 1, NULL );
set_stack_index( opcode, info );
code.instr_count = pool_item_count( code.instr_pool );
return instr_p;
}
/* Functions that support constant folding. =================================*/
static void
put_instr( int_t opcode, int_t info )
/* Put the instruction (OPCODE, INFO) at INSTR_STACK[ TOP - 1 ]. */
{
if (top > instr_stack_size)
{
instr_stack_size = renew_vector( &instr_stack,
sizeof( u_int_t ), 2 * top );
}
instr_stack[ top - 1 ] = INSTR( opcode, info );
}
/*---------------------------------------------------------------------------*/
void
buffer_instr( int_t opcode, int_t info )
/* Buffer the instructions BUILD_LIST, BUILD_RECORD, PUSH_SYMBOL,
* and PUSH_CONST for constant folding. */
{
switch (opcode)
{
case INS_PUSH_SYMBOL:
push_symbol_value( info );
put_instr( INS_PUSH_SYMBOL, info );
break;
case INS_PUSH_CONST:
push_value( pool_item( code.value_pool, info ) );
put_instr( INS_PUSH_CONST, info );
break;
case INS_BUILD_LIST:
if (top >= info)
{
/* Execute the operation in the buffer. */
build_list( info );
put_instr( INS_PUSH_CONST, -1 );
}
else
emit_instr( opcode, info );
break;
case INS_BUILD_RECORD:
if (top >= 2 * info)
{
/* Execute the operation in the buffer. */
build_record( info );
put_instr( INS_PUSH_CONST, -1 );
}
else
emit_instr( opcode, info );
break;
case INS_BUILD_PATH:
if (top >= info)
{
/* Execute the operation in the buffer. */
build_path( info );
put_instr( INS_PUSH_CONST, -1 );
}
else
emit_instr( opcode, info );
break;
case INS_DOT_OPERATION:
case INS_PLUS_OPERATION:
case INS_MINUS_OPERATION:
case INS_ASTERISK_OPERATION:
case INS_SLASH_OPERATION:
if (top >= 2)
{
/* Execute the operation in the buffer. */
switch (opcode)
{
case INS_DOT_OPERATION:
dot_operation();
if (value_stack[ top - 1 ] == NULL)
complain( "Component doesn't exist." );
break;
case INS_PLUS_OPERATION:
plus_operation();
break;
case INS_MINUS_OPERATION:
minus_operation();
break;
case INS_ASTERISK_OPERATION:
asterisk_operation();
break;
case INS_SLASH_OPERATION:
slash_operation();
break;
default:
complain( "Internal error." );
}
put_instr( INS_PUSH_CONST, -1 );
}
else
emit_instr( opcode, info );
break;
case INS_UNARY_MINUS_OP:
case INS_GET_ATTRIBUTE:
case INS_REMOVE_ATTRIBUTE:
if (top >= 1)
{
/* Execute the operation in the buffer. */
switch (opcode)
{
case INS_UNARY_MINUS_OP:
unary_minus_operation();
break;
case INS_GET_ATTRIBUTE:
push_value( get_attribute( value_stack[ --top ], info ) );
if (value_stack[ top - 1 ] == NULL)
{
complain( "No attribute \"%s\" in record.",
get_symbol_name( info ) );
}
break;
case INS_REMOVE_ATTRIBUTE:
remove_attribute( info );
break;
default:
complain( "Internal error." );
}
put_instr( INS_PUSH_CONST, -1 );
}
else
emit_instr( opcode, info );
break;
default:
emit_instr( opcode, info );
break;
}
}
/*---------------------------------------------------------------------------*/
void
buffer_push_number_instr( double number )
/* Buffer the instruction PUSH_CONST with NUMBER converted to a value. */
{
push_number_value( number );
put_instr( INS_PUSH_CONST, -1 );
}
/*---------------------------------------------------------------------------*/
void
buffer_push_string_instr( string_t string, string_t string_end )
/* Buffer the instruction PUSH_CONST with STRING converted to a value.
* STRING_END points to the string end if STRING_END != NULL. */
{
push_string_value( string, string_end );
put_instr( INS_PUSH_CONST, -1 );
}
/*---------------------------------------------------------------------------*/
void
flush_buffer( void )
/* Emit the instructions that are still in the buffer. */
{
int_t i, value_index;
for (i = 0; i < top; i++)
{
switch (OPCODE( instr_stack[i] ))
{
case INS_PUSH_CONST:
if (INSTR_INFO( instr_stack[i] ) == -1)
copy_value_to_pool( code.value_pool, value_stack[i], &value_index );
else
value_index = INSTR_INFO( instr_stack[i] );
emit_instr_local( INS_PUSH_CONST, value_index );
break;
case INS_PUSH_SYMBOL:
emit_instr_local( INS_PUSH_SYMBOL, INSTR_INFO( instr_stack[i] ) );
break;
}
}
top = 0;
}
/*---------------------------------------------------------------------------*/
value_t
get_buffer_top_value( void )
/* Test if the buffer contains a value and return the top value. */
{
if (top == 0)
complain( "Internal error." );
return value_stack[ top - 1 ];
}
/*---------------------------------------------------------------------------*/
value_t
pop_buffer_top_value( void )
/* Pop the top value in the buffer. */
{
if (top == 0)
complain( "Internal error." );
return value_stack[ --top ];
}
/*---------------------------------------------------------------------------*/
instr_t *
emit_instr( int_t opcode, int_t info )
/* Emit an instruction to the instruction pool (flushes buffer before)
* and return the address of the instruction in the pool. */
{
flush_buffer();
return emit_instr_local( opcode, info );
}
/* End of file. =============================================================*/