#ifndef _AST_H_
#define _AST_H_

typedef struct argsetlist ArgSetList;
typedef struct arithexp ArithExp;
typedef struct arrayexp ArrayExp;
typedef struct assignstm AssignStm;
typedef struct boundlist BoundList;
typedef struct constdec ConstDec;
typedef struct dec Dec;
typedef struct declist DecList;
typedef struct exp Exp;
typedef struct explist ExpList;
typedef struct idlist IDList;
typedef struct id_type ID_Type;
typedef struct if_then_else_stm If_Then_Else_Stm;
typedef struct if_then_stm If_Then_Stm;
typedef struct litexp LitExp;
typedef struct logicexp LogicExp;
typedef struct program Program;
typedef struct readstm ReadStm;
typedef struct relexp RelExp;
typedef struct returnstm ReturnStm;
typedef struct stm Stm;
typedef struct stmblock StmBlock;
typedef struct stmlist StmList;
typedef struct sub Sub;
typedef struct subcallexp SubCallExp;
typedef struct sublist SubList;
typedef struct varexp VarExp;
typedef struct vardec VarDec;
typedef struct whilestm WhileStm;
typedef struct writelnstm WritelnStm;
typedef struct writestm WriteStm;

typedef struct bound Bound;
typedef struct boundinfo BoundInfo;
typedef struct arraydatatype ArrayDataType;
typedef struct decltype DeclType;
typedef struct subheading SubHeading;
typedef struct subparams SubParams;

/* Abstract Syntax for the SRC3 Language */
/* 1. Program Structure */

/*
 * Program consists of a (possibly empty) list of declarations, following by
 * a (possibly empty) list of subroutines, followed by a statement block
 */

struct id_type {
	char *id;
	int _lineno;
};

struct program {
	int _lineno;
	ID_Type idt;
	ID_Type end_idt;

	DecList *decList;
	SubList *subList;
	StmBlock *stmBlock;

};

/* 2. Declarations */
/* a. Variable Declarations */

/* Variables may either be single variables or array variables. */
typedef enum {
	SINGLE_VAR, ARRAY_VAR
} VarDecType;

/* Variables are one of 4 base types; userefined types are not allowed */
typedef enum {
	TYPE_int = 0,
	TYPE_real = 1,
	TYPE_string = 2,
	TYPE_bool = 3,
	TYPE_intA = 4,
	TYPE_realA = 5,
	TYPE_stringA = 6,
	TYPE_boolA = 7,
	TYPE_PROG = 10,
	TYPE_SUB = 20,
	TYPE_CONST = 30,
	TYPE_MUTABLE = 50,
TYPE_ANY = 100} DataType;

/*
 * Multiple variables may be declared in a single declaration statement. This
 * allows the variables declared together to be grouped together
 */

/* !! LIST */
struct idlist {
	int _lineno;
	ID_Type idt;
	IDList *next;
};

/*
 * An array may be multidimensional; each dimension has an upper and lower
 * bound
 */

typedef enum {
	BOUND_VAL, BOUND_ID
} BoundType;

struct bound {
	int _lineno;
	BoundType Type;
	ID_Type idt;
	int Value;
	int partial;

};

struct boundinfo {
	int _lineno;
	Bound *lower_bound;
	Bound *upper_bound;
};

/* !! LIST */
struct boundlist {
	int _lineno;
	BoundInfo *bound;
	BoundList *next;
};

/*
 * A variable declaration contains the following fields: 1. idList: A list of
 * the ids of the variables declared 2. vardecType: The type of declaration
 * (single variable or array variable). 3. Type: The (base) type of the
 * variable(s) being declared 4. lower/upper_bound: the lower/upper bound for
 * the array index. (only applies when VarDecType = ARRAY_VAR)
 */

struct decltype {
	int _lineno;
	DataType _type;
	BoundList *_arraybounds;

	VarDecType vardecType;
	DataType basicDataType;
	ArrayDataType *arrayDataType;

};

struct arraydatatype {
  int _lineno;
	DataType Type;
	BoundList *boundList;
};

