shithub: mc

Download patch

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)
+	;;
+}