[go: up one dir, main page]

Menu

[897c37]: / lpc / mud.y  Maximize  Restore  History

Download this file

2226 lines (2029 with data), 51.9 kB

%{
/*
 *    $Id: mud.y,v 1.20 2012/06/10 13:07:35 dredd Exp $
 *
 *    Shattered Mud Language.
 *    
 *    An LPC derivative language.
 *
 *    A word on the type hacks below
 *    o designed to minimise changes the rest of the system
 *      (this will be fixed in due course).
 *    o Assume 32 bit ints.
 *    top 8 bits struct no     (max 255 structs)
 *    next 8 bits        (pointer depth max 255)
 *    next 8 bits        (special types static, private etc)
 *    next 8 bits        (basic types)
 *
 *    (should be done using a struct; driver should support
 *     a fully "distributable" type system - these will all
 *     be done when I work out how :-)
 *
 *    Geoff Wong (C) 1993, 1999 
 */

#include <limits.h>
#include <stdlib.h>
#include <unistd.h>
#include <malloc.h>
#include <assert.h>
#include "stack.h"
#include "opcodes.h"
#include "lpc.h"
#include "lexer.h"
#include "build.h"
#include "lexical.h"
#include "consts.h"

/* runtime stuff */
#include "../runtime/hash.h"

#define YYMAXDEPTH    600

struct field {
    unsigned int type;
    short offset;
};

Val         * find_const2(Class ** io, Shared *s);
void         yyerror(), free_local_names_from();
extern int     yylex();
void         init_globals(), print_type(int), free_locals();
void    free_type_list();

int         variable_count;    /* global vars */
int         static_variable_flag;
extern    char * zero;
Func         * prog;

extern int     current_line, uinstr, puinstr;
extern Obj     * Emulate;

int         lang_debug = 0;
int         last_type = 0;

/*
 * 'inherit_file' is used as a flag. If it is set to a string
 * after yyparse(), this string should be loaded as an object,
 * and the original object must be loaded again.
 * 'inherit_ob' will point to the super class object. This value is saved
 * so that load_object() can set up pointers to it.
 */

extern char 
    * current_file
    ;

extern Shared 
    ** inherit_file
    ;

int 
    file_number = 0, 
    inherit_number = 0
    ;

Class
    ** inherit_ob = NULL
    ;

#define MAX_BLOCK_DEPTH 16    /* big! */
int labelstack[100];

int switchstack[MAX_BLOCK_DEPTH];
int switch_depth = 0;

int LD = 0; 

union 
{
        float r;
        int i;
        lexar * s;
} ultima, penultima;

int     block_depth = 0, max_locals = 0;
int     num_args, current_type = 0;
int     no_of_classes = 0;
Func     *prog_head = 0, *CPD = 0;
Func     *initialise_func = 0;
Val     **global_table = 0;
CVal     **constant_table = 0;
struct var_def *arg_head = 0;

/* globals stuff */
struct var_def 
    * global_head = 0,
    * global_tail = 0
    ;
int num_globals = 0;
int num_constants = 0;

/* local variable stuff */
struct var_def *local_head = 0;
int num_locals;
int num_block_locals[MAX_BLOCK_DEPTH];


#define STRING_BLOCK 100
#define INITIAL_FUNC_SIZE 200

%}

%token TOK_STATIC TOK_PRIVATE TOK_TYPE TOK_CLASS TOK_SELF
%token TOK_ARRAY TOK_OBJECT TOK_STRING_DECL TOK_REALT TOK_INT
%token TOK_NUMBER TOK_STRING TOK_REAL TOK_SWITCH TOK_CASE TOK_DEFAULT
%token TOK_IDENTIFIER TOK_TYNAME TOK_NEW TOK_VOID
%token TOK_INHERIT TOK_COLON_COLON TOK_ENUM
%token TOK_AS TOK_IMPORT TOK_END TOK_MODULE
%token TOK_CATCH TOK_THROW TOK_GENERIC TOK_ABSTRACT
%token TOK_DOTDOT TOK_LEFT_ARR TOK_RIGHT_ARR
%token TOK_IF TOK_THEN TOK_ELSE
%token TOK_WHILE TOK_DO TOK_FOR TOK_BREAK TOK_CONTINUE 
%token TOK_RETURN TOK_CONST
%token TOK_LSH_EQ TOK_RSH_EQ TOK_LARROW
%token TOK_AND_EQ TOK_OR_EQ TOK_XOR_EQ
%token TOK_ADD_EQ TOK_SUB_EQ TOK_MULT_EQ TOK_DIV_EQ TOK_MOD_EQ

%union
{
    Func *lnode;
    int number;
    char *string;
    Shared * shared;
    float real;
    lexar *lexstr;
    Val * val;
}

%type <number> expr_list function_call expr import_list in_options
%type <number> constant_expr bracket_list prefix stmt_expr
%type <number> string TOK_NUMBER TOK_REAL 
%type <number> comma_expr for_expr
%type <number> switch_expr 
%type <number> number 
%type <real>   realnumber

%type <number> statements statement simple_stmt return
%type <lnode>  function_def new_global_name_decl program switch switch_case
%type <number> local_list locid_decl global_name_list 
%type <number> arglist fun_arglist new_fun_arg

%type <number> static op_assign type possible_type typelist
%type <number> basetype statprivtype variable 
%type <number> while for do block ifstmt if_expr local_declaration

%type <number> blktmp import

%type <string> TOK_IDENTIFIER TOK_STRING
%type <shared> identifier function_name newlocid new_global_name

%type <val> constant_expr2

%right TOK_CATCH 
%right OPASSIGN
%right '='
%nonassoc ':'
%right '?' 
%left TOK_LOR 
%left TOK_LAND 
%left '|' 
%left '^'
%left '&'
%nonassoc TOK_EQ TOK_NE 
%nonassoc '>' TOK_GE '<' TOK_LE 
%left TOK_LSH TOK_RSH 
%left '+' '-' 
%left '*' '%' '/' 
%left POSTINC POSTDEC
%right TOK_INC TOK_DEC 
%right '!'  '~' UMINUS
%nonassoc '@' '[' ']'  
%left TOK_ARROW
%left '.'

%%

all: 
    {
        /* initialise here! */
        prog_head = 0;
        CPD = 0;
        num_globals = 0;
        num_constants = 0;
        global_head = 0;
        free_type_list();
        penultima.s = 0;
        ultima.s = 0;
    }
    in_options
    { 
      Func *p;

      /* 
       * should check inherit_file and return if there are some
       * files to be loaded 
       */
      if (inherit_file != NULL) 
      {
        inherit_file[file_number] = 0;
        return 0;
      }
      if (inherit_ob != NULL) {
        inherit_ob[inherit_number] = 0;
      }
      p = alloc_function(0, shared_string_copy(C("*InitGlobals")), INITIAL_FUNC_SIZE, 0);
      p->next = 0;
      p->num_arg = 0;
      CPD = p;
      emit(S_ENTER, 0);
      if (inherit_ob != NULL || inherit_file != NULL) 
      {
        Shared * v;
        char pname[1024];
        int i;

        for (i = 0; inherit_ob[i]; i++) 
        {
            emit(S_PUSH8, 0);
            emit(S_PUSHCARGS, 0);    
            emit(S_PUSHCOFF, 0);
            emit(S_ADDSP, 0);
            strcpy(pname, inherit_ob[i]->name->str);
            strcat(pname, "::*InitGlobals");
            v = string_copy(pname);
            add_patch(CPD, P_CALLU, v);
            /* printf("S_CALLU: %s\n", v); */
            emit64(S_CALLU, (char *)&(v));
        }
      }
      initialise_func = p;
      prog_head = p;
      init_globals(); 
      CPD = initialise_func;
    }
    program
    /* global initialisation function */
    {
        initialise_func->next = 0;
        CPD = initialise_func;
        /* copy initialise code block to a non-global one */
        emit(S_PUSH8, 0);
        emits(S_RETURN);
        emits(S_END);
        CPD->block = code_size_fix(CPD); 
        CPD->num_var = 0;
        prog_head = $4;
        if (!prog_head) 
        {
            printf("No program returned!\n");
        }
        make_global_tables();
    }
    ;

           
