[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[PATCH] Support direct tuple access operators "tuple.N"
- Subject: [PATCH] Support direct tuple access operators "tuple.N"
- From: Quentin Carbonneaux <quentin@xxxxxx>
- Reply-to: myrddin-dev@xxxxxxxxxxxxxx
- Date: Tue, 17 Jul 2018 08:57:58 +0000
- To: myrddin-dev@xxxxxxxxxxxxxx
Hi myrddin-dev,
From the original mail:
> 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).
Thanks for the review, Ori; I updated the patch to make it properly
support tuple access lvalues. Thanks to a lexer hack I added, one
can now write 't.0.1' instead of '(t.0).1'.
Quentin
---
6/isel.c | 2 +-
6/simp.c | 4 +--
6/typeinfo.c | 35 ++++++++++++++++++--------
mi/flatten.c | 4 ++-
parse/gram.y | 2 ++
parse/infer.c | 58 ++++++++++++++++++++++++++++++--------------
parse/ops.def | 1 +
parse/tok.c | 8 +++++-
test/tests | 1 +
test/tupleaccess.myr | 14 +++++++++++
10 files changed, 96 insertions(+), 33 deletions(-)
create mode 100644 test/tupleaccess.myr
diff --git a/6/isel.c b/6/isel.c
index 7b30b04c..bb111adc 100644
--- a/6/isel.c
+++ b/6/isel.c
@@ -945,7 +945,7 @@ selexpr(Isel *s, Node *n)
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);
diff --git a/6/simp.c b/6/simp.c
index 457227eb..7ab6f15f 100644
--- a/6/simp.c
+++ b/6/simp.c
@@ -540,6 +540,7 @@ lval(Simp *s, Node *n)
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 @@ rval(Simp *s, Node *n, Node *dst)
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);
diff --git a/6/typeinfo.c b/6/typeinfo.c
index 312aefb8..0a7045b1 100644
--- a/6/typeinfo.c
+++ b/6/typeinfo.c
@@ -349,7 +349,7 @@ tyalign(Type *ty)
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 @@ tyoffset(Type *ty, Node *memb)
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
diff --git a/mi/flatten.c b/mi/flatten.c
index 5a749962..e4817d33 100644
--- a/mi/flatten.c
+++ b/mi/flatten.c
@@ -560,8 +560,9 @@ rval(Flattenctx *s, Node *n)
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 @@ lval(Flattenctx *s, Node *n)
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;
diff --git a/parse/gram.y b/parse/gram.y
index 146c8422..cf058882 100644
--- a/parse/gram.y
+++ b/parse/gram.y
@@ -784,6 +784,8 @@ prefixexpr
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
diff --git a/parse/infer.c b/parse/infer.c
index c5cad2f1..69e9f9ba 100644
--- a/parse/infer.c
+++ b/parse/infer.c
@@ -228,6 +228,9 @@ ctxstr(Node *n)
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;
@@ -1764,6 +1767,7 @@ inferexpr(Node **np, Type *ret, int *sawret)
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));
@@ -2253,27 +2257,22 @@ infercompn(Node *n, Node ***rem, size_t *nrem, Stab ***remscope, size_t *nremsco
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]));
@@ -2284,17 +2283,39 @@ infercompn(Node *n, Node ***rem, size_t *nrem, Stab ***remscope, size_t *nremsco
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)
@@ -2414,6 +2435,7 @@ postcheckpass(Node ***rem, size_t *nrem, Stab ***remscope, size_t *nremscope)
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;
diff --git a/parse/ops.def b/parse/ops.def
index 909736ec..e2f0b508 100644
--- a/parse/ops.def
+++ b/parse/ops.def
@@ -42,6 +42,7 @@ O(Obsreq, 1, OTbin, ">>=")
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)
diff --git a/parse/tok.c b/parse/tok.c
index 5c55cbcc..934f983f 100644
--- a/parse/tok.c
+++ b/parse/tok.c
@@ -657,6 +657,7 @@ number(int base)
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 @@ number(int base)
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;
diff --git a/test/tests b/test/tests
index 0dcb8434..2291b7e8 100644
--- a/test/tests
+++ b/test/tests
@@ -135,6 +135,7 @@ B arraylit E 3
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
diff --git a/test/tupleaccess.myr b/test/tupleaccess.myr
new file mode 100644
index 00000000..cbd0f05f
--- /dev/null
+++ b/test/tupleaccess.myr
@@ -0,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)
+ ;;
+}
--
2.18.0