/* UPE library - output to C
 * (c)2000 Stepan Roh
 * see license.txt for copying
 */

#include "upecc_cbe.h"

#include "upecc.h"
#include "upecc_tree.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <assert.h>

/* indentation string filled with tabs */
static char indent_str[256];

static int dump_code (char *fname, FILE *fout, id_table_t *local_id_table, tree_node_t *code, char *indent, int *free_tvar_inh, int *need_lret);

/* writes num as num->val_ptr[] assignment into output
 * fout = file to write in
 * num = number to write
 * class = identifier class
 * id = identifier name
 * indent = indentation string
 * sep = assignement separator
 * lsep = last separator
 *
 * written variable is called "<upecc_proc_name>_<class>_<id>"
 *
 * returns 0 on success, -1 on I/O error, -2 on other error
 */
static int write_num_assign (FILE *fout, upe_number_t *num, char *class, char *id, char *indent, char *sep, char *lsep) {
  int j;

  if (UPE_NUM_OF_BYTES (num->size, upecc_byte_size) <= 0) return -2;
  for (j = 0; j < UPE_NUM_OF_BYTES (num->size, upecc_byte_size); j++) {
    if (!fprintf (fout, "%s%s_%s_%s->val_ptr[%d] = %lu%s",
                  indent, upecc_proc_name, class, id, j, num->val_ptr[j], (j == UPE_NUM_OF_BYTES (num->size, upecc_byte_size) - 1) ? lsep : sep)) return -1;
  }

  return 0;
}

/* writes one statement into output
 * fname = file name (for error output only)
 * fout = file to write in
 * local_id_table = local identifiers table
 * code = statement to write
 * indent = indentation string
 * free_tvar = first free temporary variable (will be modified)
 * need_lvalue = whether we need an lvalue to output (if not - error)
 * need_lret = whether we need an lret label in the end of function we're in
 *
 * returns 0 on success, -1 on error (emits its own error messages)
 */
