#ifndef _DUMP_H_
#define _DUMP_H_ 1

extern int PARM_C_IO;      // output standard C I/O calls like printf rather than use IMP I/O names like PRINTSTRING
extern int PARM_GDB;       // issue #line directives for better gdb error reporting
extern int PARM_C_STRINGS; // Use C-style 0-terminated strings rather than IMP strings with initial length byte
extern int PARM_VERBOSE;
extern int PARM_TIDY;
extern int PARM_STDOUT;
extern int PARM_DEBUG;

extern char hashline[1024];

extern int _imp_diagnose; // set by %diagnose 0xNNNN
extern int _imp_control;  // set by %control 0xNNNN

#include "stringpool.h" // for StrToPool

#ifdef USE_EXTENDED
// support for adding %N (node) AST tuples to dump_code("...%N..."):
#include <printf.h>
int dump_codegen(FILE *stream, const struct printf_info *info, const void *const args[]);
int arginfo(const struct printf_info *info, size_t n, int argtypes[n], int size[n]);
#endif

// These macros are not to be confused with the fixed fields farther down.

// with %arrayname currently generating garbage code, I'm not sure that this test should be including anything other than FORM(ast) == F_NAME ...
// but since these macros are used in so many places I'm loth to change any of them without a reliable regression test suite in place.  Which I don't have yet.
#define IS_NAME(ast)      (FORM(ast) == F_NAME ||                       FORM(ast) == F_ARRAY_NAME || FORM(ast) == F_NAME_ARRAY_NAME)

#define IS_A_POINTER(ast) (FORM(ast) == F_NAME || FORM(ast) == F_MAP || FORM(ast) == F_ARRAY_NAME || FORM(ast) == F_NAME_ARRAY_NAME)
#define IS_PROCEDURE(ast) (FORM(ast) == F_FN || FORM(ast) == F_MAP || FORM(ast) == F_PREDICATE || FORM(ast) == F_ROUTINE)
// THIS WAS A BUG! #define IS_BINOP(ast) ((OP(ast) == AST_BINOP) || (OP(ast) == AST_COMPARE))
// because AST_COMPARE nodes are not laid out (currently) in the same order as AST_BINOP nodes,
// and the code which followed the IS_BINOP test was pulling out ti=he operator to use in
// a priority test (c_binprio[]) with the result that priorities of comparisons were completely wrong!
#define IS_BINOP(ast) (OP(ast) == AST_BINOP)
#define IS_MONOP(ast) (OP(ast) == AST_MONOP)
#define IS_STRING(tuple) (BASE_TYPE(tuple) == T_STRING)

// hopefully the IS_NAME and IS_ARRAY_NAME tests will catch F_NAME_ARRAY_NAME...
// if not a mistake may go unnoticed for a long time as namearraynames are seldom
// used in Imp programs (if indeed they ever have been).
#define REQUIRES_AUTO_DEREF(tuple) ((IS_NAME(tuple) || IS_ARRAY_NAME(tuple) || FORM(tuple) == F_MAP) && (!NO_AUTO_DEREF(tuple)))

void debug_types_inner(char *name, int ast, char *file, int line);
#define debug_types(ast) debug_types_inner(#ast, ast, __FILE__, __LINE__)
extern int debug_ast;