program: /* empty */ 
    { $$ = prog_head; /* start of program */ }
    | program
    {
        block_depth = 0;
        penultima.s = 0;
        ultima.s = 0;
    }
    function_def
    {
        if ($3 != 0) 
        {
            Func *p = (Func *)$3;
            p->next = (Func *)$1;
            $$ = p;
            /* should this also check efuns & emulatefuns? */
            if (defined_function(p->name->str)) 
            {
                yyerror("Illegal to redeclare function.");
            }
        } 
        else 
        {
            $$ = $1;
        }
    }
    ;

/*
moddef:    TOK_MODULE identifier module TOK_END identifier ';'

module: program ;
*/

in_options:    /* empty */
    { $$ = 0; }
    |     import_list
    { $$ = 0; }
    |    inheritance_list
    { $$ = 0; }
    |    inheritance_list import_list
    { $$ = 0; }
    ;

import_list:
    import
    { $$ = $1; }
    | import_list import
    { $$ = $2; }
    ;

inheritance_list:
    inheritance
    | inheritance_list inheritance
    ;

inheritance: 
    TOK_INHERIT TOK_STRING ';'
    {
        /* FIX:: MAX_INHERIT is non-dynamic brain damage */
        if (inherit_number >= MAX_INHERIT) 
        {
            free($<lexstr>2);
            yyerror("Too many files inherited.\n");
        } 
        else 
        {
            Shared * inhs = to_Cstring($<lexstr>2);

            free($<lexstr>2);
            if (inherit_ob == NULL) 
            {
                inherit_ob = (Class **)
                  malloc(sizeof(Class *) * (MAX_INHERIT+1));
                inherit_number = 0;
            }

            /* External dependency! */
            //printf("find class: %s\n", inhs->str);
            inherit_ob[inherit_number] = find_class(inhs);
            //if (inherit_ob[inherit_number]) printf("found: (%d) %s\n", 
            //    inherit_number, inherit_ob[inherit_number]->name->str);

            if (inherit_ob[inherit_number] == 0) 
            {
                if (inherit_file == NULL) 
                {
                    inherit_file = (Shared **)
                            malloc(sizeof(Shared *) * (MAX_INHERIT+1));
                    file_number = 0;
                }
                inherit_file[file_number++] = inhs;
                inherit_file[file_number] = 0;
            }
            else 
            {
                inherit_number++;
                free_string(inhs);
            }
        }
    }
    ;

import:
    TOK_IMPORT TOK_STRING identifier ';'
    {
        Shared * inhs = to_Cstring($<lexstr>2);
        Class  *iobj; 
        free($<lexstr>2);
        /* need to insure stuff is loaded. fuck fuck fuck */
        iobj = find_class(inhs);
        if (!iobj) 
        {
            /* sigh - nasty piece of work. */
            if (!inherit_file) 
            {
                inherit_file = (Shared **)
                  malloc(sizeof(char *) * (MAX_INHERIT+1));
                file_number = 0;
            }
            inherit_file[file_number++] = inhs;
            inherit_file[file_number] = 0;
            free_string($3);
        }
        else 
        {
            add_type_list($3, iobj);
        }
        $$ = 0;
    }
    |
    TOK_IMPORT TOK_SELF identifier ';'
    {
        /* FIX:: self importing */
        add_type_list($3, 0);
        $$ = 0;
    }
    ;


number:  TOK_NUMBER
    {
        emit_number($1);
        $$ = (int)$1;
    }
    ;

realnumber:
    TOK_REAL
    {
        char * x;
        $<real>$ = $<real>1;
        x = (char *)(&$<real>$);
        emit32(S_PUSHr, x);
#if 0
printf("real yacc: %f (%x %x %x %x)\n", $<real>$, x[0], x[1], x[2], x[3]);
#endif
    }
    ;

function_def: statprivtype identifier '(' 
    {
        block_depth++; 
        $<lnode>$ = alloc_function($1, $2, INITIAL_FUNC_SIZE, 0);
        CPD = $<lnode>$;

        new_function();
    }  
    arglist ')'
    {
        struct var_def *t = arg_head;    

        /* haven't taken into account ret addr and old bp on stack */
        if (arg_head != NULL) 
        {
            /* emit(S_COPY, num_args); */
            while (t) {
                emit16(S_TYPEC, t->type);
                /*
                if (t->type != T_ANY)
                    emit16(S_TYPEC, t->type);
                else
                    emit(S_ADDSP, -1);    
                */
                t = t->next;
            }
        }
        emit(S_ENTER, 0);
        CPD->num_arg = num_args;
        $<number>$ = CPD->current_I;
    }
    block
    {
        Func *p = $<lnode>4;
        patch8($<number>7 - 1, max_locals);
/*      p->num_arg = num_args; */
/*      p->type = p->type | $1; */
        $$ = p;
        CPD = $$;
        /* free the local and arg space */
        free_locals();
        emit(S_PUSH8,0);
        emits(S_RETURN);
        emits(S_END);
        p->block = code_size_fix(p); 
        p->num_var = max_locals;
        block_depth--;
        if (block_depth == 0) CPD = initialise_func;
    } 
    | statprivtype global_name_list ';'
    { $$ = 0; }
    | TOK_CONST new_global_name '=' constant_expr2 ';'
    {
        /* + these could be copy propagated later? */
        /* + need to redefine global_table in object */
        /* + copy in values when creating variable table */
        /* + also must ensure these aren't assigned to! */

        add_global_variable($2, $<val>4->type | TY_CONSTANT, $<val>4);
        $$ = 0;
    }    
    /* | simple_stmt { $$ = 0; } */
    ;
    

statprivtype: static possible_type
    { 
      /* $$ = $2; 41093 */
      current_type = $1 | $2;
      $$ = current_type;
      static_variable_flag = $$;
    }
    ;

static:  TOK_STATIC { $$ = TY_STATIC; }
    |  /* empty */ { $$ = 0; }
    | TOK_PRIVATE { $$ = TY_PRIVATE; }
    | TOK_STATIC TOK_PRIVATE { $$ = TY_STATIC | TY_PRIVATE; }
    | TOK_PRIVATE TOK_STATIC { $$ = TY_STATIC | TY_PRIVATE; } /* hack alert */
    ;

arglist: /* empty */    { $$ = 0; }
    | fun_arglist    { $$ = $1; }
    ;

fun_arglist: new_fun_arg
    | new_fun_arg ',' fun_arglist
    ;

