Skip to content

Commit 97141cf

Browse files
committed
Unparsing: add handling for group Ids
1 parent d047c3b commit 97141cf

12 files changed

+541
-139
lines changed

langkit/support/langkit_support-generic_api-unparsing.adb

Lines changed: 183 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ with GNAT.Strings;
1616
with GNATCOLL.JSON; use GNATCOLL.JSON;
1717
with GNATCOLL.Opt_Parse;
1818
with GNATCOLL.VFS; use GNATCOLL.VFS;
19-
with Prettier_Ada.Documents.Builders;
2019
with Prettier_Ada.Documents.Json;
2120

2221
with Langkit_Support.Errors; use Langkit_Support.Errors;
@@ -106,6 +105,40 @@ package body Langkit_Support.Generic_API.Unparsing is
106105
-- Decompose ``Node`` into a list of unparsing fragments and call
107106
-- ``Process`` on each fragment.
108107

108+
-----------------------
109+
-- Template symbols --
110+
-----------------------
111+
112+
-- The following map type is used during templates parsing to validate the
113+
-- names used as symbols in JSON templates, and to turn them into their
114+
-- internal representation: ``Template_Symbol``.
115+
116+
type Symbol_Info is record
117+
Source_Name : Unbounded_String;
118+
-- Name for this symbol as found in the unparsing configuration
119+
120+
Template_Sym : Template_Symbol;
121+
-- Unique identifier for this symbol
122+
123+
Has_Definition : Boolean;
124+
-- Whether we have found one definition for this symbol
125+
end record;
126+
127+
package Symbol_Parsing_Maps is new Ada.Containers.Hashed_Maps
128+
(Key_Type => Symbol_Type,
129+
Element_Type => Symbol_Info,
130+
Hash => Hash,
131+
Equivalent_Keys => "=");
132+
133+
function Lookup
134+
(Source_Name : Unbounded_String;
135+
Symbols : Symbol_Table;
136+
Symbol_Map : in out Symbol_Parsing_Maps.Map)
137+
return Symbol_Parsing_Maps.Reference_Type;
138+
-- Return a reference to the entry in ``Symbol_Map`` corresponding to the
139+
-- ```Source_Name`` symbol (converted to a ``Symbol_Type`` using
140+
-- ``Symbols``). Create a map entry if it does not exist yet.
141+
109142
----------------------
110143
-- Linear templates --
111144
----------------------
@@ -360,6 +393,42 @@ package body Langkit_Support.Generic_API.Unparsing is
360393
end case;
361394
end Iterate_On_Fragments;
362395

396+
------------
397+
-- Lookup --
398+
------------
399+
400+
function Lookup
401+
(Source_Name : Unbounded_String;
402+
Symbols : Symbol_Table;
403+
Symbol_Map : in out Symbol_Parsing_Maps.Map)
404+
return Symbol_Parsing_Maps.Reference_Type
405+
is
406+
Symbol : constant Symbol_Type :=
407+
Find (Symbols, To_Text (To_String (Source_Name)));
408+
Position : Symbol_Parsing_Maps.Cursor := Symbol_Map.Find (Symbol);
409+
Inserted : Boolean;
410+
begin
411+
if not Symbol_Parsing_Maps.Has_Element (Position) then
412+
413+
-- This is the first time we see this symbol in the current template:
414+
-- create a new internal symbol for it. All internal symbols are
415+
-- tracked as entries in ``Symbol_Map``, so we can use its length to
416+
-- compute internal symbols that are unique for the current template.
417+
418+
declare
419+
Info : constant Symbol_Info :=
420+
(Source_Name => Source_Name,
421+
Template_Sym => Template_Symbol (Symbol_Map.Length + 1),
422+
Has_Definition => False);
423+
begin
424+
Symbol_Map.Insert (Symbol, Info, Position, Inserted);
425+
pragma Assert (Inserted);
426+
end;
427+
end if;
428+
429+
return Symbol_Map.Reference (Position);
430+
end Lookup;
431+
363432
-----------
364433
-- Image --
365434
-----------
@@ -580,8 +649,9 @@ package body Langkit_Support.Generic_API.Unparsing is
580649
-- ill-formed.
581650

582651
function Parse_Template_Helper
583-
(JSON : JSON_Value;
584-
Context : in out Template_Parsing_Context) return Document_Type;
652+
(JSON : JSON_Value;
653+
Context : in out Template_Parsing_Context;
654+
Symbol_Map : in out Symbol_Parsing_Maps.Map) return Document_Type;
585655
-- Helper for ``Parse_Template``. Implement the recursive part of
586656
-- templates parsing: ``Parse_Template`` takes care of the post-parsing
587657
-- validation.
@@ -674,9 +744,24 @@ package body Langkit_Support.Generic_API.Unparsing is
674744
(JSON : JSON_Value;
675745
Context : in out Template_Parsing_Context) return Template_Type
676746
is
747+
Symbol_Map : Symbol_Parsing_Maps.Map;
748+
-- Mapping from name symbols found in the JSON (Symbol_Type) and
749+
-- "ids" (Template_Symbol).
750+
677751
Root : constant Document_Type :=
678-
Parse_Template_Helper (JSON, Context);
752+
Parse_Template_Helper (JSON, Context, Symbol_Map);
679753
begin
754+
-- Make sure that all symbols referenced in this template are also
755+
-- defined in this template.
756+
757+
for Info of Symbol_Map loop
758+
if not Info.Has_Definition then
759+
Abort_Parsing
760+
(Context,
761+
"undefined symbol: " & To_String (Info.Source_Name));
762+
end if;
763+
end loop;
764+
680765
case Context.State.Kind is
681766
when Initial =>
682767
Abort_Parsing (Context, "recursion is missing");
@@ -709,16 +794,18 @@ package body Langkit_Support.Generic_API.Unparsing is
709794
---------------------------
710795

711796
function Parse_Template_Helper
712-
(JSON : JSON_Value;
713-
Context : in out Template_Parsing_Context) return Document_Type is
797+
(JSON : JSON_Value;
798+
Context : in out Template_Parsing_Context;
799+
Symbol_Map : in out Symbol_Parsing_Maps.Map) return Document_Type is
714800
begin
715801
case JSON.Kind is
716802
when JSON_Array_Type =>
717803
declare
718804
Items : Document_Vectors.Vector;
719805
begin
720806
for D of JSON_Array'(JSON.Get) loop
721-
Items.Append (Parse_Template_Helper (D, Context));
807+
Items.Append
808+
(Parse_Template_Helper (D, Context, Symbol_Map));
722809
end loop;
723810
return Pool.Create_List (Items);
724811
end;
@@ -793,7 +880,7 @@ package body Langkit_Support.Generic_API.Unparsing is
793880
return Pool.Create_Align
794881
(Data,
795882
Parse_Template_Helper
796-
(JSON.Get ("contents"), Context));
883+
(JSON.Get ("contents"), Context, Symbol_Map));
797884
end;
798885

799886
elsif Kind in
@@ -814,7 +901,9 @@ package body Langkit_Support.Generic_API.Unparsing is
814901
then (Kind => Prettier.Inner_Root)
815902
else raise Program_Error),
816903
Contents => Parse_Template_Helper
817-
(JSON.Get ("contents"), Context));
904+
(JSON.Get ("contents"),
905+
Context,
906+
Symbol_Map));
818907

819908
elsif Kind = "fill" then
820909
declare
@@ -825,41 +914,75 @@ package body Langkit_Support.Generic_API.Unparsing is
825914
(Context, "missing ""document"" key for fill");
826915
end if;
827916
Document :=
828-
Parse_Template_Helper (JSON.Get ("document"), Context);
917+
Parse_Template_Helper
918+
(JSON.Get ("document"), Context, Symbol_Map);
829919

830920
return Pool.Create_Fill (Document);
831921
end;
832922

833923
elsif Kind = "group" then
834924
declare
835-
Document : Document_Type;
836-
Options : Prettier.Builders.Group_Options_Type :=
837-
Prettier.Builders.No_Group_Options;
838-
839-
Should_Break : JSON_Value;
925+
Document : Document_Type;
926+
Should_Break : Boolean := False;
927+
Id : Template_Symbol := No_Template_Symbol;
840928
begin
841929
if not JSON.Has_Field ("document") then
842930
Abort_Parsing
843931
(Context, "missing ""document"" key for group");
844932
end if;
845933
Document :=
846-
Parse_Template_Helper (JSON.Get ("document"), Context);
934+
Parse_Template_Helper
935+
(JSON.Get ("document"), Context, Symbol_Map);
847936

848937
if JSON.Has_Field ("shouldBreak") then
849-
Should_Break := JSON.Get ("shouldBreak");
850-
if Should_Break.Kind /= JSON_Boolean_Type then
851-
Abort_Parsing
852-
(Context,
853-
"invalid group shouldBreak: "
854-
& Should_Break.Kind'Image);
855-
end if;
856-
Options.Should_Break := Should_Break.Get;
938+
declare
939+
JSON_Should_Break : constant JSON_Value :=
940+
JSON.Get ("shouldBreak");
941+
begin
942+
if JSON_Should_Break.Kind /= JSON_Boolean_Type then
943+
Abort_Parsing
944+
(Context,
945+
"invalid group shouldBreak: "
946+
& JSON_Should_Break.Kind'Image);
947+
end if;
948+
Should_Break := JSON_Should_Break.Get;
949+
end;
857950
end if;
858951

859-
-- TODO??? (eng/libadalang/langkit#727) Handle the group
860-
-- id.
952+
-- If a symbol is given to identify this group, create an
953+
-- internal symbol for it.
861954

862-
return Pool.Create_Group (Document, Options);
955+
if JSON.Has_Field ("id") then
956+
declare
957+
JSON_Id : constant JSON_Value := JSON.Get ("id");
958+
begin
959+
if JSON_Id.Kind /= JSON_String_Type then
960+
Abort_Parsing
961+
(Context,
962+
"invalid group id: "
963+
& JSON_Id.Kind'Image);
964+
end if;
965+
966+
declare
967+
Info : Symbol_Info renames
968+
Lookup (JSON_Id.Get, Symbols, Symbol_Map);
969+
begin
970+
-- Ensure that there is no conflicting symbol
971+
-- definition in this template.
972+
973+
if Info.Has_Definition then
974+
Abort_Parsing
975+
(Context,
976+
"duplicate group id: " & JSON_Id.Get);
977+
else
978+
Info.Has_Definition := True;
979+
end if;
980+
Id := Info.Template_Sym;
981+
end;
982+
end;
983+
end if;
984+
985+
return Pool.Create_Group (Document, Should_Break, Id);
863986
end;
864987

865988
elsif Kind = "ifBreak" then
@@ -869,6 +992,8 @@ package body Langkit_Support.Generic_API.Unparsing is
869992

870993
Contents_Context : Template_Parsing_Context := Context;
871994
Flat_Context : Template_Parsing_Context := Context;
995+
996+
Group_Id : Template_Symbol := No_Template_Symbol;
872997
begin
873998
if not JSON.Has_Field ("breakContents") then
874999
Abort_Parsing
@@ -878,12 +1003,16 @@ package body Langkit_Support.Generic_API.Unparsing is
8781003

8791004
Contents :=
8801005
Parse_Template_Helper
881-
(JSON.Get ("breakContents"), Contents_Context);
1006+
(JSON.Get ("breakContents"),
1007+
Contents_Context,
1008+
Symbol_Map);
8821009

8831010
Flat_Contents :=
8841011
(if JSON.Has_Field ("flatContents")
8851012
then Parse_Template_Helper
886-
(JSON.Get ("flatContents"), Flat_Context)
1013+
(JSON.Get ("flatContents"),
1014+
Flat_Context,
1015+
Symbol_Map)
8871016
else null);
8881017

8891018
-- Unify the parsing state for both branches and update
@@ -897,10 +1026,28 @@ package body Langkit_Support.Generic_API.Unparsing is
8971026
end if;
8981027
Context.State := Contents_Context.State;
8991028

900-
-- TODO??? (eng/libadalang/langkit#727) Handle the group
901-
-- id.
1029+
-- If present, get the symbol for the given group id
1030+
1031+
if JSON.Has_Field ("groupId") then
1032+
declare
1033+
JSON_Id : constant JSON_Value :=
1034+
JSON.Get ("groupId");
1035+
begin
1036+
if JSON_Id.Kind /= JSON_String_Type then
1037+
Abort_Parsing
1038+
(Context,
1039+
"invalid group id: "
1040+
& JSON_Id.Kind'Image);
1041+
end if;
1042+
1043+
Group_Id :=
1044+
Lookup (JSON_Id.Get, Symbols, Symbol_Map)
1045+
.Template_Sym;
1046+
end;
1047+
end if;
9021048

903-
return Pool.Create_If_Break (Contents, Flat_Contents);
1049+
return Pool.Create_If_Break
1050+
(Contents, Flat_Contents, Group_Id);
9041051
end;
9051052

9061053
elsif Kind = "indent" then
@@ -909,7 +1056,8 @@ package body Langkit_Support.Generic_API.Unparsing is
9091056
(Context, "missing ""contents"" key for indent");
9101057
end if;
9111058
return Pool.Create_Indent
912-
(Parse_Template_Helper (JSON.Get ("contents"), Context));
1059+
(Parse_Template_Helper
1060+
(JSON.Get ("contents"), Context, Symbol_Map));
9131061

9141062
elsif Kind = "recurse_field" then
9151063
declare
@@ -1473,7 +1621,8 @@ package body Langkit_Support.Generic_API.Unparsing is
14731621
return Pool.Create_Group
14741622
(Instantiate_Template_Helper
14751623
(Pool, Node, Template.Group_Document, Arguments),
1476-
Template.Group_Options,
1624+
Template.Group_Should_Break,
1625+
Template.Group_Id,
14771626
Node);
14781627

14791628
when Hard_Line =>

langkit/support/langkit_support-generic_api-unparsing.ads

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,11 @@ package Langkit_Support.Generic_API.Unparsing is
169169
-- {"kind": "group", "document": <sub-template>}
170170
-- {"kind": "group", "document": <sub-template>, "shouldBreak": true}
171171
--
172+
-- An optional "id" field makes it define a symbol to reference in the
173+
-- same template:
174+
--
175+
-- {"kind": "group", "document": <sub-template>, "id": "mySymbol"}
176+
--
172177
-- * The "ifBreak" template yields an "ifBreak" Prettier document::
173178
--
174179
-- {"kind": "ifBreak", "breakContents": <sub-template>}
@@ -177,6 +182,12 @@ package Langkit_Support.Generic_API.Unparsing is
177182
-- "breakContents": <sub-template>,
178183
-- "flatContents": <sub-template>
179184
-- }
185+
-- {
186+
-- "kind": "ifBreak",
187+
-- "breakContents": <sub-template>,
188+
-- "flatContents": <sub-template>,
189+
-- "groupId": <symbol>
190+
-- }
180191
--
181192
-- * The "indent" template yields an "indent" Prettier document::
182193
--

0 commit comments

Comments
 (0)