Skip to content

Commit e83690f

Browse files
committed
Unparsing: refactor probing of template category
There are currently two kinds of template: the ones that contain "recurse", and the ones that contain "recurse_field"/"text". Due to the current template format, the only way to determine what is the kind of a given template is to look for the content of that template. Currently, this is done in the middle of the single pass JSON templates parsing. However, upcoming work will add another kind of templates: mixes of "recurse"/"text" for field templates. In order to make this machinery tractable with this new case, we need to have the detection working on the JSON as a first pass, and the translation from JSON to our internal representation as a second pass (so that translation/validation can rely on the detected kind from the start). This commit splits template parsing into these two passes, as preliminary work to support text in field templates.
1 parent 74bacab commit e83690f

File tree

2 files changed

+196
-73
lines changed

2 files changed

+196
-73
lines changed

langkit/support/langkit_support-generic_api-unparsing.adb

Lines changed: 191 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -591,15 +591,26 @@ package body Langkit_Support.Generic_API.Unparsing is
591591
-- Return the expected name for the given Member in the given Node
592592

593593
type Template_Parsing_State_Kind is
594-
(Initial, Recurse_Found, For_Recurse_Field);
594+
(Simple_Recurse, Recurse_Field);
595+
-- There are two kinds of templates we expect to find in unparsing
596+
-- configurations:
597+
--
598+
-- * Simple_Recurse templates, that cannot contain "text" nodes and
599+
-- whose linearization must yield a single "recurse" node.
600+
--
601+
-- * Recurse_Field templates, whose linearization must yield the
602+
-- sequence of "text"/"recurse_field" that is expected for a node.
603+
595604
type Template_Parsing_State
596-
(Kind : Template_Parsing_State_Kind := Initial)
605+
(Kind : Template_Parsing_State_Kind := Simple_Recurse)
597606
is record
598607
case Kind is
599-
when Initial | Recurse_Found =>
600-
null;
608+
when Simple_Recurse =>
609+
Recurse_Found : Boolean;
610+
-- Whether template parsing has found the "recurse" node
611+
-- expected for the current branch.
601612

602-
when For_Recurse_Field =>
613+
when Recurse_Field =>
603614
Linear_Template : Linear_Template_Vectors.Vector;
604615
-- Sequence of tokens/fields that the parsed template is
605616
-- supposed to yield once instantiated/formatted.
@@ -610,8 +621,6 @@ package body Langkit_Support.Generic_API.Unparsing is
610621
end case;
611622
end record;
612623

613-
Initial_State : constant Template_Parsing_State := (Kind => Initial);
614-
615624
type Template_Parsing_Context_Kind is
616625
(Node_Template, Field_Template, Sep_Template);
617626
-- Indicate which kind of template we are parsing:
@@ -641,6 +650,21 @@ package body Langkit_Support.Generic_API.Unparsing is
641650
end case;
642651
end record;
643652

653+
function Template_Kind
654+
(JSON : JSON_Value) return Template_Parsing_State_Kind;
655+
-- Determine the plausible kind for the given JSON-encoded template.
656+
-- Note that this is just a heuristic: if will return the right kind for
657+
-- a well-formed template, but will return an approximation for an
658+
-- ill-formed template.
659+
660+
function Initial_State_For
661+
(Node : Type_Ref;
662+
JSON : JSON_Value;
663+
Context : Template_Parsing_Context) return Template_Parsing_State;
664+
-- Return an inital template parsing state for the "node" template of
665+
-- ``Node``, to create from ``JSON``. Raise an Invalid_Input exception
666+
-- if the initial state found is invalid in this context.
667+
644668
function Parse_Template
645669
(JSON : JSON_Value;
646670
Context : in out Template_Parsing_Context) return Template_Type;
@@ -736,6 +760,126 @@ package body Langkit_Support.Generic_API.Unparsing is
736760
end if;
737761
end To_Struct_Member_Index;
738762