static int dump_statement (char *fname, FILE *fout, id_table_t *local_id_table, tree_node_t *code, char *indent, int *free_tvar, int need_lvalue, int *need_lret) {
  id_item_t *i;

  switch (code->type) {
    case NODE_NUM : {
      char ts[16];
      if (!fprintf (fout, "(%s_tvar_%d = upe_initnum (%ld, 0, UPE_NO_SIGN), (", upecc_proc_name, *free_tvar, code->x.num->size)) goto lerr;
      sprintf (ts, "%d", *free_tvar);
      { int r = write_num_assign (fout, code->x.num, "tvar", ts, "(", "), ", ")");
        if (r == -2) {
          err_num++;
          fprintf (stderr, "upecc: file %s line %d: expression must be at least 1 bit long\n", code->file_name, code->line_num);
          return -1;
        }
        if (r) goto lerr;
      }
      if (!fprintf (fout, "), %s_tvar_%d)", upecc_proc_name, *free_tvar)) goto lerr;
      (*free_tvar)++;
      break; }
    case NODE_VAR :
      i = (id_item_t *)lookup_id_table (local_id_table, code->x.id);
      if (!i) i = (id_item_t *)lookup_id_table (global_id_table, code->x.id);
      if (i) {
        switch (i->type) {
          case ID_VAR :
            if (!fprintf (fout, "%s_var_%s", upecc_proc_name, code->x.id)) goto lerr;
            break;
          case ID_REG :
            if (!fprintf (fout, "%s_reg_%s", upecc_proc_name, code->x.id)) goto lerr;
            break;
          case ID_CONST :
            if (need_lvalue) {
              err_num++;
              fprintf (stderr, "upecc: file %s line %d: `%s' is not lvalue\n", code->file_name, code->line_num, code->x.id);
              return -1;
            }
            if (!fprintf (fout, "%s_const_%s", upecc_proc_name, code->x.id)) goto lerr;
            break;
          case ID_INSTR_ALIAS :
            if (need_lvalue) {
              err_num++;
              fprintf (stderr, "upecc: file %s line %d: `%s' is not lvalue\n", code->file_name, code->line_num, code->x.id);
              return -1;
            }
            if (!fprintf (fout, "%s_alias_%s", upecc_proc_name, code->x.id)) goto lerr;
            break;
          default :
            err_num++;
            fprintf (stderr, "upecc: file %s line %d: `%s' is not variable, constant or register\n", code->file_name, code->line_num, code->x.id);
            return -1;
        }
      } else {
        err_num++;
        fprintf (stderr, "upecc: file %s line %d: unknown symbol `%s'\n", code->file_name, code->line_num, code->x.id);
        return -1;
      }
      break;
    case NODE_VAR_DECL :
      if (!fprintf (fout, "upe_number_t *%s_var_%s = upe_initnum (%d, 0, UPE_NO_SIGN)", upecc_proc_name, code->x.var_decl.id, code->x.var_decl.len)) goto lerr;
      break;
    case NODE_FN :
      if ((i = (id_item_t *)lookup_id_table (global_id_table, code->x.id))) {
        switch (i->type) {
          case ID_BUILTIN_FN : {
            switch (i->x.bifn.id) {

#define BIFN_CHECK_ARGS(num)\
		{ tree_node_t *args = code->sons; int anum = num;\
		  while (args) {\
                    if (anum-- <= 0) {\
                      err_num++;\
                      fprintf (stderr, "upecc: file %s line %d: too many arguments to function `%s'\n", code->file_name, code->line_num, code->x.id);\
                      return -1;\
                    }\
                    args = args->next;\
                  }\
                  if (anum > 0) {\
                    err_num++;\
                    fprintf (stderr, "upecc: file %s line %d: too few arguments to function `%s'\n", code->file_name, code->line_num, code->x.id);\
                    return -1;\
                  }\
                }

#define BIFN_2ARG_ASSIGN(xfn,xflow)\
                if (!fprintf (fout, xfn " (")) goto lerr;\
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 1, need_lret)) return -1;\
                if (!fprintf (fout, ", ")) goto lerr;\
                if (dump_statement (fname, fout, local_id_table, code->sons->next, indent, free_tvar, 0, need_lret)) return -1;\
                if (!fprintf (fout, ", &%s_" xflow ")", upecc_proc_name)) goto lerr;
            
#define BIFN_2ARG_ASSIGN_NOFLOW(xfn)\
                if (!fprintf (fout, xfn " (")) goto lerr;\
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 1, need_lret)) return -1;\
                if (!fprintf (fout, ", ")) goto lerr;\
                if (dump_statement (fname, fout, local_id_table, code->sons->next, indent, free_tvar, 0, need_lret)) return -1;\
                if (!fprintf (fout, ")")) goto lerr;
            
#define BIFN_2ARG_GETNUM_ASSIGN_NOFLOW(xfn)\
                if (!fprintf (fout, xfn " (")) goto lerr;\
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 1, need_lret)) return -1;\
                if (!fprintf (fout, ", upe_getnum (")) goto lerr;\
                if (dump_statement (fname, fout, local_id_table, code->sons->next, indent, free_tvar, 0, need_lret)) return -1;\
                if (!fprintf (fout, "))")) goto lerr;
            
              case BIFN_ASSIGN :
                BIFN_2ARG_ASSIGN_NOFLOW ("upe_copynum");
                break;
                
              case BIFN_ASSIGN_ADD :
                BIFN_2ARG_ASSIGN ("upe_addnum", "overflow");
                break;
                
              case BIFN_ASSIGN_SUB :
                BIFN_2ARG_ASSIGN ("upe_subnum", "underflow");
                break;
                
              case BIFN_ASSIGN_BITAND :
                BIFN_2ARG_ASSIGN_NOFLOW ("upe_bitandnum");
                break;
                
              case BIFN_ASSIGN_BITOR :
                BIFN_2ARG_ASSIGN_NOFLOW ("upe_bitornum");
                break;
                
              case BIFN_ASSIGN_BITXOR :
                BIFN_2ARG_ASSIGN_NOFLOW ("upe_bitxornum");
                break;
                
              case BIFN_ASSIGN_SHR :
                BIFN_2ARG_GETNUM_ASSIGN_NOFLOW ("upe_shrnum");
                break;
                
              case BIFN_ASSIGN_SHL :
                BIFN_2ARG_GETNUM_ASSIGN_NOFLOW ("upe_shlnum");
                break;
                
#define BIFN_2ARG_1DEST(xfn,xflow)\
                if (!fprintf (fout, xfn " ((%s_tvar_%d = upe_dupnum (", upecc_proc_name, *free_tvar)) goto lerr;\
                (*free_tvar)++;\
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 0, need_lret)) return -1;\
                if (!fprintf (fout, ")), ")) goto lerr;\
                if (dump_statement (fname, fout, local_id_table, code->sons->next, indent, free_tvar, 0, need_lret)) return -1;\
                if (!fprintf (fout, ", &%s_" xflow ")", upecc_proc_name)) goto lerr;
                
              case BIFN_ADD :
                BIFN_2ARG_1DEST ("upe_addnum", "overflow");
                break;
              case BIFN_SUB :
                BIFN_2ARG_1DEST ("upe_subnum", "underflow");
                break;
                
#define BIFN_2ARG_1DEST_NOFLOW(xfn)\
                if (!fprintf (fout, xfn " ((%s_tvar_%d = upe_dupnum (", upecc_proc_name, *free_tvar)) goto lerr;\
                (*free_tvar)++;\
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 0, need_lret)) return -1;\
                if (!fprintf (fout, ")), ")) goto lerr;\
                if (dump_statement (fname, fout, local_id_table, code->sons->next, indent, free_tvar, 0, need_lret)) return -1;\
                if (!fprintf (fout, ")")) goto lerr;
                
              case BIFN_AND :
                BIFN_2ARG_1DEST_NOFLOW ("upe_andnum");
                break;
              case BIFN_OR :
                BIFN_2ARG_1DEST_NOFLOW ("upe_ornum");
                break;
              case BIFN_BITAND :
                BIFN_2ARG_1DEST_NOFLOW ("upe_bitandnum");
                break;
              case BIFN_BITOR :
                BIFN_2ARG_1DEST_NOFLOW ("upe_bitornum");
                break;
              case BIFN_BITXOR :
                BIFN_2ARG_1DEST_NOFLOW ("upe_bitxornum");
                break;
                