new_fun_arg: possible_type identifier
    { 
        /* only called when processing args */
        if (find_argument($2))
            yyerrorf("Name \"%s\"appears twice in function parameter list\n", $2->str);
        else 
            add_argument($2, current_type);
        $$ = 0;
    }
    ;

possible_type: 
    /* empty */         { $$ = T_ANY; current_type = T_ANY; }
    | type              { $$ = $1; current_type = $1;  }
    ;

type: basetype          { $$ = $1; }
    | '(' typelist ')'  { $$ = $2; }
    ;

basetype: TOK_INT       { $$ = T_NUMBER; }
    | TOK_STRING_DECL   { $$ = T_STRING; }
    | TOK_ARRAY         { $$ = T_POINTER; }
    | TOK_REALT         { $$ = T_REAL; }
    | TOK_OBJECT        { $$ = T_OBJECT; }
    | TOK_VOID          { $$ = T_VOID; }
    | TOK_CLASS identifier 
    {
        struct field * x;

        x = find_type($2);
        if (x) 
        {
            $$ = x->type;
            free(x);
        }
        else $$ = 0;
        free_string($2);
    }
    ;

typelist: basetype    { $$ = $1; }
    | typelist '|' basetype    { $$ = $1 | $3; }
    ;

global_name_list: new_global_name_decl  { $$ = 0; }
     | global_name_list ',' new_global_name_decl  { $$ = 0; }
    ;

new_global_name_decl: new_global_name
    {
        add_global_variable($1, current_type, 0); 
    }
    | new_global_name '[' 
    {
        $<lnode>$ = CPD;
        CPD = initialise_func;
    }
    expr_list ']'
    {
        int x, y;

        y = current_type | ($4 << 16);
#if 0
printf("adding type %d,%d = %d\n", $4, current_type, y);
#endif
        x = add_global_variable($1, y, 0); 
        emit_number($4);
        emits(S_ARR_ALLOC);
        emit(S_POPG, x-1);
        type_check($4, T_ANY, T_NUMBER, "array declaration");
        CPD = $<lnode>3;     
    }
    | new_global_name '=' 
    {
        $<lnode>$ = CPD;
         CPD = initialise_func;
        add_global_variable($1, current_type, 0); 
    }
    expr
    {
        int x = find_global_variable($1);

/*        printf("Global no: %d.\n", x); */
        emit(S_POPG, x - 1);
        type_check($4, T_ANY, current_type, "global assignment");
        CPD = $<lnode>3;     
    }
    ;

new_global_name: identifier
    {
        if (find_global_variable($1))
            yyerrorf("Redeclaration of global variable \"%s\"\n", $1->str);
        $$ = $1;
    }
    ;

block:  '{' blktmp statements '}'
    {
        block_depth--;
        num_locals = $<number>2;
    }
    ;

blktmp: /* empty */ 
    { 
        $$ = num_locals;
        block_depth++;        /* re-set in 'block' */
    }
    ;

/*
 * Declarations and statements can be intermingled;
 * decls are statements in miniature (a block of value assignments).
 */

local_declaration: type { current_type = $1; } local_list ';' { $$ = $3; } ;

local_list: locid_decl 
      | locid_decl ',' local_list 
    ;

locid_decl: newlocid 
     {
        int x = add_local_variable($1, current_type);
        emit(S_PUSH8, 0);
        emit(S_POPL, x - 1); 
     } 
     | newlocid '[' expr_list ']'
     {
        int x, y;
        y = current_type | ($3 << 16);
        x = add_local_variable($1, y);
        emit_number($3);
        emits(S_ARR_ALLOC);
        emit(S_POPL, x - 1);
     }
     | newlocid '=' expr
     {
        int x = add_local_variable($1, current_type);
        emit(S_POPL, x - 1);
     } 
     ;

newlocid: identifier
     {
        if (find_local_block($1))
            yyerrorf("Redefinition of \"%s\" within the same block", $1->str);
        $$ = $1;
     }
     ;

statements: /* empty */ 
    { $$ = 0; }
    | statement 
    { 
        end_of_statement();
    }
    statements
    | error ';' { yyerror("Illegal statement"); $$ = 0; }
    ;

simple_stmt:
    ifstmt 
    | while 
    | for 
    | do 
    | switch { $$ = 0; }
    | return ';' { $$ = 0; }
    | expr ';' { $$ = 0; }
    | block { $$ = 0; }
    ;

statement: 
    simple_stmt
    | TOK_THROW comma_expr ';'
    { 
        emits(S_THROW);  
        $$ = 0; 
    }
    | TOK_BREAK ';'
    { 
        if ((LD == 0) && (switch_depth == 0)) 
        {
            yyerror("Break outside while, for, do, or switch construct.\n");
        }
        add_break_patch(CPD->current_I);
        emit16(S_JUMPU, 0);
        $$ = 0;
    }
    | TOK_CONTINUE ';' 
    { 
        if (LD == 0)  
        {
            yyerror("Continue outside while, for, or do loop.\n");
        }
        emit16(S_JUMP, labelstack[LD-1] - CPD->current_I - 3);
        $$ = 0;
    }
    | local_declaration { $$ = 0; }
    ;


while: TOK_WHILE
    {
        labelstack[LD++] = CPD->current_I;
        $<number>$ = CPD->current_I;
    }
    '(' expr ')'
    {
         emit16(S_JZERO,0);
         $<number>$ = CPD->current_I;
    }
    statement
    {
        end_of_statement();
        emits(S_OFLOW_CHECK);
        emit16(S_JUMP, $<number>2 - CPD->current_I - 3);  
        patch16((int)$<number>6 - 2, CPD->current_I - $<number>6);
        do_break_patch($<number>2, CPD->current_I);
        LD--;
    }
    ;

do: TOK_DO 
    {
        $<number>$ = CPD->current_I;
        labelstack[LD++] = CPD->current_I;
    }
    statement {
        end_of_statement();
    }
    TOK_WHILE '(' expr ')' 
    ';'
    {
        emits(S_OFLOW_CHECK);
        emit16(S_JNZERO, $<number>2 - CPD->current_I - 3); 
        do_break_patch($<number>2, CPD->current_I);
        LD--;
    }
    ;

for: TOK_FOR '(' for_expr ';' 
    {
        $<number>$ = CPD->current_I; 
    }
    for_expr ';'
    {
        emit16(S_JZERO, 0);
        emit16(S_JUMP, 0);
        $<number>$ = CPD->current_I;
        labelstack[LD++] = CPD->current_I;
    }
    for_expr ')' 
    {
        emit16(S_JUMP, $<number>5 - CPD->current_I - 3);
        patch16((int)($<number>8 - 2), CPD->current_I - $<number>8);
    }
    statement
    {
        end_of_statement();
        emits(S_OFLOW_CHECK);
        emit16(S_JUMP, $<number>8 - CPD->current_I - 3);
        patch16((int)($<number>8 - 5), CPD->current_I - $<number>8 + 3);
        do_break_patch($<number>5, CPD->current_I);
        LD--;
    }
    ;

for_expr: /* EMPTY */
    { emit(S_PUSH8, 1); }
    | comma_expr
    ;