763+
-------------------
764+
-- Template_Kind --
765+
-------------------
766+
767+
function Template_Kind
768+
(JSON : JSON_Value) return Template_Parsing_State_Kind
769+
is
770+
Result : Template_Parsing_State_Kind := Simple_Recurse;
771+
Abort_Recursion : exception;
772+
773+
function Kind_Matches
774+
(JSON : JSON_Value; Kind : String) return Boolean;
775+
-- Assuming that JSON is an object, return whether it has a "kind"
776+
-- field equal to Kind.
777+
778+
procedure Process (JSON : JSON_Value);
779+
procedure Process_Map_Item (Name : String; JSON : JSON_Value);
780+
781+
------------------
782+
-- Kind_Matches --
783+
------------------
784+
785+
function Kind_Matches
786+
(JSON : JSON_Value; Kind : String) return Boolean
787+
is
788+
begin
789+
return JSON.Has_Field ("kind")
790+
and then JSON.Get ("kind").Kind = JSON_String_Type
791+
and then String'(JSON.Get ("kind")) = Kind;
792+
end Kind_Matches;
793+
794+
-------------
795+
-- Process --
796+
-------------
797+
798+
procedure Process (JSON : JSON_Value) is
799+
begin
800+
case JSON.Kind is
801+
when JSON_Object_Type =>
802+
803+
-- As soon as we find a "text" template node or a
804+
-- "recurse_field" one, we know this is a "recurse_field"
805+
-- template.
806+
807+
if Kind_Matches (JSON, "text")
808+
or else Kind_Matches (JSON, "recurse_field")
809+
then
810+
Result := Recurse_Field;
811+
raise Abort_Recursion;
812+
end if;
813+
814+
JSON.Map_JSON_Object (Process_Map_Item'Access);
815+
816+
when JSON_Array_Type =>
817+
for Item of JSON_Array'(JSON.Get) loop
818+
Process (Item);
819+
end loop;
820+
821+
when others =>
822+
null;
823+
end case;
824+
end Process;
825+
826+
----------------------
827+
-- Process_Map_Item --
828+
----------------------
829+
830+
procedure Process_Map_Item (Name : String; JSON : JSON_Value) is
831+
pragma Unreferenced (Name);
832+
begin
833+
Process (JSON);
834+
end Process_Map_Item;
835+
836+
begin
837+
Process (JSON);
838+
return Result;
839+
exception
840+
when Abort_Recursion =>
841+
return Result;
842+
end Template_Kind;
843+
844+
-----------------------
845+
-- Initial_State_For --
846+
-----------------------
847+
848+
function Initial_State_For
849+
(Node : Type_Ref;
850+
JSON : JSON_Value;
851+
Context : Template_Parsing_Context) return Template_Parsing_State is
852+
begin
853+
return Result : Template_Parsing_State (Template_Kind (JSON)) do
854+
case Result.Kind is
855+
when Simple_Recurse =>
856+
Result.Recurse_Found := False;
857+
858+
when Recurse_Field =>
859+
-- Ensure that "recurse_field" templates are valid for this
860+
-- node.
861+
862+
if Is_Abstract (Node) then
863+
Abort_Parsing
864+
(Context,
865+
"text/recurse_field are valid for concrete nodes"
866+
& " only");
867+
elsif Is_Token_Node (Node) then
868+
Abort_Parsing
869+
(Context,
870+
"text/recurse_field are not valid for token nodes");
871+
elsif Is_List_Node (Node) then
872+
Abort_Parsing
873+
(Context,
874+
"text/recurse_field are not valid for list nodes");
875+
end if;
876+
877+
Result.Linear_Template := Linear_Template (Node);
878+
Result.Linear_Position := 1;
879+
end case;
880+
end return;
881+
end Initial_State_For;
882+
739883
--------------------
740884
-- Parse_Template --
741885
--------------------
@@ -763,13 +907,14 @@ package body Langkit_Support.Generic_API.Unparsing is
763907
end loop;
764908

765909
case Context.State.Kind is
766-
when Initial =>
767-
Abort_Parsing (Context, "recursion is missing");
768-
769-
when Recurse_Found =>
770-
return (Kind => With_Recurse, Root => Root);
910+
when Simple_Recurse =>
911+
if Context.State.Recurse_Found then
912+
return (Kind => With_Recurse, Root => Root);
913+
else
914+
Abort_Parsing (Context, "recursion is missing");
915+
end if;
771916

772-
when For_Recurse_Field =>
917+
when Recurse_Field =>
773918

774919
-- Make sure that the template covers all items in the linear
775920
-- template.
@@ -1207,15 +1352,18 @@ package body Langkit_Support.Generic_API.Unparsing is
12071352
procedure Process_Recurse (Context : in out Template_Parsing_Context) is
12081353
begin
12091354
case Context.State.Kind is
1210-
when Initial =>
1211-
Context.State := (Kind => Recurse_Found);
1212-
when Recurse_Found =>
1213-
Abort_Parsing (Context, "too many recursions");
1214-
when For_Recurse_Field =>
1355+
when Simple_Recurse =>
1356+
if Context.State.Recurse_Found then
1357+
Abort_Parsing (Context, "too many recursions");
1358+
else
1359+
Context.State.Recurse_Found := True;
1360+
end if;
1361+
1362+
when Recurse_Field =>
12151363
Abort_Parsing
12161364
(Context,
12171365
"using ""recurse""/""recurse_flatten"" in the same template"
1218-
& " as ""recurse_field""/tokens is invalid");
1366+
& " as ""recurse_field""/""text"" is invalid");
12191367
end case;
12201368
end Process_Recurse;
12211369

