Skip to content

Commit

Permalink
remove duplicate macro impl
Browse files Browse the repository at this point in the history
  • Loading branch information
magnetised committed Oct 26, 2023
1 parent 65703c7 commit 81aa731
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 106 deletions.
28 changes: 22 additions & 6 deletions components/electric/lib/electric/ddlx/parser/macros.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,41 @@ defmodule Electric.DDLX.Parser.Macros do
E.g.
defkeyword :in?, "IN" do
:ok
deftoken :in?, "IN" do
true
end
produces the code:
def in?(<<c0::8, c1::8>> = stmt) when c0 in [?i, ?I] and c1 in [?n, ?N] do
:ok
true
end
this only matches exact strings with no leading or trailing characters.
iex(1)> in?("In")
true
iex(2)> in?("IN")
true
iex(2)> in?("in")
true
# assuming we have a fallback `in?/1` function definition that returns `false`
# for any other input, i.e.
#
# deftoken :in?, "IN", do: true
# def in?(_), do: false
iex(2)> in?("inside")
false
"""
defmacro deftoken(function, keyword, _opts \\ [], do: block) do
defmacro deftoken(function, keyword, do: block) do
chars =
keyword
|> to_string()
|> String.codepoints()
|> Enum.map(fn char -> [String.downcase(char), String.upcase(char)] end)
|> Enum.map(fn [<<l::8>>, <<u::8>>] -> [l, u] end)
|> Enum.with_index()

chars = Enum.with_index(chars)
pattern = build_match(chars)
guard = build_guard(chars)

Expand Down
58 changes: 29 additions & 29 deletions components/electric/lib/electric/ddlx/parser/tokenizer.ex
Original file line number Diff line number Diff line change
@@ -1,35 +1,35 @@
defmodule Electric.DDLX.Parser.Tokenizer.Tokens do
import Electric.DDLX.Parser.Macros

deftoken(:token, "ALL", [], do: :ALL)
deftoken(:token, "ALTER", [], do: :ALTER)
deftoken(:token, "AND", [], do: :AND)
deftoken(:token, "ASSIGN", [], do: :ASSIGN)
deftoken(:token, "CHECK", [], do: :CHECK)
deftoken(:token, "DELETE", [], do: :DELETE)
deftoken(:token, "DISABLE", [], do: :DISABLE)
deftoken(:token, "ELECTRIC", [], do: :ELECTRIC)
deftoken(:token, "ENABLE", [], do: :ENABLE)
deftoken(:token, "FROM", [], do: :FROM)
deftoken(:token, "GRANT", [], do: :GRANT)
deftoken(:token, "IF", [], do: :IF)
deftoken(:token, "INSERT", [], do: :INSERT)
deftoken(:token, "IS", [], do: :IS)
deftoken(:token, "NOT", [], do: :NOT)
deftoken(:token, "NULL", [], do: :NULL)
deftoken(:token, "ON", [], do: :ON)
deftoken(:token, "OR", [], do: :OR)
deftoken(:token, "PRIVILEGES", [], do: :PRIVILEGES)
deftoken(:token, "READ", [], do: :READ)
deftoken(:token, "REVOKE", [], do: :REVOKE)
deftoken(:token, "SELECT", [], do: :SELECT)
deftoken(:token, "SQLITE", [], do: :SQLITE)
deftoken(:token, "TABLE", [], do: :TABLE)
deftoken(:token, "TO", [], do: :TO)
deftoken(:token, "UNASSIGN", [], do: :UNASSIGN)
deftoken(:token, "UPDATE", [], do: :UPDATE)
deftoken(:token, "USING", [], do: :USING)
deftoken(:token, "WRITE", [], do: :WRITE)
deftoken(:token, "ALL", do: :ALL)
deftoken(:token, "ALTER", do: :ALTER)
deftoken(:token, "AND", do: :AND)
deftoken(:token, "ASSIGN", do: :ASSIGN)
deftoken(:token, "CHECK", do: :CHECK)
deftoken(:token, "DELETE", do: :DELETE)
deftoken(:token, "DISABLE", do: :DISABLE)
deftoken(:token, "ELECTRIC", do: :ELECTRIC)
deftoken(:token, "ENABLE", do: :ENABLE)
deftoken(:token, "FROM", do: :FROM)
deftoken(:token, "GRANT", do: :GRANT)
deftoken(:token, "IF", do: :IF)
deftoken(:token, "INSERT", do: :INSERT)
deftoken(:token, "IS", do: :IS)
deftoken(:token, "NOT", do: :NOT)
deftoken(:token, "NULL", do: :NULL)
deftoken(:token, "ON", do: :ON)
deftoken(:token, "OR", do: :OR)
deftoken(:token, "PRIVILEGES", do: :PRIVILEGES)
deftoken(:token, "READ", do: :READ)
deftoken(:token, "REVOKE", do: :REVOKE)
deftoken(:token, "SELECT", do: :SELECT)
deftoken(:token, "SQLITE", do: :SQLITE)
deftoken(:token, "TABLE", do: :TABLE)
deftoken(:token, "TO", do: :TO)
deftoken(:token, "UNASSIGN", do: :UNASSIGN)
deftoken(:token, "UPDATE", do: :UPDATE)
deftoken(:token, "USING", do: :USING)
deftoken(:token, "WRITE", do: :WRITE)
def token(s), do: s
end

Expand Down
72 changes: 2 additions & 70 deletions components/electric/lib/electric/postgres/proxy/parser.ex
Original file line number Diff line number Diff line change
@@ -1,79 +1,11 @@
defmodule Electric.Postgres.Proxy.Parser.Macros do
@doc """
Produces a function head that matches a string in a case insensitive way.
E.g.
defkeyword :in?, "IN" do
:ok
end
produces the code:
def in?(<<c0::8, c1::8, rest::binary>> = stmt) when c0 in [?i, ?I] and c1 in [?n, ?N] do
:ok
end
## Options
- `trailing` - should the keyword be followed by whitespace, defaults to `true`.
"""
defmacro defkeyword(function, keyword, opts \\ [], do: block) do
chars =
keyword
|> String.codepoints()
|> Enum.map(fn char -> [String.downcase(char), String.upcase(char)] end)
|> Enum.map(fn [<<l::8>>, <<u::8>>] -> [l, u] end)

whitespace = if Keyword.get(opts, :trailing, false), do: [~c"\t\n\r "], else: []
chars = Enum.with_index(chars ++ whitespace)
pattern = build_match(chars)
guard = build_guard(chars)

quote do
def unquote(function)(unquote(pattern) = var!(stmt)) when unquote(guard) do
_ = var!(rest)
_ = var!(stmt)
unquote(block)
end
end
end

defp match_var(i), do: Macro.var(:"c#{i}", Elixir)

# <<c0::8, c1::8, ..., rest::binary>>
defp build_match(chars) do
{:<<>>, [],
Enum.map(chars, fn {_c, i} -> quote(do: unquote(match_var(i)) :: 8) end) ++
[quote(do: var!(rest) :: binary)]}
end

defp is_member(chars, i) do
quote do
unquote(match_var(i)) in unquote(chars)
end
end

defp build_guard([{chars, i}]) do
is_member(chars, i)
end

defp build_guard([{chars, i} | rest]) do
quote do
unquote(is_member(chars, i)) and unquote(build_guard(rest))
end
end
end

defmodule Electric.Postgres.Proxy.Parser do
alias Electric.Postgres.NameParser
alias Electric.Postgres.Proxy.Injector.State
alias Electric.Postgres.Extension.SchemaLoader
alias Electric.Postgres.Proxy.{QueryAnalyser, QueryAnalysis}
alias PgProtocol.Message, as: M

import __MODULE__.Macros
import Electric.DDLX.Parser.Macros

require Logger

Expand Down Expand Up @@ -469,7 +401,7 @@ defmodule Electric.Postgres.Proxy.Parser do
end

# case ignoring match of "electric"
defkeyword :is_electric_keyword?, "ELECTRIC" do
deftoken :is_electric_keyword?, "ELECTRIC" do
true
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ defmodule Electric.Postgres.Proxy.ParserTest do
end

test "matches with trailing stuff" do
assert Parser.is_electric_keyword?("electric raingoes")
assert Parser.is_electric_keyword?("electric")
end

test "only matches 'electric'" do
Expand Down

0 comments on commit 81aa731

Please sign in to comment.