Eigenstate: myrddin-dev mailing list

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] Support direct tuple access operators "tuple.N"


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


Follow-Ups:
Re: [PATCH] Support direct tuple access operators "tuple.N"Ori Bernstein <ori@xxxxxxxxxxxxxx>
References:
[PATCH] Support direct tuple access operators "tuple.N"Quentin Carbonneaux <quentin@xxxxxx>
Re: [PATCH] Support direct tuple access operators "tuple.N"Ori Bernstein <ori@xxxxxxxxxxxxxx>