// remember to update static char *astname[ASTCODES] in ast.c
#define A(NAME) AST_ ## NAME
typedef enum {
  A(ASTCODE_WAS_ZERO),
  A(ICONST),
  A(RCONST),
  A(ISTRINGCONST),
  A(MONOP),
  A(BINOP),
  A(VAR),
  A(RESULT),
  A(CALL),
  A(RETURN),
  A(DECLARE_FP),
  A(DECLARE),
  A(START_PARAMLIST),
  A(CONDITIONAL_RESOLVE),
  A(UNCONDITIONAL_RESOLVE),
  A(COMPARE),
  A(COMPARE2), // added to help with handling correct behaviour for double-sided conditions
  A(GOTO),
  A(IFGOTO),
  A(ONGOTO),
  A(DEFLAB),
  A(ASSIGN),
  A(LABEL),
  A(RESOLVE),
  A(DEF_SWLAB),
  A(DEF_DEFAULTSWLAB),
  A(GOTO_SWLAB),
  A(BOUNDSPAIR),
  A(SEQ),
  A(BLOCKSTART),
  A(BLOCKEND),
  A(COMMENT), // Experimental
  A(STOP),
  A(ADDRESS_OF),
  A(INDIRECT_THROUGH),
  A(FIELDSELECT),
  A(ARRAYACCESS),
  A(DYNAMICARRAYACCESS),
  A(FORMAL_PARAMETER_LIST),
  A(ACTUAL_PARAMETER),
  A(ACTUAL_PARAMETER_LIST),
  A(INDEX_LIST),
  A(PASS_PARAMETER),
  A(IMP_LINE),
  A(DOPEVECTOR),
  A(DIAGNOSE),
  A(CONTROL),
  A(MONITOR),
  A(SIGNAL),
  A(LVALUE),
  A(RVALUE),

  ASTCODES
} ASTCODE;
#undef A

#define TRUE (0==0)
#define FALSE (0!=0)

#define SPECIAL_DEFAULT 0
#define SPECIAL_BYTE_INT 1
#define SPECIAL_SHORT_INT 2
#define SPECIAL_LONG_REAL 3

#define T_INTEGER 1
#define T_REAL 2
#define T_STRING 3
#define T_RECORD 4
#define T_BOOLEAN 5 // Pascal
#define T_SET 6 // Pascal
#define T_BYTE_ENUMERATED 7
#define T_SHORT_ENUMERATED 8
#define T_POINTER 9  // for C?
#define T_CHAR 10  // for C?
#define T_GENERAL 15  // ??? Maybe imp's generic %name parameter perhaps?

#define F_VOID 0
#define F_SIMPLE 1
#define F_NAME 2
#define F_LABEL 3
#define F_RECORDFORMAT 4
#define F_SWITCH 6
#define F_ROUTINE 7
#define F_FN 8
#define F_MAP 9
#define F_PREDICATE 10
#define F_ARRAY 11
#define F_ARRAY_NAME 12
#define F_NAME_ARRAY 13
#define F_NAME_ARRAY_NAME 14

#define X_AUTO 0
#define X_OWN 1
#define X_CONST 2
#define X_EXTERNAL 3
#define X_SYSTEM 4
#define X_DYNAMIC 5
#define X_PRIM 6
#define X_PERM 7

typedef int ASTIDX;
typedef int DATATYPE;
typedef int PRECISION;

#define MAX_ARRAY_DIMS 7 /* As per ERCC manual */

#define MAX_NESTED_BLOCKS 1024 // More than Imp ever allowed...
extern int /*StringpoolIDX*/ blockname[MAX_NESTED_BLOCKS];
extern int blocklevel;

// unfortunately we are mirroring some info that actually is available in pass1:
#define MAX_SWITCHES 1024 // overkill
extern int switch_tag[MAX_SWITCHES];
extern int switch_low[MAX_SWITCHES];
extern int switch_high[MAX_SWITCHES];
//extern ASTIDX switch_decl[MAX_SWITCHES];
extern char *switches_set[MAX_SWITCHES]; // points to dynamic array of flags per switch index
extern int swstack_nextfree;
extern int swbase[MAX_NESTED_BLOCKS]; // false bottom on stack of switches indexed by blocklevel
                        // set by swbase[blocklevel] = swstack_nextfree just before
                        // incrementing blocklevel, and swstack_nextfree = swbase[blocklevel]
                        // whenever blocklevel is decremented

extern int block_type[MAX_NESTED_BLOCKS];


extern int want_extra_info;
extern int next_opd;
#define MAX_ICODE_INSTRS 30000  // overkill.
extern ASTIDX opdstack[MAX_ICODE_INSTRS];

// converts a tag into a more verbose descriptor in AST format.
// tags created by pass1 run from 0 upwards towards MAX_DESCRIPTORS.
// Tags created by me (in mktuple() etc) run from MAX_DESCRIPTORS-1 down towards 0
// Invented descriptors are used to hold the type information of intermediate
// expressions such as the result of BINOPs or of a CALL instruction etc.
#define MAX_DESCRIPTORS 0xFFFF // 400000
extern int Descriptor[MAX_DESCRIPTORS];
extern int next_free_private_tag;

