Skip to content

Commit e042af9

Browse files
oakmaccburgmer
authored andcommitted
update parser to handle && and || operators
1 parent 25faef5 commit e042af9

File tree

4 files changed

+71
-26
lines changed

4 files changed

+71
-26
lines changed

src/json_path/parser.clj

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,42 @@
11
(ns json-path.parser)
22

3-
(declare parse)
3+
(declare parse parse-expr)
44

55
(defn extract-sub-tree [start end stream]
66
(take-while #(not (= end %)) (drop-while #(= start %) stream)))
77

8-
(defn parse-expr [remaining]
9-
(let [ops {"=" :eq, "!=" :neq, "<" :lt, "<=" :lt-eq, ">" :gt, ">=" :gt-eq}
10-
supported-ops (set (keys ops))
11-
lhs (take-while #(not (supported-ops %)) remaining)
12-
op (first (drop-while #(not (supported-ops %)) remaining))
13-
rhs (rest (drop-while #(not (supported-ops %)) remaining))]
14-
(if (nil? op)
15-
[:some (parse lhs)]
16-
[(ops op) (parse lhs) (parse rhs)])))
8+
(def boolean-ops
9+
{"&&" :and, "||" :or})
10+
11+
(def boolean-ops-strings (set (keys boolean-ops)))
12+
13+
(def comparator-ops
14+
{"=" :eq, "!=" :neq, "<" :lt, "<=" :lt-eq, ">" :gt, ">=" :gt-eq})
15+
16+
(defn parse-boolean-expr [expr]
17+
(let [lhs (take-while #(not (boolean-ops-strings %)) expr)
18+
op (first (drop-while #(not (boolean-ops-strings %)) expr))
19+
rhs (rest (drop-while #(not (boolean-ops-strings %)) expr))]
20+
[(boolean-ops op) (parse-expr lhs) (parse-expr rhs)]))
21+
22+
(defn parse-comparator-expr [expr]
23+
(let [supported-ops (set (keys comparator-ops))
24+
lhs (take-while #(not (supported-ops %)) expr)
25+
op (first (drop-while #(not (supported-ops %)) expr))
26+
rhs (rest (drop-while #(not (supported-ops %)) expr))]
27+
; (if (nil? op)
28+
; [:some (parse lhs)])
29+
[(comparator-ops op) (parse lhs) (parse rhs)]))
30+
31+
(defn parse-expr [expr]
32+
(cond
33+
(some boolean-ops-strings expr) (parse-boolean-expr expr)
34+
(some (set (keys comparator-ops)) expr) (parse-comparator-expr expr)
35+
:else [:some (parse expr)]))
36+
37+
; (if (some boolean-ops-strings expr)
38+
; (parse-boolean-expr expr)
39+
; (parse-comparator-expr expr)))
1740

1841
(defn parse-indexer [remaining]
1942
(let [next (first remaining)]
@@ -36,6 +59,8 @@
3659
(empty? remaining) []
3760
(re-matches #"\d+" next) [:val (Integer/parseInt next)]
3861
(re-matches #"\d+\.\d*" next) [:val (Double/parseDouble next)]
62+
(= next "true") [:val true]
63+
(= next "false") [:val false]
3964
(= "\"" next) [:val (apply str (extract-sub-tree "\"" "\"" remaining))]
4065
(= "[" next) (do
4166
(let [idx (parse-indexer (extract-sub-tree "[" "]" remaining))
@@ -51,4 +76,4 @@
5176
[:path pth]))))))
5277

5378
(defn parse-path [path]
54-
(parse (re-seq #"<=|>=|\.\.|[.*$@\[\]\(\)\"=<>]|\d+|[\w-\/]+|\?\(|!=" path)))
79+
(parse (re-seq #"<=|>=|\.\.|[.*$@\[\]\(\)\"=<>]|\d+|[\w-\/]+|\?\(|!=|&&|true|false" path)))

src/json_path/walker.clj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
(apply op-form (map #(eval-expr % context) operands)))
88

99
(defn eval-expr [[expr-type & operands :as expr] context]
10-
(let [ops {:eq =, :neq not=, :lt <, :lt-eq <=, :gt >, :gt-eq >=}]
10+
(let [ops {:eq =, :neq not=, :lt <, :lt-eq <=, :gt >, :gt-eq >=, :and every?}]
1111
(cond
1212
(contains? ops expr-type) (eval-eq-expr (expr-type ops) context operands)
1313
(= expr-type :some) (some? (:value (walk (first operands) context)))
@@ -83,8 +83,8 @@
8383

8484
(defn walk [[opcode operand continuation] context]
8585
(let [down-obj (cond
86-
(= opcode :path) (walk-path operand context)
87-
(= opcode :selector) (walk-selector operand context))]
86+
(= opcode :path) (walk-path operand context)
87+
(= opcode :selector) (walk-selector operand context))]
8888
(if continuation
8989
(map# #(walk continuation (assoc context :current %)) down-obj)
9090
down-obj)))

test/json_path/test/parser_test.clj

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
=>
1313
[:eq [:path [[:current] [:child] [:key "foo"]]] [:val "baz"]])
1414

15-
(facts "equality expressions should be parseable"
15+
(facts "comparator expressions should be parseable"
1616
(parse-expr '("\"" "bar" "\"" "!=" "\"" "bar" "\"")) => [:neq [:val "bar"] [:val "bar"]]
1717
(parse-expr '("\"" "bar" "\"" "<" "\"" "bar" "\"")) => [:lt [:val "bar"] [:val "bar"]]
1818
(parse-expr '("\"" "bar" "\"" "<=" "\"" "bar" "\"")) => [:lt-eq [:val "bar"] [:val "bar"]]
@@ -21,6 +21,13 @@
2121
(parse-expr '("\"" "bar" "\"" "=" "42")) => [:eq [:val "bar"] [:val 42]]
2222
(parse-expr '("\"" "bar" "\"" "=" "3.1415")) => [:eq [:val "bar"] [:val 3.1415]])
2323

24+
(facts "boolean expressions should be parseable"
25+
(parse-expr '("\"" "bar" "\"" "&&" "\"" "bar" "\"")) => [:and [:some [:val "bar"]] [:some [:val "bar"]]]
26+
(parse-expr '("\"" "bar" "\"" "&&" "true")) => [:and [:some [:val "bar"]] [:some [:val true]]]
27+
(parse-expr '("false" "&&" "\"" "bar" "\"")) => [:and [:some [:val false]] [:some [:val "bar"]]]
28+
(parse-expr '("\"" "bar" "\"" "||" "\"" "bar" "\"")) => [:or [:some [:val "bar"]] [:some [:val "bar"]]]
29+
(parse-expr '("\"" "bar" "\"" "=" "42" "&&" "\"" "bar" "\"")) => [:and [:eq [:val "bar"] [:val 42]] [:some [:val "bar"]]])
30+
2431
(fact
2532
(parse-indexer '("*")) => [:index "*"]
2633
(parse-indexer '("3")) => [:index "3"]
@@ -53,6 +60,19 @@
5360
[:child]
5461
[:key "bar"]]]
5562
[:val 2]]]]]
63+
(parse-path "$[?(@.bar>42 && @.bar<44)]") => [:path [[:root]]
64+
[:selector [:filter
65+
[:and
66+
[:gt
67+
[:path [[:current]
68+
[:child]
69+
[:key "bar"]]]
70+
[:val 42]]
71+
[:lt
72+
[:path [[:current]
73+
[:child]
74+
[:key "bar"]]]
75+
[:val 44]]]]]]
5676
(parse-path "$.foo[?(@.bar=\"baz\")].hello") => [:path [[:root] [:child] [:key "foo"]]
5777
[:selector [:filter [:eq [:path [[:current]
5878
[:child]

test/json_path/test/walker_test.clj

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,16 @@
3030
(walk-path [[:root]] {:root (m/root ...root...), :current (m/root ...obj...)}) => (m/create ...root... [])
3131
(walk-path [[:root] [:child] [:key "foo"]] {:root (m/root {:foo "bar"})}) => (m/create "bar" [:foo])
3232
(walk-path [[:all-children]] {:current (m/root {:foo "bar" :baz {:qux "zoo"}})}) => (list (m/create {:foo "bar" :baz {:qux "zoo"}} [])
33-
(m/create {:qux "zoo"} [:baz]))
33+
(m/create {:qux "zoo"} [:baz]))
3434
(walk-path [[:all-children] [:key "bar"]]
3535
{:current (m/root '([{:bar "hello"}]))}) => (list (m/create "hello" [0 0 :bar]))
3636
(walk-path [[:all-children] [:key "bar"]]
3737
{:current (m/root {:foo [{:bar "wrong"}
38-
{:bar "baz"}]})}) => (list (m/create "wrong" [:foo 0 :bar])
39-
(m/create "baz" [:foo 1 :bar]))
38+
{:bar "baz"}]})}) => (list (m/create "wrong" [:foo 0 :bar])
39+
(m/create "baz" [:foo 1 :bar]))
4040
(walk-path [[:all-children] [:key "foo"]]
4141
{:current (m/root {:foo [{:foo "foo"}]})}) => (list (m/create [{:foo "foo"}] [:foo])
42-
(m/create "foo" [:foo 0 :foo])))
42+
(m/create "foo" [:foo 0 :foo])))
4343

4444
(facts
4545
(walk-selector [:index "1"] {:current (m/root ["foo", "bar", "baz"])}) => (m/create "bar" [1])
@@ -79,13 +79,13 @@
7979
(walk [:path [[:all-children]]]
8080
{:current
8181
(m/root (list {:hello {:world "foo"}}
82-
{:baz {:world "bar"}}))}) => (list (m/create [{:hello {:world "foo"}}
83-
{:baz {:world "bar"}}]
84-
[])
85-
(m/create {:hello {:world "foo"}} [0])
86-
(m/create {:world "foo"} [0 :hello])
87-
(m/create {:baz {:world "bar"}} [1])
88-
(m/create {:world "bar"} [1 :baz]))
82+
{:baz {:world "bar"}}))}) => (list (m/create [{:hello {:world "foo"}}
83+
{:baz {:world "bar"}}]
84+
[])
85+
(m/create {:hello {:world "foo"}} [0])
86+
(m/create {:world "foo"} [0 :hello])
87+
(m/create {:baz {:world "bar"}} [1])
88+
(m/create {:world "bar"} [1 :baz]))
8989
(walk [:path [[:all-children]]]
9090
{:current (m/root "scalar")}) => (list (m/create "scalar" []))
9191
(walk [:path [[:all-children] [:key "world"]]]

0 commit comments

Comments
 (0)