Skip to content

Commit c037f53

Browse files
authored
feat: convert script handling to pocos (#162)
2 parents e532243 + ba54dd1 commit c037f53

455 files changed

Lines changed: 27992 additions & 10994 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 186 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,186 @@
1+
# Advanced FileMaker Scripting Syntax
2+
3+
Status: authoritative reference for all script-step POCOs.
4+
5+
## Purpose
6+
7+
SharpFM renders FileMaker script XML as editable display text. Some XML
8+
state doesn't naturally appear in FileMaker Pro's own display (object
9+
IDs, flags, embedded newlines, etc.). To round-trip that state faithfully
10+
through display-text editing, SharpFM extends FM Pro's display grammar
11+
with a small set of named conventions — collectively, **Advanced
12+
FileMaker Scripting Syntax**.
13+
14+
The extensions are safe because **FileMaker Pro never consumes SharpFM's
15+
display text**. FM Pro reads only the binary `Mac-XMSS` clipboard
16+
payload; `POCO.ToXml()` always emits pure FM Pro XML regardless of
17+
whatever extensions are present in the display. The display text is a
18+
SharpFM-internal surface for user editing, and our display parser only
19+
ever reads our own extended output.
20+
21+
## Core invariant
22+
23+
> `ToDisplayLine()` and `FromDisplayParams()` together are lossless for
24+
> every piece of state the POCO carries. XML state that is dropped is
25+
> dropped because it carries no information, not because the display
26+
> can't express it.
27+
28+
Every POCO's docstring contains a **zero-loss audit** (see below) that
29+
enumerates XML state explicitly and marks each item as either rendered
30+
by FM Pro natively, covered by an advanced-syntax extension, or
31+
intentionally dropped with a written rationale.
32+
33+
## The three extension forms
34+
35+
Pick the form that matches the shape of the hidden state:
36+
37+
| Shape of state | Syntax | Example | Used for |
38+
|---|---|---|---|
39+
| Id annotation on a named reference | `(#id)` suffix after the name | `"Find Customer" (#7)` | Script, Layout, TableOccurrence, Field id round-trip |
40+
| Boolean / enum flag already in FM Pro's grammar | word token inline, matching FM Pro's wording | `Exit after last: On` | Flags FM Pro itself renders; we mirror its wording |
41+
| Bulk invisible state (multi-slot / structured) | trailing `; Kind: [...]` block, word-token values | `; Buttons: ["OK" commit; "Cancel" nocommit]` | Button configurations, input-field metadata, any future bulk state |
42+
43+
### Form 1 — `(#id)` suffix
44+
45+
Appended to a quoted or `Table::Field` name:
46+
47+
- `FieldRef` emits `People::FirstName (#7)` when an id is known; plain
48+
`People::FirstName` otherwise.
49+
- `PerformScriptStep` emits `"Find Customer" (#42)`.
50+
- `GoToLayoutStep` emits `"Invoices Detail" (#11)`.
51+
52+
Omitted when the id is zero or unknown — `(#0)` would be visual noise
53+
for unresolved references.
54+
55+
### Form 2 — inline word tokens
56+
57+
Parsed at a specific named prefix, matching FM Pro's own rendering of
58+
the same flag:
59+
60+
- `Exit after last: On` on found-set iterators.
61+
- `With dialog: Off` on steps that can suppress their confirmation UI.
62+
- `Restore: On|Off`**reserved**. Not currently emitted by any POCO;
63+
see "What to drop vs. surface" below for the rationale.
64+
65+
### Form 3 — trailing `; Kind: [...]` blocks
66+
67+
Bulk or structured state that doesn't reduce to a single flag:
68+
69+
- `; Buttons: ["OK" commit; "Cancel" nocommit; "" nocommit]` — used by
70+
Show Custom Dialog for its button configuration.
71+
- Future bulk state (Input Field specs, etc.) takes this form.
72+
73+
## Parsing precedence
74+
75+
Named-prefix inline tokens (Form 2) are parsed before trailing
76+
`Kind: [...]` blocks (Form 3). A display line like
77+
78+
```
79+
Go to Record/Request/Page [ Next ; Exit after last: On ; Buttons: [...] ]
80+
```
81+
82+
is tokenized as:
83+
1. Positional `Next` (Form 2 equivalent — fixed-position enum).
84+
2. Named inline `Exit after last: On` (Form 2).
85+
3. Named block `Buttons: [...]` (Form 3).
86+
87+
Form 1 (`(#id)`) is applied inside each name-bearing token — e.g. inside
88+
the bracketed name-and-id of a named ref — and does not collide with
89+
Form 2 or Form 3 separators.
90+
91+
## Zero-loss audit requirement
92+
93+
Every POCO author must complete this audit in the class XML doc
94+
comment. Template:
95+
96+
```
97+
/// <summary>
98+
/// Zero-loss audit for StepName:
99+
/// <list type="bullet">
100+
/// <item>&lt;Step&gt; attributes (enable/id/name) — round-tripped.</item>
101+
/// <item>&lt;Calculation&gt; CDATA — round-tripped via Calculation.</item>
102+
/// <item>&lt;SomeElement state="..."/&gt; — Form 2 token "Some: On|Off".</item>
103+
/// <item>&lt;Dropped/&gt; — intentionally dropped; rationale: ...</item>
104+
/// </list>
105+
/// </summary>
106+
```
107+
108+
Items fall into exactly one of these buckets:
109+
110+
1. **Rendered natively by FM Pro** — FM Pro's display grammar already
111+
covers it; SharpFM mirrors the wording.
112+
2. **Covered by an extension form** — one of the three above.
113+
3. **Intentionally dropped** — rationale required. See next section for
114+
how to judge.
115+
116+
Omitting the audit is a review blocker.
117+
118+
## What to drop vs. surface
119+
120+
Hidden state is surfaced only when a user could meaningfully change it.
121+
Some state is structurally present in XML but semantically fixed — FM
122+
Pro never alters it, never emits it in clipboard output, and no user
123+
workflow produces a different value. Round-tripping such state adds
124+
visual noise for zero information.
125+
126+
### Canonical drop: `<Restore state="False"/>` on `If`
127+
128+
Upstream `agentic-fm` snippets include the element; FM Pro's own
129+
clipboard output never does; no FM Pro user interaction produces
130+
`state="True"`. `IfStep` drops it on both read and write. The audit
131+
entry documents the drop:
132+
133+
> &lt;Restore state="False"/&gt; — intentionally dropped. FM Pro never
134+
> changes the value and never emits the element in clipboard output; it
135+
> carries no information worth round-tripping.
136+
137+
### Canonical surface: field `id` via `(#id)` suffix
138+
139+
`<Field table="T" id="12" name="F"/>` is a real identity — the id
140+
selects which field is referenced, and two fields named `F` in different
141+
tables are not interchangeable. Dropping the id would change semantics.
142+
`FieldRef` always emits `(#12)` when an id is available.
143+
144+
### The heuristic
145+
146+
- If two valid FM Pro script states would be visually identical under
147+
the display grammar without the extension, **surface** the state.
148+
- If the state has a fixed value that no user can change, **drop** it.
149+
- If you're not sure which, **surface**. Reversing a surface → drop
150+
later is non-breaking; reversing a drop → surface may break tests
151+
users wrote against the earlier display.
152+
153+
## Adjacent convention: `//` disabled-step prefix
154+
155+
Disabled steps are prefixed with `//` in display text:
156+
157+
```
158+
// Set Error Capture [ On ]
159+
```
160+
161+
Parsing strips the `//` and sets `ScriptStep.Enabled = false`. This is
162+
a document-level convention (applied to any step line) rather than a
163+
per-step extension. Covered here for completeness.
164+
165+
## Implementation touch points
166+
167+
- `FieldRef.ToDisplayString` / `FieldRef.FromDisplayToken`
168+
(`src/SharpFM.Model/Scripting/Values/FieldRef.cs`) — Form 1 reference
169+
implementation.
170+
- `CommentStep.ReturnGlyph`
171+
(`src/SharpFM.Model/Scripting/Steps/CommentStep.cs`) — the ``
172+
(U+23CE) glyph for single-line rendering of multi-line comment text.
173+
An idiom adjacent to the three forms but specific to Comment.
174+
- `ScriptLineParser.ParseLine`
175+
(`src/SharpFM.Model/Scripting/ScriptLineParser.cs`) — disabled-step
176+
prefix and bracket tokenization.
177+
- `PerformScriptStep.FromDisplayParams`,
178+
`GoToLayoutStep.FromDisplayParams` — Form 1 regex parsers for named
179+
refs with `(#id)` suffixes.
180+
181+
## Change log
182+
183+
- **2026-04** — Extracted from `docs/step-definitions.md:44-69` into
184+
its own document as part of the POCO big-bang migration. Rationale
185+
section ("what to drop vs. surface") added with `Restore` on `If` as
186+
the canonical drop example.

0 commit comments

Comments
 (0)