The problem as it’s stated is kinda ambiguous, because it’s unclear whether @contact.name whould be threated as a whole variable, or as @contact variable followed by a regulat dot. In any case, this ambuiguity might be easily resolved using the code below as a scaffold. For instance, you might have a dedicated clause for ".", check binding there, and decide whether to move forward, or use the binding.
Erlang, and hence Elixir are extremely powerful in parsing text, thanks to pattern matching.
defmodule NaiveParser do
@varname ["." | Enum.map(?a..?z, &<<&1>>)]
def parse(input, binding),
do: do_parse(input, binding, {nil, ""})
defp do_parse("", binding, {var, result}), do: result <> bound(var, binding)
defp do_parse("@" <> rest, binding, {nil, result}),
do: do_parse(rest, binding, {"", result})
defp do_parse(<<c::binary-size(1), rest::binary>>, binding, {nil, result}),
do: do_parse(rest, binding, {nil, result <> c})
defp do_parse(<<c::binary-size(1), rest::binary>>, binding, {var, result})
when c in @varname,
do: do_parse(rest, binding, {var <> c, result})
defp do_parse(<<c::binary-size(1), rest::binary>>, binding, {var, result}),
do: do_parse(rest, binding, {nil, result <> bound(var, binding) <> c})
defp bound(nil, binding), do: ""
defp bound(var, binding) do
with {substitution, ^binding} <- Code.eval_string(var, binding),
do: to_string(substitution)
end
end
str = "Hello @contact.name, how are you. It looks like your age is @contact.age, so the solution of your problem is @solution"
NaiveParser.parse(str, [contact: %{age: 27, name: "Rahul"}, solution: "None"])
#⇒ "Hello Rahul, how are you. It looks like your age is 27, so the solution of your problem is None"