shithub: spread

Download patch

ref: 4b0a8257541c46af6bca812aacba9f8564cb8c25
parent: af8848cfc823ec9fb1590bb16a2804bc6e1a714d
author: sirjofri <sirjofri@sirjofri.de>
date: Sun Jul 7 11:23:22 EDT 2024

adds range support using [A1()+A5()] syntax

--- a/README.md
+++ b/README.md
@@ -23,8 +23,8 @@
 
 The general syntax of cells should be quite obvious, but it's worth noting
 that cells divided by an `=` sign will end up being calculation functions,
-while cells divided by a `;` sign are string literals. Both will end up
-being hoc functions.
+while cells divided by a `;` sign are string literals. Only calculation
+functions (cells with `=`) will end up being hoc functions.
 
 # Usage
 
@@ -84,17 +84,37 @@
 	A1()+2*A3()
 	→ 13
 
-# Open questions
+# Macros
 
-## Hoc limitations
+## Range support
 
-- Range support. Since hoc doesn't support something like `eval`,
-  it is impossible to support ranges (`A3()..A5()`) out of the box.
-  If we need ranges we need to find a good solution or at least a
-  solid workaround, like building a list dynamically.
-  At the moment, I'm thinking about implementing a preprocessor that
-  transforms snippets like `[A1()+A5()]` to `A1()+A2()+A3()+A4()+A5()`.
-  Same should work for `[A1()*A5()]` etc.
+Spread supports some level of ranges. The syntax looks like this:
+
+	'[' CELL() op CELL() ']'
+	op := any operation character that's supported by hoc (+-*/)
+	CELL := any cell address (e. g. B12)
+
+Spaces within this expression are not supported!
+
+For example:
+
+	A1() + [B1()+B3()] + 5
+
+will be converted to (note that the range is enclosed in `()`):
+
+	A1() + (B1()+B2()+B3()) + 5
+
+This also allows more complex expressions like:
+
+	A1() + [B1()*C10()] + [D1()+D10()]
+
+Note that this expression contains multiple ranges, one range
+exceeding a single column.
+
+Note that his can also lead to extremely heavy performance impact
+if you choose to do exponentiation with many cells.
+
+# Open questions
 
 ## Bugs
 
--- a/cells.c
+++ b/cells.c
@@ -23,9 +23,12 @@
 addempty(char *s)
 {
 	P p;
+	Cell *c;
 	
 	while (s = findvaluep(s, &p)) {
-		addcell(p, strdup("0"), FUNCTION);
+		c = getcell(p);
+		if (!c)
+			addcell(p, strdup("0"), FUNCTION);
 	}
 }
 
@@ -47,9 +50,12 @@
 	if (c = getcell(cell)) {
 		if (c->value)
 			free(c->value);
+		if (c->procvalue && c->procvalue != c->value)
+			free(c->procvalue);
 		c->value = strdup(value);
 		c->type = type;
-		addempty(value);
+		preprocess(c);
+		addempty(c->procvalue);
 		return;
 	}
 	
@@ -63,7 +69,8 @@
 	c->p = cell;
 	c->value = strdup(value);
 	c->type = type;
-	addempty(value);
+	preprocess(c);
+	addempty(c->procvalue);
 }
 
 void
@@ -123,7 +130,7 @@
 	Resub match;
 	
 	if (!funcregexp)
-		funcregexp = regcomp("[A-Z]+[0-9]+[(]+[)]+");
+		funcregexp = regcomp("[A-Z]+[0-9]+\\(\\)");
 	
 	memset(&match, 0, sizeof(Resub));
 	if (regexec(funcregexp, s, &match, 1)) {
--- a/engine.c
+++ b/engine.c
@@ -289,7 +289,7 @@
 		sysfatal("code error");
 	}
 	
-	buf = smprint(h, ptoa(c->p), c->value);
+	buf = smprint(h, ptoa(c->p), c->procvalue);
 	hocwrite(buf, nil);
 	free(buf);
 }
--- a/mkfile
+++ b/mkfile
@@ -6,5 +6,6 @@
 	engine.$O\
 	util.$O\
 	cells.$O\
