Insert grammar: Form C type-awareness via lookahead (ADR-0024 §Phase D)
Form C (`insert into T (vals)`) shared the `(` opener with Form A, so its paren was an untyped Repeated(Choice(literal, ident)) — values weren't type- or count-checked at parse time (handoff-12 §2.2). New Node::Lookahead variant: a factory that peeks the source. The insert first-paren factory inspects the first token — a value literal routes the contents through the typed column_value_list (Form B dispatch contract: per-non-auto-column typed slots); an identifier or empty paren routes to a Form A column-name list. So Form C now gets the same per-column typed slots, hints, and parse-time type/count checking Form B has. The explicit-Choice-branch split is impossible here (committed-choice semantics commit after `(` matches); lookahead is the only route, and DynamicSubgrammar factories couldn't see the source. Node::Lookahead is not memoized — its output depends on source — but it returns only a small node (a Repeated, or a thin DynamicSubgrammar wrapper that delegates to the memoized column_value_list). `insert into T (` now cleanly shows Form A column candidates instead of mixed Form-A/C suggestions. Form C matrix tests updated for the type-aware behaviour.
This commit is contained in:
+13
-15
@@ -8,9 +8,19 @@ Assessment {
|
||||
cursor: 23,
|
||||
state: IncompleteAtEof,
|
||||
hint: Some(
|
||||
Prose(
|
||||
"Type a value: number, 'text', true/false, null (dates as 'YYYY-MM-DD', datetimes as 'YYYY-MM-DDTHH:MM:SS')",
|
||||
),
|
||||
Candidates {
|
||||
items: [
|
||||
Candidate {
|
||||
text: "Name",
|
||||
kind: Identifier,
|
||||
},
|
||||
Candidate {
|
||||
text: "id",
|
||||
kind: Identifier,
|
||||
},
|
||||
],
|
||||
selected: None,
|
||||
},
|
||||
),
|
||||
completion: Some(
|
||||
Completion {
|
||||
@@ -20,18 +30,6 @@ Assessment {
|
||||
),
|
||||
partial_prefix: "",
|
||||
candidates: [
|
||||
Candidate {
|
||||
text: "null",
|
||||
kind: Keyword,
|
||||
},
|
||||
Candidate {
|
||||
text: "true",
|
||||
kind: Keyword,
|
||||
},
|
||||
Candidate {
|
||||
text: "false",
|
||||
kind: Keyword,
|
||||
},
|
||||
Candidate {
|
||||
text: "Name",
|
||||
kind: Identifier,
|
||||
|
||||
+17
-15
@@ -8,9 +8,23 @@ Assessment {
|
||||
cursor: 23,
|
||||
state: IncompleteAtEof,
|
||||
hint: Some(
|
||||
Prose(
|
||||
"Type a value: number, 'text', true/false, null (dates as 'YYYY-MM-DD', datetimes as 'YYYY-MM-DDTHH:MM:SS')",
|
||||
),
|
||||
Candidates {
|
||||
items: [
|
||||
Candidate {
|
||||
text: "Email",
|
||||
kind: Identifier,
|
||||
},
|
||||
Candidate {
|
||||
text: "Name",
|
||||
kind: Identifier,
|
||||
},
|
||||
Candidate {
|
||||
text: "id",
|
||||
kind: Identifier,
|
||||
},
|
||||
],
|
||||
selected: None,
|
||||
},
|
||||
),
|
||||
completion: Some(
|
||||
Completion {
|
||||
@@ -20,18 +34,6 @@ Assessment {
|
||||
),
|
||||
partial_prefix: "",
|
||||
candidates: [
|
||||
Candidate {
|
||||
text: "null",
|
||||
kind: Keyword,
|
||||
},
|
||||
Candidate {
|
||||
text: "true",
|
||||
kind: Keyword,
|
||||
},
|
||||
Candidate {
|
||||
text: "false",
|
||||
kind: Keyword,
|
||||
},
|
||||
Candidate {
|
||||
text: "Email",
|
||||
kind: Identifier,
|
||||
|
||||
+13
-15
@@ -8,9 +8,19 @@ Assessment {
|
||||
cursor: 19,
|
||||
state: IncompleteAtEof,
|
||||
hint: Some(
|
||||
Prose(
|
||||
"Type a value: number, 'text', true/false, null (dates as 'YYYY-MM-DD', datetimes as 'YYYY-MM-DDTHH:MM:SS')",
|
||||
),
|
||||
Candidates {
|
||||
items: [
|
||||
Candidate {
|
||||
text: "Code",
|
||||
kind: Identifier,
|
||||
},
|
||||
Candidate {
|
||||
text: "Title",
|
||||
kind: Identifier,
|
||||
},
|
||||
],
|
||||
selected: None,
|
||||
},
|
||||
),
|
||||
completion: Some(
|
||||
Completion {
|
||||
@@ -20,18 +30,6 @@ Assessment {
|
||||
),
|
||||
partial_prefix: "",
|
||||
candidates: [
|
||||
Candidate {
|
||||
text: "null",
|
||||
kind: Keyword,
|
||||
},
|
||||
Candidate {
|
||||
text: "true",
|
||||
kind: Keyword,
|
||||
},
|
||||
Candidate {
|
||||
text: "false",
|
||||
kind: Keyword,
|
||||
},
|
||||
Candidate {
|
||||
text: "Code",
|
||||
kind: Identifier,
|
||||
|
||||
+17
-15
@@ -8,9 +8,23 @@ Assessment {
|
||||
cursor: 20,
|
||||
state: IncompleteAtEof,
|
||||
hint: Some(
|
||||
Prose(
|
||||
"Type a value: number, 'text', true/false, null (dates as 'YYYY-MM-DD', datetimes as 'YYYY-MM-DDTHH:MM:SS')",
|
||||
),
|
||||
Candidates {
|
||||
items: [
|
||||
Candidate {
|
||||
text: "CustId",
|
||||
kind: Identifier,
|
||||
},
|
||||
Candidate {
|
||||
text: "OrderId",
|
||||
kind: Identifier,
|
||||
},
|
||||
Candidate {
|
||||
text: "Total",
|
||||
kind: Identifier,
|
||||
},
|
||||
],
|
||||
selected: None,
|
||||
},
|
||||
),
|
||||
completion: Some(
|
||||
Completion {
|
||||
@@ -20,18 +34,6 @@ Assessment {
|
||||
),
|
||||
partial_prefix: "",
|
||||
candidates: [
|
||||
Candidate {
|
||||
text: "null",
|
||||
kind: Keyword,
|
||||
},
|
||||
Candidate {
|
||||
text: "true",
|
||||
kind: Keyword,
|
||||
},
|
||||
Candidate {
|
||||
text: "false",
|
||||
kind: Keyword,
|
||||
},
|
||||
Candidate {
|
||||
text: "CustId",
|
||||
kind: Identifier,
|
||||
|
||||
+17
-15
@@ -8,9 +8,23 @@ Assessment {
|
||||
cursor: 27,
|
||||
state: IncompleteAtEof,
|
||||
hint: Some(
|
||||
Prose(
|
||||
"Type a value: number, 'text', true/false, null (dates as 'YYYY-MM-DD', datetimes as 'YYYY-MM-DDTHH:MM:SS')",
|
||||
),
|
||||
Candidates {
|
||||
items: [
|
||||
Candidate {
|
||||
text: "Email",
|
||||
kind: Identifier,
|
||||
},
|
||||
Candidate {
|
||||
text: "Name",
|
||||
kind: Identifier,
|
||||
},
|
||||
Candidate {
|
||||
text: "id",
|
||||
kind: Identifier,
|
||||
},
|
||||
],
|
||||
selected: None,
|
||||
},
|
||||
),
|
||||
completion: Some(
|
||||
Completion {
|
||||
@@ -20,18 +34,6 @@ Assessment {
|
||||
),
|
||||
partial_prefix: "",
|
||||
candidates: [
|
||||
Candidate {
|
||||
text: "null",
|
||||
kind: Keyword,
|
||||
},
|
||||
Candidate {
|
||||
text: "true",
|
||||
kind: Keyword,
|
||||
},
|
||||
Candidate {
|
||||
text: "false",
|
||||
kind: Keyword,
|
||||
},
|
||||
Candidate {
|
||||
text: "Email",
|
||||
kind: Identifier,
|
||||
|
||||
+6
-26
@@ -1,22 +1,22 @@
|
||||
---
|
||||
source: tests/typing_surface/insert_form_c.rs
|
||||
description: "input=\"insert into Customers (1, \" cursor=26"
|
||||
description: "input=\"insert into Customers ('Alice', \" cursor=32"
|
||||
expression: "& a"
|
||||
---
|
||||
Assessment {
|
||||
input: "insert into Customers (1, ",
|
||||
cursor: 26,
|
||||
input: "insert into Customers ('Alice', ",
|
||||
cursor: 32,
|
||||
state: IncompleteAtEof,
|
||||
hint: Some(
|
||||
Prose(
|
||||
"Type a value: number, 'text', true/false, null (dates as 'YYYY-MM-DD', datetimes as 'YYYY-MM-DDTHH:MM:SS')",
|
||||
"for `Email`: Type a quoted string (e.g. 'Alice') or null",
|
||||
),
|
||||
),
|
||||
completion: Some(
|
||||
Completion {
|
||||
replaced_range: (
|
||||
26,
|
||||
26,
|
||||
32,
|
||||
32,
|
||||
),
|
||||
partial_prefix: "",
|
||||
candidates: [
|
||||
@@ -24,26 +24,6 @@ Assessment {
|
||||
text: "null",
|
||||
kind: Keyword,
|
||||
},
|
||||
Candidate {
|
||||
text: "true",
|
||||
kind: Keyword,
|
||||
},
|
||||
Candidate {
|
||||
text: "false",
|
||||
kind: Keyword,
|
||||
},
|
||||
Candidate {
|
||||
text: "Email",
|
||||
kind: Identifier,
|
||||
},
|
||||
Candidate {
|
||||
text: "Name",
|
||||
kind: Identifier,
|
||||
},
|
||||
Candidate {
|
||||
text: "id",
|
||||
kind: Identifier,
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
|
||||
+3
-3
@@ -1,11 +1,11 @@
|
||||
---
|
||||
source: tests/typing_surface/insert_form_c.rs
|
||||
description: "input=\"insert into Customers (1, 'Alice'\" cursor=33"
|
||||
description: "input=\"insert into Customers ('Alice', 'a@b.c'\" cursor=39"
|
||||
expression: "& a"
|
||||
---
|
||||
Assessment {
|
||||
input: "insert into Customers (1, 'Alice'",
|
||||
cursor: 33,
|
||||
input: "insert into Customers ('Alice', 'a@b.c'",
|
||||
cursor: 39,
|
||||
state: IncompleteAtEof,
|
||||
hint: Some(
|
||||
Prose(
|
||||
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
---
|
||||
source: tests/typing_surface/insert_form_c.rs
|
||||
description: "input=\"insert into Customers (3.14, 'a@b.c')\" cursor=37"
|
||||
expression: "& a"
|
||||
---
|
||||
Assessment {
|
||||
input: "insert into Customers (3.14, 'a@b.c')",
|
||||
cursor: 37,
|
||||
state: DefiniteErrorAt(
|
||||
23,
|
||||
),
|
||||
hint: Some(
|
||||
Prose(
|
||||
"for `Name`: Type a quoted string (e.g. 'Alice') or null (`id` auto-generated — skipped here; list columns explicitly, e.g. `insert into T (...) values (...)`, to set it.)",
|
||||
),
|
||||
),
|
||||
completion: Some(
|
||||
Completion {
|
||||
replaced_range: (
|
||||
37,
|
||||
37,
|
||||
),
|
||||
partial_prefix: "",
|
||||
candidates: [
|
||||
Candidate {
|
||||
text: "null",
|
||||
kind: Keyword,
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
parse_result: Err(
|
||||
"Invalid(definite)",
|
||||
),
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
---
|
||||
source: tests/typing_surface/insert_form_c.rs
|
||||
description: "input=\"insert into Customers ('Alice', \" cursor=32"
|
||||
expression: "& a"
|
||||
---
|
||||
Assessment {
|
||||
input: "insert into Customers ('Alice', ",
|
||||
cursor: 32,
|
||||
state: IncompleteAtEof,
|
||||
hint: Some(
|
||||
Prose(
|
||||
"for `Email`: Type a quoted string (e.g. 'Alice') or null",
|
||||
),
|
||||
),
|
||||
completion: Some(
|
||||
Completion {
|
||||
replaced_range: (
|
||||
32,
|
||||
32,
|
||||
),
|
||||
partial_prefix: "",
|
||||
candidates: [
|
||||
Candidate {
|
||||
text: "null",
|
||||
kind: Keyword,
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
parse_result: Err(
|
||||
"Invalid(at_eof)",
|
||||
),
|
||||
}
|
||||
+5
-5
@@ -1,11 +1,11 @@
|
||||
---
|
||||
source: tests/typing_surface/insert_form_c.rs
|
||||
description: "input=\"insert into Customers (1, 'Alice', 'a@b.c')\" cursor=43"
|
||||
description: "input=\"insert into Customers ('Alice', 'a@b.c')\" cursor=40"
|
||||
expression: "& a"
|
||||
---
|
||||
Assessment {
|
||||
input: "insert into Customers (1, 'Alice', 'a@b.c')",
|
||||
cursor: 43,
|
||||
input: "insert into Customers ('Alice', 'a@b.c')",
|
||||
cursor: 40,
|
||||
state: Valid,
|
||||
hint: Some(
|
||||
Candidates {
|
||||
@@ -21,8 +21,8 @@ Assessment {
|
||||
completion: Some(
|
||||
Completion {
|
||||
replaced_range: (
|
||||
43,
|
||||
43,
|
||||
40,
|
||||
40,
|
||||
),
|
||||
partial_prefix: "",
|
||||
candidates: [
|
||||
-39
@@ -1,39 +0,0 @@
|
||||
---
|
||||
source: tests/typing_surface/insert_form_c.rs
|
||||
description: "input=\"insert into Customers (null, 'Alice', 'a@b.c')\" cursor=46"
|
||||
expression: "& a"
|
||||
---
|
||||
Assessment {
|
||||
input: "insert into Customers (null, 'Alice', 'a@b.c')",
|
||||
cursor: 46,
|
||||
state: Valid,
|
||||
hint: Some(
|
||||
Candidates {
|
||||
items: [
|
||||
Candidate {
|
||||
text: "values",
|
||||
kind: Keyword,
|
||||
},
|
||||
],
|
||||
selected: None,
|
||||
},
|
||||
),
|
||||
completion: Some(
|
||||
Completion {
|
||||
replaced_range: (
|
||||
46,
|
||||
46,
|
||||
),
|
||||
partial_prefix: "",
|
||||
candidates: [
|
||||
Candidate {
|
||||
text: "values",
|
||||
kind: Keyword,
|
||||
},
|
||||
],
|
||||
},
|
||||
),
|
||||
parse_result: Ok(
|
||||
"Insert",
|
||||
),
|
||||
}
|
||||
+5
-5
@@ -1,11 +1,11 @@
|
||||
---
|
||||
source: tests/typing_surface/insert_form_c.rs
|
||||
description: "input=\"insert into Customers (3.14, 'Alice', 'a@b.c')\" cursor=46"
|
||||
description: "input=\"insert into Customers (null, 'a@b.c')\" cursor=37"
|
||||
expression: "& a"
|
||||
---
|
||||
Assessment {
|
||||
input: "insert into Customers (3.14, 'Alice', 'a@b.c')",
|
||||
cursor: 46,
|
||||
input: "insert into Customers (null, 'a@b.c')",
|
||||
cursor: 37,
|
||||
state: Valid,
|
||||
hint: Some(
|
||||
Candidates {
|
||||
@@ -21,8 +21,8 @@ Assessment {
|
||||
completion: Some(
|
||||
Completion {
|
||||
replaced_range: (
|
||||
46,
|
||||
46,
|
||||
37,
|
||||
37,
|
||||
),
|
||||
partial_prefix: "",
|
||||
candidates: [
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
---
|
||||
source: tests/typing_surface/insert_form_c.rs
|
||||
description: "input=\"insert into Customers ('Alice', 'a@b.c', 'extra')\" cursor=49"
|
||||
expression: "& a"
|
||||
---
|
||||
Assessment {
|
||||
input: "insert into Customers ('Alice', 'a@b.c', 'extra')",
|
||||
cursor: 49,
|
||||
state: DefiniteErrorAt(
|
||||
39,
|
||||
),
|
||||
hint: Some(
|
||||
Prose(
|
||||
"Submit with Enter",
|
||||
),
|
||||
),
|
||||
completion: None,
|
||||
parse_result: Err(
|
||||
"Invalid(definite)",
|
||||
),
|
||||
}
|
||||
Reference in New Issue
Block a user