Skip to content

Commit

Permalink
Fix more than 3 ORs or ANDs in a row
Browse files Browse the repository at this point in the history
  • Loading branch information
FabienHenon committed Jan 4, 2020
1 parent 8edea05 commit 24efb6e
Show file tree
Hide file tree
Showing 7 changed files with 203 additions and 33 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ First, add this module to your `mix.exs` file:
```elixir
defp deps do
[
{:fiqlex, "~> 0.1.0"},
{:fiqlex, "~> 0.1.1"},
]
end
```
Expand Down
12 changes: 12 additions & 0 deletions lib/query_builders/sql_query_builder.ex
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,18 @@ defmodule FIQLEx.QueryBuilders.SQLQueryBuilder do
iex> FIQLEx.build_query(FIQLEx.parse!("name==Hello*"), FIQLEx.QueryBuilders.SQLQueryBuilder, select: :from_selectors)
{:ok, "SELECT name FROM table WHERE name LIKE 'Hello%'"}
iex> FIQLEx.build_query(FIQLEx.parse!("name==Hello;age=ge=10;friend==true"), FIQLEx.QueryBuilders.SQLQueryBuilder, select: :from_selectors)
{:ok, "SELECT name, age, friend FROM table WHERE (name = 'Hello' AND (age >= 10 AND friend = true))"}
iex> FIQLEx.build_query(FIQLEx.parse!("name==Hello,age=ge=10,friend==true"), FIQLEx.QueryBuilders.SQLQueryBuilder, select: :from_selectors)
{:ok, "SELECT name, age, friend FROM table WHERE (name = 'Hello' OR (age >= 10 OR friend = true))"}
iex> FIQLEx.build_query(FIQLEx.parse!("name==Hello;age=ge=10;friend==true;ok"), FIQLEx.QueryBuilders.SQLQueryBuilder, select: :from_selectors)
{:ok, "SELECT name, age, friend, ok FROM table WHERE (name = 'Hello' AND (age >= 10 AND (friend = true AND ok IS NOT NULL)))"}
iex> FIQLEx.build_query(FIQLEx.parse!("name==Hello,age=ge=10,friend==true,ok"), FIQLEx.QueryBuilders.SQLQueryBuilder, select: :from_selectors)
{:ok, "SELECT name, age, friend, ok FROM table WHERE (name = 'Hello' OR (age >= 10 OR (friend = true OR ok IS NOT NULL)))"}
"""
use FIQLEx.QueryBuilder

Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ defmodule FIQLEx.MixProject do
def project do
[
app: :fiqlex,
version: "0.1.0",
version: "0.1.1",
elixir: "~> 1.8",
start_permanent: Mix.env() == :prod,
deps: deps(),
Expand Down
74 changes: 46 additions & 28 deletions src/fiql_parser.erl
Original file line number Diff line number Diff line change
Expand Up @@ -237,10 +237,10 @@ yeccpars2(27=S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_27(S, Cat, Ss, Stack, T, Ts, Tzr);
yeccpars2(28=S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_0(S, Cat, Ss, Stack, T, Ts, Tzr);
%% yeccpars2(29=S, Cat, Ss, Stack, T, Ts, Tzr) ->
%% yeccpars2_29(S, Cat, Ss, Stack, T, Ts, Tzr);
yeccpars2(30=S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2(29=S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_0(S, Cat, Ss, Stack, T, Ts, Tzr);
%% yeccpars2(30=S, Cat, Ss, Stack, T, Ts, Tzr) ->
%% yeccpars2_30(S, Cat, Ss, Stack, T, Ts, Tzr);
%% yeccpars2(31=S, Cat, Ss, Stack, T, Ts, Tzr) ->
%% yeccpars2_31(S, Cat, Ss, Stack, T, Ts, Tzr);
yeccpars2(Other, _, _, _, _, _, _) ->
Expand All @@ -257,19 +257,19 @@ yeccpars2_0(_, _, _, _, T, _, _) ->
-dialyzer({nowarn_function, yeccpars2_1/7}).
yeccpars2_1(_S, '$end', _Ss, Stack, _T, _Ts, _Tzr) ->
{ok, hd(Stack)};
yeccpars2_1(S, and_op, Ss, Stack, T, Ts, Tzr) ->
yeccpars1(S, 28, Ss, Stack, T, Ts, Tzr);
yeccpars2_1(S, or_op, Ss, Stack, T, Ts, Tzr) ->
yeccpars1(S, 29, Ss, Stack, T, Ts, Tzr);
yeccpars2_1(_, _, _, _, T, _, _) ->
yeccerror(T).

yeccpars2_2(_S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccgoto_constraint(hd(Ss), Cat, Ss, Stack, T, Ts, Tzr).

yeccpars2_3(S, and_op, Ss, Stack, T, Ts, Tzr) ->
yeccpars1(S, 30, Ss, Stack, T, Ts, Tzr);
yeccpars2_3(_S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccgoto_and_expression(hd(Ss), Cat, Ss, Stack, T, Ts, Tzr).

yeccpars2_4(S, or_op, Ss, Stack, T, Ts, Tzr) ->
yeccpars1(S, 28, Ss, Stack, T, Ts, Tzr);
yeccpars2_4(_S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccgoto_or_expression(hd(Ss), Cat, Ss, Stack, T, Ts, Tzr).

Expand Down Expand Up @@ -380,6 +380,10 @@ yeccpars2_25(_S, Cat, Ss, Stack, T, Ts, Tzr) ->
-dialyzer({nowarn_function, yeccpars2_26/7}).
yeccpars2_26(S, ')', Ss, Stack, T, Ts, Tzr) ->
yeccpars1(S, 27, Ss, Stack, T, Ts, Tzr);
yeccpars2_26(S, and_op, Ss, Stack, T, Ts, Tzr) ->
yeccpars1(S, 28, Ss, Stack, T, Ts, Tzr);
yeccpars2_26(S, or_op, Ss, Stack, T, Ts, Tzr) ->
yeccpars1(S, 29, Ss, Stack, T, Ts, Tzr);
yeccpars2_26(_, _, _, _, T, _, _) ->
yeccerror(T).

Expand All @@ -390,25 +394,35 @@ yeccpars2_27(_S, Cat, Ss, Stack, T, Ts, Tzr) ->

%% yeccpars2_28: see yeccpars2_0

yeccpars2_29(_S, Cat, Ss, Stack, T, Ts, Tzr) ->
%% yeccpars2_29: see yeccpars2_0

yeccpars2_30(S, and_op, Ss, Stack, T, Ts, Tzr) ->
yeccpars1(S, 28, Ss, Stack, T, Ts, Tzr);
yeccpars2_30(S, or_op, Ss, Stack, T, Ts, Tzr) ->
yeccpars1(S, 29, Ss, Stack, T, Ts, Tzr);
yeccpars2_30(_S, Cat, Ss, Stack, T, Ts, Tzr) ->
[_,_|Nss] = Ss,
NewStack = yeccpars2_29_(Stack),
NewStack = yeccpars2_30_(Stack),
yeccgoto_or_expression(hd(Nss), Cat, Nss, NewStack, T, Ts, Tzr).

%% yeccpars2_30: see yeccpars2_0

yeccpars2_31(S, and_op, Ss, Stack, T, Ts, Tzr) ->
yeccpars1(S, 28, Ss, Stack, T, Ts, Tzr);
yeccpars2_31(S, or_op, Ss, Stack, T, Ts, Tzr) ->
yeccpars1(S, 29, Ss, Stack, T, Ts, Tzr);
yeccpars2_31(_S, Cat, Ss, Stack, T, Ts, Tzr) ->
[_,_|Nss] = Ss,
NewStack = yeccpars2_31_(Stack),
yeccgoto_and_expression(hd(Nss), Cat, Nss, NewStack, T, Ts, Tzr).

-dialyzer({nowarn_function, yeccgoto_and_expression/7}).
yeccgoto_and_expression(0, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_4(4, Cat, Ss, Stack, T, Ts, Tzr);
yeccgoto_and_expression(5, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_4(4, Cat, Ss, Stack, T, Ts, Tzr);
yeccgoto_and_expression(0=_S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_4(_S, Cat, Ss, Stack, T, Ts, Tzr);
yeccgoto_and_expression(5=_S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_4(_S, Cat, Ss, Stack, T, Ts, Tzr);
yeccgoto_and_expression(28=_S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_29(_S, Cat, Ss, Stack, T, Ts, Tzr).
yeccpars2_4(_S, Cat, Ss, Stack, T, Ts, Tzr);
yeccgoto_and_expression(29=_S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_4(_S, Cat, Ss, Stack, T, Ts, Tzr).

-dialyzer({nowarn_function, yeccgoto_arguments/7}).
yeccgoto_arguments(7=_S, Cat, Ss, Stack, T, Ts, Tzr) ->
Expand All @@ -423,14 +437,14 @@ yeccgoto_arguments(21, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_19(19, Cat, Ss, Stack, T, Ts, Tzr).

-dialyzer({nowarn_function, yeccgoto_constraint/7}).
yeccgoto_constraint(0, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_3(3, Cat, Ss, Stack, T, Ts, Tzr);
yeccgoto_constraint(5, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_3(3, Cat, Ss, Stack, T, Ts, Tzr);
yeccgoto_constraint(28, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_3(3, Cat, Ss, Stack, T, Ts, Tzr);
yeccgoto_constraint(30=_S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_31(_S, Cat, Ss, Stack, T, Ts, Tzr).
yeccgoto_constraint(0=_S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_3(_S, Cat, Ss, Stack, T, Ts, Tzr);
yeccgoto_constraint(5=_S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_3(_S, Cat, Ss, Stack, T, Ts, Tzr);
yeccgoto_constraint(28=_S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_3(_S, Cat, Ss, Stack, T, Ts, Tzr);
yeccgoto_constraint(29=_S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_3(_S, Cat, Ss, Stack, T, Ts, Tzr).

-dialyzer({nowarn_function, yeccgoto_group/7}).
yeccgoto_group(0=_S, Cat, Ss, Stack, T, Ts, Tzr) ->
Expand All @@ -439,7 +453,7 @@ yeccgoto_group(5=_S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_2(_S, Cat, Ss, Stack, T, Ts, Tzr);
yeccgoto_group(28=_S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_2(_S, Cat, Ss, Stack, T, Ts, Tzr);
yeccgoto_group(30=_S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccgoto_group(29=_S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_2(_S, Cat, Ss, Stack, T, Ts, Tzr).

-dialyzer({nowarn_function, yeccgoto_list_argument_items/7}).
Expand All @@ -464,7 +478,11 @@ yeccgoto_list_arguments(21=_S, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccgoto_or_expression(0, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_1(1, Cat, Ss, Stack, T, Ts, Tzr);
yeccgoto_or_expression(5, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_26(26, Cat, Ss, Stack, T, Ts, Tzr).
yeccpars2_26(26, Cat, Ss, Stack, T, Ts, Tzr);
yeccgoto_or_expression(28, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_31(31, Cat, Ss, Stack, T, Ts, Tzr);
yeccgoto_or_expression(29, Cat, Ss, Stack, T, Ts, Tzr) ->
yeccpars2_30(30, Cat, Ss, Stack, T, Ts, Tzr).

-compile({inline,yeccpars2_6_/1}).
-file("src/fiql_parser.yrl", 8).
Expand Down Expand Up @@ -578,9 +596,9 @@ yeccpars2_27_(__Stack0) ->
__2
end | __Stack].

-compile({inline,yeccpars2_29_/1}).
-compile({inline,yeccpars2_30_/1}).
-file("src/fiql_parser.yrl", 2).
yeccpars2_29_(__Stack0) ->
yeccpars2_30_(__Stack0) ->
[__3,__2,__1 | __Stack] = __Stack0,
[begin
{ or_op , __1 , __3 }
Expand Down
6 changes: 3 additions & 3 deletions src/fiql_parser.yrl
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ Terminals '(' ')' or_op and_op selector arg_float arg_int arg_bool value compari
Rootsymbol or_expression.

or_expression -> and_expression : '$1'.
or_expression -> and_expression or_op and_expression : {or_op, '$1', '$3'}.
or_expression -> or_expression or_op or_expression : {or_op, '$1', '$3'}.

and_expression -> constraint : '$1'.
and_expression -> constraint and_op constraint : {and_op, '$1', '$3'}.
and_expression -> constraint : '$1'.
and_expression -> or_expression and_op or_expression : {and_op, '$1', '$3'}.

constraint -> group : '$1'.
constraint -> selector : {op, {selector, list_to_binary(extract_token('$1'))}}.
Expand Down
90 changes: 90 additions & 0 deletions test/fiql_lexer_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,96 @@ defmodule FIQLExLexerTest do
], 1}
end

test "Multiple ORs" do
payload = "my_selector1==value1,my_selector2==value2,my_selector3==value3"

assert :fiql_lexer.string(to_charlist(payload)) ==
{:ok,
[
{:selector, 1, 'my_selector1'},
{:equal, 1},
{:selector, 1, 'value1'},
{:or_op, 1},
{:selector, 1, 'my_selector2'},
{:equal, 1},
{:selector, 1, 'value2'},
{:or_op, 1},
{:selector, 1, 'my_selector3'},
{:equal, 1},
{:selector, 1, 'value3'}
], 1}
end

test "Multiple ANDs" do
payload = "my_selector1==value1;my_selector2==value2;my_selector3==value3"

assert :fiql_lexer.string(to_charlist(payload)) ==
{:ok,
[
{:selector, 1, 'my_selector1'},
{:equal, 1},
{:selector, 1, 'value1'},
{:and_op, 1},
{:selector, 1, 'my_selector2'},
{:equal, 1},
{:selector, 1, 'value2'},
{:and_op, 1},
{:selector, 1, 'my_selector3'},
{:equal, 1},
{:selector, 1, 'value3'}
], 1}
end

test "Multiple ORs (4)" do
payload =
"my_selector1==value1,my_selector2==value2,my_selector3==value3,my_selector4==value4"

assert :fiql_lexer.string(to_charlist(payload)) ==
{:ok,
[
{:selector, 1, 'my_selector1'},
{:equal, 1},
{:selector, 1, 'value1'},
{:or_op, 1},
{:selector, 1, 'my_selector2'},
{:equal, 1},
{:selector, 1, 'value2'},
{:or_op, 1},
{:selector, 1, 'my_selector3'},
{:equal, 1},
{:selector, 1, 'value3'},
{:or_op, 1},
{:selector, 1, 'my_selector4'},
{:equal, 1},
{:selector, 1, 'value4'}
], 1}
end

test "Multiple ANDs (4)" do
payload =
"my_selector1==value1;my_selector2==value2;my_selector3==value3;my_selector4==value4"

assert :fiql_lexer.string(to_charlist(payload)) ==
{:ok,
[
{:selector, 1, 'my_selector1'},
{:equal, 1},
{:selector, 1, 'value1'},
{:and_op, 1},
{:selector, 1, 'my_selector2'},
{:equal, 1},
{:selector, 1, 'value2'},
{:and_op, 1},
{:selector, 1, 'my_selector3'},
{:equal, 1},
{:selector, 1, 'value3'},
{:and_op, 1},
{:selector, 1, 'my_selector4'},
{:equal, 1},
{:selector, 1, 'value4'}
], 1}
end

test "Equal comparison" do
payload = "my_selector1==value1"

Expand Down
50 changes: 50 additions & 0 deletions test/fiql_parser_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,56 @@ defmodule FIQLExParserTest do
{:op, {:selector_and_value, "my_selector3", :equal, "value3"}}}}}
end

test "Multiple ORs" do
payload = "my_selector1==value1,my_selector2==value2,my_selector3==value3"
{:ok, tokens, _} = :fiql_lexer.string(to_charlist(payload))

assert :fiql_parser.parse(tokens) ==
{:ok,
{:or_op, {:op, {:selector_and_value, "my_selector1", :equal, "value1"}},
{:or_op, {:op, {:selector_and_value, "my_selector2", :equal, "value2"}},
{:op, {:selector_and_value, "my_selector3", :equal, "value3"}}}}}
end

test "Multiple ANDs" do
payload = "my_selector1==value1;my_selector2==value2;my_selector3==value3"
{:ok, tokens, _} = :fiql_lexer.string(to_charlist(payload))

assert :fiql_parser.parse(tokens) ==
{:ok,
{:and_op, {:op, {:selector_and_value, "my_selector1", :equal, "value1"}},
{:and_op, {:op, {:selector_and_value, "my_selector2", :equal, "value2"}},
{:op, {:selector_and_value, "my_selector3", :equal, "value3"}}}}}
end

test "Multiple ORs (4)" do
payload =
"my_selector1==value1,my_selector2==value2,my_selector3==value3,my_selector4==value4"

{:ok, tokens, _} = :fiql_lexer.string(to_charlist(payload))

assert :fiql_parser.parse(tokens) ==
{:ok,
{:or_op, {:op, {:selector_and_value, "my_selector1", :equal, "value1"}},
{:or_op, {:op, {:selector_and_value, "my_selector2", :equal, "value2"}},
{:or_op, {:op, {:selector_and_value, "my_selector3", :equal, "value3"}},
{:op, {:selector_and_value, "my_selector4", :equal, "value4"}}}}}}
end

test "Multiple ANDs (4)" do
payload =
"my_selector1==value1;my_selector2==value2;my_selector3==value3;my_selector4==value4"

{:ok, tokens, _} = :fiql_lexer.string(to_charlist(payload))

assert :fiql_parser.parse(tokens) ==
{:ok,
{:and_op, {:op, {:selector_and_value, "my_selector1", :equal, "value1"}},
{:and_op, {:op, {:selector_and_value, "my_selector2", :equal, "value2"}},
{:and_op, {:op, {:selector_and_value, "my_selector3", :equal, "value3"}},
{:op, {:selector_and_value, "my_selector4", :equal, "value4"}}}}}}
end

test "Multiple selectors separated by and and or" do
payload = "my_selector1==value1,my_selector2==value2;my_selector3==value3"
{:ok, tokens, _} = :fiql_lexer.string(to_charlist(payload))
Expand Down

0 comments on commit 24efb6e

Please sign in to comment.