#define BIFN_2ARG_0DEST_INT(xfn)\
                if (!fprintf (fout, "(%s_tvar_%d = upe_initnum (%d, 0, UPE_NO_SIGN), upe_setnum (%s_tvar_%d, " xfn " (", upecc_proc_name, *free_tvar, upecc_num_len, upecc_proc_name, *free_tvar)) goto lerr;\
                (*free_tvar)++;\
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 0, need_lret)) return -1;\
                if (!fprintf (fout, ", ")) goto lerr;\
                if (dump_statement (fname, fout, local_id_table, code->sons->next, indent, free_tvar, 0, need_lret)) return -1;\
                if (!fprintf (fout, ")))")) goto lerr;

              case BIFN_EQ :
                BIFN_2ARG_0DEST_INT ("upe_eqnum");
                break;
              case BIFN_NE :
                BIFN_2ARG_0DEST_INT ("upe_nenum");
                break;
              case BIFN_LT :
                BIFN_2ARG_0DEST_INT ("upe_ltnum");
                break;
              case BIFN_LE :
                BIFN_2ARG_0DEST_INT ("upe_lenum");
                break;
              case BIFN_GT :
                BIFN_2ARG_0DEST_INT ("upe_gtnum");
                break;
              case BIFN_GE :
                BIFN_2ARG_0DEST_INT ("upe_genum");
                break;
	      
	      case BIFN_COND_EXPR :
                if (!fprintf (fout, "(upe_isnonzero (")) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 0, need_lret)) return -1;
                if (!fprintf (fout, ") ? (")) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons->next, indent, free_tvar, 0, need_lret)) return -1;
                if (!fprintf (fout, ") : (")) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons->next->next, indent, free_tvar, 0, need_lret)) return -1;
                if (!fprintf (fout, "))")) goto lerr;
	        break;
                
	      case BIFN_BIT_RANGE :
                if (!fprintf (fout, "(%s_tvar_%d = upe_dupnum_range (", upecc_proc_name, *free_tvar)) goto lerr;
                (*free_tvar)++;
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 0, need_lret)) return -1;
                if (!fprintf (fout, ", upe_getnum (")) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons->next, indent, free_tvar, 0, need_lret)) return -1;
                if (!fprintf (fout, "), upe_getnum (")) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons->next->next, indent, free_tvar, 0, need_lret)) return -1;
                if (!fprintf (fout, ")))")) goto lerr;
	        break;
                
	      case BIFN_SHL :
                if (!fprintf (fout, "upe_shlnum ((%s_tvar_%d = upe_dupnum (", upecc_proc_name, *free_tvar)) goto lerr;
                (*free_tvar)++;
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 0, need_lret)) return -1;
                if (!fprintf (fout, ")), upe_getnum (")) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons->next, indent, free_tvar, 0, need_lret)) return -1;
                if (!fprintf (fout, "))")) goto lerr;
	        break;
                
	      case BIFN_SHR :
                if (!fprintf (fout, "upe_shrnum ((%s_tvar_%d = upe_dupnum (", upecc_proc_name, *free_tvar)) goto lerr;
                (*free_tvar)++;
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 0, need_lret)) return -1;
                if (!fprintf (fout, ")), upe_getnum (")) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons->next, indent, free_tvar, 0, need_lret)) return -1;
                if (!fprintf (fout, "))")) goto lerr;
	        break;
                
              case BIFN_PREFIX_INC :
                if (!fprintf (fout, "upe_incnum (")) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 1, need_lret)) return -1;
                if (!fprintf (fout, ", &%s_overflow)", upecc_proc_name)) goto lerr;
		break;
              
              case BIFN_PREFIX_DEC :
                if (!fprintf (fout, "upe_decnum (")) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 1, need_lret)) return -1;
                if (!fprintf (fout, ", &%s_underflow)", upecc_proc_name)) goto lerr;
		break;
              
              case BIFN_POSTFIX_INC : { int old_free_tvar, res_tvar = *free_tvar;
                if (!fprintf (fout, "(%s_tvar_%d = upe_dupnum (", upecc_proc_name, *free_tvar)) goto lerr;
                (*free_tvar)++;
                old_free_tvar = *free_tvar;
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 1, need_lret)) return -1;
                if (!fprintf (fout, "), upe_incnum (")) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, &old_free_tvar, 1, need_lret)) return -1;
                if (!fprintf (fout, ", &%s_overflow), %s_tvar_%d)", upecc_proc_name, upecc_proc_name, res_tvar)) goto lerr;
		break; }
              
              case BIFN_POSTFIX_DEC : { int old_free_tvar, res_tvar = *free_tvar;
                if (!fprintf (fout, "(%s_tvar_%d = upe_dupnum (", upecc_proc_name, *free_tvar)) goto lerr;
                (*free_tvar)++;
                old_free_tvar = *free_tvar;
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 1, need_lret)) return -1;
                if (!fprintf (fout, "), upe_decnum (")) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, &old_free_tvar, 1, need_lret)) return -1;
                if (!fprintf (fout, ", &%s_underflow), %s_tvar_%d)", upecc_proc_name, upecc_proc_name, res_tvar)) goto lerr;
		break; }
              
              case BIFN_NEG :
                if (!fprintf (fout, "upe_negnum ((%s_tvar_%d = upe_dupnum (", upecc_proc_name, *free_tvar)) goto lerr;
                (*free_tvar)++;
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 0, need_lret)) return -1;
                if (!fprintf (fout, ")))")) goto lerr;
		break;
              
              case BIFN_BITNEG :
                if (!fprintf (fout, "upe_bitnegnum ((%s_tvar_%d = upe_dupnum (", upecc_proc_name, *free_tvar)) goto lerr;
                (*free_tvar)++;
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 0, need_lret)) return -1;
                if (!fprintf (fout, ")))")) goto lerr;
		break;
              
              case BIFN_RETURN : { int j;
                int old_free_tvar = *free_tvar;
                if (!fprintf (fout, "%s_return = upe_dupnum (", upecc_proc_name)) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 0, need_lret)) return -1;
                if (!fprintf (fout, ");\n")) goto lerr;
                for (j = 0; j < *free_tvar; j++) {
                  if (!fprintf (fout, "%supe_donenum (%s_tvar_%d);\n", indent, upecc_proc_name, j)) goto lerr;
                }
                *free_tvar = old_free_tvar;
                if (!fprintf (fout, "%sgoto lret", indent)) goto lerr;
                *need_lret = 1;
                break; }
              
              case BIFN_BREAK :
                if (!fprintf (fout, "break")) goto lerr;
		break;
              
              case BIFN_CONTINUE :
                if (!fprintf (fout, "continue")) goto lerr;
		break;
		
	      case BIFN_FOR_VOID :
	        break;
                
              case BIFN_FOR : { int old_free_tvar;
                if (!fprintf (fout, "for (")) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 0, need_lret)) return -1;
                if (!fprintf (fout, "; upe_isnonzero (")) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons->next, indent, free_tvar, 0, need_lret)) return -1;
                if (!fprintf (fout, ") ;")) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons->next->next, indent, free_tvar, 0, need_lret)) return -1;
                old_free_tvar = *free_tvar;
                if (!fprintf (fout, ") {\n")) goto lerr;
                if (dump_code (fname, fout, local_id_table, code->sons->next->next->next, indent-1, free_tvar, need_lret)) return -1;
                *free_tvar = old_free_tvar;
                if (!fprintf (fout, "%s}", indent)) goto lerr;
                break; }
              
              case BIFN_WHILE : { int old_free_tvar;
                if (!fprintf (fout, "while (upe_isnonzero (")) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 0, need_lret)) return -1;
                old_free_tvar = *free_tvar;
                if (!fprintf (fout, ")) {\n")) goto lerr;
                if (dump_code (fname, fout, local_id_table, code->sons->next, indent-1, free_tvar, need_lret)) return -1;
                *free_tvar = old_free_tvar;
                if (!fprintf (fout, "%s}", indent)) goto lerr;
                break; }
              
              case BIFN_DO_WHILE :
                if (!fprintf (fout, "do {\n")) goto lerr;
                if (dump_code (fname, fout, local_id_table, code->sons->next, indent-1, free_tvar, need_lret)) return -1;
                if (!fprintf (fout, "%s} while (upe_isnonzero (", indent)) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 0, need_lret)) return -1;
                if (!fprintf (fout, "))")) goto lerr;
                break;
                
              case BIFN_IF : { int old_free_tvar;
                if (!fprintf (fout, "if (upe_isnonzero (")) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 0, need_lret)) return -1;
                old_free_tvar = *free_tvar;
                if (!fprintf (fout, ")) {\n")) goto lerr;
                if (dump_code (fname, fout, local_id_table, code->sons->next->sons, indent-1, free_tvar, need_lret)) return -1;
                *free_tvar = old_free_tvar;
                if (!fprintf (fout, "%s}", indent)) goto lerr;
                if (code->sons->next->next) {
                  if (!fprintf (fout, " else {\n")) goto lerr;
                  if (dump_code (fname, fout, local_id_table, code->sons->next->next->sons, indent-1, free_tvar, need_lret)) return -1;
                  *free_tvar = old_free_tvar;
                  if (!fprintf (fout, "%s}", indent)) goto lerr;
                }
                break; }
              
              case BIFN_UNDERFLOW :
                BIFN_CHECK_ARGS (0);
                if (!fprintf (fout, "(%s_tvar_%d = upe_initnum (%d, 0, UPE_NO_SIGN), upe_setnum (%s_tvar_%d, %s_underflow))", upecc_proc_name, *free_tvar, upecc_num_len, upecc_proc_name, *free_tvar, upecc_proc_name)) goto lerr;
                (*free_tvar)++;
		break;
		
              case BIFN_OVERFLOW :
                BIFN_CHECK_ARGS (0);
                if (!fprintf (fout, "(%s_tvar_%d = upe_initnum (%d, 0, UPE_NO_SIGN), upe_setnum (%s_tvar_%d, %s_overflow))", upecc_proc_name, *free_tvar, upecc_num_len, upecc_proc_name, *free_tvar, upecc_proc_name)) goto lerr;
                (*free_tvar)++;
		break;
		
              case BIFN_BIT_LENGTH :
                BIFN_CHECK_ARGS (1);
                if (!fprintf (fout, "(%s_tvar_%d = upe_initnum (%d, 0, UPE_NO_SIGN), upe_setnum (%s_tvar_%d, ", upecc_proc_name, *free_tvar, upecc_num_len, upecc_proc_name, *free_tvar)) goto lerr;
                (*free_tvar)++;
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 0, need_lret)) return -1;
                if (!fprintf (fout, "->size))")) goto lerr;
		break;

              case BIFN_FETCH :
                BIFN_CHECK_ARGS (3);
                if (!fprintf (fout, "(%s_mem_violation = (upe_read_mem (%s_mem, upe_getnum (", upecc_proc_name, upecc_proc_name)) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 0, need_lret)) return -1;
                if (!fprintf (fout, "), (")) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons->next, indent, free_tvar, 0, need_lret)) return -1;
                if (!fprintf (fout, "), upe_getnum (")) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons->next->next, indent, free_tvar, 0, need_lret)) return -1;
                if (!fprintf (fout, "), &%s_tvar_%d) ? 1 : 0), %s_tvar_%d)", upecc_proc_name, *free_tvar, upecc_proc_name, *free_tvar)) goto lerr;
                (*free_tvar)++;
	        break;
                
              case BIFN_STORE :
                BIFN_CHECK_ARGS (3);
                if (!fprintf (fout, "(%s_mem_violation = (upe_write_mem (%s_mem, upe_getnum (", upecc_proc_name, upecc_proc_name)) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons, indent, free_tvar, 0, need_lret)) return -1;
                if (!fprintf (fout, "), (")) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons->next, indent, free_tvar, 0, need_lret)) return -1;
                if (!fprintf (fout, "), (")) goto lerr;
                if (dump_statement (fname, fout, local_id_table, code->sons->next->next, indent, free_tvar, 0, need_lret)) return -1;
                if (!fprintf (fout, ")) ? 1 : 0), (void)0)")) goto lerr;
	        break;
                
              case BIFN_MEM_VIOLATION :
                BIFN_CHECK_ARGS (0);
                if (!fprintf (fout, "(%s_tvar_%d = upe_initnum (%d, 0, UPE_NO_SIGN), upe_setnum (%s_tvar_%d, %s_mem_violation))", upecc_proc_name, *free_tvar, upecc_num_len, upecc_proc_name, *free_tvar, upecc_proc_name)) goto lerr;
                (*free_tvar)++;
		break;
		
            }
            break; }
          case ID_FN :
          case ID_EXT_FN :
            /* args */
            if (!fprintf (fout, "(%s_tvar_%d = %s_fn_%s (", upecc_proc_name, *free_tvar, upecc_proc_name, code->x.id)) goto lerr;
            (*free_tvar)++;
            { tree_node_t *args = code->sons; int anum = i->x.fn.args_num;
              while (args) {
                if (anum-- <= 0) {
                  err_num++;
                  fprintf (stderr, "upecc: file %s line %d: too many arguments to function `%s'\n", code->file_name, code->line_num, code->x.id);
                  return -1;
                }
                if (dump_statement (fname, fout, local_id_table, args, indent, free_tvar, 0, need_lret)) return -1;
                args = args->next;
                if (args && !fprintf (fout, ", ")) goto lerr;
              }
              if (anum > 0) {
                err_num++;
                fprintf (stderr, "upecc: file %s line %d: too few arguments to function `%s'\n", code->file_name, code->line_num, code->x.id);
                return -1;
              }
            }
            if (!fprintf (fout, "))")) goto lerr;
            break;
          default :
            err_num++;
            fprintf (stderr, "upecc: file %s line %d: `%s' is not function\n", code->file_name, code->line_num, code->x.id);
            return -1;
        }
      } else {
        err_num++;
        fprintf (stderr, "upecc: file %s line %d: unknown global symbol `%s'\n", code->file_name, code->line_num, code->x.id);
        return -1;
      }
      break;
    case NODE_INSTRUCTION :
      err_num++;
      fprintf (stderr, "upecc: file %s line %d: internal compiler error (NODE_INSTRUCTION)\n", code->file_name, code->line_num);
      return -1;
    case NODE_NUM_QUOTE :
      err_num++;
      fprintf (stderr, "upecc: file %s line %d: internal compiler error (NODE_NUM_QUOTE)\n", code->file_name, code->line_num);
      return -1;
    case NODE_NUM_BIT_RANGE :
      err_num++;
      fprintf (stderr, "upecc: file %s line %d: internal compiler error (NODE_NUM_BIT_RANGE)\n", code->file_name, code->line_num);
      return -1;
  }

  return 0;
  
  lerr:
  
  err_num++;
  fprintf (stderr, "upecc: error writing to output file %s : %s\n", fname, strerror (errno));
  return -1;
}

