#define _GNU_SOURCE /* asprintf */ #include #include #include #include #include "arch.h" #include "func.h" #include "node.h" #include "ply.h" #include "sym.h" #include "type.h" /* :map */ static struct type *global_map_ktype(struct node *n) { struct node *map, *key; struct type *ktype; struct tfield *kfields, *f; int i, nargs = node_nargs(n); char *kname; map = n->expr.args; if (nargs == 2) return map->next->sym->type; ktype = calloc(1, sizeof(*ktype)); assert(ktype); kfields = calloc(nargs, sizeof(*kfields)); assert(kfields); for (key = map->next, f = kfields, i = 0; key; key = key->next, f++, i++) { asprintf(&f->name, "k%d", i); f->type = key->sym->type; } asprintf(&ktype->sou.name, ":%s_key", map->ident.name); ktype->ttype = T_STRUCT; ktype->sou.fields = kfields; type_add(ktype); return ktype; } static int global_map_type_infer(const struct func *func, struct node *n) { struct node *map = n->expr.args; if (n->sym->type || !map->sym->type) return 0; assert(map->sym->type->ttype == T_MAP); /* given `m[key]` where m's type is known, infer that the * expression's type is equal to m's value type. */ n->sym->type = map->sym->type->map.vtype; return 0; } static int global_map_static_validate(const struct func *func, struct node *n) { if (n->expr.args->ntype != N_IDENT) { _e("%#N: %N is not subscriptable.\n", n, n); return -EINVAL; } return 0; } /* :assign */ static int global_assign_type_infer_map(struct node *n) { struct node *map, *key; struct type *ktype; map = n->expr.args; for (key = map->next; key; key = key->next) { if (type_sizeof(key->sym->type) < 0) return 0; } map->sym->type = type_map_of(global_map_ktype(n), n->sym->type); return 0; } static int global_assign_type_infer(const struct func *func, struct node *n) { struct node *lval, *rval; lval = n->expr.args; rval = lval->next; if (!rval->sym->type) return 0; if (!lval->sym->type) { /* given `a = b` where b's type is known but not a's, * infer that a's type must be equal to b's */ lval->sym->type = rval->sym->type; if (node_is_map(lval)) return global_assign_type_infer_map(lval); return 0; } if (type_compatible(lval->sym->type, rval->sym->type)) return 0; _e("%#N: can't assign %N (type '%T'), to %N (type '%T').\n", n, rval, rval->sym->type, lval, lval->sym->type); return -EINVAL; } static int global_assign_static_validate(const struct func *func, struct node *n) { struct node *lval; int nargs = node_nargs(n); if (nargs != 2) { _e("%#N: expected 2 arguments, %d given.\n", n, nargs); return -EINVAL; } lval = n->expr.args; if (node_is_map(lval) || (lval->ntype == N_IDENT)) return 0; _e("%#N: %N is not assignable.\n", n, lval); return -EINVAL; } /* pid */ struct type t_pid = { .ttype = T_TYPEDEF, .tdef = { .name = ":pid", .type = &t_u32 }, }; struct type t_pid_func = { .ttype = T_FUNC, .func = { .type = &t_pid }, }; /* time */ struct type t_time = { .ttype = T_TYPEDEF, /* TODO: should be a T_FUNC with a static * signature */ .tdef = { .name = ":time", .type = &t_s64 }, }; struct type t_time_func = { .ttype = T_FUNC, .func = { .type = &t_time }, }; struct type t_block_func = { .ttype = T_FUNC, .func = { .type = &t_void, .vargs = 1 }, }; struct tfield f_assign[] = { { .type = &t_void }, { .type = &t_void }, { .type = NULL } }; struct type t_assign_func = { .ttype = T_FUNC, .func = { .type = &t_void, .args = f_assign }, }; static const struct func global_funcs[] = { { .name = ":block", .type = &t_block_func, }, { .name = "+", }, { .name = "-", }, { .name = ":assign", .type = &t_assign_func, .type_infer = global_assign_type_infer, .static_validate = global_assign_static_validate, }, { .name = ":map", /* .type = t_map_func, */ .type_infer = global_map_type_infer, .static_validate = global_map_static_validate, }, { .name = "pid", .type = &t_pid_func, }, { .name = "time", .type = &t_time_func, }, { .name = "quantize", }, { .name = NULL } }; static const struct func global_num_func = { .name = ":num", .type = &t_int, }; struct type t_string_array = { .ttype = T_ARRAY, .array = { .type = &t_char, .len = 64 }, /* TODO: tunable */ }; struct type t_string = { .ttype = T_TYPEDEF, .tdef = { .name = ":string", .type = &t_string_array }, }; static const struct func global_string_func = { .name = ":string", .type = &t_string, }; static const struct func global_ident_func = { .name = ":ident", }; static const struct func *global_sym_alloc_expr(struct node *n) { const struct func *func; int err; for (func = global_funcs; func->name; func++) { if (strcmp(func->name, n->expr.func)) continue; return func; } return NULL; } int global_sym_alloc(struct prog *prog, struct node *n) { const struct func *func; struct symtab *st = prog->locals; int err; switch (n->ntype) { case N_EXPR: func = global_sym_alloc_expr(n); break; case N_IDENT: st = prog->globals; func = &global_ident_func; break; case N_NUM: func = &global_num_func; break; case N_STRING: func = &global_string_func; break; } if (!func) return -ENOENT; err = func_static_validate(func, n); if (err) return err; n->sym = sym_alloc(st, n, func); n->sym->type = func_return_type(func); return 0; } int global_probe(struct prog *prog) { return 0; } struct provider global = { .name = ":", .sym_alloc = global_sym_alloc, .probe = global_probe, }; __attribute__((constructor)) static void global_init(void) { provider_register(&global); }