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