Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit3b41339

Browse files
committed
feat: 🎸 add ability to pretty-pint CST
1 parent4a83bc1 commit3b41339

File tree

2 files changed

+174
-107
lines changed

2 files changed

+174
-107
lines changed

‎src/__tests__/print.spec.ts‎

Lines changed: 142 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,118 +1,121 @@
1+
import{CodegenGrammar}from'../codegen/CodegenGrammar';
2+
import{ParseContext}from'../context';
13
import{grammarasjsonGrammar}from'../grammars/json';
2-
import{GrammarPrinter}from'../print';
4+
import{GrammarPrinter,printCst}from'../print';
35
import{Grammar}from'../types';
46

5-
test('can print a terminal node',()=>{
6-
constgrammar:Grammar={
7-
start:'Value',
8-
cst:{
9-
Value:{t:'value'},
10-
},
11-
};
12-
constresult=GrammarPrinter.print(grammar);
13-
expect(result).toBe('Value (terminal): "value"');
14-
});
7+
describe('GrammarPrinter',()=>{
8+
test('can print a terminal node',()=>{
9+
constgrammar:Grammar={
10+
start:'Value',
11+
cst:{
12+
Value:{t:'value'},
13+
},
14+
};
15+
constresult=GrammarPrinter.print(grammar);
16+
expect(result).toBe('Value (terminal): "value"');
17+
});
1518

16-
test('can print a terminal shorthand node',()=>{
17-
constgrammar:Grammar={
18-
start:'Value',
19-
cst:{
20-
Value:'value',
21-
},
22-
};
23-
constresult=GrammarPrinter.print(grammar);
24-
expect(result).toBe('Value (terminal): "value"');
25-
});
19+
test('can print a terminal shorthand node',()=>{
20+
constgrammar:Grammar={
21+
start:'Value',
22+
cst:{
23+
Value:'value',
24+
},
25+
};
26+
constresult=GrammarPrinter.print(grammar);
27+
expect(result).toBe('Value (terminal): "value"');
28+
});
2629

27-
test('can print a production node',()=>{
28-
constgrammar:Grammar={
29-
start:'Prod',
30-
cst:{
31-
Prod:{
32-
p:['{','}'],
33-
}
34-
},
35-
};
36-
constresult=GrammarPrinter.print(grammar);
37-
expect(result).toBe(
30+
test('can print a production node',()=>{
31+
constgrammar:Grammar={
32+
start:'Prod',
33+
cst:{
34+
Prod:{
35+
p:['{','}'],
36+
}
37+
},
38+
};
39+
constresult=GrammarPrinter.print(grammar);
40+
expect(result).toBe(
3841
`Prod (production)
3942
├─ Text (terminal): "{"
4043
└─ Text (terminal): "}"`);
41-
});
44+
});
4245

43-
test('can print a production shorthand node',()=>{
44-
constgrammar:Grammar={
45-
start:'Prod',
46-
cst:{
47-
Prod:['{','}'],
48-
},
49-
};
50-
constresult=GrammarPrinter.print(grammar);
51-
expect(result).toBe(
46+
test('can print a production shorthand node',()=>{
47+
constgrammar:Grammar={
48+
start:'Prod',
49+
cst:{
50+
Prod:['{','}'],
51+
},
52+
};
53+
constresult=GrammarPrinter.print(grammar);
54+
expect(result).toBe(
5255
`Prod (production)
5356
├─ Text (terminal): "{"
5457
└─ Text (terminal): "}"`);
55-
});
58+
});
5659

57-
test('can print a union node',()=>{
58-
constgrammar:Grammar={
59-
start:'U',
60-
cst:{
61-
U:{
62-
u:['{','}']
63-
}
64-
},
65-
};
66-
constresult=GrammarPrinter.print(grammar);
67-
expect(result).toBe(
60+
test('can print a union node',()=>{
61+
constgrammar:Grammar={
62+
start:'U',
63+
cst:{
64+
U:{
65+
u:['{','}']
66+
}
67+
},
68+
};
69+
constresult=GrammarPrinter.print(grammar);
70+
expect(result).toBe(
6871
`U (union)
6972
├─ Text (terminal): "{"
7073
└─ Text (terminal): "}"`);
71-
});
74+
});
7275

73-
test('can print a list node',()=>{
74-
constgrammar:Grammar={
75-
start:'L',
76-
cst:{
77-
L:{
78-
l:'a',
79-
}
80-
},
81-
};
82-
constresult=GrammarPrinter.print(grammar);
83-
expect(result).toBe(
76+
test('can print a list node',()=>{
77+
constgrammar:Grammar={
78+
start:'L',
79+
cst:{
80+
L:{
81+
l:'a',
82+
}
83+
},
84+
};
85+
constresult=GrammarPrinter.print(grammar);
86+
expect(result).toBe(
8487
`L (list)
8588
└─ Text (terminal): "a"`);
86-
});
89+
});
8790

88-
test('can print a reference node',()=>{
89-
constgrammar:Grammar={
90-
start:'Start',
91-
cst:{
92-
Start:{r:'ws'},
93-
ws:/\s+/,
94-
},
95-
};
96-
constresult=GrammarPrinter.print(grammar);
97-
expect(result).toBe(
91+
test('can print a reference node',()=>{
92+
constgrammar:Grammar={
93+
start:'Start',
94+
cst:{
95+
Start:{r:'ws'},
96+
ws:/\s+/,
97+
},
98+
};
99+
constresult=GrammarPrinter.print(grammar);
100+
expect(result).toBe(
98101
`Start (reference)
99102
└─ ws (terminal): /\\s+/`);
100-
});
103+
});
101104

102-
test('can print recursive grammar',()=>{
103-
constgrammar:Grammar={
104-
start:'Start',
105-
cst:{
106-
Start:{r:'Start'},
107-
},
108-
};
109-
constresult=GrammarPrinter.print(grammar);
110-
expect(result).toBe(`Start (reference) →`);
111-
});
105+
test('can print recursive grammar',()=>{
106+
constgrammar:Grammar={
107+
start:'Start',
108+
cst:{
109+
Start:{r:'Start'},
110+
},
111+
};
112+
constresult=GrammarPrinter.print(grammar);
113+
expect(result).toBe(`Start (reference) →`);
114+
});
112115

113-
test('JSON',()=>{
114-
constresult=GrammarPrinter.print(jsonGrammar);
115-
expect(result).toBe(
116+
test('JSON',()=>{
117+
constresult=GrammarPrinter.print(jsonGrammar);
118+
expect(result).toBe(
116119
`Value (production)
117120
├─ Ws (terminal): (" " | "\\n" | "\\t" | "\\r")*
118121
├─ TValue (union)
@@ -148,5 +151,53 @@ test('JSON', () => {
148151
│ │ └─ Text (terminal): "]"
149152
│ └─ Number (terminal): /\\-?(0|([1-9][0-9]{0,25}))(\\.[0-9]{1,25})?([eE][\\+\\\-]?[0-9]{1,25})?/
150153
└─ Ws →`);
154+
});
151155
});
152156

157+
describe('printCst()',()=>{
158+
test('can print JSON CST',()=>{
159+
constparser=CodegenGrammar.compile(jsonGrammar);
160+
constjson=' {"foo": ["bar", 123]}';
161+
constctx=newParseContext(json,false);
162+
constcst=parser(ctx,0);
163+
constformatted=printCst(cst!,'',json);
164+
expect(formatted).toBe(
165+
`Value 0:22 → ' {"foo": ["bar", 123]}'
166+
├─ Ws 0:1 → " "
167+
├─ TValue 1:22 → '{"foo": ["bar", 123]}'
168+
│ └─ Object 1:22 → '{"foo": ["bar", 123]}'
169+
│ ├─ Text 1:2 → "{"
170+
│ ├─ Members 2:21 → '"foo": ["bar", 123]'
171+
│ │ └─ Production 2:21 → '"foo": ["bar", 123]'
172+
│ │ ├─ Entry 2:21 → '"foo": ["bar", 123]'
173+
│ │ │ ├─ Ws 2:2 → ""
174+
│ │ │ ├─ String 2:7 → '"foo"'
175+
│ │ │ ├─ Ws 7:7 → ""
176+
│ │ │ ├─ Text 7:8 → ":"
177+
│ │ │ └─ Value 8:21 → ' ["bar", 123]'
178+
│ │ │ ├─ Ws 8:9 → " "
179+
│ │ │ ├─ TValue 9:21 → '["bar", 123]'
180+
│ │ │ │ └─ Array 9:21 → '["bar", 123]'
181+
│ │ │ │ ├─ Text 9:10 → "["
182+
│ │ │ │ ├─ Elements 10:20 → '"bar", 123'
183+
│ │ │ │ │ └─ Production 10:20 → '"bar", 123'
184+
│ │ │ │ │ ├─ Value 10:15 → '"bar"'
185+
│ │ │ │ │ │ ├─ Ws 10:10 → ""
186+
│ │ │ │ │ │ ├─ TValue 10:15 → '"bar"'
187+
│ │ │ │ │ │ │ └─ String 10:15 → '"bar"'
188+
│ │ │ │ │ │ └─ Ws 15:15 → ""
189+
│ │ │ │ │ └─ List 15:20 → ", 123"
190+
│ │ │ │ │ └─ Production 15:20 → ", 123"
191+
│ │ │ │ │ ├─ Text 15:16 → ","
192+
│ │ │ │ │ └─ Value 16:20 → " 123"
193+
│ │ │ │ │ ├─ Ws 16:17 → " "
194+
│ │ │ │ │ ├─ TValue 17:20 → "123"
195+
│ │ │ │ │ │ └─ Number 17:20 → "123"
196+
│ │ │ │ │ └─ Ws 20:20 → ""
197+
│ │ │ │ └─ Text 20:21 → "]"
198+
│ │ │ └─ Ws 21:21 → ""
199+
│ │ └─ List 21:21 → ""
200+
│ └─ Text 21:22 → "}"
201+
└─ Ws 22:22 → ""`);
202+
});
203+
});

‎src/print.ts‎

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import {
88
isTerminalShorthandNode,
99
isUnionNode,
1010
}from'./util';
11-
importtype{Grammar,GrammarNode,ParseTraceNode,RootTraceNode}from'./types';
11+
importtype{CstNode,Grammar,GrammarNode,ParseTraceNode,RootTraceNode}from'./types';
1212

1313
exportclassGrammarPrinter{
1414
publicstaticreadonlyprint=(grammar:Grammar,tab?:string):string=>{
@@ -32,7 +32,7 @@ export class GrammarPrinter {
3232
typeofnode.t==='string'
3333
?JSON.stringify(node.t)
3434
:Array.isArray(node.t)
35-
?'('+node.t.map((c)=>JSON.stringify(c)).join(' | ')+')'+(node.repeat??'')
35+
?'('+node.t.map((c)=>JSON.stringify(c)).join(' | ')+')'+(node.repeat??'')
3636
:node.t;
3737
return`${node.type??type??'Text'} (terminal):${pattern}`;
3838
}elseif(isTerminalShorthandNode(node)){
@@ -76,26 +76,27 @@ export class GrammarPrinter {
7676
}
7777
}
7878

79+
constformatMatch=(cst:CstNode,text?:string):string=>{
80+
letformatted=' '+cst.pos+':'+cst.end;
81+
if(text){
82+
constend=cst.pos+Math.min(32,cst.end-cst.pos);
83+
constslice=text.slice(cst.pos,end)+(end!==cst.end ?'...' :'');
84+
constsliceFormatted=
85+
slice.indexOf('"')>=0&&slice.indexOf("'")===-1
86+
?"'"+JSON.stringify(slice).slice(1,-1).replaceAll('\\"','"')+"'"
87+
:JSON.stringify(slice);
88+
formatted+=' → '+sliceFormatted;
89+
}
90+
returnformatted;
91+
};
92+
7993
exportconstprintTraceNode=(trace:RootTraceNode|ParseTraceNode,tab?:string,text?:string):string=>{
8094
constpattern=(traceasParseTraceNode).ptr;
8195
consttype=pattern?.type??((traceasParseTraceNode).ptr ?'Anonymous' :'Root');
8296
constmatch=(traceasParseTraceNode).match;
83-
letmatchFormatted='';
84-
if(match){
85-
matchFormatted=' '+match.pos+':'+match.end;
86-
if(text){
87-
constend=match.pos+Math.min(32,match.end-match.pos);
88-
constslice=text.slice(match.pos,end)+(end!==match.end ?'...' :'');
89-
constsliceFormatted=
90-
slice.indexOf('"')>=0&&slice.indexOf("'")===-1
91-
?"'"+JSON.stringify(slice).slice(1,-1).replaceAll('\\"','"')+"'"
92-
:JSON.stringify(slice);
93-
matchFormatted+=' → '+sliceFormatted;
94-
}
95-
}
9697
return(
9798
`${type}`+
98-
matchFormatted+
99+
(match ?formatMatch(match,text) :'')+
99100
(trace.children&&trace.children.length
100101
?printTree(
101102
tab,
@@ -104,3 +105,18 @@ export const printTraceNode = (trace: RootTraceNode | ParseTraceNode, tab?: stri
104105
:'')
105106
);
106107
};
108+
109+
exportconstprintCst=(cst:CstNode,tab?:string,text?:string):string=>{
110+
constpattern=cst.ptr;
111+
consttype=pattern.type;
112+
return(
113+
`${type}`+
114+
formatMatch(cst,text)+
115+
(cst.children&&cst.children.length
116+
?printTree(
117+
tab,
118+
cst.children.map((n)=>(tab)=>printCst(n,tab,text)),
119+
)
120+
:'')
121+
);
122+
};

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp