diff --git a/lib/spitfire.ex b/lib/spitfire.ex index b426836..0dbb370 100644 --- a/lib/spitfire.ex +++ b/lib/spitfire.ex @@ -1423,8 +1423,28 @@ defmodule Spitfire do {rhs, parser} end + parser = + if token == :"//" and not match?({:.., _, [_, _]}, lhs) do + put_error( + pre_parser, + {meta, + "the range step operator (//) must immediately follow the range definition operator (..), for example: 1..9//2. If you wanted to define a default argument, use (\\\\) instead. Syntax error before: '//'"} + ) + else + parser + end + ast = case token do + :"//" -> + case lhs do + {:.., lhs_meta, [left, middle]} -> + {:..//, lhs_meta, [left, middle, rhs]} + + _ -> + {token, newlines ++ meta, [lhs, rhs]} + end + :"not in" -> {:not, meta, [{:in, meta, [lhs, rhs]}]} @@ -2927,7 +2947,7 @@ defmodule Spitfire do if MapSet.member?(@terminals_with_comma, peek_token(parser)) or peek_token(parser) == :";" or peek in [:stab_op, :do, :end, :block_identifier] or - (is_binary_op?(peek) and peek != :dual_op) do + (is_binary_op?(peek) and peek not in [:dual_op, :ternary_op]) do {{:..., current_meta(parser), []}, parser} else meta = current_meta(parser) diff --git a/test/spitfire_test.exs b/test/spitfire_test.exs index 87eb6f4..7023904 100644 --- a/test/spitfire_test.exs +++ b/test/spitfire_test.exs @@ -2300,6 +2300,13 @@ defmodule SpitfireTest do assert Spitfire.parse("%e.(){}") == s2q("%e.(){}") assert Spitfire.parse("%e.(1){}") == s2q("%e.(1){}") assert Spitfire.parse("%e.(a, b){}") == s2q("%e.(a, b){}") + + # Ellipsis + ternary edge cases (newline and semicolon-separated) + assert Spitfire.parse("x...\n//y") == s2q("x...\n//y") + assert Spitfire.parse("x...;//y") == s2q("x...;//y") + assert Spitfire.parse("x...\n;//y") == s2q("x...\n;//y") + assert Spitfire.parse("x...\n;\n//y") == s2q("x...\n;\n//y") + assert Spitfire.parse("x...\n;\n# comment\n//y") == s2q("x...\n;\n# comment\n//y") end end @@ -2314,6 +2321,20 @@ defmodule SpitfireTest do [{[line: 1, column: 5], "unknown token: %"}]} end + test "range step operator requires a range lhs" do + code = "x...//y" + + assert {:error, _} = s2q(code) + assert {:error, _ast, errors} = Spitfire.parse(code) + + assert Enum.any?(errors, fn {_meta, message} -> + String.contains?( + message, + "the range step operator (//) must immediately follow the range definition operator (..)" + ) + end) + end + test "missing bitstring brackets" do code = """ <