/* l2xirtne.c LTX2X interpreter Routine parser */
/* parse programs and declared procedures and functions */
/* 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"
#ifndef l2xicpr_h
#include "l2xicpr.h" /* extern token code lists */
#endif
/* EXTERNALS */
extern int line_number;;
extern long exec_stmt_count;
extern TOKEN_CODE token;
extern char word_string[];
extern SYMTAB_NODE_PTR symtab_display[];
extern int level;
extern ICT *code_buffer;
extern ICT *code_bufferp;
extern STACK_ITEM *stack;
extern STACK_ITEM_PTR tos;
extern STACK_ITEM_PTR stack_frame_basep;
extern STACK_ITEM_PTR maxtos;
extern TYPE_STRUCT_PTR get_type();
/* GLOBALS */
char buffer[MAX_PRINT_LINE_LENGTH];
/* FORWARDS */
SYMTAB_NODE_PTR formal_parm_list();
SYMTAB_NODE_PTR procedure_header();
SYMTAB_NODE_PTR function_header();
ICT *create_code_segment();
/***************************************************************************/
/* init_stack initialise the runtime stack */
init_stack()
{
entry_debug("init_stack");
/* allocate runtime stack */
stack = alloc_array(STACK_ITEM, MAX_STACK_SIZE);
stack_frame_basep = tos = stack;
stack_frame_debug();
maxtos = tos; /* current max top of stack */
/* initialise the program's stack frame */
level = 1;
stack_frame_basep = tos + 1;
stack_frame_debug();
push_integer(0); /* function return value */
push_address(NULL); /* static link */
push_address(NULL); /* dynamic link */
push_address(NULL); /* return address */
exit_debug("init_stack");
return;
} /* end init_stack */
/***************************************************************************/
/***************************************************************************/
/* create_dummy_prog() create a dummy program symbol table node */
/* Based on program and program_header */
/* Must be called BEFORE any scanning or parsing */
/* returns pointer to program id node */
SYMTAB_NODE_PTR create_dummy_prog()
{
SYMTAB_NODE_PTR program_idp; /* program id */
entry_debug("creat_dummy_prog");
/* make up fake program name */
strcpy(word_string, "_PrOgRaM");
search_and_enter_local_symtab(program_idp);
program_idp->defn.key = PROG_DEFN;
program_idp->defn.info.routine.key = DECLARED;
program_idp->defn.info.routine.parm_count = 0;
program_idp->defn.info.routine.total_parm_size = 0;
program_idp->defn.info.routine.total_local_size = 0;
program_idp->typep = &dummy_type;
program_idp->label_index = 0;
enter_scope(NULL);
/* no program parameters */
program_idp->defn.info.routine.locals = NULL;
program_idp->defn.info.routine.parms = NULL;
exit_debug("create_dummy_prog");
return(program_idp);
} /* end create_dummy_prog */
/***************************************************************************/
/***************************************************************************/
/* a_function Process an EXPRESS function */
/* FUNCTION <header> <body> END_FUNCTION ; */
/* at entry, token is FUNCTION */
/* at exit, token is after END_FUNCTION ; */
a_function()
{
SYMTAB_NODE_PTR rtn_idp; /* routine id */
entry_debug("a_function");
rtn_idp = function_header();
/* sync. Should be ; */
synchronize(follow_header_list, declaration_start_list, statement_start_list);
if_token_get(SEMICOLON);
else if (token_in(declaration_start_list) || token_in(statement_start_list))
error(MISSING_SEMICOLON);
/* block or forward */
if (strcmp(word_string, "forward") != 0) {
rtn_idp->defn.info.routine.key = DECLARED;
analyze_routine_header(rtn_idp);
rtn_idp->defn.info.routine.locals = NULL;
function_body(rtn_idp);
rtn_idp->defn.info.routine.code_segment = create_code_segment();
analyze_block(rtn_idp->defn.info.routine.code_segment);
if_token_get_else_error(XEND_FUNCTION, MISSING_END_FUNCTION);
if_token_get_else_error(SEMICOLON, MISSING_SEMICOLON);
}
else {
get_token();
rtn_idp->defn.info.routine.key = FORWARD;
analyze_routine_header(rtn_idp);
}
rtn_idp->defn.info.routine.local_symtab = exit_scope();
exit_debug("a_function");
return;
} /* end A_FUNCTION */
/***************************************************************************/
/***************************************************************************/
/* function_body(rtn_idp) Process body of a function */
/* at entry, token is after ; ending the header */
/* at exit, token is after a ; and should be END_FUNCTION */
function_body(rtn_idp)
SYMTAB_NODE_PTR rtn_idp; /* routine id */
{
extern BOOLEAN block_flag;
entry_debug("function_body");
if (token_in(declaration_start_list)) {
declarations(rtn_idp);
/* synchronize(follow_decls_list, NULL, NULL); */
}
/* possibly need an else skip_declarations(rtn_idp); here */
block_flag = TRUE;
/* possibly empty list of statements */
if (token_in(statement_start_list)) {
crunch_token();
statements();
crunch_statement_marker();
change_crunched_token(END_OF_STATEMENTS);
}
block_flag = FALSE;
/* if_token_get_else_error(XEND_FUNCTION, MISSING_END_FUNCTION);
* if_token_get_else_error(SEMICOLON, MISSING_SEMICOLON);
*/
exit_debug("function_body");
return;
} /* end FUNCTION_BODY */
/***************************************************************************/
/***************************************************************************/
/* a_procedure() Process EXPRESS procedure */
/* FUN/PROC <routine_header> ; <block> */
/* at entry, token is PROCEDURE */
/* at exit, token is past final ; */
a_procedure()
{
SYMTAB_NODE_PTR rtn_idp; /* routine id */
entry_debug("a_procedure");
rtn_idp = procedure_header();
/* sync. Should be ; */
synchronize(follow_header_list, declaration_start_list, statement_start_list);
if_token_get(SEMICOLON);
else if (token_in(declaration_start_list) || token_in(statement_start_list))
error(MISSING_SEMICOLON);
/* block or forward */
if (strcmp(word_string, "forward") != 0) {
rtn_idp->defn.info.routine.key = DECLARED;
analyze_routine_header(rtn_idp);
rtn_idp->defn.info.routine.locals = NULL;
function_body(rtn_idp);
rtn_idp->defn.info.routine.code_segment = create_code_segment();
analyze_block(rtn_idp->defn.info.routine.code_segment);
if_token_get_else_error(XEND_PROCEDURE, MISSING_END_PROCEDURE);
if_token_get_else_error(SEMICOLON, MISSING_SEMICOLON);
}
else {
get_token();
rtn_idp->defn.info.routine.key = FORWARD;
analyze_routine_header(rtn_idp);
}
rtn_idp->defn.info.routine.local_symtab = exit_scope();
exit_debug("a_procedure");
return;
} /* end A_PROCEDURE */
/***************************************************************************/
/***************************************************************************/
/* procedure_header() Process a procedure header */
/* PROCEDURE <id> */
/* or PROCEDURE <id> ( <parm-list> ) */
/* returns pointer to the procedure id node. */
SYMTAB_NODE_PTR procedure_header()
{
SYMTAB_NODE_PTR proc_idp; /* procedure id */
SYMTAB_NODE_PTR parm_listp; /* formal param list */
int parm_count;
int total_parm_size;
BOOLEAN forward_flag = FALSE; /* TRUE iff forward */
entry_debug("procedure_header");
get_token();
/* if proc id has already been declared in this scope, */
/* it must be a forward */
if (token == IDENTIFIER) {
search_local_symtab(proc_idp);
if (proc_idp == NULL) {
enter_local_symtab(proc_idp);
proc_idp->defn.key = PROC_DEFN;
proc_idp->defn.info.routine.total_local_size = 0;
proc_idp->typep = &dummy_type;
proc_idp->label_index = 0;
}
else if ((proc_idp->defn.key == PROC_DEFN) &&
(proc_idp->defn.info.routine.key == FORWARD))
forward_flag = TRUE;
else error(REDEFINED_IDENTIFIER);
get_token();
}
else error(MISSING_IDENTIFIER);
/* sync. Should be ( or ; */
synchronize(follow_proc_id_list, declaration_start_list, statement_start_list);
enter_scope(NULL);
/* optional formal parameters, if FORWARD shouldn't be any, but parse */
/* for error recovery */
if (token == LPAREN) {
parm_listp = formal_parm_list(&parm_count, &total_parm_size);
if (forward_flag) error(ALREADY_FORWARDED);
else {
proc_idp->defn.info.routine.parm_count = parm_count;
proc_idp->defn.info.routine.total_parm_size = total_parm_size;
proc_idp->defn.info.routine.parms = parm_listp;
}
}
else if (!forward_flag) {
proc_idp->defn.info.routine.parm_count = 0;
proc_idp->defn.info.routine.total_parm_size = 0;
proc_idp->defn.info.routine.parms = NULL;
}
proc_idp->typep = NULL;
exit_debug("procedure_header");
return(proc_idp);
} /* end procedure_header */
/***************************************************************************/
/***************************************************************************/
/* function_header() Process a function header */
/* FUNCTION <id> : <type-id> */
/* or FUNCTION <id> ( <parm-list> ) : <type-id> */
/* returns pointer to the function id node. */
SYMTAB_NODE_PTR function_header()
{
SYMTAB_NODE_PTR func_idp, type_idp; /* function and type id */
SYMTAB_NODE_PTR parm_listp; /* formal param list */
int parm_count;
int total_parm_size;
BOOLEAN forward_flag = FALSE; /* TRUE iff forward */
entry_debug("function_header");
get_token();
/* if func id has already been declared in this scope, */
/* it must be a forward */
if (token == IDENTIFIER) {
search_local_symtab(func_idp);
if (func_idp == NULL) {
enter_local_symtab(func_idp);
func_idp->defn.key = FUNC_DEFN;
func_idp->defn.info.routine.total_local_size = 0;
func_idp->typep = &dummy_type;
func_idp->label_index = 0;
}
else if ((func_idp->defn.key == FUNC_DEFN) &&
(func_idp->defn.info.routine.key == FORWARD))
forward_flag = TRUE;
else error(REDEFINED_IDENTIFIER);
get_token();
}
else error(MISSING_IDENTIFIER);
/* sync. Should be ( or : or ; */
synchronize(follow_func_id_list, declaration_start_list, statement_start_list);
enter_scope(NULL);
/* optional formal parameters, if FORWARD shouldn't be any, but parse */
/* for error recovery */
if (token == LPAREN) {
parm_listp = formal_parm_list(&parm_count, &total_parm_size);
if (forward_flag) error(ALREADY_FORWARDED);
else {
func_idp->defn.info.routine.parm_count = parm_count;
func_idp->defn.info.routine.total_parm_size = total_parm_size;
func_idp->defn.info.routine.parms = parm_listp;
}
}
else if (!forward_flag) {
func_idp->defn.info.routine.parm_count = 0;
func_idp->defn.info.routine.total_parm_size = 0;
func_idp->defn.info.routine.parms = NULL;
}
/* for a forward, should not be a type, but parse anyway */
if (!forward_flag || (token == COLON)) {
if_token_get_else_error(COLON, MISSING_COLON);
/* changed for EXPRESS
if (token == IDENTIFIER) {
search_and_find_all_symtab(type_idp);
if (type_idp->defn.key != TYPE_DEFN) error(INVALID_TYPE);
if (!forward_flag) func_idp->typep = type_idp->typep;
get_token();
}
else {
error(MISSING_IDENTIFIER);
func_idp->typep = &dummy_type;
}
*/
if (!forward_flag) func_idp->typep = get_type();
get_token();
if (forward_flag) error(ALREADY_FORWARDED);
}
exit_debug("function_header");
return(func_idp);
} /* end function_header */
/***************************************************************************/
/***************************************************************************/
/* formal_parm_list(countp, total_size) Process formal parameter list */
/* ( VAR <id-list> : <type> ; */
/* <id-list> : <type> ; ... ) */
/* return a pointer to the head of the parameter id list */
SYMTAB_NODE_PTR formal_parm_list(countp, total_sizep)
int *countp; /* ptr to count of parameters */
int *total_sizep; /* ptr to total byte size of parameters */
{
SYMTAB_NODE_PTR parm_idp, first_idp, last_idp; /* parm ids */
SYMTAB_NODE_PTR prev_last_idp = NULL; /* last id of list */
SYMTAB_NODE_PTR parm_listp = NULL; /* parm list */
SYMTAB_NODE_PTR type_idp; /* type id */
TYPE_STRUCT_PTR parm_tp; /* parm type */
DEFN_KEY parm_defn; /* parm definition */
int parm_count = 0; /* count of parms */
int parm_offset = STACK_FRAME_HEADER_SIZE;
entry_debug("formal_parm_list");
get_token();
/* loop to process declarations seperated by ; */
while ((token == IDENTIFIER) || (token == VAR)) {
first_idp = NULL;
/* VAR parm? */
if (token == VAR) {
parm_defn = VARPARM_DEFN;
get_token();
}
else parm_defn = VALPARM_DEFN;
/* <id list> */
while (token == IDENTIFIER) {
search_and_enter_local_symtab(parm_idp);
parm_idp->defn.key = parm_defn;
parm_idp->label_index = 0;
++parm_count;
if (parm_listp == NULL) parm_listp = parm_idp;
/* link parms together */
if (first_idp == NULL) first_idp = last_idp = parm_idp;
else {
last_idp->next = parm_idp;
last_idp = parm_idp;
}
get_token();
if_token_get(COMMA);
}
if_token_get_else_error(COLON, MISSING_COLON);
/* changed following for EXPRESS
if (token == IDENTIFIER) {
search_and_find_all_symtab(type_idp);
if (type_idp->defn.key != TYPE_DEFN) error(INVALID_TYPE);
parm_tp = type_idp->typep;
get_token();
}
else {
error(MISSING_IDENTIFIER);
parm_tp = &dummy_type;
}
*/
parm_tp = get_type();
get_token();
/* assign the offset and the type to all parm ids in the sublist */
for (parm_idp = first_idp; parm_idp != NULL; parm_idp = parm_idp->next) {
parm_idp->typep = parm_tp;
parm_idp->defn.info.data.offset = parm_offset++;
}
/* link this sublist to the list of all parm ids */
if (prev_last_idp != NULL) prev_last_idp->next = first_idp;
prev_last_idp = last_idp;
/* sync: Should be ; or ) */
synchronize(follow_parms_list, NULL, NULL);
if_token_get(SEMICOLON);
} /* end while */
if_token_get_else_error(RPAREN, MISSING_RPAREN);
*countp = parm_count;
*total_sizep = parm_offset - STACK_FRAME_HEADER_SIZE;
exit_debug("formal_parm_list");
return(parm_listp);
} /* end formal_parm_list */
/***************************************************************************/
/***************************************************************************/
/* routine_call(rtn_idp, parm_check_flag) Process a call to a procedure */
/* or function */
/* return pointer to the type structure of the call */
TYPE_STRUCT_PTR routine_call(rtn_idp, parm_check_flag)
SYMTAB_NODE_PTR rtn_idp; /* routine id */
BOOLEAN parm_check_flag; /* if TRUE then check parms */
{
TYPE_STRUCT_PTR declared_routine_call(), standard_routine_call();
entry_debug("routine_call");
if ((rtn_idp->defn.info.routine.key == DECLARED) ||
(rtn_idp->defn.info.routine.key == FORWARD) ||
(!parm_check_flag)) {
exit_debug("routine_call");
return(declared_routine_call(rtn_idp, parm_check_flag));
}
else {
exit_debug("routine_call");
return(standard_routine_call(rtn_idp));
}
} /* end routine_call */
/***************************************************************************/
/***************************************************************************/
/* declared_routine_call(rtn_idp, parm_check_flag) Process a call to a */
/* declared function or procedure */
/* <id> or */
/* <id> ( <parm-list> ) */
/* The actual params are checked against the */
/* formal params for type and number. */
/* return pointer to type structure of the call */
TYPE_STRUCT_PTR declared_routine_call(rtn_idp, parm_check_flag)
SYMTAB_NODE_PTR rtn_idp; /* routine id */
BOOLEAN parm_check_flag; /* if TRUE then check parms */
{
entry_debug("declared_routine_call");
actual_parm_list(rtn_idp, parm_check_flag);
exit_debug("declared_routine_call");
return(rtn_idp->defn.key == PROC_DEFN ? NULL : rtn_idp->typep);
} /* end declared_routine_call */
/***************************************************************************/
/***************************************************************************/
/* actual_parm_list(rtn_idp, parm_check_flag) Process actual param list */
/* ( <expr-list> ) */
actual_parm_list(rtn_idp, parm_check_flag)
SYMTAB_NODE_PTR rtn_idp; /* routine id */
BOOLEAN parm_check_flag; /* if TRUE then check parms */
{
SYMTAB_NODE_PTR formal_parm_idp;
DEFN_KEY formal_parm_defn;
TYPE_STRUCT_PTR formal_parm_tp, actual_parm_tp;
entry_debug("actual_parm_list");
if (parm_check_flag) formal_parm_idp = rtn_idp->defn.info.routine.parms;
if (token == LPAREN) {
/* loop to process actual param expressions */
do {
/* get info on corresponding formal params */
if (parm_check_flag && (formal_parm_idp != NULL)) {
formal_parm_defn = formal_parm_idp->defn.key;
formal_parm_tp = formal_parm_idp->typep;
}
get_token();
/* Actual and formal parms must be consistent. */
/* Actual parm may be an expression */
if ((formal_parm_idp == NULL) ||
(formal_parm_defn == VALPARM_DEFN) ||
(!parm_check_flag)) {
actual_parm_tp = expression();
if (parm_check_flag &&
(formal_parm_idp != NULL) &&
(!is_assign_type_compatible(formal_parm_tp, actual_parm_tp)))
error(INCOMPATIBLE_TYPES);
}
/* Now the same for VAR params */
else {
if (token == IDENTIFIER) {
SYMTAB_NODE_PTR idp;
search_and_find_all_symtab(idp);
actual_parm_tp = variable(idp, VARPARM_USE);
if (formal_parm_tp != actual_parm_tp) error(INCOMPATIBLE_TYPES);
}
else { /* not a variable, but parse anyway */
actual_parm_tp = expression();
error(INVALID_VAR_PARM);
}
}
/* check if there are more actuals than formals */
if (parm_check_flag) {
if (formal_parm_idp == NULL) error(WRONG_NUMBER_OF_PARMS);
else formal_parm_idp = formal_parm_idp->next;
}
/* sync. Should be , or ) */
synchronize(follow_parm_list, statement_end_list, NULL);
} while (token == COMMA); /* end do */
if_token_get_else_error(RPAREN, MISSING_RPAREN);
}
/* check for fewer actuals than formals */
if (parm_check_flag && (formal_parm_idp != NULL)) error(WRONG_NUMBER_OF_PARMS);
exit_debug("actual_parm_list");
return;
} /* end actual_parm_list */
/***************************************************************************/
/***************************************************************************/
/* block(rtn_idp) Process a block, which consists of declarations */
/* followed by a compound statement */
old_block(rtn_idp)
SYMTAB_NODE_PTR rtn_idp;
{
extern BOOLEAN block_flag;
entry_debug("block");
declarations(rtn_idp);
/* sync. Should be ; */
synchronize(follow_decls_list, NULL, NULL);
if (token != BEGIN) error(MISSING_BEGIN);
crunch_token();
block_flag = TRUE;
compound_statement();
block_flag = FALSE;
exit_debug("block");
return;
} /* end block */
/***************************************************************************/