aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--compiler/compile.c21
-rw-r--r--docs/design/001-language-v0.md1
-rw-r--r--tests/check.py7
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'