struct vardec {
	int _lineno;
	IDList *idList;
	DeclType *declType;

};

/* b. Constant Declarations */

/*
 * Constants may be initialized to a literal value or to the value of another
 * constant (by referencing its ID) .
 */

typedef enum {
  CONST_LIT = 1,
  CONST_ID = 2} ConstType;

/*
 * A constant declaration contains the following fields 1. ConstDecType: The
 * type of declaration (Literal Value or ID) 2. Type: The (base) type of the
 * constant (only applys when ConstType = CONST_LIT) 3. intVal: The value of
 * an integer constant (only used when ConstType = CONST_LIT and Type =
 * DTINT) 4. realVal: The value of an real constant (only used when ConstType
 * = CONST_LIT and Type = DTREAL) 5. strVal: The value of a string constant
 * (only used when ConstType = CONST_LIT and Type = DTSTR) 6. idName: The
 * name of another constant ID (only used when ConstType = CONST_ID)
 */

struct constdec {
	int _lineno;
	IDList *idList;
	ConstType ConstDecType;
	LitExp *litExp;
	ID_Type idt;

};

/* c. Combining Constant and Variable Declarations */

/* All declarations are either for variables or constants */

typedef enum {
	VAR_DEC, CONST_DEC
} DecType;

/*
 * A declaration contains the following fields: 1. decType: The type of
 * declaration (constant or variable) 2. varDec: a variable declaration:
 * (only used when decType = VAR_DEC) 3. constDec: a variable declaration:
 * (only used when decType = CONST_DEC)
 */

struct dec {
	int _lineno;
	DecType decType;
	VarDec *varDec;
	ConstDec *constDec;
};

/* A linked list of declarations */

/* !! LIST */
struct declist {
	int _lineno;
	Dec *dec;
	DecList *next;
};

/* 3. Subroutine List */

/*
 * An argument set has the same fields as a VarDec; do not use the two
 * interchangeably -- make sure you use a VarDec for variable declarations
 * ONLY and an ArgSet for parameter lists ONLY
 */

/* A linked list of argument sets */

/* !! LIST */
struct argsetlist {
	int _lineno;
	VarDec *argset;
	ArgSetList *next;
};

/*
 * A subroutine declaration contains the following fields: 1. val_params: The
 * list of argument sets for all passyalue parameters. 2. ref_params: The
 * list of argument sets for all passyeference parameters. 3. returnType: the
 * type of the value returned by the subroutine:
 */

struct sub {
	int _lineno;

	SubHeading *heading;
	StmBlock *body;

	ID_Type end_idt;

};

struct subparams {
	int _lineno;
	ArgSetList *val_params;
	ArgSetList *ref_params;
};

struct subheading {
	int _lineno;
	ID_Type idt;

	SubParams *subParams;
	DeclType *return_type;
};

/* A linked list of subroutines */

/* !! LIST */
struct sublist {
	int _lineno;
	Sub *subroutine;
	SubList *next;
};

/* 4. Statements, Statement Lists, and Statement Blocks */
/* a. Assignment Statements */

/*
 * An assignment statement contains the following fields: 1. id: The ID of
 * the variable who will receive the assigned value. 2. expr: An expression
 * which will compute the assigned values.
 */

struct assignstm {
	int _lineno;
	DataType _type;
	BoundList *_arraybounds;

	VarExp *var;
	Exp *expr;

};

/* b. Input/Output Statements  */

/*
 * NOTE: Athough the language spec does not explicitly say this is possible,
 * I anticipate being able to read to either array variables or single
 * variables.
 */

typedef enum {
	READ_VAR, READ_ARRAY
} ReadType;

/*
 * A Read Statement contains two fields: 1. rType: The type of variable being
 * read (single or array) 2. id: the name of the variable whose value is
 * being read 3. offset: the offset of the array (only used when reading
 * array variables)
 */

struct readstm {
	int _lineno;
	VarExp *var;

};

/*
 * Both the write/writeln statements take a list of expressions. Each
 * expression is evaluated and its result is written to standard output. For
 * now, assume that empty lists are legal (just set the list field to null).
 * If this is an error, let the typehecker handle it.
 */

