#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// Assume the following data structures exist:
//  - an AST node 'Params' which holds a parameter id, type and next param pointer
//  - a symbol table which has these entries:

typedef enum type { INT, BOOL, FLOAT, VOID } type_t;
typedef type_t node;
typedef struct st_entry symbol_table_t;
struct st_entry
{
    char *name;
    int num_params; // 0 in case of variable
    symbol_table_t *child_symbols; // NULL in case of variable
    type_t type; // var type in case of var, return type in case of fn.
};

typedef bool (*symbol_predicate_fn)(struct st_entry*);

#define PARAMS_TYPE(x) *x
#define PARAMS_NEXT(x) (x + 1)

// and functions
struct st_entry* st_get(symbol_table_t* syms, int idx);

// We can now define a predicate factory
symbol_predicate_fn generate_call_predicate(const char* const name, node* params)
{
    bool predicate(struct st_entry* ent) {
        printf("%s %s\n", name, ent->name);
        if (strcmp(ent->name, name) != 0) return false;
        if (params == NULL && ent->num_params == 0) return true;
        node *current_param = params;
        int n = 0;
        do {
            type_t call_param_type = PARAMS_TYPE (current_param);
            type_t def_param_type = st_get(ent->child_symbols, n)->type;
            if (call_param_type != def_param_type) return false;
        } while (n++, (current_param = PARAMS_NEXT(current_param)) && n != ent->num_params);
        return true;
    }
    return predicate;
}

const char* const name = "kaas";

int main()
{
    type_t params[] = {INT, BOOL, FLOAT, INT};
    struct st_entry fn_params[] = {{ .type = INT }, { .type = BOOL }, { .type = FLOAT }, { .type = INT }};
    struct st_entry fn = { .name = "kaas", .num_params = 4, .child_symbols = fn_params, .type = INT };


    symbol_predicate_fn f = generate_call_predicate(name, params);
    if (f(&fn)) { printf("Success!\n"); }
    return 0;
}

struct st_entry* st_get(symbol_table_t* syms, int idx)
{
    return (syms + idx);
}