@@ -1230,50 +1378,16 @@ package body Langkit_Support.Generic_API.Unparsing is
12301378
function What return String
12311379
is (case Item.Kind is
12321380
when Token_Item => "text",
1233-
when Field_Item => """recurse_field""");
1381+
when Field_Item => "recurse_field");
12341382
begin
1235-
-- Ensure that the node that the context for which this template is
1236-
-- parsed supports tokens/"recurse_field" templates.
1383+
-- Ensure that it is valid to have a "recurse_field" node in this
1384+
-- template.
12371385

1238-
if Context.Kind /= Node_Template then
1239-
Abort_Parsing
1240-
(Context,
1241-
What & " is valid in ""node"" templates only");
1242-
elsif Is_Abstract (Context.Node) then
1243-
Abort_Parsing
1244-
(Context,
1245-
What & " is valid for concrete nodes only");
1246-
elsif Is_Token_Node (Context.Node) then
1247-
Abort_Parsing
1248-
(Context,
1249-
What & " is not valid for token nodes");
1250-
elsif Is_List_Node (Context.Node) then
1386+
if Context.State.Kind /= Recurse_Field then
12511387
Abort_Parsing
1252-
(Context,
1253-
What & " is not valid for list nodes");
1388+
(Context, What & " cannot appear in a ""recurse"" template");
12541389
end if;
12551390