/* writes one code block into output
 * fname = file name (for error messages only)
 * fout = file to write in
 * local_id_table = local identifiers table
 * code = code to write
 * indent = indentation string
 * free_tvar_inh = if not NULL, indicates first temporary variable which could be used and freed in the end of code block (is not modified)
 * need_lret = whether we need lret label in the end of function
 *
 * returns 0 on success, -1 on error (emits its own error messages)
 */
static int dump_code (char *fname, FILE *fout, id_table_t *local_id_table, tree_node_t *code, char *indent, int *free_tvar_inh, int *need_lret) {
  int free_tvar, first_tvar_to_done, i;

  first_tvar_to_done = free_tvar_inh ? *free_tvar_inh : 0;
  while (code) {
    free_tvar = first_tvar_to_done;
    if (fputs (indent, fout) == EOF) goto lerr;
    if (dump_statement (fname, fout, local_id_table, code, indent, &free_tvar, 0, need_lret)) return -1;
    code = code->next;
    if (!fprintf (fout, ";\n")) goto lerr;
    for (i = first_tvar_to_done; i < free_tvar; i++) {
      if (!fprintf (fout, "%supe_donenum (%s_tvar_%d);\n", indent, upecc_proc_name, i)) goto lerr;
    }
  }

  return 0;
  
  lerr:
  
  err_num++;
  fprintf (stderr, "upecc: error writing to output file %s : %s\n", fname, strerror (errno));
  return -1;
}

