Skip to content

Commit 1ea6be3

Browse files
authored
DSL Pretty Print (#91)
* Pretty print in Playground * feat: enhance formatBridge to merge continuation lines and improve indentation handling * feat: enhance formatBridge to split adjacent blocks on the same line with a blank line
1 parent 04243e6 commit 1ea6be3

9 files changed

Lines changed: 1009 additions & 131 deletions

File tree

packages/bridge-parser/src/bridge-printer.ts

Lines changed: 481 additions & 0 deletions
Large diffs are not rendered by default.

packages/bridge-parser/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ export {
2323
serializeBridge,
2424
} from "./bridge-format.ts";
2525

26+
// ── Formatter ───────────────────────────────────────────────────────────────
27+
28+
export { formatBridge } from "./bridge-printer.ts";
29+
2630
// ── Language service ────────────────────────────────────────────────────────
2731

2832
export { BridgeLanguageService } from "./language-service.ts";

packages/bridge-parser/src/parser/lexer.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export const WS = createToken({
2323
export const Comment = createToken({
2424
name: "Comment",
2525
pattern: /#[^\r\n]*/,
26-
group: Lexer.SKIPPED,
26+
group: "comments",
2727
});
2828

2929
// ── Identifiers (defined first — keywords reference via longer_alt) ────────
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
import assert from "node:assert/strict";
2+
import { describe, test } from "node:test";
3+
import { formatBridge } from "../src/index.ts";
4+
5+
/**
6+
* ============================================================================
7+
* FULL EXAMPLE TEST CASES
8+
*
9+
* These tests show complete Bridge DSL snippets with expected formatting.
10+
* Edit these to define the canonical style.
11+
* ============================================================================
12+
*/
13+
14+
describe("formatBridge - full examples", () => {
15+
test("simple tool declaration", () => {
16+
const input = `version 1.5
17+
tool geo from std.httpCall`;
18+
const expected = `version 1.5
19+
20+
tool geo from std.httpCall
21+
`;
22+
assert.equal(formatBridge(input), expected);
23+
});
24+
25+
test("tool with body", () => {
26+
const input = `version 1.5
27+
28+
tool geo from std.httpCall{
29+
.baseUrl="https://example.com"
30+
.method=GET
31+
}`;
32+
const expected = `version 1.5
33+
34+
tool geo from std.httpCall {
35+
.baseUrl = "https://example.com"
36+
.method = GET
37+
}
38+
`;
39+
assert.equal(formatBridge(input), expected);
40+
});
41+
42+
test("bridge block with assignments", () => {
43+
const input = `version 1.5
44+
45+
bridge Query.test{
46+
with input as i
47+
with output as o
48+
o.value<-i.value
49+
}`;
50+
const expected = `version 1.5
51+
52+
bridge Query.test {
53+
with input as i
54+
with output as o
55+
56+
o.value <- i.value
57+
}
58+
`;
59+
assert.equal(formatBridge(input), expected);
60+
});
61+
62+
test("define block", () => {
63+
const input = `define myHelper{
64+
with input as i
65+
o.x<-i.y
66+
}`;
67+
const expected = `define myHelper {
68+
with input as i
69+
70+
o.x <- i.y
71+
}
72+
`;
73+
assert.equal(formatBridge(input), expected);
74+
});
75+
76+
test("bridge with comment, tool handles, and pipes", () => {
77+
const input = `version 1.5
78+
79+
bridge Query.greet {
80+
#comment
81+
with std.str.toUpperCase as uc
82+
with std.str.toLowerCase as lc
83+
84+
with input as i
85+
with output as o
86+
87+
o.message <- i.name
88+
o.upper <- uc: i.name
89+
o.lower <- lc: i.name
90+
}`;
91+
const expected = `version 1.5
92+
93+
bridge Query.greet {
94+
#comment
95+
with std.str.toUpperCase as uc
96+
with std.str.toLowerCase as lc
97+
with input as i
98+
with output as o
99+
100+
o.message <- i.name
101+
o.upper <- uc:i.name
102+
o.lower <- lc:i.name
103+
}
104+
`;
105+
assert.equal(formatBridge(input), expected);
106+
});
107+
108+
test("ternary expressions preserve formatting", () => {
109+
const input = `version 1.5
110+
111+
bridge Query.pricing {
112+
with input as i
113+
with output as o
114+
115+
# String literal branches
116+
o.tier <- i.isPro ? "premium" : "basic"
117+
118+
# Numeric literal branches
119+
o.discount <- i.isPro ? 20 : 5
120+
121+
# Source ref branches — selects proPrice or basicPrice
122+
o.price <- i.isPro ? i.proPrice : i.basicPrice
123+
}
124+
`;
125+
// Should not change
126+
assert.equal(formatBridge(input), input);
127+
});
128+
129+
test("blank line between top-level blocks", () => {
130+
const input = `version 1.5
131+
132+
tool geo from std.httpCall
133+
tool weather from std.httpCall
134+
bridge Query.a {
135+
with input as i
136+
}
137+
bridge Query.b {
138+
with input as i
139+
}
140+
define helper {
141+
with input as i
142+
}`;
143+
const expected = `version 1.5
144+
145+
tool geo from std.httpCall
146+
147+
tool weather from std.httpCall
148+
149+
bridge Query.a {
150+
with input as i
151+
}
152+
153+
bridge Query.b {
154+
with input as i
155+
}
156+
157+
define helper {
158+
with input as i
159+
}
160+
`;
161+
assert.equal(formatBridge(input), expected);
162+
});
163+
164+
test("not operator preserves space", () => {
165+
const input = `o.requireMFA <- not i.verified
166+
`;
167+
// Should not change
168+
assert.equal(formatBridge(input), input);
169+
});
170+
171+
test("blank lines between comments are preserved", () => {
172+
const input = `#asdasd
173+
174+
#sdasdsd
175+
`;
176+
// Should not change
177+
assert.equal(formatBridge(input), input);
178+
});
179+
});

0 commit comments

Comments
 (0)