diff options
| -rw-r--r-- | compiler/compile.c | 21 | ||||
| -rw-r--r-- | docs/design/001-language-v0.md | 1 | ||||
| -rw-r--r-- | tests/check.py | 7 |
3 files changed, 25 insertions, 4 deletions
diff --git a/compiler/compile.c b/compiler/compile.c index 86259dd..aa073f1 100644 --- a/compiler/compile.c +++ b/compiler/compile.c @@ -372,6 +372,19 @@ static void end(Compiler *c) { separators(c); } +/* After a declared field (a message's, a group's, a record's): a comma, as + * between a value's fields, or the end of a line or a semicolon. */ +static void end_field(Compiler *c) { + if (!is(c, ",")) { + if (c->token.kind != T_NL && !is(c, ";") && !is(c, "}")) + fail_at(c, c->token.line, c->token.column, "expected a comma, newline or semicolon"); + separators(c); + return; + } + take(c); + separators(c); +} + static bool reserved_name(const char *name) { for (size_t i = 0; i < sizeof(reserved) / sizeof(*reserved); ++i) if (!strcmp(name, reserved[i])) return true; @@ -1165,7 +1178,7 @@ static void message_fields(Compiler *c, Input *in, const char *group) { expect(c, ":", "':'"); if (is(c, "{")) { message_fields(c, in, path); - end(c); + end_field(c); continue; } if (c->field_count == FIELDS) fail_at(c, name.line, name.column, "at most %d message fields are allowed", FIELDS); @@ -1189,7 +1202,7 @@ static void message_fields(Compiler *c, Input *in, const char *group) { memcpy(in->field_names[in->fields], path, NAME); in->field_types[in->fields++] = field_type; ++c->field_count; - end(c); + end_field(c); } if (*group && in->fields == first) fail_at(c, c->token.line, c->token.column, "the group '%s' has no fields", group); take(c); @@ -1214,7 +1227,7 @@ static Expr *plain_string(Compiler *c, const char *what) { return value->args[0]; } -/* record Name { field = number; field: text = "..." } */ +/* record Name { field = number, field: text = "..." } */ static void record_declaration(Compiler *c) { Token name = expect_name(c, "record name"); declare(c, &name); @@ -1252,7 +1265,7 @@ static void record_declaration(Compiler *c) { expect(c, "=", "'=' and a number, or ': text'"); r->field_number[index] = signed_number(c, "field default"); } - end(c); + end_field(c); } take(c); if (!r->fields) fail_at(c, name.line, name.column, "a record needs at least one field"); diff --git a/docs/design/001-language-v0.md b/docs/design/001-language-v0.md index 305838e..df67594 100644 --- a/docs/design/001-language-v0.md +++ b/docs/design/001-language-v0.md @@ -113,6 +113,7 @@ on edit { - `on 메시지 { }`는 메시지가 도착할 때 한 번 실행된다. 필드는 `메시지.필드`, 키는 `key`로만 쓴다. - 필드 타입은 `u32`, `number`(유한한 수), `bytes`, `text`(UTF-8)이고 가변 필드(bytes, text)는 몇 개든 어디에나 둘 수 있다. + 필드는 쉼표나 줄바꿈(또는 세미콜론)으로 나눈다. 메시지와 그룹과 레코드의 선언에서도, 값(`Kept { id: 1, t: "a" }`)에서와 같다. `input add = message { title: text, points: number }`. HTTP 호스트(`pure-serve`)는 JSON 본문의 객체를 이 필드들로 옮긴다. 도착한 메시지의 text가 UTF-8이 아니거나 수가 유한하지 않으면 그 이벤트는 실패한다. - 필드를 그룹으로 묶을 수 있다: `to: { city: text, zip: u32 }`. 그룹 안의 필드는 `메시지.그룹.필드`(`ship.to.city`)로 diff --git a/tests/check.py b/tests/check.py index 211d508..4624eb6 100644 --- a/tests/check.py +++ b/tests/check.py @@ -81,6 +81,7 @@ DIAGNOSTICS = [ ('state a = 1e400\n', '1:11', 'number must be finite'), ('view v = 1 +\n', '1:13', 'expected a number, state, or expression'), ('state a = 0\non frame {\n a = 1 a\n}\n', '3:9', 'expected newline or semicolon'), + ('state a = 0\ninput m = message { x: u32 y: number }\n', '2:28', 'expected a comma, newline or semicolon'), ('state a = 0\non frame {\n if a {\n a = 1\n } els {\n }\n}\n', '5:5', 'expected newline or semicolon'), # Each fn doubles what it inlines: f14 is past Pure's 65,535 instructions. ('\n'.join(['fn f0(x) = x + 1'] + [f'fn f{i}(x) = f{i - 1}(f{i - 1}(x))' for i in range(1, 15)] + ['view v = f14(1)']) + '\n', @@ -296,6 +297,12 @@ DIAGNOSTICS = [ ] ACCEPTED = [ + # Declared fields apart by commas, as a value's are (a message's, a group's, a record's, one after the last too). + 'record P { x = 0, y = 0, name: text }\nstate ps: list of P by x\n' + 'input add = message { x: u32, y: number, name: text }\n' + 'input ship = message {\n to: { city: text, zip: u32 },\n note: text,\n}\n' + 'on add {\n ps.add(P { x: add.x, y: add.y, name: add.name })\n}\n' + 'on ship {\n ps.add(P { x: ship.to.zip, name: ship.to.city })\n}\n', # distinct, and tests bounding a keyed list's key: a range walk (Pure reads only the items in bounds) and a range view. 'record Line {\n id = 0\n item = 0\n qty = 0\n done = 0\n}\nstate lines: list of Line by id\n' 'input deliver = message {\n order: u32\n}\non deliver {\n let start = deliver.order * 16\n' |