struct writestm {
	int _lineno;
	ExpList *list;
};

struct writelnstm {
	int _lineno;
	ExpList *list;
};

/* c. Conditional Statements */

/*
 * An ifhen statement contains two fields described as follows: 1. condition:
 * the condition being checked. 2. then_clause: this list of statements is
 * executed if the condition evaluates to true.
 */

struct if_then_stm {
	int _lineno;
	Exp *condition;

	StmList *then_clause;
};

/*
 * An ifhenlse statement contains three fields described as follows: 1.
 * condition: the condition being checked. 2. then_clause: this list of
 * statements is executed if the condition evaluates to true. 3. else_clause:
 * this list of statements is executed if the condition evaluates to false.
 */

struct if_then_else_stm {
	int _lineno;
	Exp *condition;

	StmList *then_clause;
	StmList *else_clause;
};

/* d. Flow Control Statements */

/*
 * A while statement contains the following two fields: 1. condition: an
 * expression evaluated at the beginning of the while statement and after
 * every iteration. 2. body: a list of statements that is executed every time
 * the condition evaluates to true.
 */

struct whilestm {
	int _lineno;
	Exp *condition;

	StmList *body;
};

/* A return statement evaluates the expression and returns the value */
struct returnstm {
	int _lineno;
	Exp *expr;

};

/* e. Putting all types of statements together */
typedef enum {
	STM_ASSIGN,
	STM_READ,
	STM_WRITE,
	STM_WRITELN,
	STM_IF_THEN,
	STM_IF_THEN_ELSE,
	STM_WHILE,
	STM_RETURN,
	STM_BLOCK
} StmType;

/*
 * A statement contains the following fields: 1. Type: the type of subroutine
 * call. 2. assignStm: an assign statement (only applies when Type=ASSIGN) 3.
 * read: a read statement (only applies when Type=READ) 4. write: a write
 * statement (only applies when Type=WRITE) 5. writeln: a writeln statement
 * (only applies when Type=WRITELN) 6. if_then: an ifhen statement (only
 * applies when Type=IF_THEN) 7. if_then_else: an ifhenlse statement (only
 * applies when Type=IF_THEN_ELSE) 8. while: a while statement (only applies
 * when Type=WHILE) 9. return: a return statement (only applies when
 * Type=RETURN)
 */

struct stm {
	int _lineno;
	StmType Type;
	AssignStm *assignStm;
	ReadStm *read;
	WriteStm *write;
	WritelnStm *writeln;
	If_Then_Stm *if_then;
	If_Then_Else_Stm *if_then_else;
	WhileStm *whileStm;
	ReturnStm *returnStm;
	StmBlock *stmBlock;
};

/* A linked list of statements */

/* !! LIST */
struct stmlist {
	int _lineno;
	Stm *stm;
	StmList *next;
};

/*
 * A statement block contains the following fields: 1. decList: A list of
 * locallyeclared variables. 2. stmList: A list of statements within the
 * block
 */

struct stmblock {
	int _lineno;

	SubList *subList;
	DecList *decList;
	StmList *stmList;

};

/* 6. Expressions */
/* a. Literal Expressions */

/*
 * A literal expression contains the following fields: 1. Type: The type of
 * literal variable (integer, real, string, Boolean). 2. litInt: The value of
 * a literal integer (only applies when Type=DTINT) 3. litReal: The value of
 * a literal real (only applies when Type=DTREAL) 4. litStr: A string value
 * (only applies when Type=DTSTRING)
 */

struct litexp {
	int _lineno;
	DataType _type;
	BoundList *_arraybounds;
	DataType Type;
	int litInt;
	double litReal;
	char *litStr;

};

/* b. Subroutine Calls */

/*
 * A subroutine call Expression contains the following fields: 1. id: The
 * name of the subroutine that is called. 2. expList: a (possibly empty) list
 * of expressions to be evaluated. Each expression becomes the value of a
 * parameter. 3. Type: the type of the value returned by the subroutine
 */

