#define _POSIX_C_SOURCE 200809L
#define _XOPEN_SOURCE 500

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

extern int PARM_C_STRINGS; // unimplemented option to use C IO when possible.  I might remove this.
extern int PARM_IMP_SOURCE_LINENOS; // belt & suspenders - explicit saving of line no and source file name for our own internal diags
extern int PARM_UNASS; // normally we want unassigned checks unless explicitly suppressed
extern int PARM_CHECK; // other checks with high overhead

extern int NEXT_AST;

// bit of both...
extern int PARM_OPT;   // implies UNASS=FALSE and CHECK=FALSE and IMP_SOURCE_LINENOS=FALSE - all the ones with run time overhead, plus gcc options

// opts affect how the compilation is invoked
extern int opt_stdout;
extern int opt_debug;
extern int opt_verbose;
extern int opt_link;
extern int opt_info;

extern int want_extra_info;
extern int debug_input;

extern int no_perms;
extern int suppress_perms;
extern int suppress_icode;

extern FILE *icode_file;
extern FILE *output_file;
extern FILE *source_file;

//#include <printf.h>  // register_printf_specifier to add %N (node) to printf's as in AST tuples and codegen().

enum BLOCKTYPE {
  BLOCKTYPE_NO_BLOCK = 0,
  BLOCKTYPE_BEGIN_ENDOFPROGRAM = 1,
  BLOCKTYPE_BEGIN_END = 2,
  BLOCKTYPE_ROUTINE = 3,
  BLOCKTYPE_FN = 4,
  BLOCKTYPE_PREDICATE = 5,
  BLOCKTYPE_MAP = 6,
};

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
extern int suppress_perms; // quick hack to stop outputting compiled perms.inc to C file.

#include "stringpool.h" // for StrToPool

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

extern const char *operator[128];

extern 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(UNLESSGOTO),
  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(CAST),
  A(BRACKET),
  A(UNASS_CHECK),
  A(DIVZERO_CHECK),
  A(FOR),
  
  ASTCODES
} ASTCODE;
#undef A

#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 F_EXPR 15                  // Invented code to use for composite nodes...
 
#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 0x1000000  // 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
#define USER_FIELD_COUNT(tuple) AST[tuple + FF_USER_FIELD_COUNT]
  FF_EXTENDED_FIELD_COUNT,  // count of extended fields after the explicit user fields
#define EXTENDED_FIELD_COUNT(tuple) AST[tuple + FF_EXTENDED_FIELD_COUNT]
  FF_FILE,      // source file info for error reports etc
#define SOURCE_FILE(tuple) AST[tuple + FF_FILE]
  FF_LINE,
#define SOURCE_LINE(tuple) AST[tuple + FF_LINE]
  
  FF_OP,        // The AST type (eg AST_BINOP, etc)
#define OP(tuple) AST[tuple + FF_OP]
  
  // 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_PROC_PARAM(ast) AST[(ast)+FF_IS_PROC_PARAM] // this proc/fn/map/pred is in the FP list of another procedure
  FF_IS_PROC_PARAM,
#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]

  // Remember to update mktuple_inner and COPY_FIXED_FIELDS_to_from when adding a new field
  
  FIXED_FIELDS // End marker, not an actual field.
} 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

extern int 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, ...);
#define mktuple(AST_op...) mktuple_inner(StrToPool(__FILE__), __LINE__, AST_op, END_MARKER)
extern void detuple_inner(StrpoolIDX file, int line, ASTIDX astp, void    *AST_op, ...);
#define detuple(AST_op...) detuple_inner(StrToPool(__FILE__), __LINE__, AST_op, &END_MARKER)

extern void set_fixed_field(int tuple, FIELD f, int file);
extern int  get_fixed_field(int tuple, FIELD f);
#define TUPLE_SIZE(tuple) (FIXED_FIELDS+USER_FIELD_COUNT(tuple)+EXTENDED_FIELD_COUNT(tuple))
#define USERFIELD(tuple, offset) AST[tuple+FIXED_FIELDS+(offset)]
#define EXTRAFIELD(tuple, offset) AST[tuple+FIXED_FIELDS+USER_FIELD_COUNT(tuple)+(offset)]

extern const char *safe_astname(int idx);
extern int NEW_INTERNAL_TAG(void);

// These macros are not to be confused with the fixed fields elsewhere:

// 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.
static inline int IS_NAME(int ast) {
  return (FORM(ast) == F_NAME ||
          FORM(ast) == F_ARRAY_NAME ||
          FORM(ast) == F_NAME_ARRAY_NAME);
}

static inline int IS_A_POINTER(int ast) {
  return (FORM(ast) == F_NAME ||
          FORM(ast) == F_MAP ||
          FORM(ast) == F_ARRAY_NAME ||
          FORM(ast) == F_NAME_ARRAY_NAME);
}