switch: TOK_SWITCH 
    {
        $<number>$ = CPD->current_I;
    }
    '(' expr ')'
    {
    int x;    
        /* add a global *switch[X] */
        /* this will be an array ({ const, line num, const etc }) */
        x = add_global_variable(make_next_switch(),TY_STATIC|T_ANY,0);
        emit(S_SWITCH, x - 1);        /* switch on global */
        switchstack[switch_depth++] = CPD->current_I;

        /* set up the constants */
        $<lnode>$ = CPD;
        CPD = initialise_func;
        emit_push(GLOBAL, x);
        CPD = $<lnode>$;
        $<lnode>$ = x;
    }
    '{' switch_expr '}'
    { 
        int x = $8, y;

        /* once we've emitted all the statements emit switch table */
        $$ = CPD;
        y = CPD->current_I - switchstack[switch_depth-1];
        CPD = initialise_func;
        x = x * 2 + 2;

        /* we don't need to emit this IF we emitted
           a default: case */
        emit(S_PUSH8, -2);    /* no match marker.. */
        emit_number(y);

        assert(x >= 0);
        emit16(S_AGGREGATE,x);
        emits(S_POPO);    

        CPD = $$;
        $$ = 0;
        switch_depth--;
        do_break_patch($<number>2, CPD->current_I);
    }
    ;

switch_expr: switch_case
    { $$ = 1; }
    | switch_expr switch_case
    { $$ = $1 + 1; }
    ;

switch_case: TOK_CASE 
    {
#if 1
        /* record the last line */
        $<lnode>$ = CPD;
        CPD = initialise_func;
#endif
    }
    constant_expr 
    ':' 
    {
#if 1
    int x;
        /* put the offset line number (next for code) */
        CPD = $<lnode>2;
        x = CPD->current_I - switchstack[switch_depth-1];    
        CPD = initialise_func;
        emit_number(x);
        CPD = $<lnode>2;
#endif
    }
    statements
    { 
        /* return line number? */
        $$ = 0;
    }
    | TOK_DEFAULT ':'
    {
    /* two defaults should generate an error; it doesn't yet */
    int y;
        $<lnode>$ = CPD;
        y = -(CPD->current_I-switchstack[switch_depth-1]) - 1;    
        /* number is negative so we recognise it! */
        CPD = initialise_func;
        emit(S_PUSH8, -1);    
        emit_number(y);
        CPD = $<lnode>$;
    }
    statements
    { 
        $$ = 0;
    }
    ;

comma_expr: expr
    | comma_expr ',' expr
    {
         $$ = $3;
    }
    ;
          
prefix:    TOK_INC expr 
    { 
        emit(S_COPY, 1);
        emit(S_PUSH8, 1);
        emits(S_PLUS); 
        emit_pop($2);
        type_check($2, T_ANY, T_NUMBER, "pre ++");
        $$ = $2; 
    }
    | TOK_DEC expr
    {
        emit(S_COPY, 1);
        emit(S_PUSH8, -1);
        emits(S_PLUS); 
        emit_pop($2);
        type_check($2, T_ANY, T_NUMBER, "pre --");
        $$ = $2;
    }
    ;

stmt_expr:   variable '=' expr
    { 
        type_check($1, $3, T_ANY, "variable assignment");
        $$ = $1;
        emit_pop($1);
    }
    | variable 
    {
        emit(S_COPY, 1);    
    }
    op_assign expr %prec OPASSIGN
    {
        type_check($1, $4, T_ANY, "variable op= assignment");
        $$ = $1;
        emits($3); 
        emit_pop($1);
    }
    | TOK_CATCH 
    {
        emit16(S_CATCH, 0);
        $<number>$ = CPD->current_I;
    }
    statement
    { 
        end_of_statement();
        /* back patch the correct line */
        emits(S_POPCATCH);
        patch16($<number>2 - 2, CPD->current_I - $<number>2);
        $$ = T_ANY;
    }
    | expr '?'
    {
         emit16(S_JZERO, 0);
         $<number>$ = CPD->current_I;
        /* $$ is just after jmp offset */
    }
    expr
    {

          emit16(S_JUMP, 0);
          $<number>$ = CPD->current_I;
          patch16((int)$<number>3 - 2, CPD->current_I - $<number>3);
    }
    ':' expr
    {
        patch16((int)$<number>5 - 2, CPD->current_I - $<number>5); 
        type_check($<number>4,  $<number>7, T_ANY, "conditional operator");
        $$ = $<number>4;    
    }
    | variable TOK_INC %prec POSTINC
    {
        emit(S_COPY, 1);
        emit(S_PUSH8, 1);
        emits(S_PLUS);
        emit_pop($1);
        emit(S_PUSH8, 1);
        emits(S_MINUS);
        type_check($1, T_ANY, T_NUMBER, "post inc");
        $$ = $1;
    }
    | variable TOK_DEC %prec POSTDEC
    {
        emit(S_COPY, 1);
        emit(S_PUSH8, 1);
        emits(S_MINUS);
        emit_pop($1);
        emit(S_PUSH8, 1);
        emits(S_PLUS);
        type_check($1, T_ANY, T_NUMBER, "post dec");
        $$ = $1;
    }
    | function_call { $$ = $1; }
    ;

