Skip to content

Commit 54234de

Browse files
committed
Merge branch 'mr/pmderodat/unparsing' into 'master'
Unparsing: add the "recurse_field" template See merge request eng/libadalang/langkit!1003
2 parents a69ce56 + 1579502 commit 54234de

21 files changed

+1332
-330
lines changed

langkit/support/langkit_support-generic_api-unparsing.adb

Lines changed: 875 additions & 196 deletions
Large diffs are not rendered by default.

langkit/support/langkit_support-generic_api-unparsing.ads

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,34 @@ package Langkit_Support.Generic_API.Unparsing is
169169
--
170170
-- {"kind": "markAsRoot", "contents": <sub-template>}
171171
--
172+
-- * The "recurse_field" template is valid only in "node" templates for
173+
-- concrete nodes that are neither abstract, token nor list nodes. When
174+
-- used, the whole template cannot contain any
175+
-- "recurse"/"recurse_flatten" template, and the template, once
176+
-- linearized, must reflect how the node is unparsed.
177+
--
178+
-- For example, let's consider that the ``VarDecl`` node is created
179+
-- parsing the following chunks::
180+
--
181+
-- "var" [f_name] ":" [f_type] ";"
182+
--
183+
-- Then its "node" template must contain two "recurse_field" templates
184+
-- for the two fields, in the same order, and with the same tokens in
185+
-- between. For instance::
186+
--
187+
-- [
188+
-- {"kind": "text", "text": "var"},
189+
-- {"kind": "recurse_field", "text": "f_name"},
190+
-- {
191+
-- "kind": "group",
192+
-- "document": [
193+
-- {"kind": "text", "text": ":"},
194+
-- {"kind": "recurse_field", "text": "f_type"}
195+
-- ]
196+
-- },
197+
-- {"kind": "text", "text": ";"},
198+
-- ]
199+
--
172200
-- * The "recurse_flatten" template acts like "recurse" but refines its
173201
-- result so that the document nested in "align", "fill", "group",
174202
-- "indent" templates and in 1-item document lists is returned
@@ -181,6 +209,13 @@ package Langkit_Support.Generic_API.Unparsing is
181209
-- templates that were instantiated for nodes that match at least one
182210
-- of the node types.
183211
--
212+
-- * The "text" template yields a "text" Prettier document::
213+
--
214+
-- {"kind": "text", "text": "some_text_to_unparse"}
215+
--
216+
-- It is valid only when used with "recurse_field" template: see its
217+
-- description.
218+
--
184219
-- * A JSON list yields the corresponding "list" Prettier document::
185220
--
186221
-- [{"kind": "whitespace"}, {"kind": "recurse"}]

langkit/support/langkit_support-prettier_utils.adb

Lines changed: 36 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -33,97 +33,6 @@ package body Langkit_Support.Prettier_Utils is
3333
return False;
3434
end Node_Matches;
3535

36-
-------------------------
37-
-- Is_Correct_Template --
38-
-------------------------
39-
40-
function Is_Correct_Template (Self : Document_Type) return Boolean is
41-
function Recurse_Count (Self : Document_Type) return Natural;
42-
-- Return the number of times a Recurse node is included when unparsing
43-
-- Self.
44-
45-
-------------------
46-
-- Recurse_Count --
47-
-------------------
48-
49-
function Recurse_Count (Self : Document_Type) return Natural is
50-
begin
51-
case Self.Kind is
52-
when Align =>
53-
return Recurse_Count (Self.Align_Contents);
54-
55-
when Break_Parent =>
56-
return 0;
57-
58-
when Fill =>
59-
return Recurse_Count (Self.Fill_Document);
60-
61-
when Group =>
62-
return Recurse_Count (Self.Group_Document);
63-
64-
when Hard_Line =>
65-
return 0;
66-
67-
when Hard_Line_Without_Break_Parent =>
68-
return 0;
69-
70-
when If_Break =>
71-
declare
72-
Count_Break : constant Natural :=
73-
Recurse_Count (Self.If_Break_Contents);
74-
Count_Flat : constant Natural :=
75-
Recurse_Count (Self.If_Break_Flat_Contents);
76-
begin
77-
if Count_Break /= Count_Flat then
78-
raise Invalid_Input with
79-
"ifBreak alternatives have an inconsistent recurse"
80-
& " structure";
81-
end if;
82-
return Count_Break;
83-
end;
84-
85-
when Indent =>
86-
return Recurse_Count (Self.Indent_Document);
87-
88-
when Line =>
89-
return 0;
90-
91-
when List =>
92-
return Result : Natural := 0 do
93-
for I in 1 .. Self.List_Documents.Last_Index loop
94-
Result :=
95-
Result
96-
+ Recurse_Count (Self.List_Documents.Element (I));
97-
end loop;
98-
end return;
99-
100-
when Literal_Line =>
101-
return 0;
102-
103-
when Recurse | Recurse_Flatten =>
104-
return 1;
105-
106-
when Soft_Line =>
107-
return 0;
108-
109-
when Token =>
110-
111-
-- Token documents are not supposed to appear in templates
112-
113-
raise Program_Error;
114-
115-
when Trim =>
116-
return 0;
117-
118-
when Whitespace =>
119-
return 0;
120-
end case;
121-
end Recurse_Count;
122-
123-
begin
124-
return Recurse_Count (Self) = 1;
125-
end Is_Correct_Template;
126-
12736
--------------------------
12837
-- To_Prettier_Document --
12938
--------------------------
@@ -228,7 +137,7 @@ package body Langkit_Support.Prettier_Utils is
228137
when Literal_Line =>
229138
return Literal_Line;
230139

