@@ -175,6 +175,7 @@ defmodule Spitfire do
175175 :")" -> parser
176176 :"]" -> parser
177177 :"}" -> parser
178+ :end -> parser
178179 _ -> next_token ( parser )
179180 end
180181
@@ -313,15 +314,17 @@ defmodule Spitfire do
313314 defp parse_comma_list ( parser , opts \\ [ ] ) do
314315 opts = Keyword . put ( opts , :precedence , @ comma )
315316 { expr , parser } = parse_expression ( parser , opts )
316- items = [ expr ]
317+ # we zip together the expression and parser state so that we can potentially
318+ # backtrack later
319+ items = [ { expr , parser } ]
317320
318321 { items , parser } =
319322 while peek_token ( parser ) == :"," <- { items , parser } do
320323 parser = parser |> next_token ( ) |> next_token ( )
321324
322325 { item , parser } = parse_expression ( parser , opts )
323326
324- { [ item | items ] , parser }
327+ { [ { item , parser } | items ] , parser }
325328 end
326329
327330 { Enum . reverse ( items ) , parser }
@@ -472,6 +475,7 @@ defmodule Spitfire do
472475 defp parse_comma ( parser , lhs ) do
473476 parser = parser |> next_token ( ) |> eat_eol ( )
474477 { exprs , parser } = parse_comma_list ( parser )
478+ { exprs , _ } = Enum . unzip ( exprs )
475479
476480 { { :comma , [ ] , [ lhs | exprs ] } , eat_eol ( parser ) }
477481 end
@@ -654,8 +658,6 @@ defmodule Spitfire do
654658
655659 parser = dec_stab_depth ( parser )
656660
657- dbg ( parser )
658-
659661 parser =
660662 case peek_token ( parser ) do
661663 :end ->
@@ -676,6 +678,7 @@ defmodule Spitfire do
676678 { ast , next_token ( parser ) }
677679 else
678680 { pairs , parser } = parse_comma_list ( parser |> next_token ( ) |> eat_eol ( ) )
681+ { pairs , _ } = Enum . unzip ( pairs )
679682 ast = { { :. , [ ] , [ lhs ] } , [ ] , pairs }
680683
681684 { ast , parser |> next_token ( ) |> eat_eol ( ) }
@@ -802,6 +805,7 @@ defmodule Spitfire do
802805 { { :%{} , meta , [ ] } , parser }
803806 else
804807 { pairs , parser } = parse_comma_list ( parser , is_map: true )
808+ { pairs , _ } = Enum . unzip ( pairs )
805809
806810 parser = eat_at ( parser , :eol , 1 )
807811
@@ -820,29 +824,71 @@ defmodule Spitfire do
820824
821825 defp parse_tuple_literal ( % { current_token: { :"{" , _ } } = parser ) do
822826 meta = current_meta ( parser )
827+ orig_parser = parser
823828 parser = parser |> next_token ( ) |> eat_eol ( )
824829
825- if current_token ( parser ) == :"}" do
826- { { :{} , meta , [ ] } , parser }
827- else
828- { pairs , parser } = parse_comma_list ( parser )
830+ cond do
831+ current_token ( parser ) == :"}" ->
832+ { { :{} , meta , [ ] } , parser }
829833
830- parser = eat_at ( parser , :eol , 1 )
834+ current_token ( parser ) in [ :end , :"]" , :")" ] ->
835+ # if the current token is the wrong kind of ending delimiter, we revert to the previous parser
836+ # state, put an error, and inject a closing brace to simulate a completed tuple
837+ parser = put_error ( orig_parser , { meta , "missing closing brace for tuple" } )
831838
832- parser =
833- case peek_token ( parser ) do
834- :"}" ->
835- parser |> next_token ( ) |> eat_eol ( )
839+ parser = next_token ( parser )
836840
837- _ ->
838- put_error ( parser , { current_meta ( parser ) , "missing closing brace for tuple" } )
839- end
841+ parser =
842+ parser
843+ |> put_in ( [ :current_token ] , { :fake_closing_brace , nil } )
844+ |> put_in ( [ :peek_token ] , parser . current_token )
845+ |> update_in ( [ :tokens ] , & [ parser . peek_token | & 1 ] )
840846
841- if length ( pairs ) == 2 do
842- { pairs |> List . wrap ( ) |> List . to_tuple ( ) , parser }
843- else
844- { { :{} , meta , List . wrap ( pairs ) } , parser }
845- end
847+ { { :{} , meta , [ ] } , parser }
848+
849+ true ->
850+ { pairs , parser } = parse_comma_list ( parser )
851+
852+ parser = eat_at ( parser , :eol , 1 )
853+
854+ { pairs , parser } =
855+ case peek_token ( parser ) do
856+ :"}" ->
857+ pairs = pairs |> Enum . unzip ( ) |> elem ( 0 )
858+ { pairs , parser |> next_token ( ) |> eat_eol ( ) }
859+
860+ _ ->
861+ [ { potential_error , parser } , { item , parser_for_errors } | rest ] = all_pairs = Enum . reverse ( pairs )
862+
863+ # if the last item is an unknown token error, that means that it parsed past the
864+ # recovery point and we need to insert a fake closing brace, and backtrack
865+ # the errors from the previous item.
866+ { pairs , parser } =
867+ case potential_error do
868+ { :__error__ , _ , [ "unknown token: " <> _ ] } ->
869+ { [ { item , parser } | rest ] ,
870+ parser
871+ |> put_in ( [ :current_token ] , { :fake_closing_brace , nil } )
872+ |> put_in ( [ :peek_token ] , parser . current_token )
873+ |> put_in ( [ :errors ] , parser_for_errors . errors )
874+ |> update_in ( [ :tokens ] , & [ parser . peek_token | & 1 ] ) }
875+
876+ _ ->
877+ { all_pairs , parser }
878+ end
879+
880+ parser = put_error ( parser , { meta , "missing closing brace for tuple" } )
881+
882+ { pairs , _ } = pairs |> Enum . reverse ( ) |> Enum . unzip ( )
883+
884+ { pairs , parser }
885+ end
886+
887+ if length ( pairs ) == 2 do
888+ { pairs |> List . wrap ( ) |> List . to_tuple ( ) , parser }
889+ else
890+ { { :{} , meta , List . wrap ( pairs ) } , parser }
891+ end
846892 end
847893 end
848894
@@ -853,6 +899,7 @@ defmodule Spitfire do
853899 { [ ] , parser }
854900 else
855901 { pairs , parser } = parse_comma_list ( parser , is_list: true )
902+ { pairs , _ } = Enum . unzip ( pairs )
856903
857904 parser = eat_at ( parser , :eol , 1 )
858905
@@ -889,6 +936,8 @@ defmodule Spitfire do
889936 |> eat_eol ( )
890937 |> parse_comma_list ( )
891938
939+ { pairs , _ } = Enum . unzip ( pairs )
940+
892941 parser = eat_at ( parser , :eol , 1 )
893942
894943 parser =
@@ -974,7 +1023,8 @@ defmodule Spitfire do
9741023 parser = pop_nesting ( parser )
9751024
9761025 if parser . nestings == [ ] && current_token ( parser ) == :do do
977- parse_do_block ( parser , { token , meta , Enum . reverse ( args ) } )
1026+ res = parse_do_block ( parser , { token , meta , Enum . reverse ( args ) } )
1027+ res
9781028 else
9791029 { { token , meta , Enum . reverse ( args ) } , parser }
9801030 end
@@ -1093,8 +1143,6 @@ defmodule Spitfire do
10931143 end
10941144
10951145 def eat_at ( % { tokens: tokens } = parser , token , idx ) when is_list ( tokens ) do
1096- dbg ( )
1097-
10981146 tokens =
10991147 case Enum . at ( tokens , idx ) do
11001148 { ^ token , _ , _ } ->
0 commit comments