1256-
-- Ensure that this template does not already has a "recurse"
1257-
-- template, and that we have a linear template for validation.
1258-
1259-
case Context.State.Kind is
1260-
when Initial =>
1261-
Context.State :=
1262-
(Kind => For_Recurse_Field,
1263-
Linear_Template => Linear_Template
1264-
(Context.Node),
1265-
Linear_Position => 1);
1266-
1267-
when Recurse_Found =>
1268-
Abort_Parsing
1269-
(Context,
1270-
"""recurse""/""recurse_flatten"" and " & What
1271-
& " cannot appear in the same template");
1272-
1273-
when For_Recurse_Field =>
1274-
null;
1275-
end case;
1276-
12771391
-- Now validate this new item: it must match what the linear template
12781392
-- expects next.
12791393

@@ -1363,7 +1477,7 @@ package body Langkit_Support.Generic_API.Unparsing is
13631477
To_Struct_Member_Index (Name, Node);
13641478
Context : Template_Parsing_Context :=
13651479
(Kind => Field_Template,
1366-
State => Initial_State,
1480+
State => (Simple_Recurse, Recurse_Found => False),
13671481
Node => Node,
13681482
Field => From_Index (Language, Member));
13691483
begin
@@ -1441,21 +1555,29 @@ package body Langkit_Support.Generic_API.Unparsing is
14411555
procedure Process (Name : String; Value : JSON_Value) is
14421556
Key : constant Type_Index := To_Type_Index (Name);
14431557
Node : constant Type_Ref := From_Index (Language, Key);
1444-
Context : Template_Parsing_Context :=
1445-
(Kind => Node_Template,
1446-
Node => Node,
1447-
State => Initial_State);
14481558
Config : constant Node_Config_Access := new Node_Config_Record'
14491559
(Node_Template => No_Template,
14501560
Field_Configs => <>,
14511561
List_Sep => No_Template);
14521562
begin
14531563
Result.Node_Configs.Insert (Key, Config);
14541564

1455-
Config.Node_Template :=
1456-
(if Value.Has_Field ("node")
1457-
then Parse_Template (Value.Get ("node"), Context)
1458-
else Pool.Create_Recurse);
1565+
if Value.Has_Field ("node") then
1566+
declare
1567+
JSON_Template : constant JSON_Value := Value.Get ("node");
1568+
Context : Template_Parsing_Context :=
1569+
(Kind => Node_Template,
1570+
Node => Node,
1571+
State => <>);
1572+
begin
1573+
Context.State :=
1574+
Initial_State_For (Node, JSON_Template, Context);
1575+
Config.Node_Template :=
1576+
Parse_Template (JSON_Template, Context);
1577+
end;
1578+
else
1579+
Config.Node_Template := Pool.Create_Recurse;
1580+
end if;
14591581

14601582
if Value.Has_Field ("fields") then
14611583
Load_Field_Configs
@@ -1472,7 +1594,8 @@ package body Langkit_Support.Generic_API.Unparsing is
14721594
Context : Template_Parsing_Context :=
14731595
(Kind => Sep_Template,
14741596
Node => Node,
1475-
State => Initial_State);
1597+
State => (Kind => Simple_Recurse,
1598+
Recurse_Found => False));
14761599
begin
14771600
Config.List_Sep :=
14781601
Parse_Template (Value.Get ("sep"), Context);

testsuite/tests/ada_api/unparsing/test.out

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,21 +77,21 @@ a: Int=2
7777
# invalid_innerroot.json
7878
template for VarDecl.f_name: missing "contents" key for innerRoot
7979
# invalid_recurse_field.json
80-
template for VarDecl.f_name: "recurse_field" is valid in "node" templates only
80+
template for VarDecl.f_name: recurse_field cannot appear in a "recurse" template
8181
# invalid_recurse_field2.json
8282
"node" template for Stmt: missing "field" key for recurse_field
8383
# invalid_recurse_field3.json
8484
"node" template for Stmt: invalid recurse_field field: JSON_NULL_TYPE
8585
# invalid_recurse_field4.json
86-
"node" template for Decl: "recurse_field" is valid for concrete nodes only
86+
"node" template for Decl: text/recurse_field are valid for concrete nodes only
8787
# invalid_recurse_field5.json
88-
"node" template for StmtList: text is not valid for list nodes
88+
"node" template for StmtList: text/recurse_field are not valid for list nodes
8989
# invalid_recurse_field6.json
9090
invalid field for Stmt: f_foo
9191
# invalid_recurse_field7.json
92-
"node" template for Name: text is not valid for token nodes
92+
"node" template for Name: text/recurse_field are not valid for token nodes
9393
# invalid_recurse_field8.json
94-
"node" template for VarDecl: "recurse"/"recurse_flatten" and text cannot appear in the same template
94+
"node" template for VarDecl: using "recurse"/"recurse_flatten" in the same template as "recurse_field"/"text" is invalid
9595
# invalid_recurse_field9.json
9696
"node" template for VarDecl: token "var" expected, got field f_name
9797
Expected:

0 commit comments

Comments
 (0)