From fb29719b1ce83fca511c1f310b388e0af65da257 Mon Sep 17 00:00:00 2001 From: Jed Barber Date: Thu, 10 Dec 2020 23:21:56 +1100 Subject: Bugfixes and extra parser combinators --- src/packrat-parse_graphs.adb | 1 + src/packrat-parsers.adb | 295 +++++++++++++++++++++++++++++++++++-------- src/packrat-parsers.ads | 137 +++++++++++++++++++- 3 files changed, 375 insertions(+), 58 deletions(-) diff --git a/src/packrat-parse_graphs.adb b/src/packrat-parse_graphs.adb index 99ce360..434c0a0 100644 --- a/src/packrat-parse_graphs.adb +++ b/src/packrat-parse_graphs.adb @@ -246,6 +246,7 @@ package body Packrat.Parse_Graphs is SU.Append (Result, Latin.LF & Latin.LF); end loop; SU.Delete (Result, SU.Length (Result) - 1, SU.Length (Result)); + SU.Append (Result, Latin.LF); return SU.To_String (Result); end Debug_String; diff --git a/src/packrat-parsers.adb b/src/packrat-parsers.adb index 60102f4..875a765 100644 --- a/src/packrat-parsers.adb +++ b/src/packrat-parsers.adb @@ -324,7 +324,7 @@ package body Packrat.Parsers is Adjust.Clear; for N of Temp.Results loop Adjust.Include - ((Finish => N.Finish, + ((Finish => Integer'Max (R.Finish, N.Finish), Value => Elem_Holds.To_Holder (Element (R.Value) & Element (N.Value)), Tokens => Tok_Holds.To_Holder (Element (R.Tokens) & Element (N.Tokens)))); end loop; @@ -431,6 +431,9 @@ package body Packrat.Parsers is end Finish_Root; + + + package body Parse_Parts is Context : Parser_Context := Empty_Context; @@ -601,13 +604,6 @@ package body Packrat.Parsers is Salt : Combinator_Result := Combo (Input, Context, Start); Processed : Result_Sets.Set; begin - if Salt.Status = Failure then - Ada.Strings.Unbounded.Append - (Context.Error_String, - Packrat.Errors.Encode (Traits.Label_Enum'Image (Label), Start)); - else - Context.Error_String := +""; - end if; for R of Salt.Results loop Processed.Include ((Finish => R.Finish, @@ -691,6 +687,32 @@ package body Packrat.Parsers is end Sequence; + -- This exists purely to get around errors that would otherwise + -- result from using Sequence internally due to the access types. + function Sequence_2 + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result + is + function Actual + (Context : in out Parser_Context) + return Combinator_Result + is + function Cont_Param is new Continue (Part_Two); + Salt : Combinator_Result; + begin + Salt := Part_One (Input, Context, Start); + Salt := Cont_Param (Salt, Input, Context); + return Salt; + end Actual; + function Curt is new Curtailment (To_Key (Start, Sequence_2'Access), Input, Actual); + function Memo is new Memoize (To_Key (Start, Sequence_2'Access), Curt); + begin + return Memo (Context); + end Sequence_2; + + function Choice (Input : in Traits.Element_Array; Context : in out Parser_Context; @@ -721,6 +743,28 @@ package body Packrat.Parsers is end Choice; + -- This exists because otherwise Sequence_2 would look weird. + function Choice_2 + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result + is + function Actual + (Context : in out Parser_Context) + return Combinator_Result is + begin + return Merge + (Choice_One (Input, Context, Start), + Choice_Two (Input, Context, Start)); + end Actual; + function Curt is new Curtailment (To_Key (Start, Choice_2'Access), Input, Actual); + function Memo is new Memoize (To_Key (Start, Choice_2'Access), Curt); + begin + return Memo (Context); + end Choice_2; + + function Count (Input : in Traits.Element_Array; Context : in out Parser_Context; @@ -742,6 +786,13 @@ package body Packrat.Parsers is while Salt.Status /= Failure loop Counter := Counter + 1; exit when Counter = Number; + if Salt.Status = Optional_More or Salt.Status = Needs_More or + (for some P of Salt.Results => P.Finish = Input'Last) + then + Salt.Results.Clear; + Salt.Status := Needs_More; + exit; + end if; Salt := Cont_Param (Salt, Input, Context); end loop; Complete_Status (Salt, Context.Allow_Incomplete); @@ -770,7 +821,7 @@ package body Packrat.Parsers is Counter : Natural := 0; begin if Start > Input'Last then - return Empty_Fail; + return Salt; end if; if Minimum = 0 then Merge (Salt, Empty (Input, Context, Start)); @@ -780,7 +831,15 @@ package body Packrat.Parsers is Counter := Counter + 1; if Counter >= Minimum then Merge (Salt, Temp); - elsif Temp.Status = Optional_More or Temp.Status = Needs_More then + if Temp.Status = Optional_More or Temp.Status = Needs_More or + (for some P of Temp.Results => P.Finish = Input'Last) + then + Salt.Status := Optional_More; + exit; + end if; + elsif Temp.Status = Optional_More or Temp.Status = Needs_More or + (for some P of Temp.Results => P.Finish = Input'Last) + then Salt.Status := Needs_More; exit; end if; @@ -796,6 +855,75 @@ package body Packrat.Parsers is end Many; + function Followed_By + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result + is + function Actual + (Context : in out Parser_Context) + return Combinator_Result is + begin + case Param (Input, Context, Start).Status is + when Success | Optional_More => + return Empty (Input, Context, Start); + when Needs_More => + return Salt : Combinator_Result do + if Context.Allow_Incomplete then + Salt.Status := Needs_More; + end if; + end return; + when Failure => + return Salt : Combinator_Result do + if Context.Allow_Incomplete and Start > Input'Last then + Salt.Status := Needs_More; + end if; + end return; + end case; + end Actual; + function Curt is new Curtailment (To_Key (Start, Followed_By'Access), Input, Actual); + function Memo is new Memoize (To_Key (Start, Followed_By'Access), Curt); + begin + return Memo (Context); + end Followed_By; + + + function Not_Followed_By + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result + is + function Actual + (Context : in out Parser_Context) + return Combinator_Result is + begin + case Param (Input, Context, Start).Status is + when Success | Optional_More => + return Salt : Combinator_Result; + when Needs_More => + if Context.Allow_Incomplete then + return Salt : Combinator_Result do + Salt.Status := Needs_More; + end return; + else + return Empty (Input, Context, Start); + end if; + when Failure => + return Empty (Input, Context, Start); + end case; + end Actual; + function Curt is new Curtailment (To_Key (Start, Not_Followed_By'Access), Input, Actual); + function Memo is new Memoize (To_Key (Start, Not_Followed_By'Access), Curt); + begin + return Memo (Context); + end Not_Followed_By; + + + + + function Many_Until (Input : in Traits.Element_Array; Context : in out Parser_Context; @@ -806,41 +934,11 @@ package body Packrat.Parsers is (Context : in out Parser_Context) return Combinator_Result is - function Not_Empty_Param is new Not_Empty (Param); - function Cont_Param is new Continue (Not_Empty_Param); - Salt, Temp : Combinator_Result; - Adjust : Result_Sets.Set; - Counter : Natural := 0; + function Not_Till is new Not_Followed_By (Test); + function Till is new Followed_By (Test); + function Sep_End_By is new Separate_End_By (Param, Not_Till, Till, Minimum); begin - if Start > Input'Last then - return Empty_Fail; - end if; - if Minimum = 0 then - Merge (Salt, Empty (Input, Context, Start)); - end if; - if Test (Input (Start)) then - return Salt; - end if; - Temp := Not_Empty_Param (Input, Context, Start); - while Temp.Status /= Failure loop - Counter := Counter + 1; - if Counter >= Minimum then - Merge (Salt, Temp); - elsif Temp.Status = Optional_More or Temp.Status = Needs_More then - Salt.Status := Needs_More; - exit; - end if; - Adjust.Clear; - for R of Temp.Results loop - if R.Finish = Input'Last or else not Test (Input (R.Finish + 1)) then - Adjust.Include (R); - end if; - end loop; - Temp.Results.Assign (Adjust); - Temp := Cont_Param (Temp, Input, Context); - end loop; - Complete_Status (Salt, Context.Allow_Incomplete); - return Salt; + return Sep_End_By (Input, Context, Start); end Actual; function Curt is new Curtailment (To_Key (Start, Many_Until'Access), Input, Actual); function Memo is new Memoize (To_Key (Start, Many_Until'Access), Curt); @@ -876,6 +974,81 @@ package body Packrat.Parsers is end Optional; + function Separate_By + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result + is + function Actual + (Context : in out Parser_Context) + return Combinator_Result + is + function Not_Empty_Item is new Not_Empty (Item); + function Sep_Seq is new Sequence_2 (Separator, Not_Empty_Item); + function Many_Sep_Seq is new Many + (Sep_Seq, (if Minimum = 0 then Minimum else Minimum - 1)); + function Full_Seq is new Sequence_2 (Not_Empty_Item, Many_Sep_Seq); + begin + if Minimum = 0 then + return Merge (Empty (Input, Context, Start), Full_Seq (Input, Context, Start)); + else + return Full_Seq (Input, Context, Start); + end if; + end Actual; + function Curt is new Curtailment (To_Key (Start, Separate_By'Access), Input, Actual); + function Memo is new Memoize (To_Key (Start, Separate_By'Access), Curt); + begin + return Memo (Context); + end Separate_By; + + + function Separate_End_By + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result + is + function Actual + (Context : in out Parser_Context) + return Combinator_Result + is + function Sep_By is new Separate_By (Item, Separator, Minimum); + function End_Seq is new Sequence_2 (Sep_By, Ender); + begin + return End_Seq (Input, Context, Start); + end Actual; + function Curt is new Curtailment (To_Key (Start, Separate_End_By'Access), Input, Actual); + function Memo is new Memoize (To_Key (Start, Separate_End_By'Access), Curt); + begin + return Memo (Context); + end Separate_End_By; + + + function Between + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result + is + function Actual + (Context : in out Parser_Context) + return Combinator_Result + is + function Ignore_Start is new Ignore (Starter); + function Ignore_End is new Ignore (Ender); + function Part_Seq is new Sequence_2 (Ignore_Start, Item); + function Full_Seq is new Sequence_2 (Part_Seq, Ignore_End); + begin + return Full_Seq (Input, Context, Start); + end Actual; + function Curt is new Curtailment (To_Key (Start, Between'Access), Input, Actual); + function Memo is new Memoize (To_Key (Start, Between'Access), Curt); + begin + return Memo (Context); + end Between; + + @@ -1045,24 +1218,22 @@ package body Packrat.Parsers is return Combinator_Result is Part : Combo_Result_Part; - My_Offset : Natural; begin if Start > Input'Last then - return Empty_Fail; + return Salt : Combinator_Result; end if; if Input'Last - Start < Number - 1 then - if not Context.Allow_Incomplete then - return Empty_Fail; - end if; - My_Offset := Input'Last - Start; - else - My_Offset := Number - 1; + return Salt : Combinator_Result do + if Context.Allow_Incomplete then + Salt.Status := Needs_More; + end if; + end return; end if; return Salt : Combinator_Result do - Part.Finish := Start + My_Offset; - Part.Value := Elem_Holds.To_Holder (Input (Start .. Start + My_Offset)); + Part.Finish := Start + Number - 1; + Part.Value := Elem_Holds.To_Holder (Input (Start .. Start + Number - 1)); Salt.Results.Include (Part); - Salt.Status := (if My_Offset < Number - 1 then Needs_More else Success); + Salt.Status := Success; end return; end Actual; function Call is new Memoize (To_Key (Start, Take'Access), Actual); @@ -1177,6 +1348,20 @@ package body Packrat.Parsers is end Not_Empty; + function End_Of_Input + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result is + begin + if Start > Input'Last then + return Empty (Input, Context, Start); + else + return Salt : Combinator_Result; + end if; + end End_Of_Input; + + end Packrat.Parsers; diff --git a/src/packrat-parsers.ads b/src/packrat-parsers.ads index 03b262c..a98170f 100644 --- a/src/packrat-parsers.ads +++ b/src/packrat-parsers.ads @@ -113,7 +113,6 @@ package Packrat.Parsers is return Combinator_Result; generic - Label : in Traits.Label_Enum; with function Combo (Input : in Traits.Element_Array; Context : in out Parser_Context; @@ -153,6 +152,23 @@ package Packrat.Parsers is Start : in Positive) return Combinator_Result; + generic + with function Part_One + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result; + with function Part_Two + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result; + function Sequence_2 + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result; + generic Params : in Combinator_Array; function Choice @@ -161,6 +177,23 @@ package Packrat.Parsers is Start : in Positive) return Combinator_Result; + generic + with function Choice_One + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result; + with function Choice_Two + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result; + function Choice_2 + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result; + generic with function Param (Input : in Traits.Element_Array; @@ -187,6 +220,33 @@ package Packrat.Parsers is Start : in Positive) return Combinator_Result; + generic + with function Param + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result; + function Followed_By + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result; + + generic + with function Param + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result; + function Not_Followed_By + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result; + + + + generic with function Param (Input : in Traits.Element_Array; @@ -194,8 +254,10 @@ package Packrat.Parsers is Start : in Positive) return Combinator_Result; with function Test - (Item : in Traits.Element_Type) - return Boolean; + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result; Minimum : in Natural := 0; function Many_Until (Input : in Traits.Element_Array; @@ -215,6 +277,69 @@ package Packrat.Parsers is Start : in Positive) return Combinator_Result; + generic + with function Item + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result; + with function Separator + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result; + Minimum : in Natural := 0; + function Separate_By + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result; + + generic + with function Item + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result; + with function Separator + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result; + with function Ender + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result; + Minimum : in Natural := 0; + function Separate_End_By + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result; + + generic + with function Starter + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result; + with function Item + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result; + with function Ender + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result; + function Between + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result; + @@ -317,6 +442,12 @@ package Packrat.Parsers is Start : in Positive) return Combinator_Result; + function End_Of_Input + (Input : in Traits.Element_Array; + Context : in out Parser_Context; + Start : in Positive) + return Combinator_Result; + private -- cgit