@malloydata/malloy 0.0.384 → 0.0.386

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/dist/api/foundation/config_resolve.js +12 -20
  2. package/dist/internal.d.ts +2 -0
  3. package/dist/internal.js +3 -1
  4. package/dist/lang/prettify/binary-chain.d.ts +3 -0
  5. package/dist/lang/prettify/binary-chain.js +65 -0
  6. package/dist/lang/prettify/block-body.d.ts +4 -0
  7. package/dist/lang/prettify/block-body.js +74 -0
  8. package/dist/lang/prettify/error-listener.d.ts +6 -0
  9. package/dist/lang/prettify/error-listener.js +19 -0
  10. package/dist/lang/prettify/field-properties.d.ts +3 -0
  11. package/dist/lang/prettify/field-properties.js +57 -0
  12. package/dist/lang/prettify/formatter.d.ts +17 -0
  13. package/dist/lang/prettify/formatter.js +145 -0
  14. package/dist/lang/prettify/index.d.ts +19 -0
  15. package/dist/lang/prettify/index.js +139 -0
  16. package/dist/lang/prettify/inline-renderer.d.ts +3 -0
  17. package/dist/lang/prettify/inline-renderer.js +101 -0
  18. package/dist/lang/prettify/leaf.d.ts +9 -0
  19. package/dist/lang/prettify/leaf.js +313 -0
  20. package/dist/lang/prettify/out.d.ts +12 -0
  21. package/dist/lang/prettify/out.js +74 -0
  22. package/dist/lang/prettify/pick-case.d.ts +5 -0
  23. package/dist/lang/prettify/pick-case.js +222 -0
  24. package/dist/lang/prettify/rules.d.ts +13 -0
  25. package/dist/lang/prettify/rules.js +103 -0
  26. package/dist/lang/prettify/sections.d.ts +4 -0
  27. package/dist/lang/prettify/sections.js +261 -0
  28. package/dist/lang/prettify/tokens.d.ts +13 -0
  29. package/dist/lang/prettify/tokens.js +160 -0
  30. package/dist/lang/prettify/types.d.ts +9 -0
  31. package/dist/lang/prettify/types.js +7 -0
  32. package/dist/lang/run-malloy-parser.d.ts +23 -0
  33. package/dist/lang/run-malloy-parser.js +32 -8
  34. package/dist/version.d.ts +1 -1
  35. package/dist/version.js +1 -1
  36. package/package.json +4 -4
@@ -77,32 +77,24 @@ function extractCompiledConnections(node) {
77
77
  return out;
78
78
  }
79
79
  /**
80
- * Add a bare `{is: typeName}` compiled entry for each registered connection
81
- * type not already represented in `compiledConnections`. Only runs when the
82
- * POJO sets `includeDefaultConnections: true`. Property values (including
83
- * reference-shaped defaults like DuckDB's `{config: 'rootDirectory'}`) are
84
- * *not* filled in here that is the job of the async lookup resolver.
80
+ * For each registered connection type T, add a bare `{is: T}` compiled
81
+ * entry named T unless one already exists under that name. Only runs when
82
+ * the POJO sets `includeDefaultConnections: true`. Property values
83
+ * (including reference-shaped defaults like DuckDB's
84
+ * `{config: 'rootDirectory'}`) are filled in later by the async lookup
85
+ * resolver, not here.
85
86
  *
86
- * Skip rules:
87
- * - Type already in use: some existing entry has `is: typeName`.
88
- * - Name already taken: some existing entry is *named* `typeName`, even
89
- * if its `is` points elsewhere. This protects a user who writes
90
- * `{duckdb: {is: 'postgres', ...}}` naming an entry after a type but
91
- * pointing at a different backend from being clobbered.
87
+ * The skip is purely name-based: the `is` of a user entry is irrelevant.
88
+ * `{duckdb: {is: 'postgres'}}` shadows the duckdb phantom (slot taken);
89
+ * `{dankdb: {is: 'duckdb'}}` does not (slot `duckdb` is still free, and
90
+ * both end up reachable). This name-only rule is the contract hosts rely
91
+ * on e.g. the VS Code connections sidebar advertises defaults by name,
92
+ * and the runtime must resolve them under those same names.
92
93
  *
93
94
  * Mutates `compiledConnections` in place.
94
95
  */