expr:
    '[' expr_list ']'
    {
        emit16(S_AGGREGATE, (short) $<number>2);
        $$ = T_POINTER;
    }
    |
    '(' '{' expr_list '}' ')'
    {
        emit16(S_AGGREGATE, (short) $<number>3);
        $$ = T_POINTER;
    }
    | variable { $$ = $1; }
    | TOK_NEW identifier
    {
        struct field * x;
        x = find_type($2);

        if (x) 
        {
            emit(S_PUSH8, x->offset);
            emit(S_PUSH8, 1);
            emits(S_ARR_ALLOC);
            $$ = x->type;
            free(x);
        }

        free_string($2);
    }
    | '(' comma_expr ')' 
    { $$ = $2; }
    | constant_expr
    | expr '[' TOK_DOTDOT 
    { 
        emit(S_PUSH8, 0); 
    }
    expr ']'
      {
        /* emits(S_EXTRACT);  */
        emit(S_EFUND, F_EXTRACT);
        type_check($1, T_ANY,  T_POINTER|T_STRING, "extract arg 1");
        type_check($5, T_ANY,  T_NUMBER, "extract arg 3");
        $$ = $1;
    }
    | expr '[' expr TOK_DOTDOT ']'
      {     
        int x = INT_MAX;
        emit32(S_PUSHi, (char *)&x);
        /* emits(S_EXTRACT); */
        emit(S_EFUND, F_EXTRACT);
        type_check($1, T_ANY,  T_POINTER|T_STRING, "extract arg 1");
        type_check($3, T_ANY,  T_NUMBER, "extract arg 2");
        $$ = $1;
    }
    | expr '[' expr TOK_DOTDOT expr ']'
      { 
          /* emits(S_EXTRACT);  */
        emit(S_EFUND, F_EXTRACT);
        type_check($1, T_ANY,  T_POINTER|T_STRING, "extract arg 1");
        type_check($3, T_ANY,  T_NUMBER, "extract arg 2");
        type_check($5, T_ANY,  T_NUMBER, "extract arg 3");
        $$ = $1;
    }
    | expr TOK_LOR 
    {
          emit(S_COPY, 1);
          emit16(S_JNZERO, 0);
          $<number>$ = CPD->current_I;
    }
    expr
    {
/*      type_check($1, $4, T_ANY); */
          $$ = T_NUMBER;
          emits(S_LOR);
          patch16($<number>3 -2, CPD->current_I - $<number>3);
    }
    | expr TOK_LAND 
    { 
      emit(S_COPY, 1);
      emit16(S_JZERO, 0); 
      $<number>$ = CPD->current_I;
    }
    expr
    { 
/*    type_check($1, $4, T_ANY); */
    $$ = T_NUMBER;
      emits(S_LAND); 
      patch16($<number>3 -2, CPD->current_I - $<number>3);
    }
    | expr '|' expr
        { 
      type_check($1, $3, T_NUMBER, "arithmetic or");
      $$ = $1;
      emits(S_OR); 
    }
    | expr '^' expr
    {
      type_check($1, $3, T_NUMBER, "arithmetic xor");
      $$ = $1;
      emits(S_XOR); 
    }
    | expr '&' expr
    {
      type_check($1, $3, T_NUMBER, "arithmetic and");
      $$ = $1;
       emits(S_AND); 
    }
    | expr TOK_EQ expr
    {
       type_check($1, $3, T_ANY, "equals");
      $$ = T_NUMBER;
      emits(S_EQ); 
    }
    | expr TOK_NE expr
    {
      type_check($1, $3, T_ANY, "not equals");
      $$ = T_NUMBER;
      emits(S_NE); 
    }
    | expr '>' expr
    {
      type_check($1, $3, T_STRING|T_REAL|T_NUMBER, ">");
      $$ = T_NUMBER;
       emits(S_GT);
    }
    | expr TOK_GE expr
    {
      type_check($1, $3, T_STRING|T_REAL|T_NUMBER, ">=");
      $$ = T_NUMBER;
      emits(S_GE);
    }
    | expr '<' expr
    {
      type_check($1, $3, T_STRING|T_REAL|T_NUMBER, "<");
      $$ = T_NUMBER;
      emits(S_LT);
    }
    | expr TOK_LE expr
    {
      type_check($1, $3, T_STRING|T_REAL|T_NUMBER, "<=");
      $$ = T_NUMBER;
      emits(S_LE);
    }
    | expr TOK_LSH expr
    {
        type_check($1, $3, T_NUMBER, "lsh");
        $$ = T_NUMBER;
        emits(S_LSH);
    }
    | expr TOK_RSH expr
    { 
        type_check($1, $3, T_NUMBER, "rsh");
        $$ = T_NUMBER;
        emits(S_RSH);
    }

    | expr '+' expr
    {
    /* const fix hack should do constant check on last 2 pushes,
       if both strings or numbers then add them remove 2 instrs
       and add the new push */
        if (!constant_fold(S_PLUS)) 
        {
            emits(S_PLUS);
            type_check($1,T_ANY,T_REAL|T_NUMBER|T_STRING|T_POINTER, "plus");
            type_check($3,T_ANY,T_REAL|T_NUMBER|T_STRING|T_POINTER, "plus");
            if (($1 & T_POINTER) || ($3 & T_POINTER)) 
            {
                if (!($1 & $3)) yyerror("Illegal array addition\n");
            }
            $$ = ($1 > $3) ? $1 : $3;
        }
    }
    | expr '-' expr
    { 
        if (!constant_fold(S_MINUS)) {
            emits(S_MINUS);
            type_check($1, $3, T_REAL|T_NUMBER, "minus");
            $$ = $1;
        }
    }
    | expr '*' expr
    {
    /*    if (!constant_fold(S_MULT)) */
        emits(S_MULT);
        type_check($1, $3, T_REAL|T_NUMBER, "multiply");
        $$ = $1;
    }    
    | expr '%' expr
    {    
    /*    if (!constant_fold(S_MOD)) */
        emits(S_MOD);
        type_check($1, $3, T_NUMBER, "modulo");
        $$ = $1;
    }    
    | expr '/' expr
    {
    /*    if (!constant_fold(S_DIVIDE))  */
        emits(S_DIVIDE);
        type_check($1, $3, T_REAL|T_NUMBER, "divide");
        $$ = $1;
    }    
    | prefix
    | '!' expr
    {
        emits(S_LNOT); 
        $$ =  T_NUMBER;
    }
    | '~' expr
    {
        type_check($2, T_ANY, T_NUMBER, "arithmetic not");
        $$ = T_NUMBER;
        emits(S_NOT); 
    }
    | '-' expr %prec UMINUS
    {
        type_check($2, T_ANY, T_NUMBER | T_REAL, "negate");
        $$ = $2;
#if 1
        if (uinstr == S_PUSH8) {
        char x;
            x = -(CPD->block[CPD->current_I-1]);
            CPD->block[CPD->current_I-1] = x;
        }
        /* should also do PUSHi & PUSHr */
        else
#endif
         emits(S_NEGATE);
    }
    | stmt_expr
    ;

op_assign: TOK_AND_EQ { $$ = S_AND; }
      | TOK_OR_EQ     { $$ = S_OR; }
      | TOK_XOR_EQ    { $$ = S_XOR; }
      | TOK_LSH_EQ    { $$ = S_LSH; }
      | TOK_RSH_EQ    { $$ = S_RSH; }
      | TOK_ADD_EQ    { $$ = S_PLUS; }
      | TOK_SUB_EQ    { $$ = S_MINUS; }
      | TOK_MULT_EQ   { $$ = S_MULT; }
      | TOK_MOD_EQ    { $$ = S_MOD; }
      | TOK_DIV_EQ    { $$ = S_DIVIDE; }
    ;

return: TOK_RETURN
    {
        emit(S_PUSH8, 0); 
        emits(S_RETURN);
    }
    | TOK_RETURN comma_expr
    { 
        type_check($2, T_ANY, CPD->type, "return type of function");
        emits(S_RETURN); 
    }
    ;

expr_list: /* empty */         { $$ = 0; }
    | expr                { $$ = 1; }
    | expr_list ',' expr  { $$ = $1 + 1; }
    ;


constant_expr: string
    {
        penultima.s = ultima.s;
        ultima.s = $<lexstr>1;
        $$ = T_STRING;
    }
    | number
    {
        penultima.i = ultima.i;
        ultima.i = $1;
        if (!($1)) $$ = T_ANY;    /* zero hack :( */
        else $$ = T_NUMBER;
    }
    | realnumber
    {
/*
        penultima.r = ultima.r;
        ultima.r = $<real>1;
        $$ = $<real>1;
*/
        $$ = T_REAL;
    }
    ;

constant_expr2:    TOK_STRING
    {
        $$ = share_Cstring(to_Cstring($<lexstr>1));
        free($<lexstr>1);
    }
    | TOK_NUMBER
    {
        $$ = alloc_value();
        $$->type = T_NUMBER;
        $$->u.number = $1;
    }
    | '-' TOK_NUMBER
    {
        $$ = alloc_value();
        $$->type = T_NUMBER;
        $$->u.number = - $2;
    }                                  
    | TOK_REAL
    {
        $$ = alloc_value();
        $$->type = T_REAL;
        $$->u.number = $1;
    }
    | '-' TOK_REAL
    {
        $$ = alloc_value();
        $$->type = T_REAL;
        $$->u.number = - $2;
    }                             
    | identifier
    {
        Val * x;

        if ((x = find_constant($1))) 
        {
/*        printf("Global no: %d.\n", $$); */
            $$ = alloc_value();
            // FIX: look up global
            assign_value($$, x);
        }
        else
        {
            yyerror("Only constant values maybe used in switch expressions\n");
        }
        free_string($1);
    }
    ;