#define MAX_AST 0x800000  // until I make it flex.
extern int AST[MAX_AST];

typedef enum {
  FF_FIELD_ZERO_ERROR = 0, // if you used this field you probably didn't initialise something properly!
  FF_USER_FIELD_COUNT,     // count of user fields in a specific AST type
  FF_EXTENDED_FIELD_COUNT,  // count of extended fields after the explicit user fields
  FF_FILE,      // source file info for error reports etc
  FF_LINE,
  
  FF_OP,        // The AST type (eg AST_BINOP, etc)
  
  // New common fields such as type info can be added here.  And indeed that
  // is what I'm adding right now.  For ease of access and to avoid too much
  // of a rewrite, I'll use exactly the same info as laid out in the $DEF tags.

  // Having added these fields to everything, they will now be accessed via
  // individual macros that delve into the system fields of an AST record.
  // They will be removed from the $DEF ast, and accessed explicitly when
  // needed, eg accessing FORM will now be done by macro FORM(astidx) instead
  // of via a call to detuple(astidx, ..., &FORM, ...)
  
  FF_BASE_TYPE,
#define BASE_TYPE(ast) AST[(ast)+FF_BASE_TYPE]
  FF_FORM,
#define FORM(ast) AST[(ast)+FF_FORM]
  FF_BASE_SIZE_CODE,
#define BASE_SIZE_CODE(ast) AST[(ast)+FF_BASE_SIZE_CODE]
  FF_BASE_SIZE_BYTES,
#define BASE_SIZE_BYTES(ast) AST[(ast)+FF_BASE_SIZE_BYTES]
  FF_LINKAGE,
#define LINKAGE(ast) AST[(ast)+FF_LINKAGE]
  FF_SPECIAL,
#define SPECIAL(ast) AST[(ast)+FF_SPECIAL]
  FF_IMP_NAME_IDX,
#define IMP_NAME_IDX(ast) AST[(ast)+FF_IMP_NAME_IDX]
  FF_C_NAME_IDX,
#define C_NAME_IDX(ast) AST[(ast)+FF_C_NAME_IDX]
  FF_EXTERNAL_NAME_IDX,
#define EXTERNAL_NAME_IDX(ast) AST[(ast)+FF_EXTERNAL_NAME_IDX]
  FF_IS_BASE_NAME,
#define IS_BASE_NAME(ast) AST[(ast)+FF_IS_BASE_NAME]
  FF_IS_ARRAY,
#define IS_ARRAY(ast) AST[(ast)+FF_IS_ARRAY]
  FF_IS_ARRAY_NAME,
#define IS_ARRAY_NAME(ast) AST[(ast)+FF_IS_ARRAY_NAME]
  FF_IS_SPEC_ONLY,
#define IS_SPEC_ONLY(ast) AST[(ast)+FF_IS_SPEC_ONLY]
  FF_NO_AUTO_DEREF,
#define NO_AUTO_DEREF(ast) AST[(ast)+FF_NO_AUTO_DEREF]
  FF_NO_UNASS,
#define NO_UNASS(ast) AST[(ast)+FF_NO_UNASS]
  FF_STRING_CAPACITY,
#define STRING_CAPACITY(ast) AST[(ast)+FF_STRING_CAPACITY]
  FF_RECORD_FORMAT, // a tuple of type AST_RECORD_FORMAT
#define RECORD_FORMAT(ast) AST[(ast)+FF_RECORD_FORMAT]
  FF_FORMAL_PARAM_LIST, // a tuple of type AST_DECLARE_FP
#define FORMAL_PARAM_LIST(ast) AST[(ast)+FF_FORMAL_PARAM_LIST]
  FF_ACTUAL_PARAM_LIST, // a tuple of type AST_PARAMETER_LIST
#define ACTUAL_PARAM_LIST(ast) AST[(ast)+FF_ACTUAL_PARAM_LIST]
  FF_INDEX_LIST, // a tuple of type AST_INDEX_LIST
#define INDEX_LIST(ast) AST[(ast)+FF_INDEX_LIST]
  FF_BOUNDS1D, // static 1D array with fixed bounds (AST_BOUNDSPAIR tuple)
#define BOUNDS1D(ast) AST[(ast)+FF_BOUNDS1D]
  FF_DOPEVECTOR, // auto arrays with dynamic bounds
#define DOPEVECTOR(ast) AST[(ast)+FF_DOPEVECTOR]
  FF_INITVALUES, // a fixed length array (correspnding to BOUNDS1D lower & upper limits
                 // a scalar init is effectively stored as a 1-item array for consistency
#define INITVALUES(ast) AST[(ast)+FF_INITVALUES]
  FIXED_FIELDS
} FIELD;


