Yes, but who turns <<a, b, c>> into: {:<<>>, [], [{:a, [], Elixir}, {:b, [], Elixir}, {:c, [], Elixir}]}? Isn’t it the parser? (I assume it is, even though I have no idea how Elixir parses Elixir).
This is not the same as turning: def d(x), do: x into:
{:def, [context: Elixir, import: Kernel],
[{:f, [context: Elixir], [{:x, [], Elixir}]}, [do: {:x, [], Elixir}]]}
The parser seems to use different rules in each case: in the def case, the parser just expands function calls into AST nodes according to what you call the “syntax” of the language. In the <<>> case, the parser uses some rules (different from just expanding function calls) and turns it into: {:<<>>, [], [{:a, [], Elixir}, {:b, [], Elixir}, {:c, [], Elixir}]}.
Those rules are the syntax.
Of course the parser doesn’t know that <<>> is a binary, it’s not it’s job. Iterpreting the AST generated by the parser is semantic analysis or something like that. The parser doesn’t even know that the :<<>> atom is non rebindable. But it does know that when it finds <<...>> it must output the AST [{:<<>>, [], [...]}].






