/* "variable" == things which can be written as lvalues. */

variable: identifier
    {  
        int x;
        Val * v;
        if ((x = find_local_variable($1))) 
        {
            emit_push(LOCAL, x-1);
        }
        else if ((x = find_argument($1))) 
        {
            emit_push(LOCAL, x);
        }
        else if ((x = find_global_variable($1))) 
        {
            emit_push(GLOBAL, x);
/*        printf("Global no: %d.\n", $$); */
        }
        else if ((v = find_constant($1))) 
        {
            /* what should we do? */
            emit_constant(v);
        }
        else yyerrorf("Identifier %s used but not declared", $1->str);
        free_string($1);
        $$ = last_type;
    }
    | expr '[' expr ']'
    { 
        int t, r;
        /* ADDIND should typecheck too! */
        emits(S_ADDIND);
        $$ = 0;
        type_check($3, T_ANY, T_NUMBER, "array access arg 2");
        t = ($1 & 0x00ff0000) >> 16;
        if (!t) {
            if ($1 & 0xff000000)
                yyerror("Non class accessed\n");
            else {
                $$ = T_ANY;
                type_check($1,T_ANY,T_POINTER|T_STRING,
                 "array access arg 1");
            }
        }
        else {
            r = $1 & 0xff00ffff;
            t--;
            $$ = (t << 16) | r;
        }
    }
    | expr '.' identifier
    {
        struct field * x;
        x = find_type_info($1, $3);
        if (x) {
            emit(S_PUSH8, x->offset);
            emits(S_ADDIND);
            type_check($1, T_ANY, T_POINTER, "class access arg 1");
            $$ = x->type;
            free(x);
        }
        else $$ = 0;
        free_string($3);
    }
    ;


string: TOK_STRING
    {    /* blech */
        add_patch(CPD, P_STRING, $<lexstr>1);
        emit64(S_PUSHs, zero);
/*        $<lexstr>$ = (lexstr *)p; */
        $<lexstr>$ = $<lexstr>1;
    }
    ;

bracket_list:
    '(' expr_list ')'
    { $$ = $2; } 
    ;

function_call:  
    function_name 
    {
        struct keyword * x = 0;

        x = lookup_emulate($1);
        if (!x) x = lookup_predef($1->str);

        if (x && (x->token == F_EMULATE)) 
        {
            Shared * v;

            /* make it a call_other to Emulate.. */
            /* argh need a S_PUSHo opcode now..  */
            v = string_copy(EMULATE_EFUN);
/*            add_patch(CPD, P_CSTRING, v);  */
            emit64(S_PUSHs, (char *) &(v));
            // v = shared_string_copy($1);
/*            add_patch(CPD, P_CSTRING, v);  */
            // emit32(S_PUSHs, (char *) &(v));
        }
        else if (!x || (x && (x->token != F_CALL_OTHER))) 
        {
            emit(S_PUSH8, 0); /* dummy this_object() */
        }
        $<lnode>$ = x;
    }
    bracket_list 
    { 
        struct keyword * x;
        int p, n;

        x = (struct keyword *)$<string>2;
        if (x) 
        {
            // check the no. of parameters is correct */
            p = x->params;
            if (p > 0) 
            {
                n = $3;
                while (n < p) 
                {
                    emit(S_PUSH8, 0);
                    n++;
                }
                if (n > p) 
                {
                    emit(S_ADDSP, n - p);
                }    
            }

            emit(S_PUSHCARGS, $3);

            if (x->token == F_CALL_OTHER) 
            {
                /* special case for call_other efun() */
                byte n = $3 - 2;
                /* patch no of args to be correct */
                if ($3 < 2) {
                     yyerror("Not enough args to call_other()");
                }
                /* adjust number of params */
                CPD->block[CPD->current_I-1] = n;
                emits(S_CALLOF);
            }
            else if (x->token == F_EMULATE) 
            {
                Shared * ss = shared_string_copy($1);

                /* string should be added to patch list */
                /*        add_patch(CPD, P_CSTRING, x); */
                emit64(S_CALLO, (char *)&(ss));
                free(x);
            }
            else 
            {
                //emit(S_PUSHCARGS, $3);    /* no of params */
                emit(S_EFUN, (char)x->token);    
            }
        }
        else 
        {
            /* It's a local function call. */
            Shared * v;

            emit(S_PUSHCARGS, $3);
            emit(S_PUSHCOFF, 0);
            emit(S_ADDSP, 0);    /* varargs fix - blech */
            v = shared_string_copy($1);
            add_patch(CPD, P_CALLU, v);
            emit64(S_CALLU, (char *)&(v)); 
        }          
        /*
         * check function calls later 
         * should be able to do this without
         * checking the function list again 
         */
        free_string($1);
        $$ = T_ANY;
     }
     | expr TOK_ARROW identifier bracket_list
     {
        Shared * x = shared_string_copy($3);

        emit(S_PUSHCARGS, $4);
        /* string should be added to patch list */
/*        add_patch(CPD, P_CSTRING, x); */
        emit64(S_CALLO, (char *)&(x)); 
        free_string($3);
        $$ = T_ANY;
    }        
    | expr TOK_ARROW '(' expr ')' bracket_list
    {
        emit(S_PUSHCARGS, $6);
        emits(S_CALLOF); 
        $$ = T_ANY;
    }
    | expr '@' '(' expr ')'  TOK_ARROW identifier '(' expr ')'
    {
        /* an expr_list in the brackets would be nice..  */
        /* need to re-order stack so that $1 is on top */
        /* also identifier probably needs to be on the stack */
        /* emits(S_CALL_OUT); */
        emit(S_EFUND, F_NCALL_OUT);
        $$ = T_ANY;
    }
    ;

function_name: identifier
    { $$ = $1; }
    | TOK_STRING TOK_COLON_COLON identifier
    {
        /* are the following space leaks? (or fuck up counts anyway?) */
        /* perhaps all should use string_copy and we should free elsewhere. */
        Shared *t = to_Cstring($<lexstr>1);
        char *p = (char *)malloc($3->length + t->length + 3);

        strcpy(p, t->str); strcat(p, "::"); strcat(p, $3->str);
        $$ = string_copy(p);
        free_string(t);
        free($<lexstr>1);
        free_string($3);
        free(p);
    }
    | TOK_COLON_COLON identifier
    {
        char *p = (char *)malloc($2->length + 3);
        strcpy(p, "::"); 
        strcat(p, $2->str);
        $$ = string_copy(p);
        free_string($2);
        free(p);
    }
    ;

ifstmt: TOK_IF if_expr
    statement 
    {
        patch16((int)$<number>2 - 2, CPD->current_I - $<number>2);
    }
    | TOK_IF if_expr
    statement
    TOK_ELSE
    {
        emit16(S_JUMP, 0);
        patch16((int)$<number>2 - 2, CPD->current_I - $<number>2);
        $<number>$ = CPD->current_I;
    }
    statement
    {
        patch16((int)$<number>5 - 2, CPD->current_I - $<number>5);
    }
    ;

if_expr: '(' expr ')'
    {
        emit16(S_JZERO, 0);
        $$ = CPD->current_I;
    }
    ;

