Skip to content

Commit c96069e

Browse files
committed
fix: parse newline ternary continuation after ellipsis
Ellipsis was treated as standalone before `//` when the ternary operator arrived with newline metadata, producing a mismatched AST for forms like `x...\n//y`. Detect newline-associated ternary continuation and keep existing semicolon-separated behavior intact (e.g. `x...;//y`). Adds a regression assertions in the property-regression test block.
1 parent ec594dd commit c96069e

2 files changed

Lines changed: 32 additions & 8 deletions

File tree

lib/spitfire.ex

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2920,14 +2920,18 @@ defmodule Spitfire do
29202920

29212921
defp parse_ellipsis_op(parser) do
29222922
trace "parse_ellipsis_op", trace_meta(parser) do
2923-
peek = peek_token_type(parser)
2924-
2925-
# `...` is standalone when followed by a terminal, stab op, keyword
2926-
# or binary operators (except :dual_op)
2927-
if MapSet.member?(@terminals_with_comma, peek_token(parser)) or
2928-
peek_token(parser) == :";" or
2929-
peek in [:stab_op, :do, :end, :block_identifier] or
2930-
(is_binary_op?(peek) and peek != :dual_op) do
2923+
peek_type = peek_token_type(parser)
2924+
peek = peek_token(parser)
2925+
2926+
standalone? =
2927+
MapSet.member?(@terminals_with_comma, peek) or
2928+
peek == :";" or
2929+
peek_type in [:stab_op, :do, :end, :block_identifier] or
2930+
(is_binary_op?(peek_type) and peek_type != :dual_op)
2931+
2932+
# `...` is standalone when followed by a terminal, stab op, keyword,
2933+
# or binary operator (except :dual_op), unless it continues a newline ternary (`//`).
2934+
if standalone? and not newline_ternary_continuation?(parser) do
29312935
{{:..., current_meta(parser), []}, parser}
29322936
else
29332937
meta = current_meta(parser)
@@ -2953,6 +2957,22 @@ defmodule Spitfire do
29532957
end
29542958
end
29552959

2960+
defp newline_ternary_continuation?(parser) do
2961+
cond do
2962+
peek_token(parser) == :eol ->
2963+
peek_token_skip_eoe(parser) == :ternary_op
2964+
2965+
peek_token_type(parser) == :ternary_op ->
2966+
case parser |> next_token() |> current_newlines() do
2967+
nl when is_integer(nl) and nl > 0 -> true
2968+
_ -> false
2969+
end
2970+
2971+
true ->
2972+
false
2973+
end
2974+
end
2975+
29562976
# Formats a struct type AST to a string for error messages
29572977
defp format_struct_type({:__aliases__, _, parts}) do
29582978
Enum.map_join(parts, ".", fn

test/spitfire_test.exs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2300,6 +2300,10 @@ defmodule SpitfireTest do
23002300
assert Spitfire.parse("%e.(){}") == s2q("%e.(){}")
23012301
assert Spitfire.parse("%e.(1){}") == s2q("%e.(1){}")
23022302
assert Spitfire.parse("%e.(a, b){}") == s2q("%e.(a, b){}")
2303+
2304+
# Ellipsis + ternary edge cases (newline and semicolon-separated)
2305+
assert Spitfire.parse("x...\n//y") == s2q("x...\n//y")
2306+
assert Spitfire.parse("x...;//y") == s2q("x...;//y")
23032307
end
23042308
end
23052309

0 commit comments

Comments
 (0)