+	preproc.$O\
 
 </sys/src/cmd/mkone
--- /dev/null
+++ b/preproc.c
@@ -1,0 +1,121 @@
+#include <u.h>
+#include <libc.h>
+#include <regexp.h>
+#include <String.h>
+#include "spread.h"
+
+Reprog *rrange = nil;
+
+/* begin test code */
+/* acid:
+ * acid: new()
+ * acid: *PC=testpreproc
+ * acid: cont()
+ */
+void
+t(char *s)
+{
+	Cell c;
+	
+	c.procvalue = nil;
+	c.value = strdup(s);
+	preprocess(&c);
+	fprint(2, "'%s' → '%s'\n", s, c.procvalue);
+	free(c.value);
+}
+
+void
+testpreproc(void)
+{
+	t("A1()+[A1()/B4()]+A5()");
+	t("A1()+[A1()+B4()]+A5()");
+	t("A1()+[A1()-B4()]+A5()");
+	t("A1()+[A1()*B4()]+A5()");
+	exits(nil);
+}
+/* end test code */
+
+void
+appendrange(String *s, char sign, P a, P b)
+{
+	int x, y;
+	P na, nb;
+	P p;
+	char *ps;
+	int first;
+	
+	na.x = a.x < b.x ? a.x : b.x;
+	na.y = a.y < b.y ? a.y : b.y;
+	nb.x = a.x > b.x ? a.x : b.x;
+	nb.y = a.y > b.y ? a.y : b.y;
+	
+	first = 1;
+	s_putc(s, '(');
+	for (x = na.x; x <= nb.x; x++)
+		for (y = na.y; y <= nb.y; y++) {
+			p.x = x;
+			p.y = y;
+			ps = ptoa(p);
+			if (!first)
+				s_putc(s, sign);
+			first = 0;
+			s_append(s, ps);
+			s_append(s, "()");
+		}
+	s_putc(s, ')');
+}
+
+int
+prange(Cell *c)
+{
+	Resub match[4];
+	char *s;
+	String *str;
+	P a, b;
+	char sign;
+	
+	if (!rrange)
+		rrange = regcomp("\\[([A-Z]+[0-9]+\\(\\))([+\\-*/])([A-Z]+[0-9]+\\(\\))\\]");
+	assert(rrange);
+	
+	memset(match, 0, 4*sizeof(Resub));
+	
+	s = c->procvalue ? c->procvalue : c->value;
+	str = nil;
+	
+	while (regexec(rrange, s, match, 4)) {
+		if (!str)
+			str = s_new();
+		
+		s_nappend(str, s, match[0].sp-s);
+		
+		s = match[0].ep;
+		sign = *match[2].sp;
+		a = atop(match[1].sp);
+		b = atop(match[3].sp);
+		
+		appendrange(str, sign, a, b);
+		match[0].sp = nil;
+		match[0].ep = nil;
+	}
+	
+	if (str) {
+		s_append(str, s);
+		c->procvalue = strdup(s_to_c(str));
+		s_free(str);
+	} else
+		c->procvalue = c->value;
+	
+	return 1;
+}
+
+int
+preprocess(Cell *c)
+{
+	int r = 0;
+	
+	if (!prange(c))
+		r++;
+	
+	return !r;
+}
--- a/spread.h
+++ b/spread.h
@@ -24,6 +24,7 @@
 void foreachcell(void (*f)(Cell*,void*), void*);
 int sortcells(void);
 int updatecells(void);
+int preprocess(Cell*);
 
 void toupperil(char*);
 P atop(char*);
@@ -41,6 +42,7 @@
 	P p;
 	char *value;
 	int type;
+	char *procvalue;
 	
 	Cell **points;
 	int size;
--- a/test/test.rc
+++ b/test/test.rc
@@ -12,6 +12,10 @@
 A2;hello
 A4=A1()+A3()
 A3=5
+B1=2
+B2=3
+B3=4
+B4=100+[B1()+B3()]
 EOF
 
 ../6.out /tmp/test.sp