aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--compiler/compile.c78
-rw-r--r--tests/check.py41
2 files changed, 89 insertions, 30 deletions
diff --git a/compiler/compile.c b/compiler/compile.c
index ae55ac6..cdb059a 100644
--- a/compiler/compile.c
+++ b/compiler/compile.c
@@ -53,7 +53,9 @@ enum { STATE_TRANSIENT = 256 };
enum { STATE_NUM = 0, STATE_TEXT = 1, STATE_CURSOR = 2, STATE_LIST = 3, STATE_INDEX = 4, STATE_BOOL = 5, STATE_I64 = 6,
UNIT_CHAR = 0, UNIT_LINE = 1 };
/* Pure's view types, record field types (0 a number, 1 a text, 2 a bool, 3 an i64) and what a local holds. */
-enum { VIEW_NUM = 0, VIEW_TEXT = 1, VIEW_TABLE = 2, VIEW_BOOL = 3, VIEW_I64 = 4, LOCAL_BOOL = 2, LOCAL_I64 = 3, LOCAL_LIST = 4 };
+/* A view's type is a record field's (Pure 027's 4e). */
+enum { VIEW_NUM = 0, VIEW_TEXT = 1, VIEW_BOOL = 2, VIEW_I64 = 3, VIEW_LIST = 4, VIEW_BYTES = 5, LOCAL_BOOL = 2, LOCAL_I64 = 3,
+ LOCAL_LIST = 4 };
/* LIST: a list in a record (Pure 027). RECORD: a record's or a variant's value, its fields on the stack in its order
* (027's 2c); TUPLE: the values a fn gives (owner: its instance); NOTHING: what a fn that gives none gives. */
typedef enum { NUM = 1, BYTES, TEXT, CURSOR, UNIT, INDEX, BOOL, I64, LIST, RECORD, TUPLE, NOTHING } Type;
@@ -238,12 +240,15 @@ typedef struct {
unsigned library_record_count;
Input inputs[INPUTS];
unsigned input_count, field_count;
- /* Views in order; a table (no value) is followed by its columns. */
+ /* Views in order; a table (no value) is followed by its columns, or has none (each ... without them: its list's
+ * items as they are, view_whole). */
char view_names[VIEWS][NAME], view_row[VIEWS][NAME], view_list[VIEWS][NAME];
Expr *view_value[VIEWS];
Stmt *view_body[VIEWS]; /* a view that computes: its statements, return last (view_value) */
Expr *view_filter[VIEWS]; /* a table's where condition, or NULL */
- unsigned char view_byte[VIEWS]; /* a filtered table's list + 1 */
+ int view_source[VIEWS]; /* a list view over a list state: that state + 1 */
+ int view_record[VIEWS]; /* a list view's or a list column's items' record + 1 (a map's 0) */
+ bool view_whole[VIEWS]; /* each ... without columns */
int view_table[VIEWS]; /* a column's table + 1, or 0 */
int view_arguments[VIEWS]; /* the input of a view's arguments + 1, or 0 */
int view_line[VIEWS][2], view_column[VIEWS][2]; /* a table's row name and list name */
@@ -1935,6 +1940,10 @@ static void table_declaration(Compiler *c, unsigned table) {
take(c);
c->view_filter[table] = expression(c, 0, false);
}
+ if (!is(c, "{")) { /* its list's items as they are: their fields its columns */
+ c->view_whole[table] = true;
+ return;
+ }
expect(c, "{", "'{' on the line of the table");
separators(c);
while (!is(c, "}")) {
@@ -4934,7 +4943,16 @@ static void statements(Compiler *c, const Stmt *s) {
/* a view's body's value, its last line */
if (c->handling) fail_at(c, s->line, s->column, "return gives a view's body its value; a handler returns nothing");
if (s->next || c->nested) fail_at(c, s->line, s->column, "return is the last line of a view's body");
- c->returned = value(c, s->value, false, 0);
+ const Expr *v = s->value;
+ int named = v->kind == E_NAME && parameter(c, v->name) < 0 ? state_index(c, v->name) : -1;
+ if (named >= 0 && c->state_types[named] == STATE_LIST) { /* a list it built: the view's rows (Pure 027's 4e) */
+ int list = list_state(c, v->name, v->line, v->column, NULL);
+ if (!c->state_event[list])
+ fail_at(c, v->line, v->column, "a view's body returns a list it built; a state's list is a view as it is, "
+ "as in view all = %s", v->name);
+ emit(c, v->line, v->column, LIST_VALUE, (uint32_t)list, 0);
+ c->returned = (Kind){LIST, (int)module_record(c, c->state_library[list], c->state_target[list])};
+ } else c->returned = value(c, s->value, false, 0);
c->has_returned = true;
break;
}
@@ -5352,9 +5370,13 @@ static void generate(Compiler *c, unsigned char **module, size_t *size) {
if (c->view_table[v] || length <= 6 || strcmp(c->view_names[v] + length - 6, "_focus") || !e || e->kind != E_MEMBER ||
strcmp(e->member, "position") || e->args[0]->kind != E_NAME) continue;
int anchor = state_index(c, e->args[0]->name), table = -1;
- for (unsigned t = 0; t < c->views; ++t)
- if (!c->view_table[t] && *c->view_list[t] && strlen(c->view_names[t]) == length - 6 &&
- !memcmp(c->view_names[t], c->view_names[v], length - 6)) table = (int)t;
+ for (unsigned t = 0; t < c->views; ++t) { /* a list view over a list state: each ..., or the state as it is */
+ const Expr *whole = c->view_value[t] && !c->view_body[t] && c->view_value[t]->kind == E_NAME ? c->view_value[t] : NULL;
+ int listed = whole ? state_index(c, whole->name) : -1;
+ if (!c->view_table[t] && (*c->view_list[t] || (listed >= 0 && c->state_types[listed] == STATE_LIST)) &&
+ strlen(c->view_names[t]) == length - 6 && !memcmp(c->view_names[t], c->view_names[v], length - 6))
+ table = (int)t;
+ }
if (anchor < 0 || c->state_types[anchor] != STATE_INDEX || table < 0) continue;
if (c->input_count == INPUTS) fail_at(c, c->view_line[v][0], c->view_column[v][0], "at most 32 inputs are allowed");
if (c->handler_count == HANDLERS) fail_at(c, c->view_line[v][0], c->view_column[v][0], "at most 64 handlers are allowed");
@@ -5416,13 +5438,20 @@ static void generate(Compiler *c, unsigned char **module, size_t *size) {
* list in place of code; its columns may read its row. */
c->message = -1;
for (unsigned i = 0; i < c->views; ++i) {
- if (!c->view_value[i]) {
- const char *row = c->view_row[i], *kind = declared_kind(c, row);
- if (kind) fail_at(c, c->view_line[i][0], c->view_column[i][0], "'%s' is already declared as %s", row, kind);
- int list = list_state(c, c->view_list[i], c->view_line[i][1], c->view_column[i][1], NULL);
- c->view_types[i] = VIEW_TABLE;
- c->view_code[i][0] = (uint32_t)list;
- c->view_code[i][1] = 0;
+ const Expr *named = c->view_value[i] && !c->view_body[i] && !c->view_table[i] ? c->view_value[i] : NULL;
+ int whole = named && named->kind == E_NAME && !c->view_arguments[i] ? state_index(c, named->name) : -1;
+ if (whole >= 0 && c->state_types[whole] != STATE_LIST) whole = -1;
+ if (!c->view_value[i] || whole >= 0) { /* a list view over a list state: a map, or its items as they are */
+ int list = whole >= 0 ? list_state(c, named->name, named->line, named->column, NULL)
+ : list_state(c, c->view_list[i], c->view_line[i][1], c->view_column[i][1], NULL);
+ if (whole < 0) {
+ const char *row = c->view_row[i], *kind = declared_kind(c, row);
+ if (kind) fail_at(c, c->view_line[i][0], c->view_column[i][0], "'%s' is already declared as %s", row, kind);
+ }
+ c->view_types[i] = VIEW_LIST;
+ c->view_source[i] = list + 1;
+ c->view_record[i] = whole >= 0 || c->view_whole[i] ? (int)module_record(c, c->state_library[list], c->state_target[list]) + 1 : 0;
+ c->view_code[i][0] = c->view_code[i][1] = 0;
if (c->view_filter[i]) {
/* The filter runs as a column of the list: it reads the table's row. */
c->row = (int)i;
@@ -5431,7 +5460,6 @@ static void generate(Compiler *c, unsigned char **module, size_t *size) {
c->view_code[i][0] = c->code_count;
truth(c, c->view_filter[i], false, 0, "where");
c->view_code[i][1] = c->code_count - c->view_code[i][0];
- c->view_byte[i] = (unsigned char)(list + 1);
c->filtering = false;
}
continue;
@@ -5446,10 +5474,14 @@ static void generate(Compiler *c, unsigned char **module, size_t *size) {
statements(c, c->view_body[i]);
kind = c->returned;
} else kind = value(c, c->view_value[i], false, 0);
- if (kind.type != NUM && kind.type != BOOL && kind.type != I64 && kind.type != TEXT)
- fail_at(c, c->view_value[i]->line, c->view_value[i]->column, "a %s is a number, a bool, an i64 or text, not %s",
- c->row >= 0 ? "column" : "view", type_name(kind.type));
- c->view_types[i] = kind.type == TEXT ? VIEW_TEXT : kind.type == BOOL ? VIEW_BOOL : kind.type == I64 ? VIEW_I64 : VIEW_NUM;
+ if (kind.type != NUM && kind.type != BOOL && kind.type != I64 && kind.type != TEXT && kind.type != BYTES && kind.type != LIST)
+ fail_at(c, c->view_value[i]->line, c->view_value[i]->column,
+ "a %s is a number, a bool, an i64, text, bytes or a list, not %s", c->row >= 0 ? "column" : "view",
+ type_name(kind.type));
+ static const int types[] = {[NUM] = VIEW_NUM, [TEXT] = VIEW_TEXT, [BOOL] = VIEW_BOOL, [I64] = VIEW_I64, [LIST] = VIEW_LIST,
+ [BYTES] = VIEW_BYTES};
+ c->view_types[i] = types[kind.type];
+ c->view_record[i] = kind.type == LIST ? kind.owner + 1 : 0; /* a list it leaves: its items' record */
c->view_code[i][1] = c->code_count - c->view_code[i][0];
}
c->row = -1;
@@ -5510,7 +5542,7 @@ static void generate(Compiler *c, unsigned char **module, size_t *size) {
record_field_name[i][k] = intern(strings, &used, c->records[i].field_names[k]);
}
*size = 38 + 16u * c->states + 16u * c->input_count + 8u * fields + 8u * c->constants +
- 8u * c->record_count + 16u * c->record_fields + 16u * c->views + 12u * c->handler_count + 12u * c->instance_count +
+ 8u * c->record_count + 16u * c->record_fields + 20u * c->views + 12u * c->handler_count + 12u * c->instance_count +
4u * signature_items + 16u * c->code_count + used + c->constant_used;
unsigned char *p = *module = allocate(c, *size);
memcpy(p, "PUREMOD7", 8);
@@ -5588,11 +5620,13 @@ static void generate(Compiler *c, unsigned char **module, size_t *size) {
else if (r->field_kinds[k] == LIST) p = put64(p, (uint64_t)r->field_record[k]); /* the record of its items */
else p = put_double(p, r->field_number[k]);
}
- for (unsigned i = 0; i < c->views; ++i) {
+ for (unsigned i = 0; i < c->views; ++i) { /* 20 bytes: type, 0, table + 1, record + 1, list + 1, block */
p = put32(p, view_name[i]);
*p++ = (unsigned char)c->view_types[i];
- *p++ = c->view_byte[i];
+ *p++ = 0;
p = put16(p, (uint32_t)c->view_table[i]);
+ p = put16(p, (uint32_t)c->view_record[i]);
+ p = put16(p, (uint32_t)c->view_source[i]);
p = put32(p, c->view_code[i][0]);
p = put32(p, c->view_code[i][1]);
}
diff --git a/tests/check.py b/tests/check.py
index 444c444..05977e2 100644
--- a/tests/check.py
+++ b/tests/check.py
@@ -221,7 +221,7 @@ DIAGNOSTICS = [
'6:12', "a cursor of 'a' cannot point into 'b'"),
('state a: text\nstate at: cursor in a\ninput k = key KeyK\non k.down {\n at = 3\n}\n', '5:8',
'expected a cursor, not a number'),
- ('state a: text\nstate at: cursor in a\nview v = at\n', '3:10', 'a view is a number, a bool, an i64 or text, not a cursor'),
+ ('state a: text\nstate at: cursor in a\nview v = at\n', '3:10', 'a view is a number, a bool, an i64, text, bytes or a list, not a cursor'),
('state a: text\nstate at: cursor in a\nview v = "{at}"\n', '3:12', 'a string shows numbers, bools, i64s and text, not a cursor'),
('state x = 1\nstate t: text = "{x}"\n', '2:17', 'a text state starts from a plain string'),
('view v = "a\\qb"\n', '1:12', 'unknown escape'),
@@ -246,10 +246,10 @@ DIAGNOSTICS = [
(RECORD + 'state xs: list of T\nstate ys: list of T\nstate at: index of xs\nview v = ys[at].a\n', '7:13',
"an index of 'xs' cannot point into 'ys'"),
(RECORD + 'state xs: list of T\nstate at: index of xs\nview v = xs[at].b\n', '6:10', "record 'T' has no field 'b'"),
- (RECORD + 'state xs: list of T\nstate at: index of xs\nview v = xs[at]\n', '6:10', 'a view is a number, a bool, an i64 or text, not an index'),
- (RECORD + 'state xs: list of T\nview v = xs\n', '5:10', "'xs' is a list; read xs.count"),
+ (RECORD + 'state xs: list of T\nstate at: index of xs\nview v = xs[at]\n', '6:10', 'a view is a number, a bool, an i64, text, bytes or a list, not an index'),
+ (RECORD + 'state xs: list of T\nview v {\n return xs\n}\n', '6:10', "a view's body returns a list it built"),
(RECORD + 'state xs: list of T\nview v = xs.size\n', '5:10', "a list has count, first and last, not 'size'"),
- (RECORD + 'state xs: list of T\nstate at: index of xs\nview v = at\n', '6:10', 'a view is a number, a bool, an i64 or text, not an index'),
+ (RECORD + 'state xs: list of T\nstate at: index of xs\nview v = at\n', '6:10', 'a view is a number, a bool, an i64, text, bytes or a list, not an index'),
(RECORD + 'state xs: list of T\nstate at: index of xs\n' + ON_K + ' at = 3\n}\n', '8:8', 'expected an index, not a number'),
(RECORD + 'state xs: list of T\n' + ON_K + ' xs = 1\n}\n', '7:3', 'a list changes by append, insert, remove'),
(RECORD + 'record U {\n a = 0\n}\nstate xs: list of T\n' + ON_K + ' xs.append(U { a: 1 })\n}\n', '10:13',
@@ -284,7 +284,7 @@ DIAGNOSTICS = [
(LIST + 'view t = each x in xs {\n a = 1\n a = 2\n}\n', '8:3', "duplicate column 'a'"),
(LIST + 'view t = each x in xs {\n a = x.a\n}\nview v = x.a\n', '9:10', "unknown state 'x'"),
(LIST + 'view t = each at in xs {\n a = 1\n}\n', '6:15', "'at' is already declared as a list index"),
- (LIST + 'view t = each x in xs {\n c = x\n}\n', '7:7', 'a column is a number, a bool, an i64 or text, not an index'),
+ (LIST + 'view t = each x in xs {\n c = x\n}\n', '7:7', 'a column is a number, a bool, an i64, text, bytes or a list, not an index'),
('view each = 1\n', '1:6', "'each' is reserved"),
# fns
('fn f(x) = x\nview v = f(1, 2)\n', '2:10', 'f expects 1 argument'),
@@ -306,7 +306,7 @@ DIAGNOSTICS = [
(LIST + 'view v = sum(dt for x in xs)\n', '6:14', 'dt is not available in count, sum, max or min'),
(LIST + 'view v = sum(x.a for x xs)\n', '6:24', "expected 'in' and a list, as in sum(x in tasks)"),
(LIST + 'view w = each r in xs where r {\n n = r.a\n}\n', '6:29', 'expected a bool, not an index'),
- (LIST + 'view v = first(x in xs)\n', '6:10', 'a view is a number, a bool, an i64 or text, not an index'),
+ (LIST + 'view v = first(x in xs)\n', '6:10', 'a view is a number, a bool, an i64, text, bytes or a list, not an index'),
(LIST + 'view w = each r in xs where count(y in xs) > 0 {\n n = r.a\n}\n', '6:29',
"a table's where cannot hold a count, sum, max or min"),
# Keyed lists: a key field of the record, placed by add, found by key, never changed.
@@ -535,6 +535,11 @@ on forever.down {
'''
ACCEPTED = [
+ # Views of any value (Pure 027's 4e): a list as it is, filtered, a map with a list and a bytes column, a list a body
+ # built, bytes.
+ 'record Tag {\n v: i64\n}\nrecord T {\n s: text\n b: bytes\n tags: list of Tag\n}\nstate xs: list of T\n'
+ 'view all = xs\nview some = each x in xs where x.s.chars > 1\nview map = each x in xs {\n tags = x.tags\n raw = x.b\n}\n'
+ 'view made {\n let ys: list of Tag\n ys.append(Tag { v: i64(xs.count) })\n return ys\n}\nview blob = bytes("zz")\n',
# Fns that call themselves (Pure 027's 2c): a block, several values, records' values made, held, matched, given and
# appended, an item and a field's record passed as values, a walk of a list a value holds, a text's characters.
FUNCTIONS,
@@ -1693,6 +1698,20 @@ view tags {
}
return n
}
+view board = columns
+view busy = each col in columns where col.name.chars > 1
+view names = each col in columns {
+ label = col.name
+ cards = col.cards
+ raw = bytes(col.name)
+}
+view titles {
+ let ts: list of text
+ for col in columns {
+ for c in col.cards { ts.append(c.title) }
+ }
+ return ts
+}
"""
@@ -1759,7 +1778,8 @@ def documents_model(aup, pure_run, work, seeds, steps=160):
def stored_nested(aup, pure_run, work, seeds, steps=200):
"""Pure 027's stage 3: a stored list whose items hold lists whose items hold lists (columns of cards of tags), run
on a store restarting at random points (some runs saving in bursts) and in memory, against a model: every step's
- fault, states and a view reading the lists alike, a failed event leaving nothing."""
+ fault, states and a view reading the lists alike, a failed event leaving nothing. Its list views (027's 4e) too: the
+ list as it is, filtered, a map with a list and a bytes column, and a list a view's body builds."""
import copy
import shutil
import struct
@@ -1830,7 +1850,12 @@ def stored_nested(aup, pure_run, work, seeds, steps=200):
assert snapshot['fault'] == fault, (where, snapshot['fault'])
states = snapshot['states']
assert strict(states['columns'], want) and states['cards'] == count, (where, states['columns'], want)
- assert snapshot['views']['tags'] == sum(len(c['tags']) for col in want for c in col['cards']), where
+ views = snapshot['views']
+ assert views['tags'] == sum(len(c['tags']) for col in want for c in col['cards']), where
+ assert strict(views['board'], want) and strict(views['busy'], [col for col in want if len(col['name']) > 1]), where
+ assert strict(views['names'], [{'label': col['name'], 'cards': col['cards'], 'raw': col['name'].encode().hex()}
+ for col in want]), (where, views['names'])
+ assert views['titles'] == [{'value': c['title']} for col in want for c in col['cards']], (where, views['titles'])
assert all(states[name] == [] for name in states if name not in ('columns', 'cards')), where
compared += 1
return compared, restarts