Usage
sense fmt parses to the AST and re-emits it — it’s a printer over
exactly the grammar in the Grammar Reference, not a
separate tool with its own rules. Directory targets are searched
recursively for every .sns file, no naming restriction (unlike
sense test, which only matches test_*.sns/
*_test.sns in a directory).
The two guarantees
Never changes behavior
Verified against every real example in
examples/, not just synthetic
snippets: run a file, format it, run the formatted version, compare
output. Identical, every time.Idempotent
fmt(fmt(x)) == fmt(x). Formatting an already-formatted file is a
no-op — there’s exactly one canonical form, not a moving target.Minimal parens, not “no parens”
The AST already records the exact grouping the parser produced —sense fmt doesn’t need to re-derive meaning, only re-print it correctly. The
printer walks operator precedence (the same levels as the grammar: or <
and < equality < comparison < + - < * / % < unary < call/index/member)
and adds a paren only where the child’s own precedence is lower than what
the parent position requires:
Blank lines: preserved, not imposed
Wherever the source had at least one blank line immediately before a statement or comment, the output gets exactly one — multiple consecutive blanks collapse to one, and none stays none. There’s no rule that forces a blank line around every function or agent a user didn’t already separate; adding vertical space nobody asked for would cut against Sense’s own minimum-lines goal (see Non-Goals).Comments are re-attached, not dropped
Comments aren’t part of the AST — the lexer discards them before the parser ever sees a token stream (see Indentation-Sensitive Lexing) — sosense fmt captures them separately and re-attaches them by position:
# — text is never lost, but re-printed with normalized spacing
(# text, statement # text).
What gets canonicalized, not preserved verbatim
A few constructs have exactly one printed form regardless of how they were written, which is what idempotence requires — the input doesn’t need to round-trip, only the output does:What this doesn’t do (yet)
Line-width wrapping
Line-width wrapping
A very long line stays one very long line — no 80/100/120-column
reflow.
Alignment
Alignment
Consecutive
= signs, trailing comments across several lines, etc.
are never column-aligned.Configuration
Configuration
There is exactly one style. No settings file, no options beyond
--write/--check.Ignore lists
Ignore lists
Directory discovery has no
.gitignore-style exclusion mechanism —
every .sns file under the given directory is in scope.Continue
CLI reference
sense run/sense repl/sense test — the rest of the CLI.Grammar reference
The exact grammar
sense fmt prints.
