diff options
| author | 2022-08-07 15:26:53 +0200 | |
|---|---|---|
| committer | 2022-08-07 15:29:04 +0200 | |
| commit | 92bbf45f333bbf9190befc52a6bc114dc2957e41 (patch) | |
| tree | 87196fb07a02e1eb6cb5048da86dadd30a69ddc1 | |
| parent | 0a4f81e86d21d056329a2b7b2186152e9b9c2375 (diff) | |
basic templates (generics)
| -rw-r--r-- | bootstrap/all.h | 35 | ||||
| -rw-r--r-- | bootstrap/cgen.c | 5 | ||||
| -rw-r--r-- | bootstrap/dump.c | 30 | ||||
| -rw-r--r-- | bootstrap/parse.c | 208 | ||||
| -rw-r--r-- | bootstrap/test2.cff | 21 | ||||
| -rw-r--r-- | bootstrap/util.c | 6 |
6 files changed, 253 insertions, 52 deletions
diff --git a/bootstrap/all.h b/bootstrap/all.h index 8c80fe4..aba2bc3 100644 --- a/bootstrap/all.h +++ b/bootstrap/all.h @@ -70,6 +70,8 @@ enum toktype { TKident, TKmacident, TKgensym, + TKtype, + TKexpr, TKeof, }; #define NUM_KEYWORDS TKintlit @@ -91,6 +93,8 @@ struct tok { const char *str; int strlen; }; + const struct type *ty; + struct expr *ex; }; }; @@ -121,8 +125,9 @@ struct parser { slice_t(struct expanarg) args; struct toktree toks; const char *name; - struct span spp,span; + struct span span; int idx; + bool tepl; } *curexpan; // macro expansions int expanno; const struct type *targty; @@ -178,6 +183,13 @@ struct type { const struct type *ty; }) flds; slice_t(struct decl *) decls; + slice_t(struct teplarg { + bool typ; + union { + const struct type *ty; + struct tok tok; + }; + }) tpargs; bool fwd; int id; } agg; @@ -218,6 +230,7 @@ enum decltype { Dlet, Dstatic, Dmacro, + Dtepl, }; struct decl { @@ -235,9 +248,27 @@ struct decl { int id, fnid; } var; struct macro macro; + struct tepl { + enum decltype t; + enum typetype tkind; + const char *name; + struct env *env; + slice_t(struct teplparam) params; + struct teplcache { + struct teplcache *next; + slice_t(struct teplarg) args; + const struct type *ty; + } *cache; + struct toktree toks; + } tepl; }; }; +struct teplparam { + const char *name; + bool typ; +}; + struct env { struct env *parent; struct decls { @@ -444,7 +475,7 @@ bswap64(u64 x) { (slice)->n = (v)->length) #define container_of(x, T, fld) \ - (T *)((char *)(x) - offsetof(T, fld)) + ((T *)((char *)(x) - offsetof(T, fld))) /// ///////////////////////////////// diff --git a/bootstrap/cgen.c b/bootstrap/cgen.c index 91429ee..587f546 100644 --- a/bootstrap/cgen.c +++ b/bootstrap/cgen.c @@ -303,7 +303,7 @@ genstmt(struct stmt *stmt) { if (decl.externp) genstatic(1, decl.name, &decl.var); break; - case Dtype: case Dmacro: + case Dtype: case Dmacro: case Dtepl: break; } break; @@ -550,8 +550,7 @@ gendecl(struct decl *decl, bool toplevel) { case Dlet: assert(!toplevel); break; - case Dtype: - case Dmacro: + case Dtype: case Dtepl: case Dmacro: break; } } diff --git a/bootstrap/dump.c b/bootstrap/dump.c index 84c4bbe..dae457a 100644 --- a/bootstrap/dump.c +++ b/bootstrap/dump.c @@ -3,6 +3,8 @@ static void pritype(const struct type *ty) { + const char *kind; + assert(ty); if (ty->konst) epri("const "); @@ -44,13 +46,25 @@ pritype(const struct type *ty) { epri("%s", ty->enu.name ? ty->enu.name : "(anonymous enum)"); break; case TYstruct: - epri("%s", ty->agg.name ? ty->agg.name : "(anonymous struct)"); - break; + kind = "struct"; + goto agg; case TYunion: - epri("%s", ty->agg.name ? ty->agg.name : "(anonymous union)"); - break; + kind = "union"; + goto agg; case TYeunion: - epri("%s", ty->agg.name ? ty->agg.name : "(anonymous tagged union)"); + kind = "tagged union"; + agg: + epri("%s", ty->agg.name ? ty->agg.name : "(anonymous %s)", kind); + if (ty->agg.tpargs.n) { + epri("<"); + int n = ty->agg.tpargs.n; + for (int i = 0; i < n; ++i) { + epri("%t", ty->agg.tpargs.d[i].ty); + if (i < n - 1) + epri(", "); + } + epri("%bc", '>'); + } break; } } @@ -141,6 +155,10 @@ tok2str(struct tok tok) { } buf[i] = '\0'; strcat(buf, "'"); + } else if (tok.t == TKtype) { + return "<type parameter>"; + } else if (tok.t == TKexpr) { + return "<const parameter>"; } else { snprintf(buf, sizeof buf - 1, "`%c'", tok.t); } @@ -417,6 +435,8 @@ dumpdecl(const struct decl *decl, int ws) { } epri("}\n"); break; + case Dtepl: + assert(0); } } diff --git a/bootstrap/parse.c b/bootstrap/parse.c index 7ec725d..4094edb 100644 --- a/bootstrap/parse.c +++ b/bootstrap/parse.c @@ -256,22 +256,6 @@ readstrlit(struct parser *P, u8 delim, char *s, size_t n) { #define MAX_MACROEXPAND_ITER 100 -/* -// guard used at macro arg expansion to avoid it expanding into itself, e.g.: -// defmacro foo(x,y) [(x+y)] -// let x, y ...; -// foo(x,y) -// would cause infinite recursion otherwise -static bool -ismacrodupe(struct parser *P, struct toktree toks) { - for (struct expan *ep = P->curexpan; ep; ep = ep->prev) { - if (ep->toks.d == toks.d) - return 1; - } - return 0; -} -*/ - static struct tok lex(struct parser *P) { u8 c; @@ -584,6 +568,103 @@ mkslicetype(const struct type *child) { }); } +static struct expr * +exprdup(struct expr ex) { + return memcpy(xmalloc(sizeof ex), &ex, sizeof ex); +} + +static const struct type *parseagg(struct parser *P, const char *name, int kind, struct decl **retdecl); + +static const struct type * +parseexpandtepl(struct parser *P, struct tepl *tepl) { + struct expanarg *args = xcalloc(tepl->params.n, sizeof *args); + struct expan expan = { P->curexpan, .name = tepl->name }; + struct tok tok; + const struct type *ty = NULL; + int i = 0; + + expan.span = container_of(tepl, struct decl, tepl)->span; + expan.tepl = 1; + while (!lexmatch(P, &tok, '>')) { + struct teplparam *par = tepl->params.d + i; + struct toktree toks = {{ xmalloc(sizeof (struct tok)), 1 }}; + + if (par->typ) { + toks.d->span = P->curspan; + const struct type *ty = parsetype(P); + toks.d->t = TKtype; + toks.d->ty = ty; + } else { + struct expr ex = parseexpr(P); + if (!fold(&ex)) + fatal(P, ex.span, "template parameter is not constant expression"); + toks.d->span = P->tokspan; + toks.d->t = TKexpr; + toks.d->ex = exprdup(ex); + } + + args[i].name = par->name; + args[i].toks = toks; + + ++i; + if (!lexmatch(P, &tok, ',')) { + lexexpect(P, '>'); + break; + } + } + if (i != tepl->params.n) + fatal(P, tok.span, "invalid argument count for template `%s'", tepl->name); + + slice_t(struct teplarg) tpargs = { + xmalloc(tepl->params.n * sizeof(struct teplarg)), + tepl->params.n + }; + struct teplcache *cache; + + for (int i = 0; i < tepl->params.n; ++i) { + if (tepl->params.d[i].typ) { + tpargs.d[i].typ = 1; + tpargs.d[i].ty = args[i].toks.d[0].ty; + } else { + assert(0); + } + } + + // TODO hashmap ? + for (cache = tepl->cache; cache; cache = cache->next) { + for (int i = 0; i < tepl->params.n; ++i) { + if (tepl->params.d[i].typ) { + if (cache->args.d[i].ty != tpargs.d[i].ty) + goto next; + } else { + assert(0); + } + } + free(tpargs.d); + return cache->ty; + next:; + } + + struct env env = { tepl->env }; + WITH_TMPCHANGE(struct env *, P->curenv, &env) { + struct decl *decl; + expan.args.d = args; + expan.args.n = i; + expan.toks = tepl->toks; + ++P->expanno; + P->curexpan = memcpy(xmalloc(sizeof expan), &expan, sizeof expan); + ty = parseagg(P, tepl->name, tepl->tkind, &decl); + } + memcpy(&((struct type *)ty)->agg.tpargs, &tpargs, sizeof tpargs); + cache = xcalloc(sizeof *cache, 1); + cache->next = tepl->cache; + memcpy(&cache->args, &tpargs, sizeof tpargs); + cache->ty = ty; + tepl->cache = cache; + + return ty; +} + static const struct type * parsetype(struct parser *P) { struct tok tok; @@ -642,11 +723,17 @@ parsetype(struct parser *P) { const struct decl *decl = finddecl(P, tok.str); if (!decl) fatal(P, P->tokspan, "%T is not defined", tok); - if (decl->t != Dtype) + if (decl->t == Dtype) { + return decl->ty; + } else if (decl->t == Dtepl && decl->tepl.t == Dtype) { + lexexpect(P, '<'); + return parseexpandtepl(P, (struct tepl *)&decl->tepl); + } else fatal(P, P->tokspan, "%T is not a type", tok); - return decl->ty; } else if (lexmatch(P, &tok, TKkw_fn)) { return parsefntype(P); + } else if (lexmatch(P, &tok, TKtype)) { + return tok.ty; } fatal(P, P->tokspan, "expected type (near %T)", tok); } @@ -683,12 +770,6 @@ islvalue(const struct expr *ex) { /** Expression parsing **/ -static struct expr * -exprdup(struct expr ex) { - return memcpy(xmalloc(sizeof ex), &ex, sizeof ex); -} - - static struct blockstmt parseblock0(struct parser *P); static struct stmt parseblock(struct parser *P); static void parseexpandmacro(struct parser *P, const struct macro *macro); @@ -1852,10 +1933,8 @@ parseexpandmacro(struct parser *P, const struct macro *macro) { for (;;) { tok = lex(P); switch (tok.t) { - case '[': ++bkbal; break; - case ']': --bkbal; break; - case '{': ++bcbal; break; - case '}': --bcbal; break; + case '[': ++bkbal; break; case ']': --bkbal; break; + case '{': ++bcbal; break; case '}': --bcbal; break; case '(': ++pabal; break; case ')': if (--pabal >= 0) @@ -2150,12 +2229,9 @@ parsemacrocase(struct parser *P) { while (bkbal || pabal || bcbal) { tok = lex(P); switch (tok.t) { - case '(': ++pabal; break; - case ')': --pabal; break; - case '[': ++bkbal; break; - case ']': --bkbal; break; - case '{': ++bcbal; break; - case '}': --bcbal; break; + case '(': ++pabal; break; case ')': --pabal; break; + case '[': ++bkbal; break; case ']': --bkbal; break; + case '{': ++bcbal; break; case '}': --bcbal; break; case TKeof: fatal(P, espan, "unterminated macro definition"); } @@ -2417,6 +2493,58 @@ doimport(struct parser *P, const char *path) { return cf; } +static struct tepl +parseaggtepl(struct parser *P, int kind, const char *name) { + struct tepl tepl = {Dtype, kind, name}; + vec_t(struct teplparam) params = {0}; + struct tok tok; + vec_t(struct tok) toks = {0}; + + while (!lexmatch(P, &tok, '>')) { + struct teplparam p; + const char *name = lexexpects(P, TKident, "parameter name").str; + + p.name = name; + if ((tok = lexpeek(P)).t == '>' || tok.t == ',') { + p.typ = 1; + } else { + assert(0); + } + + vec_push(¶ms, p); + + if (!lexmatch(P, NULL, ',')) { + lexexpect(P, '>'); + break; + } + } + lexexpect(P, '{'); + vec_push(&toks, (struct tok){'{'}); + + int pabal = 0, // ( ) parens balance + bkbal = 0, // [ ] brackets .. + bcbal = 1; // { } braces .. + + while (pabal || bkbal || bcbal) { + tok = lex(P); + switch (tok.t) { + case '[': ++bkbal; break; case ']': --bkbal; break; + case '{': ++bcbal; break; case '}': --bcbal; break; + case '(': ++pabal; break; case ')': --pabal; break; + } + if (tok.t == TKident) + for (int i = 0; i < params.length; ++i) + if (!strcmp(params.data[i].name, tok.str)) + tok.t = TKmacident; + vec_push(&toks, tok); + } + + tepl.env = P->curenv; + vec_slice_cpy(&tepl.params, ¶ms); + vec_slice_cpy(&tepl.toks, &toks); + return tepl; +} + struct staticvaryarg { struct parser *P; bool externp, toplevel; @@ -2490,14 +2618,22 @@ parsedecl(decl_yielder_t yield, void *yarg, struct parser *P, bool toplevel) { decl.macro.name = name; } else if (lexmatch(P, &tok, TKkw_enum)) { if (externp) fatal(P, tok.span, "enum cannot be `extern'"); + decl.t = Dtype; decl.name = lexexpects(P, TKident, "enum name").str; decl.ty = parseenum(P, decl.name); } else if (lexmatch(P, &tok, TKkw_struct)) { kind = TYstruct; if (externp) fatal(P, tok.span, "struct cannot be `extern'"); agg: - decl.name = lexexpects(P, TKident, "struct name").str; - decl.ty = parseagg(P, decl.name, kind, &decl2); + decl.span = tok.span; + decl.name = lexexpects(P, TKident, "type name").str; + if (!lexmatch(P, NULL, '<')) { + decl.t = Dtype; + decl.ty = parseagg(P, decl.name, kind, &decl2); + } else { + decl.t = Dtepl; + decl.tepl = parseaggtepl(P, kind, decl.name); + } if (decl2) goto noput; } else if (lexmatch(P, &tok, TKkw_union)) { if (externp) fatal(P, tok.span, "union cannot be `extern'"); diff --git a/bootstrap/test2.cff b/bootstrap/test2.cff index ee24d45..66603ba 100644 --- a/bootstrap/test2.cff +++ b/bootstrap/test2.cff @@ -1,6 +1,21 @@ import "libc.hff"; -struct Node { - next *Node, - value int, +struct Node<T> { + link *Node, + value T, + + fn ok(self Node) void { + } +} + +extern fn main() void { + let n Node<int> = {#null, 0}; + let n Node<int> = {&n, 1}; + + printf("n %d\n", n.value); + printf("n link %d\n", n.link.value); + + let x Node<f32> = {}; + n->ok(); + x->ok(); } diff --git a/bootstrap/util.c b/bootstrap/util.c index 43aa1b4..20f1772 100644 --- a/bootstrap/util.c +++ b/bootstrap/util.c @@ -118,7 +118,7 @@ eprifileline(struct span span) { } // line begin for (i = span.idx; i > 0 && src[i] != '\n'; --i) ; - ++i; + if (i > 0) ++i; // line end for (j = span.idx; src[j] && src[j] != '\n'; ++j) ; @@ -148,8 +148,8 @@ fatal(struct parser *P, struct span span, const char *fmt, ...) { for (struct expan *ep = P->curexpan; ep; ep = ep->prev, ++i) { if (ep->name && (i < 8 || !ep->prev || !ep->prev->prev)) { span = ep->span; - epri("* while expanding macro `%s' at %s:%d:%d\n", - ep->name, + epri("* while expanding %s `%s' at %s:%d:%d\n", + ep->tepl ? "template" : "macro", ep->name, fileid2path(ep->span.fileid), span.line, span.col); eprifileline(span); } else if (ep->name && i == 10) { |