diff options
| -rw-r--r-- | docs/design/002-dsl-direction.md | 4 | ||||
| -rw-r--r-- | src/compile.c | 24 | ||||
| -rw-r--r-- | tests/check.py | 17 |
3 files changed, 38 insertions, 7 deletions
diff --git a/docs/design/002-dsl-direction.md b/docs/design/002-dsl-direction.md index 9d695f9..7924083 100644 --- a/docs/design/002-dsl-direction.md +++ b/docs/design/002-dsl-direction.md @@ -147,6 +147,10 @@ view board = each task in tasks { # 항목마다 한 행 - **표 보기.** `view 이름 = each 행 in 리스트 { 열 = 값 ... }`는 리스트 항목마다 행 하나를 낸다. 열은 수나 text이고 행 이름(인덱스)과 다른 상태를 읽는다. 호스트는 보여 줄 행의 칸만 Pure에 물어 계산한다. 목록을 화면 모델로 복사하거나 보이는 범위를 앱이 계산하는 코드(가상 스크롤)가 필요 없다. 열 이름은 표 안에서만 유일하면 된다. +- **걸러진 표.** `view open_list = each t in tasks where !t.done { label = t.title }`는 조건이 참인 항목마다 행 + 하나를 낸다(리스트 순서 그대로). 열의 행 이름은 여전히 항목이라 `t.position`은 리스트 안의 위치다. Pure가 상태가 + 바뀐 뒤 한 번 걸러 행과 항목의 대응을 두므로, 열린 일·끝난 일처럼 같은 리스트를 여러 목록으로 보여도 상태를 + 둘로 나눠 맞출 필요가 없다. 조건 안에는 집계를 둘 수 없다. - 목록·표·포커스·톤 같은 인터페이스 어휘는 여전히 aup에 없다. Vega가 이 투영 위에서 제공한다. 검증: 표 열과 조건 보기를 더한 할 일 목록이 참조 모델과 4,800단계에서 같고, 고르지 않은 가지가 계산되지 않음을 확인한다. diff --git a/src/compile.c b/src/compile.c index 0c90e18..2eac523 100644 --- a/src/compile.c +++ b/src/compile.c @@ -134,6 +134,8 @@ typedef struct { /* Views in order; a table (no value) is followed by its columns. */ char view_names[VIEWS][NAME], view_row[VIEWS][NAME], view_list[VIEWS][NAME]; Expr *view_value[VIEWS]; + Expr *view_filter[VIEWS]; /* a table's where condition, or NULL */ + unsigned char view_byte[VIEWS]; /* a filtered table's list + 1 */ int view_table[VIEWS]; /* a column's table + 1, or 0 */ int view_line[VIEWS][2], view_column[VIEWS][2]; /* a table's row name and list name */ unsigned views; @@ -144,7 +146,7 @@ typedef struct { int row; /* the table whose column is being generated, or -1 */ /* The fold whose block is being generated: its item's name and list, and * the fn scope it began in (whose parameters its block cannot read). */ - bool folding; + bool folding, filtering; char fold_name[NAME]; int fold_list; Scope *fold_scope; @@ -1097,7 +1099,8 @@ static void state_declaration(Compiler *c) { ++c->states; } -/* view name = each row in list { column = value ... }: one row per item. */ +/* view name = each row in list [where condition] { column = value ... }: one + * row per item, or per item the condition keeps. */ static void table_declaration(Compiler *c, unsigned table) { take(c); /* each */ Token row = expect_name(c, "a name for each item"); @@ -1110,6 +1113,10 @@ static void table_declaration(Compiler *c, unsigned table) { c->view_column[table][0] = row.column; c->view_line[table][1] = list.line; c->view_column[table][1] = list.column; + if (word(c, "where")) { /* a row only for the items it keeps */ + take(c); + c->view_filter[table] = expression(c, 0, false); + } expect(c, "{", "'{' on the line of the table"); separators(c); while (!is(c, "}")) { @@ -1650,6 +1657,7 @@ static Kind value(Compiler *c, const Expr *e, bool handler, int height) { int list = state_index(c, e->member); if (list < 0 || c->state_types[list] != STATE_LIST) fail_at(c, e->line, e->column, "'%s' is not a list", e->member); if (c->folding) fail_at(c, e->line, e->column, "a count, sum, max or min cannot hold another"); + if (c->filtering) fail_at(c, e->line, e->column, "a table's where cannot hold a count, sum, max or min"); unsigned at = c->code_count, joins = c->joins; emit(c, e->line, e->column, FOLD, 0, 0); c->folding = true; @@ -1920,6 +1928,16 @@ static void generate(Compiler *c, unsigned char **module, size_t *size) { c->view_types[i] = 2; c->view_code[i][0] = (uint32_t)list; 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; + c->filtering = true; + c->view_code[i][0] = c->code_count; + number(c, c->view_filter[i], false, 0); + 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; } c->row = c->view_table[i] - 1; @@ -2021,7 +2039,7 @@ static void generate(Compiler *c, unsigned char **module, size_t *size) { for (unsigned i = 0; i < c->views; ++i) { p = put32(p, view_name[i]); *p++ = (unsigned char)c->view_types[i]; - *p++ = 0; + *p++ = c->view_byte[i]; p = put16(p, (uint32_t)c->view_table[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 ef8549f..3716ff6 100644 --- a/tests/check.py +++ b/tests/check.py @@ -176,6 +176,9 @@ DIAGNOSTICS = [ "'n' is a parameter outside this count, sum, max or min"), (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 number, 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"), ] ACCEPTED = [ @@ -989,13 +992,18 @@ view done_points = sum( view longest = max(t.title.chars for t in tasks) view fewest_open = min(t.points for t in tasks where !t.done) view summary = "{count(t in tasks where !t.done)} open of {tasks.count}" +view open_list = each t in tasks where !t.done { + label = t.title + item = t.position +} """ def folds(aup, pure_run, work, seeds, steps=300): - """count, sum, max and min over a list against a Python model after - random adds, toggles, moves and removals (an empty max or min has no - value).""" + """count, sum, max and min over a list, and a table filtered by a where, + against a Python model after random adds, toggles, moves and removals (an + empty max or min has no value; a filtered table's rows keep the list's + order and its columns read the item).""" source = work / 'folds.aup' source.write_text(FOLDS_SOURCE) compile_module(aup, source, work / 'folds.pmod') @@ -1024,7 +1032,8 @@ def folds(aup, pure_run, work, seeds, steps=300): 'done_points': sum(t['points'] for t in tasks if t['done']), 'longest': max((len(t['title']) for t in tasks), default=None), 'fewest_open': min((t['points'] for t in open_tasks), default=None), - 'summary': f'{len(open_tasks)} open of {len(tasks)}'}) + 'summary': f'{len(open_tasks)} open of {len(tasks)}', + 'open_list': [{'label': t['title'], 'item': i} for i, t in enumerate(tasks) if not t['done']]}) # Toggling or removing in an empty list fails; pure-run then exits 1 but shows every event. lines = run(pure_run, work / 'folds.pmod', stdin='\n'.join(commands) + '\n', check=False).stdout.splitlines() shown = [json.loads(line)['views'] for line in lines] |
