/****************************************************/
/* File: symtab.h                                   */
/* Symbol table interface for the CMINUS COMPILER   */
/* Modified by Yenru0                               */
/****************************************************/

#ifndef _SYMTAB_H_
#define _SYMTAB_H_

#include "globals.h"

/**
 * it is the size of the hash table
*/
#define SYMTAB_SIZE 211

#define MAX_SCOPE_DEPTH 1557

#define MAX_PARAM_COUNT 32

/* the list of line numbers of the source 
 * code in which a variable is referenced
 */
typedef struct LineListEntry {
    int lineno;
    struct LineListEntry *next;
} * LineList;

/* The record in the bucket lists for
 * each variable, including name, 
 * assigned memory location, and
 * the list of line numbers in which
 * it appears in the source code
 */
typedef struct BucketListEntry {
    char *name;
    LineList lines;
    SymbolKind symbolKind;
    
    ExpType type;
    ExpType param_types[MAX_PARAM_COUNT];
    char* param_names[MAX_PARAM_COUNT];
    int param_count;
    ExpType returnType;
    
    int memloc; /* memory location for variable */
    struct BucketListEntry *next;
} * BucketList;

struct Scope {
    char *name;
    int depth;

    struct Scope *parent;
    struct Scope *child;
    struct Scope *child_last;
    struct Scope *next_sibling;

    int child_count;

    int location;
    BucketList hashTable[SYMTAB_SIZE];
};

extern Scope scope_global;// no sibling no parent

extern Scope scope_stack[MAX_SCOPE_DEPTH];
extern int scope_stack_top;

/**
 * before using the symbol table, initialize the global scope
 */
void st_init(void);

/**
 * create a new scope with given name
 * @note it does not link parent or insert into stack/list
 * @param scope_name: name of the scope
 * @return the created scope
 */
Scope scope_new(char *scope_name);
/**
 * pop the current scope from the scope stack
 */
void pop_scope(void);
/**
 * push a scope into the scope stack
 * @note it does link the parent or siblings to construct tree
 * @param scope: the scope to be pushed
 */
void push_scope(Scope scope);
/**
 * get the top of the scope stack wit
 * @return the current scope or NULL if the stack is empty
 */
Scope curr_scope(void);
/**
 * insert a variable into the symbol table of the current scope
 * or add a line number if it already exists
 * @param name name of the variable
 * @param symbolkind kind of the symbol
 * @param type type of the variable
 * @param lineno line number of the variable
 * @return 0 if success, -1 if failure
 */
BucketList st_try_insert(char *name, SymbolKind symbolkind, ExpType type, int lineno);

/**
 * insert a line number into the variable's line list
 * @param entry the bucket list entry of the variable
 * @param lineno the line number to be inserted
 */
void st_entry_insert_line(BucketList entry, int lineno);

/**
 * lookup a variable in the current scope
 * @param name name of the variable to lookup
 * @return the bucket list entry of the variable or NULL if not found
 */
BucketList st_lookup_current(char *name);
/**
 * lookup a variable from the top scope to root
 * @param name name of the variable to lookup
 * @return the bucket list entry of the variable or NULL if not found
 */
BucketList st_lookup(char *name);

/**
 * lookup a variable from the given scope to root
 * @param name name of the variable to lookup
 * @param scope the scope to start lookup from
 * @return the bucket list entry of the variable or NULL if not found
 */
BucketList st_lookup_from(char *name, Scope scope);

void printSymTab(FILE *listing);

#endif