static inline int IS_PROCEDURE(int ast) {
  return (FORM(ast) == F_FN ||
          FORM(ast) == F_MAP ||
          FORM(ast) == F_PREDICATE ||
          FORM(ast) == F_ROUTINE);
}

static inline int CALLTYPE_IS_PROC(int tuple) {
  if (OP(tuple) != AST_CALL) {
    //fprintf(stderr, "CALLTYPE_IS_PROC: not an AST_CALL\n");
    return FALSE;
  }
  ASTIDX decl = USERFIELD(tuple, 0);
  if (OP(decl) != AST_VAR) {
    //fprintf(stderr, "CALLTYPE_IS_PROC: not an AST_VAR:\n");
    debug_types(decl);
    return FALSE;
  }
  //fprintf(stderr, "CALLTYPE_IS_PROC: FORM is %s\n", forms_imp[FORM(decl) & 15]);
  return FORM(decl) == F_ROUTINE;
}

#ifdef NEVER
static inline int IS_ATOMIC(const ASTIDX node) {
  if (OP(node) == AST_BINOP) return FALSE;
  if (OP(node) == AST_MONOP) return FALSE;  // maybe this should be allowed?
  if (OP(node) == AST_INDIRECT_THROUGH) return FALSE; // and this?
  if (OP(node) == AST_ADDRESS_OF) return FALSE; // possibly even this?
  if (OP(node) == AST_RESOLVE) return FALSE;
  // Add any more that might mess up bracket removal...
  return TRUE;
}
#endif

// 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 the operator to use in
// a priority test (c_binprio[]) with the result that priorities of comparisons were completely wrong!
static inline int IS_BINOP(int ast) {
  return (OP(ast) == AST_BINOP);
}

static inline int IS_MONOP(int ast) {
  return (OP(ast) == AST_MONOP);
}

static inline int IS_STRING(int tuple) {
  return (BASE_TYPE(tuple) == T_STRING);
}

static inline int IS_DATA(int ast) {
  return (OP(ast) == AST_ICONST ||
          OP(ast) == AST_RCONST ||
          OP(ast) == AST_ISTRINGCONST ||
          OP(ast) == AST_MONOP ||
          OP(ast) == AST_BINOP ||
          OP(ast) == AST_VAR ||
          (OP(ast) == AST_CALL && !CALLTYPE_IS_PROC(ast)) ||
          OP(ast) == AST_UNCONDITIONAL_RESOLVE ||
          OP(ast) == AST_RESOLVE ||
          OP(ast) == AST_ADDRESS_OF ||
          OP(ast) == AST_INDIRECT_THROUGH ||
          OP(ast) == AST_FIELDSELECT ||
          OP(ast) == AST_ARRAYACCESS ||
          OP(ast) == AST_DYNAMICARRAYACCESS ||
          OP(ast) == AST_CAST ||
          OP(ast) == AST_BRACKET);
}

// 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).
static inline int REQUIRES_AUTO_DEREF(int tuple) {
  return ((IS_NAME(tuple) ||
       /*  IS_ARRAY_NAME(tuple) ||                 -- An arrayname parameter should be indexed just like an array in C */
           FORM(tuple) == F_MAP) && (!NO_AUTO_DEREF(tuple))
          );
}

static inline int IS_ICONST(const ASTIDX node) {
  return OP(node) == AST_ICONST;
}

static inline int IS_RCONST(const ASTIDX node) {
  return OP(node) == AST_RCONST;
}

static inline int IS_ZERO(const ASTIDX node) {
  return IS_ICONST(node) && USERFIELD(node, 0) == 0;
}

//
//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)

typedef enum {
  WANT_LVALUE,            // %L
  WANT_RVALUE,            // %R
  WANT_ADDRESS_OF,        // %A
  WANT_INDIRECT_THROUGH   // %P
} gentype;
extern void codegen_data(ASTIDX tuple);

/* 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 */

// in icode.c
extern void dump_imp_to_c_string(char *impstr);
extern void show_source_code(int target_line, char *fname);
extern char *source_indent;
extern char *comment_indent;

/* Headers for icode.c */

#define MAX_LABEL_IDS (64*1024)
extern int label_id[MAX_LABEL_IDS];        /* init to 0 means not used */
// not sure yet if safe to conflate the two types of label...
extern int user_label_id[MAX_LABEL_IDS];   /* init to 0 means not used */

extern const char *icode_name[256];
extern int get_next_lineno(void);
extern int get_icode(FILE *f);
extern int getbyte(void);
extern int getshort(void);
extern int gettag(void);
extern int getwordconst(void);
extern char *getname(void);
extern char *getrawstring(void);
extern char *getimpstring(void);
extern char *getmcstring(int *cp);


extern short int getforwardlab_from_tag(short int *tag);
extern short int getbackwardlab_from_tag(short int *tag);

extern short int setforwardlab_from_tag(short int tag);
extern short int setbackwardlab_from_tag(short int tag);

extern int get_next_user_label(void);
/* End of headers for icode.c */