// Imp77's internal types are fine & dandy but packed too tightly
// This will make the code clearer...

#define MAX_USER_FIELDS 32 // arbitrary.  I think we're already in excess of 16

#define SOURCE_FILE(tuple) AST[tuple + FF_FILE]
#define SOURCE_LINE(tuple) AST[tuple + FF_LINE]

#define USER_FIELD_COUNT(tuple) AST[tuple + FF_USER_FIELD_COUNT]

#define EXTENDED_FIELD_COUNT(tuple) AST[tuple + FF_EXTENDED_FIELD_COUNT]

#define TUPLE_SIZE(tuple) (FIXED_FIELDS+USER_FIELD_COUNT(tuple)+EXTENDED_FIELD_COUNT(tuple))

#define OP(tuple) AST[tuple + FF_OP]

#define USERFIELD(tuple, offset) AST[tuple+FIXED_FIELDS+(offset)]
#define EXTRAFIELD(tuple, offset) AST[tuple+FIXED_FIELDS+USER_FIELD_COUNT(tuple)+(offset)]

extern int END_MARKER;
#define mktuple(AST_op...) mktuple_inner(StrToPool(__FILE__), __LINE__, AST_op, END_MARKER)
#define detuple(AST_op...) detuple_inner(StrToPool(__FILE__), __LINE__, AST_op, &END_MARKER)

extern const char *comparison[128];
extern const char *tag_fields[];
extern const char *astname[ASTCODES];
extern const char *translate[4];
extern const char *types[16];
extern const char *forms[16];
extern const char *ostates[8];
typedef struct { int mask; char *name; } OSPECIAL;
extern const OSPECIAL oflags[3];
extern void debug_tuple(ASTIDX tuple, char *mess);
extern  int mktuple_inner(StrpoolIDX file, int line,              ASTCODE  AST_op, ...);
extern void detuple_inner(StrpoolIDX file, int line, ASTIDX astp, void    *AST_op, ...);

extern void set_fixed_field(int tuple, FIELD f, int file);
extern int  get_fixed_field(int tuple, FIELD f);
extern const char *safe_astname(int idx);
extern int NEW_INTERNAL_TAG(void);

//
//ASTIDX pop_icode(void);
//
extern ASTIDX CHECKPOP(void);
extern int POPQ(int *ast);
extern ASTIDX POP(void);
extern void PUSH(const int tag);
//extern void PUSH_TAG(const int tag);
extern void PUSH_ICONST(const int iconst);
extern void PUSH_RCONST(const char *rconst);
extern void PUSH_IMPSTRINGCONST(const char *isconst);
extern char *SHOW(const ASTIDX tuple);
extern void ASSVAL(ASTIDX val, ASTIDX dest);
extern void PUSH_MONOP(int op, ASTIDX val);;
extern void PUSH_BINOP(int op, ASTIDX left, ASTIDX right);

extern void codegen_inner(char *file, int line, int tuple);  // generate code from AST
#define codegen(params...) codegen_inner(__FILE__,__LINE__,params)

/* previously in codegen.c */
extern char commentbuff[1024*1024];
extern char *cbp;

extern void dump_code(char *s, ...);  // write text to output file
extern void dump(char *s, ...);
extern void dump_comment(char *s, ...);
extern void dump_imp_source(char *s, ...);
//extern void hexdump_code(int lines);
/* End of headers from original codegen.c */

#endif