231-
when Recurse | Recurse_Flatten =>
140+
when Recurse | Recurse_Field | Recurse_Flatten =>
232141
raise Program_Error with "uninstantiated template";
233142

234143
when Soft_Line =>
@@ -499,6 +408,36 @@ package body Langkit_Support.Prettier_Utils is
499408
end return;
500409
end Create_Recurse;
501410

411+
--------------------
412+
-- Create_Recurse --
413+
--------------------
414+
415+
function Create_Recurse (Self : in out Document_Pool) return Template_Type
416+
is
417+
begin
418+
return (Kind => With_Recurse, Root => Self.Create_Recurse);
419+
end Create_Recurse;
420+
421+
--------------------------
422+
-- Create_Recurse_Field --
423+
--------------------------
424+
425+
function Create_Recurse_Field
426+
(Self : in out Document_Pool;
427+
Field : Struct_Member_Ref;
428+
Position : Positive) return Document_Type is
429+
begin
430+
return Result : constant Document_Type :=
431+
new Document_Record'
432+
(Kind => Recurse_Field,
433+
Node => No_Lk_Node,
434+
Recurse_Field_Ref => Field,
435+
Recurse_Field_Position => Position)
436+
do
437+
Self.Register (Result);
438+
end return;
439+
end Create_Recurse_Field;
440+
502441
----------------------------
503442
-- Create_Recurse_Flatten --
504443
----------------------------
@@ -730,7 +669,7 @@ package body Langkit_Support.Prettier_Utils is
730669
when Literal_Line =>
731670
Extend_Spacing (Last_Spacing, Newline);
732671

733-
when Recurse | Recurse_Flatten =>
672+
when Recurse | Recurse_Field | Recurse_Flatten =>
734673
raise Program_Error;
735674

736675
when Soft_Line =>
@@ -895,6 +834,10 @@ package body Langkit_Support.Prettier_Utils is
895834
when Recurse =>
896835
Put_Line ("recurse");
897836

837+
when Recurse_Field =>
838+
Put_Line
839+
("recurse_field: " & Debug_Name (Document.Recurse_Field_Ref));
840+
898841
when Recurse_Flatten =>
899842
Put_Line ("recurse_flatten:");
900843
for I in 1 .. Document.Recurse_Flatten_Types.Last_Index loop

langkit/support/langkit_support-prettier_utils.ads

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ private package Langkit_Support.Prettier_Utils is
6464
List,
6565
Literal_Line,
6666
Recurse,
67+
Recurse_Field,
6768
Recurse_Flatten,
6869
Soft_Line,
6970
Token,
@@ -116,6 +117,19 @@ private package Langkit_Support.Prettier_Utils is
116117
when Recurse =>
117118
null;
118119

120+
when Recurse_Field =>
121+
Recurse_Field_Ref : Struct_Member_Ref;
122+
-- Node field on which to recurse
123+
124+
Recurse_Field_Position : Positive;
125+
-- 1-based index for this field in the list of fields for the
126+
-- owning node.
127+
--
128+
-- This information is in theory redundant with the field
129+
-- reference, but using an index allows template instantantiation
130+
-- code to use an array rather than a map to store information
131+
-- related to fields: more simple and probably more efficient.
132+
119133
when Recurse_Flatten =>
120134
Recurse_Flatten_Types : Type_Vectors.Vector;
121135

@@ -134,25 +148,26 @@ private package Langkit_Support.Prettier_Utils is
134148
end case;
135149
end record;
136150

137-
function Is_Correct_Template (Self : Document_Type) return Boolean;
138-
-- Return whether ``Self`` is a valid template document. This ensures that
139-
-- formatting the instantiated template will yield exactly once the
140-
-- sub-document corresponding to the Recurse item.
141-
--
142-
-- An example to clarify: suppose we need a template to unparse a node::
143-
--
144-
-- ["recurse", "recurse"]
145-
--
146-
-- Will be invalid, as the node is unparse twice. Similarly::
147-
--
148-
-- ["whitespace"]
149-
--
150-
-- Will be invalid, as the node will not be included in the unparsing.
151-
152151
function To_Prettier_Document
153152
(Document : Document_Type) return Prettier.Document_Type;
154153
-- Turn an unparsing document into an actual Prettier document
155154