identifier: TOK_IDENTIFIER 
     {
        $$ = string_copy($1);
     } 
     ;
%%

int defined_function(char *s)
{
    Func *p;

    for(p = prog_head; p; p = p->next) 
    {
        if (strcmp(p->name->str, s) == 0)
        return 1;
    }
    return 0;
} 

/*
 *   All the code below here is 
 *   Copyright (C), Geoff Wong, June 1993.
 *   geoff@serc.rmit.edu.au
 */

int inherited_globals = 0;

void init_globals()
{
    int i;
    inherited_globals = 0;
#if 1
    if (inherit_ob == NULL) return;
    for (i = 0; inherit_ob[i]; i++) {
        inherited_globals += inherit_ob[i]->num_variables;
    }
#endif
}


/* tail insertion */
int add_global_variable(Shared * s, int type, Val * val)
{
    struct var_def *t;

    if (lang_debug) printf("D: add global %s\n", s->str);
    t = (struct var_def *)malloc(sizeof(struct var_def));
    t->name = s;
    if (!type) type = 1;
    t->type = type;
    t->next = 0;
    t->v = val;
    if (!global_head) {
        global_tail = t;
        global_head = t;
    }
    else {
        global_tail->next = t;
        global_tail = t;
    }
    if (type & TY_CONSTANT) {
        num_constants++;
    }
    else num_globals++;
    return num_globals + inherited_globals;
}

int find_global_variable(Shared * s)
{
    struct var_def *t = global_head;

    int d = 0, i, j, count = 1;
    while (t) 
    {
        if ((t->type & TY_CONSTANT) == 0) 
        {
            d++;
            if (t->name == s) 
            {
/*              printf("find_global %s\n", t->name);  */
                last_type = t->type & 0xffff00ff;
                /* get rid of static */
                return d + inherited_globals;
            }
        }
        t = t->next;
    }
#if 1    /* if we want to access inherited vars directly */
    if (!inherit_ob) return 0;
    /* all globals are copied into each level of the object */
    for (i = 0; inherit_ob[i]; i++) 
    {
        for (j = 0; j < inherit_ob[i]->num_variables; j++) {
            if (inherit_ob[i]->global_table[j]->u.string == s) {
            last_type = inherit_ob[i]->global_table[j]->type;
                return count;
            }
            count++;
        }
    }
#endif
    return 0;
}

Val * find_constant(Shared * s)
{
    struct var_def *t = global_head;
    Val * v;

    while (t) 
    {
        if ((t->name == s) && (t->type & TY_CONSTANT)) {
/*            printf("find_constant %s\n", t->name);  */
            last_type = t->type & 0xffff00ff;
                     /* get rid of static (const etc) */
            return t->v;
        }
        t = t->next;
    }    
    if (!inherit_ob) return 0;
    /* have to recurse into the objects */
    
    v = find_const2(inherit_ob, s);
    return v;
}

Val * find_const2(Class ** io, Shared * s)
{
    int i, j;
    Val * v;

        for (i = 0; io[i]; i++) {
        for (j = 0; j < io[i]->num_constants; j++) {
            if (io[i]->constant_table[j]->name == s) {
                last_type =
                      io[i]->constant_table[j]->type;
                return io[i]->constant_table[j]->val;
            }
        }
        if (io[i]->inherit) {
            v = find_const2(io[i]->inherit, s);
            if (v) return v;    
        }
        }
    return 0;
}

void make_global_tables()
{
    struct var_def *t = global_head, *x;
    int d = 0, ct = 0;
    Val **table = 0;
    CVal ** consttable = 0;

#if 0
printf("%s : num = %d\n", current_file, num_globals);
#endif

    if (!num_globals) table = 0; 
    else table = (Val **) malloc(sizeof(Val *) * (num_globals + 1));
    if (!num_constants) consttable = 0;
    else consttable = (CVal **) malloc(sizeof(CVal *) * (num_constants + 1));
    while (t && (d < num_globals || ct < num_constants)) 
    {
        if (t->type & TY_CONSTANT) 
        {
            consttable[ct++] = 
                make_const(t->name, t->v, t->type);
        } 
        else 
        {
            table[d++] = share_Cstring(t->name);
            table[d-1]->type = t->type;    
        }
        x = t;
        t = t->next;
        free(x);
    }

    global_table = table;
    constant_table = consttable;

    return;
}

/* arguments name stuff */
/* head insertion */

int find_argument(Shared *s)
{
    int d;
    struct var_def *t = arg_head;
    d = 0;

    while (t && d < num_args) 
    {
        d++;
        if (t->name == s) {
            last_type = t->type;
            return -d;
        }
        t = t->next;
    }    
    return 0;
}

int add_argument(Shared *s, int type)
{
    struct var_def *t;

    if (lang_debug)    printf("D: add argument %s (%d)\n", s->str, type);

    t = (struct var_def *)malloc(sizeof(struct var_def));
    t->name = s;
    t->v = 0;
    t->next = arg_head;
    t->type = type;
    arg_head = t;
    num_args++;
    return num_args;
}

/* head insertion for locals. */
int add_local_variable(Shared *s, int type)
{
    struct var_def *t;

    if (lang_debug) printf("D: add local %s\n", s->str);

    t = (struct var_def *)malloc(sizeof(struct var_def));
    t->name = s;
    t->next = local_head;
    t->v = 0;
    t->type = type;
    local_head = t;
    num_locals++;
    max_locals++;
    num_block_locals[block_depth]++;
    return max_locals;
}

int find_local_variable(Shared *s)
{
struct var_def *t;
int d = max_locals;
    t = local_head;
#if 0
    while (t && d > num_locals) {
        d--;    
        t = t->next;
    }
#endif
        while (t && d > 0) {
        if (t->name == s) {
/*            printf("find_local %s\n", t->name); */
            last_type = t->type;
            return d;
        }
        t = t->next;
        d--;
        }    
    return 0;
}

int find_local_block(Shared *s)
{
    struct var_def *t;
    int d = max_locals, i = 0;

    t = local_head;
    while (t && d > num_locals) 
    {
        d--;    
        t = t->next;
    }
    while (t && i < num_block_locals[block_depth]) 
    {
        i++;
        d--;
        if (t->name == s) return d;
        t = t->next;
    }
    return 0;
}

void free_locals()
{
    struct var_def *x, *t = local_head;

    while (t) 
    {
        x = t;
        t = t->next;
        free(x);
    }
    t = arg_head;
    while (t) 
    {
        x = t;
        t = t->next;
        free(x);
    }
    local_head = 0;
    arg_head = 0;
}