struct subcallexp {
	int _lineno;
	DataType _type;
	BoundList *_arraybounds;
	ID_Type idt;

	ExpList *expList;

};

/* c. Variable and Array Expressions */

/*
 * An expression involving an identifier simply needs the name of the ID
 */

/*
 * An array expression has the following fields: 1. id: The name of the array
 * 2. offsets: A (nonempty) list of expressions use to calculate the offset
 * into the array. An nimensional array will have n elements in this list
 */

typedef enum {
	VAR_BASIC, VAR_ARRAY
} varType;

struct varexp {
	int _lineno;
	DataType _type;
	BoundList *_arraybounds;
	varType Type;
	ID_Type idt;

	ExpList *offsets;

};

/* d. Logical Expressions */

typedef enum {
	_AND, _OR, _NOT
} LogicOp;
/*
 * A logical expression contains the following fields: 1. op: The logical
 * operator 2. x: the left hand operand of a binary logical operator
 * (_AND/_OR) or the sole operand of a unary logical operator (_NOT) 3. y:
 * the right hand operand of a binary logical operator (_AND/_OR) (does NOT
 * apply if op=_NOT)
 */

struct logicexp {
	int _lineno;
	DataType _type;
	BoundList *_arraybounds;
	LogicOp op;
	Exp *x;
	Exp *y;

};

/* e. Arithmetic Expressions */

typedef enum {
	_UPLUS, _UMINUS, _PLUS, _MINUS, _MUL, _DIV, _REM
} ArithOp;

/*
 * An arithmetic expression contains the following fields: 1. op: The
 * arithmetic operator 2. x: the left hand operand of a binary arithmetic
 * operator (_PLUS/_MINUS/_MUL/_DIV/_REM) or the sole operand of a unary
 * arithmetic operator (_UPLUS/_UMINUS) 3. y: the right hand operand of a
 * binary arithmetic operator (_PLUS/_MINUS/_MUL/_DIV/_REM). This variable
 * does NOT apply to unary arithmetic operators (_UPLUS/_UMINUS).
 */

struct arithexp {
	int _lineno;
	DataType _type;
	BoundList *_arraybounds;
	ArithOp op;
	Exp *x;
	Exp *y;

};

/* f. Relational Expressions */

typedef enum {
	_LT, _LTE, _GT, _GTE, _EQ, _NEQ
} RelOp;

/*
 * A relational expression contains the following fields: 1. op: The
 * relational operator 2. x: the left hand operand of a relational operator
 * 3. y: the right hand operand of a relational operator NOTE: All relational
 * operators are BINARY
 */

struct relexp {
	int _lineno;
	DataType _type;
	BoundList *_arraybounds;
	RelOp op;
	Exp *x;
	Exp *y;

};

/* g. Putting the Different Types of Expressions Together */

typedef enum {
	VAR_EXP, ARRAY_EXP, LIT_EXP, CALL_EXP,
	LOGIC_EXP, ARITH_EXP, REL_EXP, NESTED_EXP
} ExpType;

/*
 * An expression contains the following fields: 1. Type: The type of the
 * expression 2. varExp: a variable expression (applies when Type=VAR_EXP) 3.
 * arrayExp: an array expression (applies when Type=ARRAY_EXP) 4. litExp: a
 * literal expression (applies when Type=LIT_EXP)
 * 
 * 5. callExp: a subroutine call (applies when Type=CALL_EXP) 6. logicExp: a
 * logical expression (applies when Type=LOGIC_EXP) 7. arithExp: an
 * arithmetic expression (applies when Type=ARITH_EXP) 8. relExp: a
 * relational expression (applies when Type=REL_EXP)
 */

struct exp {
	int _lineno;
	DataType _type;
	BoundList *_arraybounds;

	ExpType Type;
	VarExp *varExp;
	LitExp *litExp;
	SubCallExp *callExp;
	LogicExp *logicExp;
	ArithExp *arithExp;
	RelExp *relExp;
	Exp *nestedExp;

};

/* h. Lists of Expressions */

/* !! LIST */
struct explist {
	int _lineno;
	Exp *exp;
	ExpList *next;
};

#endif
