Back to take 1

This commit is contained in:
José Valim
2026-06-25 21:31:19 +02:00
parent 332f2ca791
commit 42fb7d2be3
2 changed files with 48 additions and 23 deletions
+12 -10
View File
@@ -13,7 +13,6 @@ Nonterminals
expr container_expr block_expr access_expr
no_parens_expr no_parens_zero_expr no_parens_one_expr no_parens_one_ambig_expr
bracket_expr bracket_at_expr bracket_arg matched_expr unmatched_expr sub_matched_expr
at_stab_expr
unmatched_op_expr matched_op_expr no_parens_op_expr no_parens_many_expr
comp_op_eol at_op_eol unary_op_eol and_op_eol or_op_eol capture_op_eol
dual_op_eol mult_op_eol power_op_eol concat_op_eol xor_op_eol pipe_op_eol
@@ -113,7 +112,6 @@ expr_list -> expr_list eoe expr : ['$3' | annotate_eoe('$2', '$1')].
expr -> matched_expr : '$1'.
expr -> no_parens_expr : '$1'.
expr -> unmatched_expr : '$1'.
expr -> at_stab_expr : '$1'.
%% In Elixir we have three main call syntaxes: with parentheses,
%% without parentheses and with do blocks. They are represented
@@ -251,13 +249,6 @@ no_parens_op_expr -> arrow_op_eol no_parens_expr : warn_pipe('$1', '$2'), {'$1',
%% Allow when (and only when) with keywords
no_parens_op_expr -> when_op_eol call_args_no_parens_kw : {'$1', '$2'}.
at_stab_expr -> at_op_eol no_parens_one_expr stab_op_eol expr :
build_at_stab('$1', '$2', {'$3', '$4'}).
at_stab_expr -> at_op_eol no_parens_one_ambig_expr stab_op_eol expr :
build_at_stab('$1', '$2', {'$3', '$4'}).
at_stab_expr -> at_op_eol no_parens_many_expr stab_op_eol expr :
build_at_stab('$1', '$2', {'$3', '$4'}).
no_parens_one_ambig_expr -> dot_op_identifier call_args_no_parens_ambig : build_no_parens('$1', '$2').
no_parens_one_ambig_expr -> dot_identifier call_args_no_parens_ambig : build_no_parens('$1', '$2').
@@ -364,6 +355,12 @@ stab_expr -> empty_paren stab_op_eol_and_expr :
build_op_with_meta([], '$2', parens_meta('$1')).
stab_expr -> empty_paren when_op expr stab_op_eol_and_expr :
build_op_with_meta([{'when', meta_from_token('$2'), ['$3']}], '$4', parens_meta('$1')).
stab_expr -> at_op_eol no_parens_one_expr stab_op_eol_and_expr :
build_at_stab('$1', '$2', '$3').
stab_expr -> at_op_eol no_parens_one_ambig_expr stab_op_eol_and_expr :
build_at_stab('$1', '$2', '$3').
stab_expr -> at_op_eol no_parens_many_expr stab_op_eol_and_expr :
build_at_stab('$1', '$2', '$3').
stab_expr -> call_args_no_parens_all stab_op_eol_and_expr :
build_op(unwrap_when(unwrap_splice('$1')), '$2').
stab_expr -> stab_parens_many stab_op_eol_and_expr :
@@ -781,11 +778,16 @@ build_unary_op({_Kind, {Line, Column, _}, '//'}, Expr) ->
{'/', [{line, Line} | Outer], [{'/', [{line, Line} | Inner], nil}, Expr]};
build_unary_op({_Kind, Location, Op}, Expr) ->
{Op, meta_from_location(Location), [Expr]}.
{Op, meta_from_location(Location), [normalize_unary_op(Op, Expr)]}.
build_at_stab(At, {Call, Meta, Args}, {Op, Right}) ->
build_unary_op(At, {Call, Meta, [build_op(Args, Op, Right)]}).
normalize_unary_op('@', {Call, Meta, [[{'->', _, [_, _]} = Stab]]}) when is_atom(Call), is_list(Meta) ->
{Call, Meta, [Stab]};
normalize_unary_op(_, Expr) ->
Expr.
build_nullary_op({_Kind, Location, Op}) ->
{Op, meta_from_location(Location), []}.
@@ -7,8 +7,14 @@ defmodule Kernel.TypingSyntaxTest do
use ExUnit.Case, async: true
describe "@ with -> in quote" do
test "wraps the arrow as the attribute call argument at the root" do
assert parse_quote("@sig foo -> bar") ==
test "does not parse arrows at the root" do
assert_raise SyntaxError, ~r/syntax error before: '->'/, fn ->
parse_quote("@sig foo -> bar")
end
end
test "wraps parenthesized arrows as the attribute call argument at the root" do
assert parse_quote("(@sig foo -> bar)") ==
{:@, [line: 1],
[
{:sig, [line: 1],
@@ -16,8 +22,14 @@ defmodule Kernel.TypingSyntaxTest do
]}
end
test "wraps multi-argument arrows as the attribute call argument at the root" do
assert parse_quote("@sig foo, bar -> baz") ==
test "does not parse multi-argument arrows at the root" do
assert_raise SyntaxError, ~r/syntax error before: '->'/, fn ->
parse_quote("@sig foo, bar -> baz")
end
end
test "wraps parenthesized multi-argument arrows as the attribute call argument at the root" do
assert parse_quote("(@sig foo, bar -> baz)") ==
{:@, [line: 1],
[
{:sig, [line: 1],
@@ -159,14 +171,29 @@ defmodule Kernel.TypingSyntaxTest do
[
do:
{:@, [line: 2],
[
{:sig, [line: 2], [[{:->, [line: 2], [[], {:bar, [line: 2], nil}]}]]}
]}
[{:sig, [line: 2], [{:->, [line: 2], [[], {:bar, [line: 2], nil}]}]}]}
]
]}
end
test "allows explicit parenthesized arrows with arguments as attribute call arguments" do
test "normalizes parenthesized arrows to the same AST as arrow arguments" do
assert parse_quote("""
quote do
pre
@sig foo -> bar
post
end
""") ==
parse_quote("""
quote do
pre
@sig (foo -> bar)
post
end
""")
end
test "normalizes explicit parenthesized arrows with arguments as attribute call arguments" do
assert parse_quote("""
quote do
@sig (foo -> bar)
@@ -179,11 +206,7 @@ defmodule Kernel.TypingSyntaxTest do
{:@, [line: 2],
[
{:sig, [line: 2],
[
[
{:->, [line: 2], [[{:foo, [line: 2], nil}], {:bar, [line: 2], nil}]}
]
]}
[{:->, [line: 2], [[{:foo, [line: 2], nil}], {:bar, [line: 2], nil}]}]}
]}
]
]}