int constant_fold(int type)
{
    int size = 10, flag = 0, r;

    if (type == S_PLUS) 
    {
        if (puinstr == S_PUSHs &&
            uinstr == S_PUSHs) 
        {
#if 0
        return 0;
printf("constant fold %s + %s\n", penultima.s,
                ultima.s;
#endif
            /* should this ever happen?? */
            if (!penultima.s || !ultima.s) return 0;

            lex_strcat(penultima.s, ultima.s);

            /* nuke off folded instructions */
            CPD->current_I = CPD->current_I - (sizeof(Shared *)*2 + 2);

            /* need to nuke off last 2 from patch list */
            remove_patch(CPD);
            remove_patch(CPD);

#if 0
            Patch *old; 

            old = ((Patch *)CPD->patch);
            CPD->patch = (char **)((Patch *)CPD->patch)->next->next;
            CPD->num_str = CPD->num_str - 2;
            free(old->next);
            free(old);
#endif

            add_patch(CPD, P_STRING, penultima.s);
            emit64(S_PUSHs, zero);

            ultima.s = penultima.s;
            penultima.s = 0;
            return 1;
        }
    }
    return 0;
    
    if ( (puinstr == S_PUSHi && uinstr == S_PUSH8) ||
         (puinstr == S_PUSH8 && uinstr == S_PUSHi) ) {
        size = 7;
        flag = 1;
    }
    if  (puinstr == S_PUSH8 && uinstr == S_PUSH8) {
        size = 4;
        flag = 1;
    }
    
    if (flag || (puinstr == S_PUSHi &&
            uinstr == S_PUSHi)) {
        switch(type)
        {    
        case S_PLUS:
        r = ultima.i + penultima.i;
#if 0
printf("constant fold %d + %d in %s\n", penultima.i, ultima.i, current_file);
#endif
        break;
        case S_MULT:
        r = penultima.i * ultima.i;
        break;
        case S_DIVIDE:
        r = penultima.i / ultima.i;
        break;
        case S_MOD:
        r = penultima.i % ultima.i;
        break;
        case S_MINUS:
        r = penultima.i - ultima.i;
        default:
            break;
        }
        CPD->current_I = CPD->current_I - (short)size;
        if (r <= MAXFIXEDINT && r >= MINFIXEDINT)
        {
            emit(S_PUSH8, r);
        }
        else 
        { 
            emit32(S_PUSHi, (char *)&r);
        }
        puinstr = 0;
        ultima.i = r;
        penultima.i = 0;
        return 1;
    }

#if 0
    if (CPD->block[p-11] == S_PUSHr &&
            CPD->block[p-6] == S_PUSHr) {
        switch(type)
        {    
        case S_PLUS:
        z = ultima.r + penultima.r;
        break;
        case S_MULT:
        z = ultima.r * penultima.r;
        break;
        case S_DIVIDE:
        z = ultima.r / penultima.r;
        break;
        case S_MOD:
        yyerror("Illegal modulo on type real.\n");
        break;
        case S_MINUS:
        z = ultima.r - penultima.r;
        break;
        default:
            break;
        }
        CPD->current_I = CPD->current_I - size;
        emit32(S_PUSHr, (char *)&z);
        return 1;
    }
#endif
    return 0;
}

int type_check(int a, int b, int c, char * where)
{
    return 1;
/* FIX: ALERT:  this isn't correct for pointer offsets and structs */
      if (!(a & b)) {
              yyerrorf("Types don't match in %s\n", where);
#if 0
        fprintf(stderr,"NM on a "); print_type(a);
        fprintf(stderr,"NM on b "); print_type(b);
#endif
              return 0;
      }
      if (!(a & c)) {
#if 0
printf("type expr = %d\n", a);
        fprintf(stderr,"IT on a "); print_type(a);
#endif
              yyerrorf("Illegal type present in %s\n", where);
              return 0;
      }
      return 1;
}

void print_type(int x)
{
    switch (x) {
        case T_NUMBER:
            fprintf(stderr,"type integer\n");
            break;
        case T_REAL:
            fprintf(stderr,"type real\n");
            break;
        case T_POINTER:
            fprintf(stderr,"type pointer\n");
            break;
        case T_OBJECT:
            fprintf(stderr,"type object\n");
            break;
        case T_ANY:
            fprintf(stderr,"type any\n");
            break;
        case T_STRING:
            fprintf(stderr,"type string\n");
            break;
        default:    
            fprintf(stderr,"Unknown type %d\n", x);
            break;
    }
            
}

typedef struct TL 
{
    Shared * name;
    Class * obj;
} Type_list;

#define MAX_CLASSES 16 /* I know - small to start with 
            but hey this many unloaded classes would fuck up! */

Type_list * type_list[MAX_CLASSES];

void
add_type_list(Shared * name, Class * object)
{
    Type_list * tmp;

    // FIX: space leak .. these never get freed
    tmp = (Type_list *) malloc (sizeof(Type_list));
    tmp->name = name;
    tmp->obj = object;
    type_list[no_of_classes] = tmp;
    no_of_classes++;
}

void free_type_list()
{
    int i;
    for (i = 0; i < no_of_classes; i++)
    {
        if (type_list[i]) free_string(type_list[i]->name);
        free(type_list[i]);
        type_list[i] = 0;
    }
    no_of_classes = 0;
}

/*
    we overload struct field (blech!)
    using offset for the size we want
    and 
*/
struct field * find_type(Shared * name)
{
    struct field * tmp;
    int i;
#if 0
    printf("find_type\n");
#endif
    for (i = 0; i < no_of_classes; i++) {
        if (type_list[i]->name == name) {
            tmp = (struct field *)malloc(sizeof(struct field));
            tmp->offset = type_list[i]->obj->num_variables;
            tmp->type = ((i+1) << 24) | T_POINTER;
            return tmp;
        }
    }
    yyerrorf("No such class: %s imported.\n", name->str);
    return 0;
}

/*
    returns a struct of "offset" and type of offset 
*/
struct field * find_type_info(unsigned int typestuff, Shared * fieldname)
{
    unsigned int classnum;
    struct field * tmp;
    Class * tobj;
    int i;

    if (typestuff & 0x00ff0000) 
    {
        yyerror("Type error accessing an array as a class.\n");
        return 0;
    }
    classnum = ((typestuff & 0xff000000) >> 24) -1 ;
    if (classnum >= no_of_classes) {
        yyerror("Failed to resolve class type correctly.\n");
        return 0;
    }
    tmp = (struct field *)malloc(sizeof(struct field));
#if 0
    printf("find_type_info %d.\n", classnum);
#endif
    tobj = type_list[classnum]->obj;    
/*    printf("find_type_info: %s.\n", tobj->name); */
    for (i = 0; i < (int)tobj->num_variables; i++) {
#if 0
        printf("type info: %s\n", tobj->global_table[i]->u.string);
#endif
        if (tobj->global_table[i]->u.string == fieldname) {
            tmp->offset = i;
            tmp->type = tobj->global_table[i]->type & 0xff;
            return tmp;
        }
    }    
    yyerrorf("Unknown fieldname used in class %s.\n", tobj->name->str);
    return 0;
}

/*
    The emulate file contains emulation functions only at
    the "top" level (inherited files do not have their functions
    added to the emulation).

    (this could be changed using "find_program" - but this
     has other side effects and is slow compared to 1 hash lookup).

    Should return 0 if the fun is "::<something>"
    so we can do direct calls to the driver and ignore the
    simulated efuns.
*/


struct keyword * lookup_emulate(Shared * fun)
{
    struct keyword * retval;
    Func * pr = 0;

    if (!Emulate) return NULL;

    pr = Hfunction(fun, Emulate->name);
    if (!pr) return 0;
    retval = (struct keyword *) malloc(sizeof(struct keyword));
    retval->word = pr->name;
    retval->token = (short)F_EMULATE;
    retval->params = -1;
    return retval;
}

void end_of_statement()
{
    if (uinstr == S_POPI) 
    {
        CPD->block[CPD->current_I-1] = S_POPO;
        uinstr = S_POPI;
    }
}