/* write code to output file fname
 * returns 0 on success, -1 on error (emits its own error messages)
 */
int write_output_cbe (char *fname) {
  FILE *fout; char *id; void *info; int has_user_init = 0;

  if (!(fout = fopen (fname, "w"))) {
    err_num++;
    fprintf (stderr, "upecc: error opening output file %s : %s\n", fname, strerror (errno));
    return -1;
  }
  
  { int i;
    for (i = 0; i < 255; i++) indent_str[i] = '\t';
    indent_str[255] = 0;
  }

  /* header */

  if (fputs ("/* generated by " UPECC_VERSION_STR " */\n", fout) == EOF) goto lerr;
  if (!fprintf (fout, "
#define upe_byte_size %d
#define UPE_FREE_NULL
#define UPE_LOW_MEM_FN %s_low_mem
void %s_low_mem (void);
#include \"upe.c\"

upe_mem_t *%s_mem;
int %s_mem_violation;

int %s_overflow;
int %s_underflow;

", upecc_byte_size, upecc_proc_name, upecc_proc_name, upecc_proc_name, upecc_proc_name, upecc_proc_name, upecc_proc_name)) goto lerr;

  /* constants, variables, etc. */
  init_id_table_walk (global_id_table);
  while (id_table_walk_next (global_id_table, &id, &info)) {
    id_item_t *i = (id_item_t *)info;
    switch (i->type) {
      case ID_VAR :
        if (!fprintf (fout, "upe_number_t *%s_var_%s;\n", upecc_proc_name, id)) goto lerr;
        break;
      case ID_REG :
        if (!fprintf (fout, "upe_number_t *%s_reg_%s;\n", upecc_proc_name, id)) goto lerr;
        break;
      case ID_CONST :
        if (!fprintf (fout, "upe_number_t *%s_const_%s;\n", upecc_proc_name, id)) goto lerr;
        break;
      case ID_EXT_FN :
      case ID_FN : {
          if (!strcmp (id, "init")) has_user_init = 1;
          if (!fprintf (fout, "upe_number_t *%s_fn_%s (", upecc_proc_name, id)) goto lerr;
          /* args */
          { tree_node_t *args = i->x.fn.args;
            if (!args && !fprintf (fout, "void")) goto lerr;
            while (args) {
              if (!fprintf (fout, "upe_number_t *%s_var_%s", upecc_proc_name, args->x.id)) goto lerr;
              args = args->next;
              if (args && !fprintf (fout, ", ")) goto lerr;
            }
          }
          if (!fprintf (fout, ");\n")) goto lerr;
        }
        break;
      default :
        break;
    }
  }
  
  if (fputc ('\n', fout) == EOF) goto lerr;
  
  /* functions */
  init_id_table_walk (global_id_table);
  while (id_table_walk_next (global_id_table, &id, &info)) {
    id_item_t *i = (id_item_t *)info;
    switch (i->type) {
      case ID_FN : { int j; tree_node_t *ocode; int need_lret;
          if (!fprintf (fout, "upe_number_t *%s_fn_%s (", upecc_proc_name, id)) goto lerr;
          /* args */
          { tree_node_t *args = i->x.fn.args;
            if (!args && !fprintf (fout, "void")) goto lerr;
            while (args) {
              if (!fprintf (fout, "upe_number_t *%s_var_%s", upecc_proc_name, args->x.id)) goto lerr;
              args = args->next;
              if (args && !fprintf (fout, ", ")) goto lerr;
            }
          }
          if (!fprintf (fout, ") {\n")) goto lerr;
          for (j = 0; j < i->x.fn.code->temp_vars_num; j++) {
            if (!fprintf (fout, "\tupe_number_t *%s_tvar_%d;\n", upecc_proc_name, j)) goto lerr;
          }
          if (!fprintf (fout, "\tupe_number_t *%s_return = NULL;\n", upecc_proc_name)) goto lerr;
          need_lret = 0;
          if (dump_code (fname, fout, i->x.fn.id_table, i->x.fn.code, indent_str + 254, NULL, &need_lret)) return -1;
          if (need_lret) {
            if (!fprintf (fout, "\tlret:\n")) goto lerr;
          }
          ocode = i->x.fn.code;
          while (ocode->type == NODE_VAR_DECL) {
            if (!fprintf (fout, "\tupe_donenum (%s_var_%s);\n", upecc_proc_name, ocode->x.var_decl.id)) goto lerr;
            ocode = ocode->next;
          }
          if (!fprintf (fout, "\treturn %s_return;\n}\n", upecc_proc_name)) goto lerr;
        }
        break;
      default :
        break;
    }
  }
  
  /* instructions */
  if (!fprintf (fout, "\nint %s_step (void) {\n", upecc_proc_name)) goto lerr;
  { tree_node_t *t = upecc_instructions, *as, *icode, *istat;
    int mem_tvar, free_tvar, free_tvar_to_done, j, need_lret, pflag, max_bit = 0;
    while (t) {
      free_tvar = 0;
      
      if (!fprintf (fout, "\t{\n")) goto lerr;
      
      /* declarations */
      
      for (j = 0; j < t->temp_vars_num; j++) {
        if (!fprintf (fout, "\t\tupe_number_t *%s_tvar_%d;\n", upecc_proc_name, j)) goto lerr;
      }
      
      as = t->sons->sons;
      while (as) {
        if (as->sons->next->next) {
          if (!fprintf (fout, "\t\tupe_number_t *%s_alias_%s;\n", upecc_proc_name, as->sons->next->next->x.id)) goto lerr;
        }
        if (as->sons->next->x.num_bit_range.to > max_bit) max_bit = as->sons->next->x.num_bit_range.to;
        as = as->next;
      }

      /* memory reading */

      mem_tvar = free_tvar;
      if (!fprintf (fout, "\t\tif (upe_read_mem (%s_mem, 0, %s_reg_%s, %d, &%s_tvar_%d)) return -1;\n", upecc_proc_name, upecc_proc_name, upecc_ip_reg, UPE_NUM_OF_BYTES (max_bit + 1, upecc_byte_size), upecc_proc_name, mem_tvar)) return -1;
      free_tvar++;
            
      as = t->sons->sons;
      while (as) {
        assert (as->sons->next->type == NODE_NUM_BIT_RANGE);
        
        if (!fprintf (fout, "\t\t%s_tvar_%d = upe_dupnum_range (%s_tvar_%d, %d, %d);\n", upecc_proc_name, free_tvar, upecc_proc_name, mem_tvar, as->sons->next->x.num_bit_range.from, as->sons->next->x.num_bit_range.to)) goto lerr;
        if (as->sons->next->next) {
          if (!fprintf (fout, "\t\t%s_alias_%s = %s_tvar_%d;\n", upecc_proc_name, as->sons->next->next->x.id, upecc_proc_name, free_tvar)) goto lerr;
        }
        
        free_tvar++;
        as = as->next;
      }
      
      /* main if{} test (after instructions) */
      
      if (!fprintf (fout, "\t\tif ((")) goto lerr;
      
      as = t->sons->sons; j = 1; pflag = 0;
      while (as) {
        char ts[16];
        assert (as->sons->type == NODE_NUM_QUOTE);
        if (!as->sons->x.num_quote.is_quote) {
          if (pflag) {
            if (!fprintf (fout, ") && (")) goto lerr;
          }
          if (!fprintf (fout, "(%s_tvar_%d = upe_initnum (%ld, 0, UPE_NO_SIGN), (", upecc_proc_name, free_tvar, as->sons->x.num_quote.num->size)) goto lerr;
          sprintf (ts, "%d", free_tvar);
          { int r = write_num_assign (fout, as->sons->x.num_quote.num, "tvar", ts, "(", "), ", ")");
            if (r == -2) {
              err_num++;
              fprintf (stderr, "upecc: file %s line %d: expression must be at least 1 bit long\n", as->sons->file_name, as->sons->line_num);
              return -1;
            }
          }
          if (!fprintf (fout, "), upe_eqnum (%s_tvar_%d, %s_tvar_%d))", upecc_proc_name, free_tvar, upecc_proc_name, j)) goto lerr;
          free_tvar++;
          pflag = 1;
        }
        j++;
        as = as->next;
      }
      if (!pflag) {
        if (!fprintf (fout, "1")) goto lerr;
      }
      if (!fprintf (fout, ")) {\n")) goto lerr;
      
      /* instructions branches */
      
      free_tvar_to_done = free_tvar;
      icode = t->sons->next;
      while (icode) {
        int free_tvar_to_done_in_if;
      
        free_tvar = free_tvar_to_done;
        
        if (!fprintf (fout, "\t\t\tif (upe_isnonzero (")) goto lerr;
        if (dump_statement (fname, fout, t->x.id_table, icode->sons, indent_str + 251, &free_tvar, 0, 0)) return -1;
        if (!fprintf (fout, ")) {\n")) goto lerr;
        
        free_tvar_to_done_in_if = free_tvar;
        
        istat = icode->sons->next;
        need_lret = 0;
        if (dump_code (fname, fout, t->x.id_table, istat, indent_str + 251, &free_tvar, &need_lret)) return -1;
        
        if (!fprintf (fout, "\t\t\t}\n")) goto lerr;
      
        for (j = free_tvar_to_done; j < free_tvar_to_done_in_if; j++) {
          if (!fprintf (fout, "\t\t\tupe_donenum (%s_tvar_%d);\n", upecc_proc_name, j)) goto lerr;
        }

        icode = icode->next;
      }
      
      if (!fprintf (fout, "\t\t}\n")) goto lerr;
      
      /* temporary variables deleting */
      
      for (j = 0; j < free_tvar_to_done; j++) {
        if (!fprintf (fout, "\t\tupe_donenum (%s_tvar_%d);\n", upecc_proc_name, j)) goto lerr;
      }
      
      if (!fprintf (fout, "\t}\n")) goto lerr;
      
      t = t->next;
    }
  }
  if (!fprintf (fout, "\treturn 0;\n}\n\n")) goto lerr;
  
  /* initialisation */
  if (!fprintf (fout, "\nint %s_init (void) {\n", upecc_proc_name)) goto lerr;
  init_id_table_walk (global_id_table);
  while (id_table_walk_next (global_id_table, &id, &info)) {
    id_item_t *i = (id_item_t *)info;
    switch (i->type) {
      case ID_VAR : {
        if (!fprintf (fout, "\t%s_var_%s = upe_initnum (%lu, 0, UPE_NO_SIGN);
\tupe_zeronum (%s_var_%s);
", upecc_proc_name, id, i->x.len, upecc_proc_name, id)) goto lerr;
        break; }
      case ID_REG : {
        if (!fprintf (fout, "\t%s_reg_%s = upe_initnum (%lu, 0, UPE_NO_SIGN);
\tupe_zeronum (%s_reg_%s);
", upecc_proc_name, id, i->x.len, upecc_proc_name, id)) goto lerr;
        break; }
      case ID_CONST :
        if (!fprintf (fout, "\t%s_const_%s = upe_initnum (%lu, 0, UPE_NO_SIGN);
", upecc_proc_name, id, i->x.num->size)) goto lerr;
        { int r = write_num_assign (fout, i->x.num, "const", id, indent_str + 254, ";\n", ";\n");
          if (r == -2) {
            err_num++;
            fprintf (stderr, "upecc: constant `%s' must be at least 1 bit long\n", id);
            return -1;
          }
          if (r) goto lerr;
        }
        break;
      default :
        break;
    }
  }
  
  /* byte orders */
  
  { int i, j;
    for (i = 0; i < upe_byte_order_len; i++) {
      if (upe_byte_order[i]) {
        if (!fprintf (fout, "\tupe_add_byte_order (\"")) goto lerr;
        for (j = 0; j <= i; j++) {
          if (fputc (upe_byte_order[i][j] + 1 + '0', fout) == EOF) goto lerr;
        }
        if (!fprintf (fout, "\");\n")) goto lerr;
      }
    }
  }
  if (has_user_init && !fprintf (fout, "\t%s_fn_init ();\n", upecc_proc_name)) goto lerr;
  if (!fprintf (fout, "\treturn 0;\n}\n\n")) goto lerr;

  fclose (fout);
  
  return 0;
  
  lerr:
  
  err_num++;
  fprintf (stderr, "upecc: error writing to output file %s : %s\n", fname, strerror (errno));
  return -1;
}
