/* l2xixstm.c LTX2X interpreter statement executor routines */
/* Written by: Peter Wilson, CUA
[email protected] */
/* This code is partly based on algorithms presented by Ronald Mak in */
/* "Writing Compilers & Interpreters", John Wiley & Sons, 1991 */
#include <stdio.h>
#include "l2xicmon.h"
#include "l2xierr.h"
#include "l2xiscan.h"
#include "l2xisymt.h"
#include "l2xiprse.h"
#include "l2xiidbg.h"
#include "l2xiexec.h"
/* GLOBALS */
BOOLEAN executed_return; /* TRUE iff return statement executed */
/* EXTERNALS */
extern int level;
extern int exec_line_number;;
extern long exec_stmt_count;
extern ICT *code_segmentp; /* code segment ptr */
extern ICT *statement_startp; /* ptr to start of statement */
extern TOKEN_CODE ctoken; /* token from code segment */
extern STACK_ITEM *stack; /* runtime stack */
extern STACK_ITEM_PTR tos; /* ptr to top of runtime stack */
extern STACK_ITEM_PTR stack_frame_basep; /* ptr to stack fame base */
extern BOOLEAN stack_flag;
extern BOOLEAN is_value_undef();
extern STRING get_stacked_string();
/* MACROS */
/* is_undef(tp1) TRUE iff type tp1 is undef */
#define is_undef(tp1) (tp1 == any_typep)
/***************************************************************************/
/* exec_statement() Execute a statement by calling appropriate routine */
/* returns the token code of the statement */
TOKEN_CODE exec_statement()
{
TOKEN_CODE stmt_tok;
entry_debug("exec_statement (l2xixstm.c)");
if (ctoken == STATEMENT_MARKER) {
exec_line_number = get_statement_cmarker();
++exec_stmt_count;
statement_startp = code_segmentp;
trace_statement_execution();
get_ctoken();
}
stmt_tok = ctoken;
switch (ctoken) {
case IDENTIFIER: {
SYMTAB_NODE_PTR idp = get_symtab_cptr();
if (idp->defn.key == PROC_DEFN ||
idp->defn.key == FUNC_DEFN) exec_routine_call(idp);
else exec_assignment_statement(idp);
break;
}
case BEGIN: {
exec_compound_statement();
break;
}
case CASE: {
exec_case_statement();
break;
}
case IF: {
exec_if_statement();
break;
}
case REPEAT: {
exec_grepeat_statement();
break;
}
case SEMICOLON:
case END:
case ELSE:
case UNTIL: {
break;
}
/* extensions for EXPRESS and ltx2x */
case XSKIP : {
break;
}
case XESCAPE : {
break;
}
case XRETURN : {
exec_return_statement();
exit_debug("exec_statement at XRETURN");
return(stmt_tok);
}
case END_OF_STATEMENTS: {
exit_debug("exec_statement at END_OF_STATEMENTS");
return(stmt_tok);
}
case ENDCODE: {
exit_debug("exec_statement at ENDCODE");
return(stmt_tok);
}
/* case ENDCODE: { added for ltx2x to stop execution */
/* exit_debug("exec_statement");
* return;
* break;
* }
*/
default: {
runtime_error(UNIMPLEMENTED_RUNTIME_FEATURE);
break;
}
} /* end switch */
while (ctoken == SEMICOLON) get_ctoken();
exit_debug("exec_statement");
return(stmt_tok);
} /* end exec_statement */
/***************************************************************************/
/***************************************************************************/
/* exec_return_statement() Execute a return statement */
/* at entry, ctoken = RETURN */
/* at exit, ctoken is token after RETURN */
exec_return_statement()
{
entry_debug("exec_return_statement");
get_ctoken();
/* if (ctoken == LPAREN) exec_expression(); */
executed_return = TRUE;
exit_debug("exec_return_statement");
return;
} /* end EXEC_RETURN_STATEMENT */
/***************************************************************************/
/***************************************************************************/
/* exec_assignment_statement(idp) Execute an assignment statement */
/* */
exec_assignment_statement(idp)
SYMTAB_NODE_PTR idp; /* target variable id */
{
STACK_ITEM_PTR targetp; /* ptr to assignment target */
TYPE_STRUCT_PTR target_tp, base_target_tp, expr_tp;
BOOLEAN data_area;
entry_debug("exec_assignment_statement");
data_area = FALSE;
/* Assignment to function id: target is first item of appropriate stack frame */
if (idp->defn.key == FUNC_DEFN) {
STACK_ITEM_PTR hp;
int delta; /* difference in levels */
hp = (STACK_ITEM_PTR) stack_frame_basep;
delta = level - idp->level - 1;
while (delta-- > 0) {
hp = (STACK_ITEM_PTR) get_static_link((ADDRESS) hp);
}
targetp = (STACK_ITEM_PTR) hp;
target_tp = idp->typep;
get_ctoken();
}
/* Assignment to variable: Routine exec_variable leaves target address */
/* on top of stack */
else {
if ((idp->typep->form == ARRAY_FORM) || (idp->typep->form == ENTITY_FORM)) {
data_area = TRUE;
debug_print("data_area is TRUE\n");
}
target_tp = exec_variable(idp, TARGET_USE);
targetp = (STACK_ITEM_PTR) get_address(tos);
pop(); /* pop off the target address */
}
base_target_tp = base_type(target_tp);
/* Routine exec-expression leaves expression value on top of stack */
get_ctoken();
expr_tp = exec_expression();
if (stack_flag) {
log_print("Assignment LHS: ");
expression_type_debug(target_tp);
log_print("Assignment RHS: ");
expression_type_debug(expr_tp);
}
/* do the assignment */
exec_the_assign(targetp, target_tp, expr_tp);
trace_data_store(idp, idp->typep, targetp, target_tp);
exit_debug("exec_assignment_statement");
return;
} /* end exec_assignment_statement */
/***************************************************************************/
/***************************************************************************/
/* exec_the_assign(targetp, target_tp, expr_tp) Do the actual assignment */
/* targetp, target_tp are the target and its type; */
/* expr_tp is the RHS type with its value on top of the stack. */
/* The current token is unchanged */
exec_the_assign(targetp, target_tp, expr_tp)
STACK_ITEM_PTR targetp; /* ptr to LHS */
TYPE_STRUCT_PTR target_tp; /* ptr to type of LHS */
TYPE_STRUCT_PTR expr_tp; /* ptr to type of RHS */
{
TYPE_STRUCT_PTR base_target_tp; /* ptr to LHS base type */
STACK_TYPE rhstype; /* type on top of the stack */
int size;
entry_debug("exec_the_assign");
if (is_undef(expr_tp) || is_value_undef(tos)) {
put_undef(targetp);
pop();
exit_debug("exec_the_assign at undef");
return;
}
rhstype = get_stackval_type(tos);
if (expr_tp->form == ARRAY_FORM) { /* then RHS is an array (element?) */
if (rhstype == STKADD) {
copy_value(tos, get_address(tos));
}
}
base_target_tp = base_type(target_tp);
if ((target_tp == real_typep) && (base_type(expr_tp) == integer_typep)) {
/* real := integer */
put_real(targetp, (XPRSAREAL) get_integer(tos));
}
else if (target_tp == logical_typep) {
/* logical := logical */
put_logical(targetp, get_logical(tos));
}
else if (target_tp->form == STRING_FORM && expr_tp->form == STRING_FORM) {
/* string := string */
exec_string_assign((STACK_ITEM_PTR) targetp);
}
else if (target_tp->form == ARRAY_FORM) {
if (base_type(expr_tp) == target_tp->info.array.elmt_typep) { /* array := el */
copy_value(targetp, tos);
}
else { /* assume array := array */
ICT *ptr1 = (ICT *) targetp;
ICT *ptr2 = get_address(tos);
size = target_tp->size;
while (size--) *ptr1++ = *ptr2++;
}
}
else if (target_tp->form == ENTITY_FORM ) {
/* entity := entity */
ICT *ptr1 = (ICT *) targetp;
ICT *ptr2 = get_address(tos);
size = target_tp->size;
while (size--) *ptr1++ = *ptr2++;
}
else if ((base_target_tp == integer_typep) ||
(target_tp->form == ENUM_FORM)) {
/* Range check assignment to integer or enumeration subrange */
if ((target_tp->form == SUBRANGE_FORM) &&
((get_integer(tos) < target_tp->info.subrange.min) ||
(get_integer(tos) > target_tp->info.subrange.max))) {
runtime_error(VALUE_OUT_OF_RANGE);
}
/* integer := integer */
/* enumeration := enumeration */
put_integer(targetp, get_integer(tos));
}
else {
/* real := real */
put_real(targetp, get_real(tos));
}
pop(); /* pop expression value */
exit_debug("exec_the_assign");
return;
} /* end EXEC_THE_ASSIGN */
/***************************************************************************/
/***************************************************************************/
/* exec_string_assign Execute string := string */
/* */
exec_string_assign(targetp)
STACK_ITEM_PTR targetp; /* the LHS */
{
STRING rhs; /* the RHS */
STRING lhs;
int num;
int maxchrs;
entry_debug("exec_string_assign");
rhs = get_stacked_string(tos); /* top of stack points to the string */
free(targetp->value.string);
num = strlen(rhs);
sprintf(dbuffer, "strlen(str) = %d, str = %s\n", num, rhs);
debug_print(dbuffer);
lhs = alloc_bytes(num+1);
sprintf(dbuffer, "lhs = %d", lhs);
debug_print(dbuffer);
strcpy(lhs, rhs);
sprintf(dbuffer, ", str = %s\n", lhs);
debug_print(dbuffer);
put_string(targetp, lhs);
/* set_string(targetp, rhs); */
exit_debug("exec_string_assign");
return;
} /* end EXEC_STRING_ASSIGN */
/***************************************************************************/
/***************************************************************************/
/* set_string(var_idp, str) Attaches string str to variable */
set_string(var_idp, str)
SYMTAB_NODE_PTR var_idp; /* variable in the symbol table */
STRING str; /* the string */
{
int num;
int maxchrs = 527;
TYPE_STRUCT_PTR strtyp;
entry_debug("set_string (l2xixstm.c)");
sprintf(dbuffer, "var_idp = %d\n", var_idp);
debug_print(dbuffer);
num = strlen(str);
sprintf(dbuffer, "num = strlen(str) = %d\n", num);
debug_print(dbuffer);
debug_print(str);
strtyp = var_idp->typep;
/* maxchrs = var_idp->typep->info.string.max_length; */
/* maxchrs = strtyp->info.string.max_length; */
sprintf(dbuffer, "\nmaxchrs = %d\n", maxchrs);
debug_print(dbuffer);
if (num > maxchrs) {
runtime_error(RUNTIME_STRING_TOO_LONG);
free(var_idp->info);
var_idp->info = alloc_bytes(maxchrs + 1);
strncpy(var_idp->info, str, maxchrs);
var_idp->info[maxchrs] = '\0';
strtyp->info.string.length = maxchrs;
}
else {
/* free(var_idp->info); */
var_idp->info = alloc_bytes(num+1);
strcpy(var_idp->info, str);
/* strtyp->info.string.length = num; */
}
exit_debug("set_string");
} /* end SET_STRING */
/***************************************************************************/
/***************************************************************************/
/* exec_routine_call(rtn_idp) Execute procedure or function call. */
/* return pointer to the type structure */
TYPE_STRUCT_PTR exec_routine_call(rtn_idp)
SYMTAB_NODE_PTR rtn_idp; /* routine id */
{
TYPE_STRUCT_PTR exec_declared_routine_call();
TYPE_STRUCT_PTR exec_standard_routine_call();
entry_debug("exec_routine_call");
if (rtn_idp->defn.info.routine.key == DECLARED) {
exit_debug("exec_routine_call");
return(exec_declared_routine_call(rtn_idp));
}
else {
exit_debug("exec_routine_call");
return(exec_standard_routine_call(rtn_idp));
}
} /* end exec_routine_call */
/***************************************************************************/
/***************************************************************************/
/* exec_declared_routine_call(rtn_idp) Execute a call to a declared */
/* function or procedure */
/* return pointer to the type structure */
TYPE_STRUCT_PTR exec_declared_routine_call(rtn_idp)
SYMTAB_NODE_PTR rtn_idp; /* routine id */
{
int old_level = level; /* level of caller */
int new_level = rtn_idp->level + 1; /* level of callee */
STACK_ITEM_PTR new_stack_frame_basep;
STACK_ITEM_PTR hp; /* ptr to frame header */
entry_debug("exec_declared_routine_call");
/* set up stack frame of callee */
new_stack_frame_basep = tos + 1;
push_stack_frame_header(old_level, new_level);
/* push parameter values onto the stack */
get_ctoken();
if (ctoken == LPAREN) {
exec_actual_parms(rtn_idp);
get_ctoken(); /* the token after the RPAREN */
}
/* set the return address in the new stack frame, and execute callee */
level = new_level;
stack_frame_basep = new_stack_frame_basep;
hp = stack_frame_basep;
/* put_address(hp->return_address, (code_segmentp - 1)); */
put_return_address(hp, (code_segmentp - 1));
/* execute(rtn_idp); changed this call for EXPRESS */
exec_algorithm(rtn_idp);
/* return from callee */
level = old_level;
get_ctoken(); /* first token after return */
exit_debug("exec_declared_routine_call");
return(rtn_idp->defn.key == PROC_DEFN ? NULL : rtn_idp->typep);
} /* end exec_declared_routine_call */
/***************************************************************************/
/***************************************************************************/
/* exec_actual_parms(rtn_idp) Push the values of the actual parameters */
/* onto the stack */
exec_actual_parms(rtn_idp)
SYMTAB_NODE_PTR rtn_idp; /* id of callee routine */
{
SYMTAB_NODE_PTR formal_idp; /* formal param id */
TYPE_STRUCT_PTR formal_tp, actual_tp;
entry_debug("exec_actual_parms");
/* loop to execute actual params */
for (formal_idp = rtn_idp->defn.info.routine.parms;
formal_idp != NULL;
formal_idp = formal_idp->next) {
formal_tp = formal_idp->typep;
get_ctoken();
/* value parameter */
if (formal_idp->defn.key == VALPARM_DEFN) {
actual_tp = exec_expression();
/* Range check for a subrange formal param */
if (formal_tp->form == SUBRANGE_FORM) {
TYPE_STRUCT_PTR base_formal_tp = base_type(formal_tp);
XPRSAINT value;
value = get_integer(tos);
if ((value < formal_tp->info.subrange.min) ||
(value > formal_tp->info.subrange.max)) {
runtime_error(VALUE_OUT_OF_RANGE);
}
}
else if ((formal_tp == real_typep) &&
(base_type(actual_tp) == integer_typep)) {
/* real formal := integer actual */
put_real(tos, (XPRSAREAL) get_integer(tos));
}
if ((formal_tp->form == ARRAY_FORM) ||
(formal_tp->form == ENTITY_FORM)) {
/* formal param is array or entity. Make a copy */
int size = formal_tp->size;
ICT *ptr1 = alloc_array(ICT, size);
ICT *ptr2 = get_address(tos); /* ??????????????????? */
ICT *save_ptr = ptr1;
while (size--) *ptr1++ = *ptr2++;
put_address(tos, save_ptr);
}
} /* end value param */
/* a VAR parameter */
else {
SYMTAB_NODE_PTR idp = get_symtab_cptr();
exec_variable(idp, VARPARM_USE);
}
} /* end for loop */
exit_debug("exec_actual_parms");
} /* end exec_actual_parms */
/***************************************************************************/
/***************************************************************************/
/* exec_compound_statement() Execute a compound statement */
/* */
exec_compound_statement()
{
entry_debug("exec_compound_statement");
get_ctoken();
while (ctoken != END) exec_statement();
get_ctoken();
exit_debug("exec_compound_statement");
return;
} /* end exec_compound_statement */
/***************************************************************************/
/***************************************************************************/
/* exec_case_statement() Execute a CASE statement */
/* CASE <expr> OF */
/* <case-branch> */
/* END */
exec_case_statement()
{
XPRSAINT case_expr_value; /* CASE expr value */
XPRSAINT case_label_count; /* CASE label count */
XPRSAINT case_label_value; /* CASE label value */
ADDRESS branch_table_location; /* branch table address */
ADDRESS case_branch_location; /* CASE branch address */
TYPE_STRUCT_PTR case_expr_tp; /* CASE expr type */
BOOLEAN done = FALSE;
BOOLEAN found_otherwise = FALSE;
ADDRESS otherwise_location;
entry_debug("exec_case_statement");
get_ctoken(); /* token after CASE */
branch_table_location = get_address_cmarker();
/* evaluate the CASE expr */
get_ctoken();
case_expr_tp = exec_expression();
case_expr_value = get_integer(tos);
pop(); /* expression value */
/* search the branch table for the expr value */
code_segmentp = branch_table_location;
get_ctoken();
case_label_count = get_cinteger();
while (!done && case_label_count--) {
case_label_value = get_cinteger();
case_branch_location = get_caddress();
done = case_label_value == case_expr_value;
if (case_label_value == XOTHERWISE) {
found_otherwise = TRUE;
otherwise_location = case_branch_location;
}
}
/* if found, goto the appropriate CASE branch */
if (case_label_count >= 0) {
code_segmentp = case_branch_location;
get_ctoken();
exec_statement();
code_segmentp = get_address_cmarker();
get_ctoken();
}
else if (found_otherwise) {
code_segmentp = otherwise_location;
get_ctoken();
exec_statement();
code_segmentp = get_address_cmarker();
get_ctoken();
}
else {
runtime_error(INVALID_CASE_VALUE);
}
exit_debug("exec_case_statement");
return;
} /* end exec_case_statement */
/***************************************************************************/
/***************************************************************************/
/* exec_if_statement() Execute an IF statement */
/* IF <expr> THEN <stmt> END_IF */
/* or */
/* IF <expr> THEN <stmt> ELSE <stmt> END_IF */
exec_if_statement()
{
ADDRESS false_location; /* address of false branch */
BOOLEAN test;
entry_debug("exec_if_statement");
get_ctoken(); /* token after if */
false_location = get_address_cmarker();
/* evaluate the boolean expression */
get_ctoken();
exec_expression();
test = get_logical(tos) == TRUE_REP;
pop(); /* boolean value */
if (test) { /* do the TRUE branch */
get_ctoken(); /* token after THEN */
while (ctoken != ELSE && ctoken != XEND_IF) exec_statement();
if (ctoken == ELSE) {
get_ctoken();
code_segmentp = get_address_cmarker();
get_ctoken(); /* token after false stmt */
}
}
else { /* do the ELSE branch if there is one */
code_segmentp = false_location;
get_ctoken();
if (ctoken == ELSE ) {
get_ctoken();
get_address_cmarker(); /* skip the address marker */
get_ctoken();
while(ctoken != XEND_IF) exec_statement();
}
}
get_ctoken(); /* after the END_IF */
exit_debug("exec_if_statement");
return;
} /* end exec_if_statement */
/***************************************************************************/
/***************************************************************************/
/* exec_grepeat_statement() Execute an EXPRESS REPEAT statement */
/* REPEAT [ <inc_control> ] <while_control> <until_control> */
/* <stmt-list> END_REPEAT; */
/* at entry: ctoken is REPEAT */
/* at exit: ctoken is after END_REPEAT; */
exec_grepeat_statement()
{
SYMTAB_NODE_PTR control_idp; /* control var id */
TYPE_STRUCT_PTR control_tp; /* control var type */
STACK_ITEM_PTR targetp; /* ptr to control target */
ADDRESS loop_start_location; /* address of start of loop */
ADDRESS loop_end_location; /* address of end of loop */
ADDRESS until_start_location;
ADDRESS to_start_location;
ADDRESS while_start_location;
ADDRESS statements_start_location;
BOOLEAN loop_done = FALSE;
BOOLEAN is_increment_control = FALSE; /* TRUE iff there is an inc. control */
int control_value; /* value of control var */
int initial_value, final_value, delta_value;
TOKEN_CODE stmt_tok;
entry_debug("exec_grepeat_statement (l2xixstm.c)");
/* the first time through */
get_ctoken(); /* code (address marker) token after REPEAT */
loop_end_location = get_address_cmarker();
sprintf(dbuffer, "loop_end_location = %d\n", loop_end_location);
debug_print(dbuffer);
get_ctoken(); /* source token after REPEAT */
if (ctoken == FOR) { /* increment control */
is_increment_control = TRUE;
get_ctoken(); /* IDENTIFIER for the variable */
/* get address of control var's stack item */
control_idp = get_symtab_cptr();
control_tp = exec_variable(control_idp, TARGET_USE);
targetp = (STACK_ITEM_PTR) get_address(tos);
pop(); /* pop control var's address */
/* evaluate the initial expression */
get_ctoken();
exec_expression();
initial_value = get_integer(tos);
pop(); /* initial value */
put_integer(targetp, initial_value);
control_value = initial_value;
/* evaluate the final expression */
get_ctoken();
to_start_location = code_segmentp -1;
sprintf(dbuffer, "to_start_location = %d\n", to_start_location);
debug_print(dbuffer);
exec_expression();
final_value = get_integer(tos);
pop(); /* final value */
/* get the increment */
get_ctoken();
exec_expression();
delta_value = get_integer(tos);
pop(); /* delta value */
/* check the bound */
if ((delta_value >= 0 && control_value > final_value) ||
(delta_value < 0 && control_value < final_value)) {
code_segmentp = loop_end_location;
get_ctoken();
loop_done = TRUE;
}
if (loop_done) {
exit_debug("exec_grepeat_statement");
return;
}
}
/* check the WHILE condition */
get_ctoken();
while_start_location = code_segmentp -1;
sprintf(dbuffer, "while_start_location = %d\n", while_start_location);
debug_print(dbuffer);
exec_expression();
if (get_logical(tos) == FALSE_REP) { /* finished */
code_segmentp = loop_end_location;
get_ctoken();
loop_done = TRUE;
}
pop(); /* the WHILE value */
if (loop_done) {
exit_debug("exec_grepeat_statement");
return;
}
/* skip the UNTIL condition */
get_ctoken();
until_start_location = code_segmentp -1;
sprintf(dbuffer, "until_start_location = %d\n", until_start_location);
debug_print(dbuffer);
while (ctoken != STATEMENT_MARKER) get_ctoken();
statements_start_location = code_segmentp -1;
sprintf(dbuffer, "statements_start_location = %d\n", statements_start_location);
debug_print(dbuffer);
/* do the statements */
do {
stmt_tok = exec_statement();
if (stmt_tok == XSKIP) {
code_segmentp = until_start_location;
break;
}
else if (stmt_tok == XESCAPE) {
code_segmentp = loop_end_location;
get_ctoken();
loop_done = TRUE;
pop();
exit_debug("exec_grepeat_statement");
return;
}
} while (ctoken != XEND_REPEAT);
/* This finishes the first pass, do subsequent passes */
do {
/* check the UNTIL expression */
code_segmentp = until_start_location;
get_ctoken();
exec_expression();
if (get_logical(tos) == TRUE_REP) { /* finished */
code_segmentp = loop_end_location;
get_ctoken();
loop_done = TRUE;
}
pop(); /* the UNTIL value */
if (loop_done) {
exit_debug("exec_grepeat_statement");
return;
}
/* increment control now */
if (is_increment_control) {
/* perform the increment */
control_value = get_integer(targetp) + delta_value;
put_integer(targetp, control_value);
/* do the check */
code_segmentp = to_start_location;
get_ctoken();
exec_expression();
get_integer(tos);
if ((delta_value >= 0 && control_value > final_value) ||
(delta_value < 0 && control_value < final_value)) {
code_segmentp = loop_end_location;
get_ctoken();
loop_done = TRUE;
}
pop(); /* the to value */
if (loop_done) {
exit_debug("exec_grepeat_statement");
return;
}
}
/* check the WHILE */
code_segmentp = while_start_location;
get_ctoken();
exec_expression();
if (get_logical(tos) == FALSE_REP) {
code_segmentp = loop_end_location;
get_ctoken();
loop_done = TRUE;
}
pop(); /* the WHILE value */
if (loop_done) {
exit_debug("exec_grepeat_statement");
return;
}
/* and now back to executing the statements */
code_segmentp = statements_start_location;
get_ctoken();
do {
exec_statement();
} while (ctoken != XEND_REPEAT);
/* start again checking the UNTIL condition */
code_segmentp = until_start_location;
} while(TRUE);
exit_debug("exec_grepeat_statement");
return;
} /* end EXEC_GREPEAT_STATEMENT */
/***************************************************************************/