95
96
  function fabricateMissingConnections(compiledConnections) {
96
- const presentTypes = new Set();
97
- for (const entry of Object.values(compiledConnections)) {
98
- const isNode = entry.entries['is'];
99
- if ((isNode === null || isNode === void 0 ? void 0 : isNode.kind) === 'value' && typeof isNode.value === 'string') {
100
- presentTypes.add(isNode.value);
101
- }
102
- }
103
97
  for (const typeName of (0, registry_1.getRegisteredConnectionTypes)()) {
104
- if (presentTypes.has(typeName))
105
- continue;
106
98
  if (compiledConnections[typeName])
107
99
  continue;
108
100
  compiledConnections[typeName] = {
@@ -1,3 +1,5 @@
1
1
  export { mkArrayDef, mkArrayTypeDef, mkFieldDef, mkModelDef, mkQuerySourceDef, mkSQLSourceDef, mkTableSourceDef, pathToKey, } from './model';
2
2
  export { TinyParseError, TinyParser } from './dialect/tiny_parser';
3
3
  export type { TinyToken } from './dialect/tiny_parser';
4
+ export { prettify } from './lang/prettify';
5
+ export type { PrettifyResult, PrettifyError } from './lang/prettify';
package/dist/internal.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * SPDX-License-Identifier: MIT
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.TinyParser = exports.TinyParseError = exports.pathToKey = exports.mkTableSourceDef = exports.mkSQLSourceDef = exports.mkQuerySourceDef = exports.mkModelDef = exports.mkFieldDef = exports.mkArrayTypeDef = exports.mkArrayDef = void 0;
7
+ exports.prettify = exports.TinyParser = exports.TinyParseError = exports.pathToKey = exports.mkTableSourceDef = exports.mkSQLSourceDef = exports.mkQuerySourceDef = exports.mkModelDef = exports.mkFieldDef = exports.mkArrayTypeDef = exports.mkArrayDef = void 0;
8
8
  // Unstable implementation-facing exports for Malloy packages, tests, and
9
9
  // advanced integrations. Do not treat this surface as public API.
10
10
  var model_1 = require("./model");
@@ -19,4 +19,6 @@ Object.defineProperty(exports, "pathToKey", { enumerable: true, get: function ()
19
19
  var tiny_parser_1 = require("./dialect/tiny_parser");
20
20
  Object.defineProperty(exports, "TinyParseError", { enumerable: true, get: function () { return tiny_parser_1.TinyParseError; } });
21
21
  Object.defineProperty(exports, "TinyParser", { enumerable: true, get: function () { return tiny_parser_1.TinyParser; } });
22
+ var prettify_1 = require("./lang/prettify");
23
+ Object.defineProperty(exports, "prettify", { enumerable: true, get: function () { return prettify_1.prettify; } });
22
24
  //# sourceMappingURL=internal.js.map
@@ -0,0 +1,3 @@
1
+ import { ParserRuleContext } from 'antlr4ts';
2
+ import type { Formatter } from './formatter';
3
+ export declare function formatBinaryChain(f: Formatter, ctx: ParserRuleContext): void;
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright Contributors to the Malloy project
4
+ * SPDX-License-Identifier: MIT
5
+ *
6
+ * RULE: BINARY CHAIN — formatBinaryChain
7
+ *
8
+ * Handles `and`/`or`/`??`/`+`/`-` chains. The grammar is left-recursive, so a
9
+ * chain `a + b + c` parses as `((a + b) + c)`. Only the OUTERMOST chain
10
+ * context emits; inner same-class contexts fall through to default recursion
11
+ * so the outer can collect all operands.
12
+ *
13
+ * Inline if it fits. Otherwise: first operand inline; each subsequent
14
+ * operator+operand on its own line at +1 indent (leading-operator style).
15
+ *
16
+ * We deliberately do NOT break at comparison operators — see prettify header.
17
+ */
18
+ Object.defineProperty(exports, "__esModule", { value: true });
19
+ exports.formatBinaryChain = formatBinaryChain;
20
+ const antlr4ts_1 = require("antlr4ts");
21
+ const tree_1 = require("antlr4ts/tree");
22
+ const tokens_1 = require("./tokens");
23
+ const leaf_1 = require("./leaf");
24
+ function formatBinaryChain(f, ctx) {
25
+ // Same chain class as our parent? Then we're an inner node of the chain —
26
+ // let the outermost one collect all operands and emit.
27
+ if (ctx.parent && ctx.parent.constructor === ctx.constructor) {
28
+ for (let i = 0; i < ctx.childCount; i++)
29
+ f.format(ctx.getChild(i));
30
+ return;
31
+ }
32
+ const operands = [];
33
+ const operators = [];
34
+ const collect = (n) => {
35
+ if (n instanceof antlr4ts_1.ParserRuleContext && n.constructor === ctx.constructor) {
36
+ // Inner same-class chain node. Grammar guarantees getChild(1) is a
37
+ // TerminalNode (the operator); the guard makes that explicit without
38
+ // a cast.
39
+ collect(n.getChild(0));
40
+ const op = n.getChild(1);
41
+ if (op instanceof tree_1.TerminalNode)
42
+ operators.push(op);
43
+ operands.push(n.getChild(2));
44
+ }
45
+ else {
46
+ operands.push(n);
47
+ }
48
+ };
49
+ collect(ctx);
50
+ const inlineLen = (0, leaf_1.approxInlineSpan)(f, ctx._start.tokenIndex, ctx._stop.tokenIndex);
51
+ if (f.o.lineLengthSoFar() + 1 + inlineLen <= tokens_1.LINE_BUDGET) {
52
+ for (let i = 0; i < ctx.childCount; i++)
53
+ f.format(ctx.getChild(i));
54
+ return;
55
+ }
56
+ f.format(operands[0]);
57
+ f.o.indent++;
58
+ for (let i = 0; i < operators.length; i++) {
59
+ f.o.nl();
60
+ f.format(operators[i]);
61
+ f.format(operands[i + 1]);
62
+ }
63
+ f.o.indent--;
64
+ }
65
+ //# sourceMappingURL=binary-chain.js.map
@@ -0,0 +1,4 @@
1
+ import { ParserRuleContext } from 'antlr4ts';
2
+ import type { Formatter } from './formatter';
3
+ export declare function formatTopLevel(f: Formatter, ctx: ParserRuleContext): void;
4
+ export declare function formatBlockBody(f: Formatter, ctx: ParserRuleContext): void;
@@ -0,0 +1,74 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright Contributors to the Malloy project
4
+ * SPDX-License-Identifier: MIT
5
+ *
6
+ * RULE: BLOCK BODY
7
+ *
8
+ * A `{ … }` body containing statements (extend body, view body, etc.). Walks
9
+ * children; between adjacent statements, preserves a single user-supplied
10
+ * blank line *only if the kinds differ*. Same-kind adjacent statements never
11
+ * get a blank.
12
+ *
13
+ * Also: top-level body — forces a blank line before each statement after the
14
+ * first, regardless of source spacing (top-level statements should breathe).
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.formatTopLevel = formatTopLevel;
18
+ exports.formatBlockBody = formatBlockBody;
19
+ const antlr4ts_1 = require("antlr4ts");
20
+ const tree_1 = require("antlr4ts/tree");
21
+ const rules_1 = require("./rules");
22
+ const tokens_1 = require("./tokens");
23
+ const leaf_1 = require("./leaf");
24
+ function statementKind(ctx) {
25
+ for (const r of rules_1.STATEMENT_KIND_BY_CTX) {
26
+ if (ctx instanceof r.ctxClass)
27
+ return r.kind;
28
+ }
29
+ return ctx.constructor.name;
30
+ }
31
+ function formatTopLevel(f, ctx) {
32
+ let emittedFirst = false;
33
+ for (let i = 0; i < ctx.childCount; i++) {
34
+ const c = ctx.getChild(i);
35
+ if (c instanceof tree_1.TerminalNode) {
36
+ const tok = c.symbol;
37
+ if (tok.type !== antlr4ts_1.Token.EOF)
38
+ (0, leaf_1.emitVisibleToken)(f, tok, tok.tokenIndex);
39
+ continue;
40
+ }
41
+ if (c instanceof antlr4ts_1.ParserRuleContext) {
42
+ if (emittedFirst)
43
+ f.needBlank = true;
44
+ f.format(c);
45
+ emittedFirst = true;
46
+ }
47
+ }
48
+ }
49
+ function formatBlockBody(f, ctx) {
50
+ let lastChild = null;
51
+ let lastChildEndLine = 0;
52
+ for (let i = 0; i < ctx.childCount; i++) {
53
+ const c = ctx.getChild(i);
54
+ if (c instanceof tree_1.TerminalNode) {
55
+ // OCURLY / CCURLY / SEMI — let the leaf walker handle them.
56
+ const tok = c.symbol;
57
+ if (tok.type !== antlr4ts_1.Token.EOF)
58
+ (0, leaf_1.emitVisibleToken)(f, tok, tok.tokenIndex);
59
+ continue;
60
+ }
61
+ if (c instanceof antlr4ts_1.ParserRuleContext) {
62
+ if (lastChild !== null) {
63
+ const userHadBlank = c._start.line - lastChildEndLine > 1;
64
+ const sameKind = statementKind(lastChild) === statementKind(c);
65
+ if (userHadBlank && !sameKind)
66
+ f.o.blank();
67
+ }
68
+ f.format(c);
69
+ lastChild = c;
70
+ lastChildEndLine = (0, tokens_1.endLineOf)(c._stop);
71
+ }
72
+ }
73
+ }
74
+ //# sourceMappingURL=block-body.js.map
@@ -0,0 +1,6 @@
1
+ import type { ANTLRErrorListener, RecognitionException, Recognizer } from 'antlr4ts';
2
+ import type { PrettifyError } from './types';
3
+ export declare class CollectingErrorListener implements ANTLRErrorListener<any> {
4
+ errors: PrettifyError[];
5
+ syntaxError<T>(_recognizer: Recognizer<T, any>, _offendingSymbol: T | undefined, line: number, charPositionInLine: number, msg: string, _e: RecognitionException | undefined): void;
6
+ }
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright Contributors to the Malloy project
4
+ * SPDX-License-Identifier: MIT
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.CollectingErrorListener = void 0;
8
+ /* eslint-disable @typescript-eslint/no-explicit-any */
9
+ class CollectingErrorListener {
10
+ constructor() {
11
+ this.errors = [];
12
+ }
13
+ syntaxError(_recognizer, _offendingSymbol, line, charPositionInLine, msg, _e) {
14
+ this.errors.push({ message: msg, line, column: charPositionInLine });
15
+ }
16
+ }
17
+ exports.CollectingErrorListener = CollectingErrorListener;
18
+ /* eslint-enable @typescript-eslint/no-explicit-any */
19
+ //# sourceMappingURL=error-listener.js.map
@@ -0,0 +1,3 @@
1
+ import type * as parser from '../lib/Malloy/MalloyParser';
2
+ import type { Formatter } from './formatter';
3
+ export declare function formatFieldProperties(f: Formatter, ctx: parser.FieldPropertiesContext): void;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright Contributors to the Malloy project
4
+ * SPDX-License-Identifier: MIT
5
+ *
6
+ * RULE: POSTFIX `{…}` — formatFieldProperties
7
+ *
8
+ * `expr { kw: ... }` — filter shortcut and friends. Inline if it fits;
9
+ * otherwise emit as a block with each inner statement on its own line. Inner
10
+ * statements are rendered atomically via formatTokenRange (no recursive
11
+ * section-list rewriting): they're part of the expression.
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.formatFieldProperties = formatFieldProperties;
15
+ const antlr4ts_1 = require("antlr4ts");
16
+ const tokens_1 = require("./tokens");
17
+ const leaf_1 = require("./leaf");
18
+ const inline_renderer_1 = require("./inline-renderer");
19
+ function formatFieldProperties(f, ctx) {
20
+ const startIdx = ctx._start.tokenIndex;
21
+ const stopIdx = ctx._stop.tokenIndex;
22
+ const hasComments = (0, leaf_1.hasCommentsInRange)(f, startIdx, stopIdx);
23
+ if (!hasComments) {
24
+ const inline = (0, inline_renderer_1.renderItemInline)(f, ctx);
25
+ if (f.o.lineLengthSoFar() + 1 + inline.length <= tokens_1.LINE_BUDGET) {
26
+ f.o.space();
27
+ f.o.text(inline);
28
+ (0, leaf_1.note)(f, tokens_1.L.CCURLY, stopIdx, ctx._stop);
29
+ return;
30
+ }
31
+ }
32
+ // Wrapped form. Each inner statement runs through the leaf walker
33
+ // (formatTokenRange) so comments inside it are preserved naturally.
34
+ f.o.space();
35
+ f.o.text('{');
36
+ f.o.indent++;
37
+ // Advance past the OCURLY ourselves so flushHiddenBefore for the first inner
38
+ // statement doesn't try to re-emit it.
39
+ f.lastEmittedIdx = startIdx;
40
+ for (let i = 0; i < ctx.childCount; i++) {
41
+ const c = ctx.getChild(i);
42
+ // Skip OCURLY / CCURLY / SEMI; only inner statement contexts get walked.
43
+ if (!(c instanceof antlr4ts_1.ParserRuleContext))
44
+ continue;
45
+ f.o.nl();
46
+ (0, leaf_1.formatTokenRange)(f, c._start.tokenIndex, c._stop.tokenIndex);
47
+ }
48
+ // Flush any tail hidden tokens between the last inner statement and the
49
+ // closing `}` so trailing comments emit at the inner indent rather than
50
+ // disappearing.
51
+ (0, leaf_1.flushHiddenBefore)(f, stopIdx);
52
+ f.o.indent--;
53
+ f.o.nl();
54
+ f.o.text('}');
55
+ (0, leaf_1.note)(f, tokens_1.L.CCURLY, stopIdx, ctx._stop);
56
+ }
57
+ //# sourceMappingURL=field-properties.js.map
@@ -0,0 +1,17 @@
1
+ import { Token } from 'antlr4ts';
2
+ import type { ParseTree } from 'antlr4ts/tree';
3
+ import { Out } from './out';
4
+ export declare class Formatter {
5
+ readonly src: string;
6
+ readonly tokens: Token[];
7
+ o: Out;
8
+ lastEmittedIdx: number;
9
+ lastEmittedType: number | null;
10
+ prevTokenEndLine: number;
11
+ needBlank: boolean;
12
+ parenDepth: number;
13
+ parenBreaks: boolean[];
14
+ private rootFormatted;
15
+ constructor(src: string, tokens: Token[]);
16
+ format(node: ParseTree): void;
17
+ }
@@ -0,0 +1,145 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright Contributors to the Malloy project
4
+ * SPDX-License-Identifier: MIT
5
+ *
6
+ * Formatter: per-format-call state container plus the parse-tree dispatcher.
7
+ *
8
+ * State (Out buffer, last-emitted token tracking, paren-depth stack) is
9
+ * mutable and read/written directly by the per-rule free functions in sibling
10
+ * modules. The dispatcher is the only method on the class — it routes each
11
+ * parse-tree node to the appropriate per-rule formatter, falling back to
12
+ * left-to-right child recursion.
13
+ *
14
+ * The class itself is not exported beyond ./index, so its fields being
15
+ * "package-public" is hygiene only — no external API leakage.
16
+ */
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
29
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
30
+ }) : function(o, v) {
31
+ o["default"] = v;
32
+ });
33
+ var __importStar = (this && this.__importStar) || (function () {
34
+ var ownKeys = function(o) {
35
+ ownKeys = Object.getOwnPropertyNames || function (o) {
36
+ var ar = [];
37
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
38
+ return ar;
39
+ };
40
+ return ownKeys(o);
41
+ };
42
+ return function (mod) {
43
+ if (mod && mod.__esModule) return mod;
44
+ var result = {};
45
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
46
+ __setModuleDefault(result, mod);
47
+ return result;
48
+ };
49
+ })();
50
+ Object.defineProperty(exports, "__esModule", { value: true });
51
+ exports.Formatter = void 0;
52
+ const antlr4ts_1 = require("antlr4ts");
53
+ const tree_1 = require("antlr4ts/tree");
54
+ const parser = __importStar(require("../lib/Malloy/MalloyParser"));
55
+ const out_1 = require("./out");
56
+ const rules_1 = require("./rules");
57
+ const leaf_1 = require("./leaf");
58
+ const block_body_1 = require("./block-body");
59
+ const sections_1 = require("./sections");
60
+ const field_properties_1 = require("./field-properties");
61
+ const pick_case_1 = require("./pick-case");
62
+ const binary_chain_1 = require("./binary-chain");
63
+ class Formatter {
64
+ constructor(src, tokens) {
65
+ this.src = src;
66
+ this.tokens = tokens;
67
+ this.o = new out_1.Out();
68
+ // -- Per-token state (updated by `note(f, ...)` after every token emit) --
69
+ // Index of the last token (visible or implicitly skipped) we've accounted
70
+ // for. flushHiddenBefore won't re-emit anything at or before this index.
71
+ this.lastEmittedIdx = -1;
72
+ // Type of the last visible token emitted. Drives spacing decisions
73
+ // (e.g. CALL_HUG_AFTER membership for `(`).
74
+ this.lastEmittedType = null;
75
+ // 1-based line in the SOURCE where the last emitted token ended. Used to
76
+ // distinguish trailing-comments (same line) from leading-comments (different
77
+ // line).
78
+ this.prevTokenEndLine = 0;
79
+ // After end-of-top-level-statement, the next statement-starter gets a blank
80
+ // line. Consumed once, then reset.
81
+ this.needBlank = false;
82
+ // -- Paren-pair state --
83
+ this.parenDepth = 0;
84
+ // For each open paren-pair: did we choose to break args/content across
85
+ // lines? Stack matches parenDepth.
86
+ this.parenBreaks = [];
87
+ // Set when format() is invoked at the top level (a MalloyDocument). Guards
88
+ // against accidental reuse of a Formatter instance — the Out buffer
89
+ // accumulates and would emit garbage on a second call. Construct a fresh
90
+ // Formatter per top-level call.
91
+ this.rootFormatted = false;
92
+ }
93
+ format(node) {
94
+ if (node instanceof tree_1.TerminalNode) {
95
+ const tok = node.symbol;
96
+ if (tok.type !== antlr4ts_1.Token.EOF)
97
+ (0, leaf_1.emitVisibleToken)(this, tok, tok.tokenIndex);
98
+ return;
99
+ }
100
+ if (!(node instanceof antlr4ts_1.ParserRuleContext))
101
+ return;
102
+ // RULE: TOP-LEVEL BODY — one blank line between adjacent statements.
103
+ if (node instanceof parser.MalloyDocumentContext) {
104
+ if (this.rootFormatted) {
105
+ throw new Error('Formatter is single-use; construct a new instance per top-level format call');
106
+ }
107
+ this.rootFormatted = true;
108
+ return (0, block_body_1.formatTopLevel)(this, node);
109
+ }
110
+ // RULE: POSTFIX `{…}` — filter shortcut etc.
111
+ if (node instanceof parser.FieldPropertiesContext) {
112
+ return (0, field_properties_1.formatFieldProperties)(this, node);
113
+ }
114
+ // RULE: BLOCK BODY — walk children, blank-lines between different kinds.
115
+ if (node instanceof parser.ExplorePropertiesContext ||
116
+ node instanceof parser.QueryPropertiesContext ||
117
+ node instanceof parser.QueryExtendStatementListContext) {
118
+ return (0, block_body_1.formatBlockBody)(this, node);
119
+ }
120
+ // RULE: SECTION-STATEMENT — table-driven dispatch.
121
+ for (const rule of rules_1.SECTION_STATEMENT_RULES) {
122
+ if (node instanceof rule.ctxClass) {
123
+ return (0, sections_1.formatSectionStatement)(this, node, rule);
124
+ }
125
+ }
126
+ // RULE: PICK / CASE / BINARY CHAIN.
127
+ if (node instanceof parser.PickStatementContext)
128
+ return (0, pick_case_1.formatPickStatement)(this, node);
129
+ if (node instanceof parser.PickContext)
130
+ return (0, pick_case_1.formatPick)(this, node);
131
+ if (node instanceof parser.CaseStatementContext)
132
+ return (0, pick_case_1.formatCaseStatement)(this, node);
133
+ if (node instanceof parser.ExprLogicalAndContext ||
134
+ node instanceof parser.ExprLogicalOrContext ||
135
+ node instanceof parser.ExprCoalesceContext ||
136
+ node instanceof parser.ExprAddSubContext) {
137
+ return (0, binary_chain_1.formatBinaryChain)(this, node);
138
+ }
139
+ // Default: recurse on children left-to-right.
140
+ for (let i = 0; i < node.childCount; i++)
141
+ this.format(node.getChild(i));
142
+ }
143
+ }
144
+ exports.Formatter = Formatter;
145
+ //# sourceMappingURL=formatter.js.map
@@ -0,0 +1,19 @@
1
+ import type { PrettifyResult } from './types';
2
+ export type { PrettifyError, PrettifyResult } from './types';
3
+ /**
4
+ * Pretty-print a Malloy source string.
5
+ *
6
+ * **Experimental — this API may vanish or change at any time without notice.**
7
+ * It is exposed only via `@malloydata/malloy/internal` and is not covered by
8
+ * any compatibility commitment. Do not depend on it from anything you can't
9
+ * fix in a single PR.
10
+ *
11
+ * Parses the input, walks the parse tree, and emits a reformatted string.
12
+ *
13
+ * `errors` surfaces parse errors only (lexer + parser). Semantic / compile
14
+ * errors aren't checked here. If `errors.length > 0` you have a bigger problem
15
+ * than formatting — output is best-effort and not guaranteed to round-trip.
16
+ *
17
+ * @experimental
18
+ */
19
+ export declare function prettify(src: string): PrettifyResult;
@@ -0,0 +1,139 @@
1
+ "use strict";
2
+ /*
3
+ * Copyright Contributors to the Malloy project
4
+ * SPDX-License-Identifier: MIT
5
+ *
6
+ * ============================================================================
7
+ * Malloy pretty-printer (experimental, /internal export — no stability promise)
8
+ * ============================================================================
9
+ *
10
+ * Architecture
11
+ * ------------
12
+ * 1. Lex + parse the input.
13
+ * 2. Walk the parse tree from `Formatter.format(node)`. The dispatcher routes
14
+ * each rule context to a per-rule free function in a sibling module;
15
+ * everything unhandled recurses to children, eventually reaching terminal
16
+ * tokens that emit through `emitVisibleToken` (the leaf).
17
+ * 3. The leaf walker handles per-token spacing/indentation/comments. It is
18
+ * v1's behaviour and is also the fallback when parsing fails.
19
+ *
20
+ * File layout
21
+ * -----------
22
+ * - ./out — Out buffer (indent, newlines, single-space coalescing).
23
+ * - ./tokens — LINE_BUDGET, INDENT_STR; classification sets
24
+ * (SECTION_TOKENS, BINARY_OPS, CALL_HUG_AFTER, etc.);
25
+ * findMatching, endLineOf.
26
+ * - ./rules — SECTION_STATEMENT_RULES + STATEMENT_KIND_BY_CTX.
27
+ * A maintainer adding a new section keyword lands here.
28
+ * - ./error-listener — CollectingErrorListener.
29
+ * - ./types — PrettifyError, PrettifyResult.
30
+ * - ./formatter — Formatter class (state + format() dispatcher).
31
+ * - ./leaf — emitVisibleToken + per-token state mutators
32
+ * (note, flushHiddenBefore, startStatementLine, …)
33
+ * and the small read-only helpers used by rule
34
+ * formatters (approxInlineSpan, hasCommentsInRange,
35
+ * formatTokenRange).
36
+ * - ./inline-renderer — renderItemInline (flat-string form for budget
37
+ * measurement and column alignment).
38
+ * - ./block-body — formatBlockBody, formatTopLevel.
39
+ * - ./sections — formatSectionStatement / formatSectionList.
40
+ * - ./field-properties — formatFieldProperties (postfix `{…}`).
41
+ * - ./pick-case — formatPickStatement, formatCaseStatement.
42
+ * - ./binary-chain — formatBinaryChain.
43
+ * - ./index (this) — prettify() entry point + type re-exports.
44
+ *
45
+ * Decisions worth knowing
46
+ * -----------------------
47
+ * - Comparison operators (`=`, `!=`, `<`, `>`, …) are kept glued to their
48
+ * operands. We only break chains at and/or/??/+/-. Justification: LHS/RHS
49
+ * of a comparison reads as one unit; breaking inside is more confusing
50
+ * than the line being long.
51
+ * - SQL strings (`"""…"""`, including `%{…}` malloy interpolations) and
52
+ * block annotations (`#" … "`) are emitted verbatim from source. We don't
53
+ * own a SQL formatter; annotation indentation is significant.
54
+ * - `;` is the compact-inline statement separator. Wrapped form drops it
55
+ * (newlines do the job); inline form keeps it.
56
+ * - `,` in section-list bare flow: intra-line yes, end-of-line never.
57
+ * - Single-arg function calls don't wrap (no point — nowhere useful to break).
58
+ * - `(` hugs only after a known-callable token (CALL_HUG_AFTER); after `is`,
59
+ * `as`, `extend`, `on`, `when`, etc. the `(` is grouping and gets a space.
60
+ *
61
+ * Adding a new section-statement
62
+ * ------------------------------
63
+ * Add a row to SECTION_STATEMENT_RULES in ./rules with the rule's context
64
+ * class, the keyword token type(s), the list-context accessor, and the
65
+ * item-kind tag. Add a corresponding entry to listItems() in ./sections.
66
+ *
67
+ * Note: section keywords NOT in the table fall through to the leaf walker
68
+ * (which produces correct-but-plain output). Add a row only when the default
69
+ * isn't good enough — flow-fill, alignment, or annotation handling.
70
+ *
71
+ * Inter-token spacing
72
+ * -------------------
73
+ * The classifier `leadingAction(prev, next)` in ./tokens is the single
74
+ * source of truth for what separator (glue, hug, or coalescing space) goes
75
+ * between two adjacent tokens. Both walkers — emitVisibleToken in ./leaf
76
+ * and renderItemInline in ./inline-renderer — consult it, so a change to
77
+ * `leadingAction` (e.g. adding a token type that hugs `(`) takes effect in
78
+ * both paths automatically. Walker-specific concerns (newlines, indent,
79
+ * paren-wrap decisions, compact-inline `, ` / `; `) live in the walker.
80
+ */
81
+ Object.defineProperty(exports, "__esModule", { value: true });
82
+ exports.prettify = prettify;
83
+ const antlr4ts_1 = require("antlr4ts");
84
+ const run_malloy_parser_1 = require("../run-malloy-parser");
85
+ const error_listener_1 = require("./error-listener");
86
+ const formatter_1 = require("./formatter");
87
+ const leaf_1 = require("./leaf");
88
+ /**
89
+ * Pretty-print a Malloy source string.
90
+ *
91
+ * **Experimental — this API may vanish or change at any time without notice.**
92
+ * It is exposed only via `@malloydata/malloy/internal` and is not covered by
93
+ * any compatibility commitment. Do not depend on it from anything you can't
94
+ * fix in a single PR.
95
+ *
96
+ * Parses the input, walks the parse tree, and emits a reformatted string.
97
+ *
98
+ * `errors` surfaces parse errors only (lexer + parser). Semantic / compile
99
+ * errors aren't checked here. If `errors.length > 0` you have a bigger problem
100
+ * than formatting — output is best-effort and not guaranteed to round-trip.
101
+ *
102
+ * @experimental
103
+ */
104
+ function prettify(src) {
105
+ const lexerErrors = new error_listener_1.CollectingErrorListener();
106
+ const parserErrors = new error_listener_1.CollectingErrorListener();
107
+ const { tokenStream, parser: malloyParser } = (0, run_malloy_parser_1.makeMalloyParser)(src, {
108
+ lexerErrorListener: lexerErrors,
109
+ parserErrorListener: parserErrors,
110
+ });
111
+ tokenStream.fill();
112
+ const tokens = tokenStream.getTokens();
113
+ let root = null;
114
+ try {
115
+ root = malloyParser.malloyDocument();
116
+ }
117
+ catch {
118
+ root = null;
119
+ }
120
+ const f = new formatter_1.Formatter(src, tokens);
121
+ if (root) {
122
+ f.format(root);
123
+ }
124
+ else {
125
+ // Parse failed. Fall back to leaf-only emission so we still produce
126
+ // something reasonable.
127
+ for (let i = 0; i < tokens.length; i++) {
128
+ const t = tokens[i];
129
+ if (t.channel !== antlr4ts_1.Token.HIDDEN_CHANNEL && t.type !== antlr4ts_1.Token.EOF) {
130
+ (0, leaf_1.emitVisibleToken)(f, t, i);
131
+ }
132
+ }
133
+ }
134
+ return {
135
+ result: f.o.toString(),
136
+ errors: [...lexerErrors.errors, ...parserErrors.errors],
137
+ };
138
+ }
139
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,3 @@
1
+ import type { ParserRuleContext } from 'antlr4ts';
2
+ import type { Formatter } from './formatter';
3
+ export declare function renderItemInline(f: Formatter, ctx: ParserRuleContext): string;