155+
type Template_Kind is (No_Template_Kind, With_Recurse, With_Recurse_Field);
156+
subtype Some_Template_Kind is
157+
Template_Kind range With_Recurse .. With_Recurse_Field;
158+
type Template_Type (Kind : Template_Kind := No_Template_Kind) is record
159+
case Kind is
160+
when No_Template_Kind =>
161+
null;
162+
163+
when With_Recurse | With_Recurse_Field =>
164+
Root : Document_Type;
165+
end case;
166+
end record;
167+
-- Template document extended with information about how to instantiate it
168+
169+
No_Template : constant Template_Type := (Kind => No_Template_Kind);
170+
156171
type Document_Pool is tagged private;
157172
-- Allocation pool for ``Document_Type`` nodes
158173

@@ -226,6 +241,15 @@ private package Langkit_Support.Prettier_Utils is
226241
function Create_Recurse (Self : in out Document_Pool) return Document_Type;
227242
-- Return a ``Recurse`` node
228243

244+
function Create_Recurse (Self : in out Document_Pool) return Template_Type;
245+
-- Return a ``Recurse`` node wrapped in a ``With_Recurse`` template
246+
247+
function Create_Recurse_Field
248+
(Self : in out Document_Pool;
249+
Field : Struct_Member_Ref;
250+
Position : Positive) return Document_Type;
251+
-- Return a ``Recurse_Field`` node
252+
229253
function Create_Recurse_Flatten
230254
(Self : in out Document_Pool;
231255
Types : in out Type_Vectors.Vector) return Document_Type;
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"node_configs": {
3+
"ParamSpec": {
4+
"node": [
5+
{"kind": "recurse_field", "field": "f_name"},
6+
"softline",
7+
{"kind": "text", "text": ":"},
8+
"softline",
9+
{"kind": "recurse_field", "field": "f_type_expr"},
10+
"line",
11+
{"kind": "recurse_field", "field": "f_default_expr"}
12+
],
13+
"fields": {
14+
"f_default_expr": ["line", "recurse"]
15+
}
16+
},
17+
"VarDecl": {
18+
"node": [
19+
{"kind": "recurse_field", "field": "f_is_null"},
20+
{
21+
"kind": "group",
22+
"document": [
23+
{"kind": "text", "text": "var"},
24+
{"kind": "recurse_field", "field": "f_name"},
25+
{"kind": "text", "text": ":"}
26+
]
27+
},
28+
{"kind": "recurse_field", "field": "f_type_expr"},
29+
{"kind": "text", "text": "="},
30+
{"kind": "recurse_field", "field": "f_value"},
31+
{"kind": "text", "text": ";"}
32+
],
33+
"fields": {
34+
"f_value": {"kind": "indent", "contents": "recurse"}
35+
}
36+
}
37+
}
38+
}

testsuite/tests/ada_api/unparsing/commands.adb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,10 @@ begin
107107
Check ("cmd_literalline.json");
108108
Check ("cmd_markasroot.json");
109109
Check ("cmd_recurse.json");
110+
Check
111+
("cmd_recurse_field.json",
112+
"var i: Int = 0;" & ASCII.LF
113+
& "def f(i: Int): Int {i;}");
110114
Check
111115
("cmd_recurse_flatten.json",
112116
"var i: Int = AAAAAAAAAAAAAAAAAA"

testsuite/tests/ada_api/unparsing/expected_concrete_syntax.lkt

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,24 @@ class CallArg: FooNode {
3939
@parse_field expr: Expr
4040
}
4141

42+
@abstract class Decl: FooNode {
43+
@abstract @parse_field name: Name
44+
}
45+
46+
class FunDecl: Decl {
47+
@parse_field name: Name
48+
@parse_field args: ASTList[FooNode, ParamSpec]
49+
@parse_field return_type: Name
50+
@parse_field body: ASTList[FooNode, Stmt]
51+
}
52+
53+
class VarDecl: Decl {
54+
@parse_field is_null: NullQual
55+
@parse_field name: Name
56+
@parse_field type_expr: Name
57+
@parse_field value: Expr
58+
}
59+
4260
@abstract class Expr: FooNode {
4361
}
4462

@@ -64,13 +82,6 @@ class Ref: Expr {
6482
@parse_field name: Name
6583
}
6684

67-
class FunDecl: FooNode {
68-
@parse_field name: Name
69-
@parse_field args: ASTList[FooNode, ParamSpec]
70-
@parse_field return_type: Name
71-
@parse_field body: ASTList[FooNode, Stmt]
72-
}
73-
7485
class Name: FooNode implements TokenNode {
7586
}
7687

@@ -86,10 +97,3 @@ class ParamSpec: FooNode {
8697
class Stmt: FooNode {
8798
@parse_field expr: Expr
8899
}
89-
90-
class VarDecl: FooNode {
91-
@parse_field is_null: NullQual
92-
@parse_field name: Name
93-
@parse_field type_expr: Name
94-
@parse_field value: Expr
95-
}

0 commit comments

Comments
 (0)