DSL parser, async DB worker, types, history, metadata, polish
Track 1 implementation plus polish round. Parser (chumsky): - Grammar-based DSL producing a typed Command AST. - create table X with pk [name:type[,name:type...]] supports arbitrary names, any user type, compound PKs natively. Bare form errors with a friendly hint pointing at `with pk`. - add column to table X: Name (type); drop table X. - Required clauses use keyword grammar; -- reserved for opt-in flags (ADR-0009). Custom Rich reasons preferred when surfacing chumsky errors so unknown-type messages list valid alternatives. Database (ADR-0010, ADR-0012): - rusqlite + STRICT tables + foreign_keys=ON. - Dedicated worker thread; mpsc Request inbox, oneshot replies. - Typed DbError with friendly_message() hook for H1. - Internal __rdbms_playground_columns metadata table preserves user-facing types across schema reads, atomically maintained alongside DDL via Connection transactions. list_tables hides it via the new __rdbms_ internal-table convention. Types (ADR-0005, ADR-0011): - All ten user-facing types: text, int, real, decimal, bool, date, datetime, blob, serial, shortid. - Type::fk_target_type() for FK-side column-type rule (Serial->Int, ShortId->Text, others identity) -- foundation for the FK iteration. App / Runtime / UI: - update() stays pure-sync; runtime dispatches DSL via spawned tasks, results post back as AppEvent::Dsl*. - Items panel renders live tables list; output panel shows the user-facing structure of the current table after each DDL. - In-memory command history (Up/Down, draft preservation, consecutive-duplicate dedup) -- I2 partial. - Mouse capture removed; terminal native text selection restored (toggle approach revisited when scroll/click features land). Docs: - ADRs 0009 (DSL syntax conventions), 0010 (DB worker), 0011 (FK type compat), 0012 (internal metadata table). - requirements.md progress notes; new V4 entry for the scrollable session-log + inline rich rendering + Markdown export direction. Tests: 103 passing (91 lib + 12 integration), 0 skipped. Clippy clean with nursery enabled.
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
# ADR-0009: DSL command syntax conventions
|
||||
|
||||
## Status
|
||||
|
||||
Accepted
|
||||
|
||||
## Context
|
||||
|
||||
As the DSL grows, its commands need consistent surface
|
||||
conventions. Without an explicit rule, every command would
|
||||
invent its own way of expressing optional vs. required parts,
|
||||
and the surface would drift toward an unreadable soup.
|
||||
|
||||
The decision is informed by experience from this iteration:
|
||||
when we initially proposed `create table X --pk` the most
|
||||
common form (a basic table with a primary key) required a `--`
|
||||
flag, which is cosmetically wrong — `--` reads as "extra
|
||||
option," and the most-used form should not look like one.
|
||||
|
||||
## Decision
|
||||
|
||||
The DSL surface follows three rules.
|
||||
|
||||
### 1. Required clauses use keyword grammar
|
||||
|
||||
Required parts of a command are written in plain words and
|
||||
read like English. Examples:
|
||||
|
||||
- `create table <Name> with pk <name>:<type>`
|
||||
- `add column to table <Name>: <Name> (<Type>)`
|
||||
- `drop table <Name>`
|
||||
|
||||
The `with` clause format is the canonical pattern for
|
||||
attaching required structural information to an entity-creating
|
||||
command, and is reusable: future iterations may add `with
|
||||
index`, `with check`, etc. Multiple `with` clauses on the same
|
||||
command are allowed in principle.
|
||||
|
||||
### 2. Optional flags use `--prefix`
|
||||
|
||||
Flags signal "I am asking for an extra capability or
|
||||
non-default behaviour." Examples planned for later iterations:
|
||||
|
||||
- `add 1:n relationship on Customers.Id=Orders.CustId --create-fk`
|
||||
(auto-creates the FK column instead of requiring it to exist)
|
||||
- *(future)* `--rename-on-clash`, `--no-strict`, etc.
|
||||
|
||||
A user reading "with pk id:serial" sees only what's needed; a
|
||||
user reading "...with pk id:serial --some-flag" sees that they
|
||||
have asked for something beyond default. The visual distinction
|
||||
is intentional.
|
||||
|
||||
### 3. One sigil only — `:` for the simple-mode advanced escape
|
||||
|
||||
Per ADR-0003, prefixing a single line with `:` in simple mode
|
||||
treats that one submission as if it were entered in advanced
|
||||
mode. This is the only sigil in the system. App-level commands,
|
||||
DSL commands, and SQL all use plain words.
|
||||
|
||||
### Lexical rules
|
||||
|
||||
- **Keywords are case-insensitive.** `CREATE TABLE Customers
|
||||
WITH PK email:TEXT` is equivalent to `create table Customers
|
||||
with pk email:text`.
|
||||
- **Identifiers are case-preserving.** `Customers` and
|
||||
`customers` are different identifiers if a backend would
|
||||
treat them as such (we follow SQLite's case-insensitive
|
||||
identifier rules at the schema level but preserve the user's
|
||||
written casing in display).
|
||||
- **Whitespace is liberal.** Any amount of horizontal whitespace
|
||||
between tokens is accepted, including around punctuation
|
||||
(`,`, `:`, `(`, `)`).
|
||||
|
||||
## Consequences
|
||||
|
||||
- The basic, most-common form of any command remains readable
|
||||
and free of cosmetic punctuation. New users see only words.
|
||||
- Optional adornments are visually distinct, encouraging
|
||||
discoverability of advanced features without forcing them on
|
||||
beginners.
|
||||
- New commands inherit a uniform shape: keyword-based clauses
|
||||
for required parts, `--` flags for opt-ins. Drift is bounded
|
||||
by this rule.
|
||||
- The grammar implementation (`chumsky`) maps cleanly onto this
|
||||
structure: a `with_clause` rule can be reused across
|
||||
commands, and flag parsing has a single representation when
|
||||
it lands.
|
||||
Reference in New Issue
Block a user