ref: f4a43f90e3ffe1712aec13566e8ac545da4161c4
parent: 8a3e8add6e0180565312d1079395ad39de4d9ce3
author: Quentin Carbonneaux <quentin@c9x.me>
date: Tue Jul 17 04:57:58 EDT 2018
Support direct tuple access operators "tuple.N" This patch adds tuple access expressions. If t is a tuple, its N-th component can be retrieved with the syntax t.N. Of course, the components are zero indexed. I believe the code also works if 't' is a pointer to a tuple (but I have not checked this).
--- a/6/isel.c
+++ b/6/isel.c
@@ -945,7 +945,7 @@
case Obandeq: case Obxoreq: case Obsleq: case Obsreq: case Omemb:
case Oslbase: case Osllen: case Ocast: case Outag: case Oudata:
case Otup: case Oarr: case Ostruct:
- case Oslice: case Oidx: case Osize: case Otupget:
+ case Oslice: case Oidx: case Osize: case Otupget: case Otupmemb:
case Obreak: case Ocontinue:
case Numops:
dump(n, stdout);
--- a/6/simp.c
+++ b/6/simp.c
@@ -540,6 +540,7 @@
case Ovar: r = loadvar(s, n, NULL); break;
case Oidx: r = loadidx(s, args[0], args[1]); break;
case Oderef: r = deref(rval(s, args[0], NULL), NULL); break;
+ case Otupmemb: r = rval(s, n, NULL); break;
case Omemb: r = rval(s, n, NULL); break;
case Ostruct: r = rval(s, n, NULL); break;
case Oucon: r = rval(s, n, NULL); break;
@@ -1137,8 +1138,7 @@
u = idxaddr(s, t, n->expr.args[1]);
r = load(u);
break;
- /* array.len slice.len are magic 'virtual' members.
- * they need to be special cased. */
+ case Otupmemb:
case Omemb:
t = membaddr(s, n);
r = load(t);
--- a/6/typeinfo.c
+++ b/6/typeinfo.c
@@ -349,7 +349,7 @@
return min(align, Ptrsz);
}
-/* gets the byte offset of 'memb' within the aggregate type 'aggr' */
+/* gets the byte offset of 'memb' within the aggregate type 'ty' */
ssize_t
tyoffset(Type *ty, Node *memb)
{
@@ -360,16 +360,31 @@
if (ty->type == Typtr)
ty = tybase(ty->sub[0]);
- assert(ty->type == Tystruct);
- off = 0;
- for (i = 0; i < ty->nmemb; i++) {
- off = alignto(off, decltype(ty->sdecls[i]));
- if (!strcmp(namestr(memb), declname(ty->sdecls[i])))
- return off;
- off += size(ty->sdecls[i]);
+ switch (memb->type) {
+ case Nname:
+ assert(ty->type == Tystruct);
+ off = 0;
+ for (i = 0; i < ty->nmemb; i++) {
+ off = alignto(off, decltype(ty->sdecls[i]));
+ if (!strcmp(namestr(memb), declname(ty->sdecls[i])))
+ return off;
+ off += size(ty->sdecls[i]);
+ }
+ die("bad offset");
+ return 0;
+ case Nlit:
+ assert(ty->type == Tytuple);
+ assert(memb->lit.intval < ty->nsub);
+ off = 0;
+ for (i = 0; i < memb->lit.intval; i++) {
+ off += tysize(ty->sub[i]);
+ off = alignto(off, ty->sub[i+1]);
+ }
+ return off;
+ default:
+ die("bad offset node type");
+ return 0;
}
- die("bad offset");
- return 0;
}
size_t
--- a/mi/flatten.c
+++ b/mi/flatten.c
@@ -560,8 +560,9 @@
if (ty->type == Tyslice || ty->type == Tyarray) {
r = seqlen(s, args[0], exprtype(n));
} else {
+ case Otupmemb:
t = rval(s, args[0]);
- r = mkexpr(n->loc, Omemb, t, args[1], NULL);
+ r = mkexpr(n->loc, exprop(n), t, args[1], NULL);
r->expr.type = n->expr.type;
}
break;
@@ -696,6 +697,7 @@
case Ovar: r = n; break;
case Oidx: r = rval(s, n); break;
case Oderef: r = rval(s, n); break;
+ case Otupmemb: r = rval(s, n); break;
case Omemb: r = rval(s, n); break;
case Ostruct: r = rval(s, n); break;
--- a/parse/gram.y
+++ b/parse/gram.y
@@ -786,6 +786,8 @@
postfixexpr
: postfixexpr Tdot Tident
{$$ = mkexpr($1->loc, Omemb, $1, mkname($3->loc, $3->id), NULL);}
+ | postfixexpr Tdot Tintlit
+ {$$ = mkexpr($1->loc, Otupmemb, $1, mkint($3->loc, $3->intval), NULL);}
| postfixexpr Tinc
{$$ = mkexpr($1->loc, Opostinc, $1, NULL);}
| postfixexpr Tdec
--- a/parse/infer.c
+++ b/parse/infer.c
@@ -228,6 +228,9 @@
case Omemb:
bprintf(buf, sizeof buf, "<%s>.%s", t1, namestr(args[1]));
break;
+ case Otupmemb:
+ bprintf(buf, sizeof buf, "<%s>.%llu", t1, args[1]->lit.intval);
+ break;
default:
bprintf(buf, sizeof buf, "%s:%s", d, t);
break;
@@ -1769,6 +1772,7 @@
break;
/* special cases */
+ case Otupmemb: /* @a.N -> @b, verify type(@a.N)==@b later */
case Omemb: /* @a.Ident -> @b, verify type(@a.Ident)==@b later */
infersub(n, ret, sawret, &isconst);
settype(n, mktyvar(n->loc));
@@ -2258,27 +2262,22 @@
Type *t;
size_t i;
int found;
+ int ismemb;
+ uvlong idx;
aggr = n->expr.args[0];
memb = n->expr.args[1];
+ ismemb = n->expr.op == Omemb;
found = 0;
t = tybase(tf(type(aggr)));
/* all array-like types have a fake "len" member that we emulate */
- if (t->type == Tyslice || t->type == Tyarray) {
+ if (ismemb && (t->type == Tyslice || t->type == Tyarray)) {
if (!strcmp(namestr(memb), "len")) {
constrain(n, type(n), traittab[Tcnum]);
constrain(n, type(n), traittab[Tcint]);
found = 1;
}
- /*
- * otherwise, we search aggregate types for the member, and unify
- * the expression with the member type; ie:
- *
- * x: aggrtype y : memb in aggrtype
- * ---------------------------------------
- * x.y : membtype
- */
} else {
if (tybase(t)->type == Typtr)
t = tybase(tf(t->sub[0]));
@@ -2289,17 +2288,39 @@
lappend(rem, nrem, n);
lappend(remscope, nremscope, curstab());
return;
- } else if (tybase(t)->type != Tystruct) {
- fatal(n, "type %s does not support member operators near %s",
- tystr(t), ctxstr(n));
}
- nl = t->sdecls;
- for (i = 0; i < t->nmemb; i++) {
- if (!strcmp(namestr(memb), declname(nl[i]))) {
- unify(n, type(n), decltype(nl[i]));
- found = 1;
- break;
+ if (ismemb) {
+ /*
+ * aggregate types for the member, and unify the expression with the
+ * member type; ie:
+ *
+ * x: aggrtype y : memb in aggrtype
+ * ---------------------------------------
+ * x.y : membtype
+ */
+ if (tybase(t)->type != Tystruct)
+ fatal(n, "type %s does not support member operators near %s",
+ tystr(t), ctxstr(n));
+ nl = t->sdecls;
+ for (i = 0; i < t->nmemb; i++) {
+ if (!strcmp(namestr(memb), declname(nl[i]))) {
+ unify(n, type(n), decltype(nl[i]));
+ found = 1;
+ break;
+ }
}
+ } else {
+ /* tuple access; similar to the logic for member accesses */
+ if (tybase(t)->type != Tytuple)
+ fatal(n, "type %s does not support tuple access operators near %s",
+ tystr(t), ctxstr(n));
+ assert(memb->type == Nlit);
+ idx = memb->lit.intval;
+ if (idx >= t->nsub)
+ fatal(n, "cannot access element %llu of a tuple of type %s near %s",
+ idx, tystr(t), ctxstr(n));
+ unify(n, type(n), t->sub[idx]);
+ found = 1;
}
}
if (!found)
@@ -2419,6 +2440,7 @@
pushstab(postcheckscope[i]);
if (n->type == Nexpr) {
switch (exprop(n)) {
+ case Otupmemb:
case Omemb: infercompn(n, rem, nrem, remscope, nremscope); break;
case Ocast: checkcast(n, rem, nrem, remscope, nremscope); break;
case Ostruct: checkstruct(n, rem, nrem, remscope, nremscope); break;
--- a/parse/ops.def
+++ b/parse/ops.def
@@ -42,6 +42,7 @@
O(Oidx, 1, OTmisc, NULL)
O(Oslice, 1, OTmisc, NULL)
O(Omemb, 1, OTmisc, NULL)
+O(Otupmemb, 1, OTmisc, NULL)
O(Osize, 1, OTmisc, NULL)
O(Ocall, 0, OTmisc, NULL)
O(Ocast, 1, OTmisc, NULL)
--- a/parse/tok.c
+++ b/parse/tok.c
@@ -657,6 +657,7 @@
int start;
int c;
int isfloat;
+ int maybefloat;
int unsignedval;
/* because we allow '_' in numbers, and strtod/stroull don't, we
* need a buffer that holds the number without '_'.
@@ -669,7 +670,12 @@
isfloat = 0;
start = fidx;
nbuf = 0;
- for (c = peek(); isxdigit(c) || c == '.' || c == '_'; c = peek()) {
+ /* allow floating point literals only if the previous token was
+ * not a dot; this lets the user write "foo.1.2" to access nested
+ * tuple fields.
+ */
+ maybefloat = !curtok || (curtok->type != Tdot);
+ for (c = peek(); isxdigit(c) || (maybefloat && c == '.') || c == '_'; c = peek()) {
next();
if (c == '_')
continue;
--- a/test/tests
+++ b/test/tests
@@ -135,6 +135,7 @@
B structlit E 42
B livestructlit E 21
B tuple E 42
+B tupleaccess P 'a: 0, b: 5, c: 2'
B slgrow E 42
B tyrec E 42
B infer-named E 99
--- /dev/null
+++ b/test/tupleaccess.myr
@@ -1,0 +1,14 @@
+use std
+
+const foo = {
+ -> (1, 2, (3, 4))
+}
+
+const main = {
+ match foo()
+ | x:
+ x.0 = 0
+ (x.2).1 = 5
+ std.put("a: {}, b: {}, c: {}\n", x.0, x.2.1, foo().1)
+ ;;
+}