home *** CD-ROM | disk | FTP | other *** search
-
- /********************************************
- fcall.c
- copyright 1991, Michael D. Brennan
-
- This is a source file for mawk, an implementation of
- the AWK programming language.
-
- Mawk is distributed without warranty under the terms of
- the GNU General Public License, version 2, 1991.
- ********************************************/
-
-
- /*$Log: fcall.c,v $
- * Revision 1.7 1995/08/27 15:46:47 mike
- * change some errmsgs to compile_errors
- *
- * Revision 1.6 1995/06/09 22:58:24 mike
- * cast to shutup solaris cc on comparison of short to ushort
- *
- * Revision 1.5 1995/06/06 00:18:26 mike
- * change mawk_exit(1) to mawk_exit(2)
- *
- * Revision 1.4 1995/04/21 14:20:14 mike
- * move_level variable to fix bug in arglist patching of moved code.
- *
- * Revision 1.3 1995/02/19 22:15:37 mike
- * Always set the call_offset field in a CA_REC (for obscure
- * reasons in fcall.c (see comments) there.)
- *
- * Revision 1.2 1993/07/17 13:22:52 mike
- * indent and general code cleanup
- *
- * Revision 1.1.1.1 1993/07/03 18:58:11 mike
- * move source to cvs
- *
- * Revision 5.4 1993/01/09 19:03:44 mike
- * code_pop checks if the resolve_list needs relocation
- *
- * Revision 5.3 1993/01/07 02:50:33 mike
- * relative vs absolute code
- *
- * Revision 5.2 1993/01/01 21:30:48 mike
- * split new_STRING() into new_STRING and new_STRING0
- *
- * Revision 5.1 1991/12/05 07:55:54 brennan
- * 1.1 pre-release
- *
- */
-
- #include "mawk.h"
- #include "symtype.h"
- #include "code.h"
-
- /* This file has functions involved with type checking of
- function calls
- */
-
- static FCALL_REC *PROTO(first_pass, (FCALL_REC *)) ;
- static CA_REC *PROTO(call_arg_check, (FBLOCK *, CA_REC *,
- INST *, unsigned)) ;
- static int PROTO(arg_cnt_ok, (FBLOCK *, CA_REC *, unsigned)) ;
- static void PROTO(relocate_arglist, (CA_REC *, int, unsigned, int)) ;
-
-
- static int check_progress ;
- /* flag that indicates call_arg_check() was able to type
- check some call arguments */
-
- /* type checks a list of call arguments,
- returns a list of arguments whose type is still unknown
- */
- static CA_REC *
- call_arg_check(callee, entry_list, start, line_no)
- FBLOCK *callee ;
- CA_REC *entry_list ;
- INST *start ; /* to locate patch */
- unsigned line_no ; /* for error messages */
- {
- register CA_REC *q ;
- CA_REC *exit_list = (CA_REC *) 0 ;
-
- check_progress = 0 ;
-
- /* loop :
- take q off entry_list
- test it
- if OK zfree(q) else put on exit_list */
- while (q = entry_list)
- {
- entry_list = q->link ;
-
- if (q->type == ST_NONE)
- {
- /* try to infer the type */
- /* it might now be in symbol table */
- if (q->sym_p->type == ST_VAR)
- {
- /* set type and patch */
- q->type = CA_EXPR ;
- start[q->call_offset + 1].ptr = (PTR) q->sym_p->stval.cp ;
- }
- else if (q->sym_p->type == ST_ARRAY)
- {
- q->type = CA_ARRAY ;
- start[q->call_offset].op = A_PUSHA ;
- start[q->call_offset + 1].ptr = (PTR) q->sym_p->stval.array ;
- }
- else /* try to infer from callee */
- {
- switch (callee->typev[q->arg_num])
- {
- case ST_LOCAL_VAR:
- q->type = CA_EXPR ;
- q->sym_p->type = ST_VAR ;
- q->sym_p->stval.cp = ZMALLOC(CELL) ;
- q->sym_p->stval.cp->type = C_NOINIT ;
- start[q->call_offset + 1].ptr =
- (PTR) q->sym_p->stval.cp ;
- break ;
-
- case ST_LOCAL_ARRAY:
- q->type = CA_ARRAY ;
- q->sym_p->type = ST_ARRAY ;
- q->sym_p->stval.array = new_ARRAY() ;
- start[q->call_offset].op = A_PUSHA ;
- start[q->call_offset + 1].ptr =
- (PTR) q->sym_p->stval.array ;
- break ;
- }
- }
- }
- else if (q->type == ST_LOCAL_NONE)
- {
- /* try to infer the type */
- if (*q->type_p == ST_LOCAL_VAR)
- {
- /* set type , don't need to patch */
- q->type = CA_EXPR ;
- }
- else if (*q->type_p == ST_LOCAL_ARRAY)
- {
- q->type = CA_ARRAY ;
- start[q->call_offset].op = LA_PUSHA ;
- /* offset+1 op is OK */
- }
- else /* try to infer from callee */
- {
- switch (callee->typev[q->arg_num])
- {
- case ST_LOCAL_VAR:
- q->type = CA_EXPR ;
- *q->type_p = ST_LOCAL_VAR ;
- /* do not need to patch */
- break ;
-
- case ST_LOCAL_ARRAY:
- q->type = CA_ARRAY ;
- *q->type_p = ST_LOCAL_ARRAY ;
- start[q->call_offset].op = LA_PUSHA ;
- break ;
- }
- }
- }
-
- /* if we still do not know the type put on the new list
- else type check */
- if (q->type == ST_NONE || q->type == ST_LOCAL_NONE)
- {
- q->link = exit_list ;
- exit_list = q ;
- }
- else /* type known */
- {
- if (callee->typev[q->arg_num] == ST_LOCAL_NONE)
- callee->typev[q->arg_num] = q->type ;
- else if (q->type != callee->typev[q->arg_num])
- compile_error("type error in arg(%d) in call to %s",
- q->arg_num + 1, callee->name) ;
-
- ZFREE(q) ;
- check_progress = 1 ;
- }
- } /* while */
-
- return exit_list ;
- }
-
-
- static int
- arg_cnt_ok(fbp, q, line_no)
- FBLOCK *fbp ;
- CA_REC *q ;
- unsigned line_no ;
- {
- if ((int)q->arg_num >= (int)fbp->nargs)
- /* casts shutup stupid warning from solaris sun cc */
- {
- compile_error("too many arguments in call to %s", fbp->name) ;
- return 0 ;
- }
- else return 1 ;
- }
-
-
- FCALL_REC *resolve_list ;
- /* function calls whose arg types need checking
- are stored on this list */
-
-
- /* on first pass thru the resolve list
- we check :
- if forward referenced functions were really defined
- if right number of arguments
- and compute call_start which is now known
- */
-
- static FCALL_REC *
- first_pass(p)
- register FCALL_REC *p ;
- {
- FCALL_REC dummy ;
- register FCALL_REC *q = &dummy ; /* trails p */
-
- q->link = p ;
- while (p)
- {
- if (!p->callee->code)
- {
- /* callee never defined */
- compile_error("function %s never defined", p->callee->name) ;
- /* delete p from list */
- q->link = p->link ;
- /* don't worry about freeing memory, we'll exit soon */
- }
- /* note p->arg_list starts with last argument */
- else if (!p->arg_list /* nothing to do */ ||
- !p->arg_cnt_checked &&
- !arg_cnt_ok(p->callee, p->arg_list, p->line_no))
- {
- q->link = p->link ; /* delete p */
- /* the ! arg_list case is not an error so free memory */
- ZFREE(p) ;
- }
- else
- {
- /* keep p and set call_start */
- q = p ;
- switch (p->call_scope)
- {
- case SCOPE_MAIN:
- p->call_start = main_start ;
- break ;
-
- case SCOPE_BEGIN:
- p->call_start = begin_start ;
- break ;
-
- case SCOPE_END:
- p->call_start = end_start ;
- break ;
-
- case SCOPE_FUNCT:
- p->call_start = p->call->code ;
- break ;
- }
- }
- p = q->link ;
- }
- return dummy.link ;
- }
-
- /* continuously walk the resolve_list making type deductions
- until this list goes empty or no more progress can be made
- (An example where no more progress can be made is at end of file
- */
-
- void
- resolve_fcalls()
- {
- register FCALL_REC *p, *old_list, *new_list ;
- int progress ; /* a flag */
-
- old_list = first_pass(resolve_list) ;
- new_list = (FCALL_REC *) 0 ;
- progress = 0 ;
-
- while (1)
- {
- if (!old_list)
- {
- /* flop the lists */
- old_list = new_list ;
- if (!old_list /* nothing left */
- || !progress /* can't do any more */ )
- return ;
-
- new_list = (FCALL_REC *) 0 ; progress = 0 ;
- }
-
- p = old_list ;
- old_list = p->link ;
-
- if (p->arg_list = call_arg_check(p->callee, p->arg_list,
- p->call_start, p->line_no))
- {
- /* still have work to do , put on new_list */
- progress |= check_progress ;
- p->link = new_list ; new_list = p ;
- }
- else
- {
- /* done with p */
- progress = 1 ;
- ZFREE(p) ;
- }
- }
- }
-
- /* the parser has just reduced a function call ;
- the info needed to type check is passed in. If type checking
- can not be done yet (most common reason -- function referenced
- but not defined), a node is added to the resolve list.
- */
- void
- check_fcall(callee, call_scope, move_level, call, arg_list, line_no)
- FBLOCK *callee ;
- int call_scope ;
- int move_level ;
- FBLOCK *call ;
- CA_REC *arg_list ;
- unsigned line_no ;
- {
- FCALL_REC *p ;
-
- if (!callee->code)
- {
- /* forward reference to a function to be defined later */
- p = ZMALLOC(FCALL_REC) ;
- p->callee = callee ;
- p->call_scope = call_scope ;
- p->move_level = move_level ;
- p->call = call ;
- p->arg_list = arg_list ;
- p->arg_cnt_checked = 0 ;
- p->line_no = line_no ;
- /* add to resolve list */
- p->link = resolve_list ; resolve_list = p ;
- }
- else if (arg_list && arg_cnt_ok(callee, arg_list, line_no))
- {
- /* usually arg_list disappears here and all is well
- otherwise add to resolve list */
-
- if (arg_list = call_arg_check(callee, arg_list,
- code_base, line_no))
- {
- p = ZMALLOC(FCALL_REC) ;
- p->callee = callee ;
- p->call_scope = call_scope ;
- p->move_level = move_level ;
- p->call = call ;
- p->arg_list = arg_list ;
- p->arg_cnt_checked = 1 ;
- p->line_no = line_no ;
- /* add to resolve list */
- p->link = resolve_list ; resolve_list = p ;
- }
- }
- }
-
-
- /* code_pop() has just moved some code. If this code contains
- a function call, it might need to be relocated on the
- resolve list too. This function does it.
- */
-
- void
- relocate_resolve_list(scope, move_level, fbp, orig_offset, len, delta)
- int scope ;
- int move_level ;
- FBLOCK *fbp ;
- int orig_offset ;
- unsigned len ;
- int delta ; /* relocation distance */
- {
- FCALL_REC *p = resolve_list ;
-
- while (p)
- {
- if (scope == p->call_scope && move_level == p->move_level &&
- (scope == SCOPE_FUNCT ? fbp == p->call : 1))
- {
- relocate_arglist(p->arg_list, orig_offset,
- len, delta) ;
- }
- p = p->link ;
- }
- }
-
- static void
- relocate_arglist(arg_list, offset, len, delta)
- CA_REC *arg_list ;
- int offset ;
- unsigned len ;
- int delta ;
- {
- register CA_REC *p ;
-
- if (!arg_list) return ;
-
- p = arg_list ;
- /* all nodes must be relocated or none, so test the
- first one */
-
- /* Note: call_offset is always set even for args that don't need to
- be patched so that this check works. */
- if ( p->call_offset < offset || p->call_offset >= offset + len )
- return ;
-
- /* relocate the whole list */
- do
- {
- p->call_offset += delta ;
- p = p->link ;
- }
- while (p);
- }
-
-
-
-
-
- /* example where typing cannot progress
-
- { f(z) }
-
- function f(x) { print NR }
-
- # this is legal, does something useful, but absurdly written
- # We have to design so this works
- */
-