Replies: 3 comments 10 replies
-
There have been discussions in the past regarding this. In this mailing list discussion, José mentions some of the reasons why literals are unwrapped. To summarize, they optimized the AST for macro creation, not for reconstruction or for other kind of use cases. For example, if you were to write a macro that used the following syntax: schema "some_table" do
field :foo, :string
end You write the macro clause like so: defmacro schema(table, do: block) do
# ...
end But if every node were wrapped, you'd have to write something like this: defmacro foo({:"[]", _, [{{{:__atom__, :do}, _, _}, block}]}) do
# ...
end More complex scenarios would require much more verbose pattern matching. The truth is that pattern matching shines on identifying relatively simple patterns, and not so much when the structure is quite nested, due to the lack of composability of patterns out of the box.
This happens because in Elixir, most of the constructs are implemented as macros, which can be overrided in some contexts, and the pipe operator is no exception. The Elixir AST is a bit "lispy" in this regard, and something like an "Expression node" doesn't make that much sense. Syntax can mean completely different things depending on the context. It's cumbersome in many cases, yes, but it's what we have. This caused me troubles too when writing a function that converts usage of So while I agree that the situation is not ideal for tools like RuboCop or Sourceror, this design is used to great extent to extend the language. There are other issues like comments being discarded in the AST, so I had to also send some PRs to Elixir so we can preserve them. The purpose of Sourceror is to have an AST more aligned with your idea of "every node is wrapped". So in Sourceror As far as I know, we're the first to do this kind of stuff for Elixir, so there's a lot of work to do. My focus so far was in providing all the necessary primitives so we can start building more complex tools. For example, the Zipper is technically capable of navigating the AST and find a keyword list that contains a given key, but there's not a set of predicates or DSL to make it easier yet: alias Sourceror.Zipper, as: Z
def go_to_kw_element(ast, key) when is_atom(key) do
ast
# Create the zipper from the AST
|> Z.zip()
# Find a list node
|> Z.find(&match?({:__block__, _, [items]} when is_list(items), &1))
# Find an element that has the given `key`
|> Z.find(&match?({{:__block__, _, [^key]}, _}, &1))
end The code is still a bit verbose, and |
Beta Was this translation helpful? Give feedback.
-
Another comparable for consideration: the Query Language used by Treesitter. |
Beta Was this translation helpful? Give feedback.
-
We can create collaboration to use Pathex for AST traversal. I already have an example, but I am unable to test it properly, since |
Beta Was this translation helpful? Give feedback.
-
From #14
It's true that the builtin pattern matching makes this easier to do in elixir (Ruby got pattern matching too though), but in my experience matching on different levels or for things that are not supported (e.g. keyword matching
foo
, or map/keyword with key matchingfoo
) that it shines ("any child" / "last child", etc.).My first impression of the elixir AST is that of sore disappointment though. The metadata is missing the equivalent of
parser
's SourceMap nor access to their source at all.Compared to:
This shows also that literals are unwrapped (e.g.
42
vs{:int, _, 42}
), which seems (to my newbie eyes) as a dire mistake, which compounds with the previous point.Also, I didn't test much, but the fact that
42 |> IO.inspect
andIO.inspect(42)
do not produce the same AST structure (meta data excluded) seems to also be a grave error (again, in my newbie's eyes). I fail to see how these could possible behave differently.Finally, macros complicate things, as it makes
~w[a b]
and["a", "b"]
theoretically different even though they should be for most practical purposes considered equivalent.That's correct, I'd use
Zipper
.I wanted to do something like
graspjs
for Ruby, but never managed to do it and now I'm doing more elixirBeta Was this translation helpful? Give feedback.
All reactions