`create table … with pk` parsed column types as `name:type`,
while `add column` uses `name(type)`. Unify on the parens
form so column-type syntax is consistent across the DSL:
create table T with pk id(serial), name(text)
Only `COL_SPEC` changes (`:` → `( … )`); `build_create_table`
reads columns by role, so it is unaffected. The `:` that
separates table from column in `add column` / `drop column`
is unchanged. Sweeps the test suite, the typing-surface
matrix (two `after_colon` cells renamed to `after_paren`,
4 snapshots regenerated), the friendly catalog's usage
templates, ADR-0009's example, and requirements.md.
1039 passing / 0 failing / 1 ignored; clippy clean.
3.1 KiB
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:TEXTis equivalent tocreate table Customers with pk email:text. - Identifiers are case-preserving.
Customersandcustomersare 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: awith_clauserule can be reused across commands, and flag parsing has a single representation when it lands.