Skip to content

Commit 9c9cded

Browse files
authored
Merge pull request #47 from ProgramingIsTheFuture/implementing_new_features
Improving the parser and implementing new features for query-json
2 parents 158ec23 + e10de99 commit 9c9cded

File tree

8 files changed

+307
-73
lines changed

8 files changed

+307
-73
lines changed

README.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -124,21 +124,22 @@ The report shows that **query-json** is between 2x and 5x faster than **jq** in
124124
125125
#### [Builtin operators and functions](https://stedolan.github.io/jq/manual/v1.6/#Builtinoperatorsandfunctions)
126126
127+
- Abs: `abs`
127128
- Addition: `+`
128129
- Subtraction: `-`
129130
- Multiplication, division, modulo: `*`, `/`, and `%`
130131
- `length`
131132
- `keys`
132133
- `map`
133134
- `select`
134-
- `has(key)` ⚠️
135-
- `in` ⚠️
135+
- `has(key)`
136+
- `in`
136137
- `path(path_expression)` ⚠️
137138
- `to_entries`, `from_entries`, `with_entries` ⚠️
138139
- `any`, `any(condition)`, `any(generator; condition)` ⚠️
139140
- `all`, `all(condition)`, `all(generator; condition)` ⚠️
140141
- `flatten`
141-
- `range(upto)`, `range(from;upto)` `range(from;upto;by)` ⚠️
142+
- `range(upto)`, `range(from;upto)` `range(from;upto;by)`
142143
- `floor`, `sqrt` ⚠️
143144
- `tonumber`, `tostring` ⚠️
144145
- `type` ⚠️
@@ -147,12 +148,12 @@ The report shows that **query-json** is between 2x and 5x faster than **jq** in
147148
- `group_by(path_expression)` ⚠️
148149
- `min, max, min_by(path_exp), max_by(path_exp)` ⚠️
149150
- `unique, unique_by(path_exp)` ⚠️
150-
- `reverse` ⚠️
151+
- `reverse`
151152
- `contains(element)` ⚠️
152153
- `index(s), rindex(s)` ⚠️
153154
- `startswith(str)`, `endswith(str)` ⚠️
154155
- `explode`, `implode` ⚠️
155-
- `split(str)`, `join(str)` ⚠️
156+
- `split(str)`, `join(str)`
156157
- `while(cond; update)`, `until(cond; next)` ⚠️
157158
- `recurse(f)`, `recurse`, `recurse(f; condition)`, `recurse_down` ⚠️
158159
- `walk(f)` ⚠️
@@ -161,7 +162,7 @@ The report shows that **query-json** is between 2x and 5x faster than **jq** in
161162
162163
#### [Conditionals and Comparisons](https://stedolan.github.io/jq/manual/v1.6/#ConditionalsandComparisons)
163164
- `==`, `!=`
164-
- `if-then-else` ⚠️
165+
- `if-then-else`
165166
- `>`, `>=`, `<=`, `<`
166167
- `and`, `or`, `not` ⚠️
167168
- `break` 🔴

source/Ast.ml

Lines changed: 22 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,21 @@ type literal =
55
| Null (* null *)
66
[@@deriving show { with_path = false }]
77

8+
type op =
9+
| Add
10+
| Sub
11+
| Mult
12+
| Div
13+
| Eq
14+
| Neq
15+
| Gt (* > *)
16+
| St (* < *)
17+
| Ge (* >=*)
18+
| Se (* <= *)
19+
| And
20+
| Or
21+
[@@deriving show { with_path = false }]
22+
823
type expression =
924
| Identity (* . *)
1025
| Empty (* empty *)
@@ -13,7 +28,7 @@ type expression =
1328
| Literal of literal
1429
(* Constructors *)
1530
| List of expression list (* [ expr ] *)
16-
| Object of (string * expression) list (* {} *)
31+
| Object of (expression * expression option) list (* {} *)
1732
(* Objects *)
1833
| Walk of expression (* walk() *)
1934
| Transpose of expression (* transpose() *)
@@ -32,7 +47,7 @@ type expression =
3247
| Implode (* implode *)
3348
| Any (* any *)
3449
| All (* all *)
35-
| In (* in *)
50+
| In of expression (* in *)
3651
| Recurse (* recurse *)
3752
| RecurseDown (* recurse_down *)
3853
| ToEntries (* to_entries *)
@@ -43,7 +58,7 @@ type expression =
4358
| IsNan
4459
(* Array *)
4560
| Index of int (* .[1] *)
46-
| Range of int * int (* range(1, 10) *)
61+
| Range of int * int option * int option (* range(1, 10) *)
4762
| Flatten (* flatten *)
4863
| Head (* head *)
4964
| Tail (* tail *)
@@ -65,10 +80,8 @@ type expression =
6580
| Some_ of expression (* some, Some_ to not collide with option *)
6681
| Find of expression (* find(x) *)
6782
(* operations *)
68-
| Addition of expression * expression (* + *)
69-
| Subtraction of expression * expression (* - *)
70-
| Division of expression * expression (* / *)
71-
| Multiply of expression * expression (* * *)
83+
| Abs
84+
| Operation of expression * op * expression
7285
(* Generic *)
7386
| Length (* length *)
7487
| Contains of expression (* contains *)
@@ -82,18 +95,9 @@ type expression =
8295
| Join of expression (* join *)
8396
| Path of expression (* path(x) *)
8497
(* Logic *)
85-
| If of expression (* if *)
86-
| Then of expression (* then *)
87-
| Else of expression (* else *)
98+
| IfThenElse of
99+
expression * expression * expression (* If then (elseif) else end *)
88100
| Break (* break *)
89101
(* Conditionals *)
90-
| And of expression * expression (* and *)
91-
| Or of expression * expression (* or *)
92102
| Not (* not *)
93-
| Greater of expression * expression (* > *)
94-
| Lower of expression * expression (* < *)
95-
| GreaterEqual of expression * expression (* >= *)
96-
| LowerEqual of expression * expression (* <= *)
97-
| Equal of expression * expression (* == *)
98-
| NotEqual of expression * expression (* != *)
99103
[@@deriving show { with_path = false }]

source/Compiler.ml

Lines changed: 141 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,77 @@ let keys (json : Json.t) =
6666
Output.return (`List (Json.keys json |> List.map (fun i -> `String i)))
6767
| _ -> Error (make_error "keys" json)
6868

69+
let has (json : Json.t) key =
70+
match key with
71+
| String key -> (
72+
match json with
73+
| `Assoc list -> Output.return (`Bool (List.mem_assoc key list))
74+
| _ -> Error (make_error "has" json))
75+
| Number n -> (
76+
match json with
77+
| `List list ->
78+
Output.return (`Bool (List.length list - 1 >= int_of_float n))
79+
| _ -> Error (make_error "has" json))
80+
| _ -> Error (make_error "has" json)
81+
82+
let in_ (json : Json.t) expr =
83+
match json with
84+
| `Int n -> (
85+
match expr with
86+
| List list -> Output.return (`Bool (List.length list - 1 >= n))
87+
| _ -> Error (make_error "in" json))
88+
| `String key -> (
89+
match expr with
90+
| Object list ->
91+
let cmp_literal_str key = function
92+
| Literal (String s) when s = key -> true
93+
| _ -> false
94+
in
95+
let s = List.map fst list |> List.find_opt (cmp_literal_str key) in
96+
Output.return (`Bool (Option.is_some s))
97+
| _ -> Error (make_error "in" json))
98+
| _ -> Error (make_error "in" json)
99+
100+
let range ?step from upto =
101+
let rec range ?(step = 1) start stop =
102+
if step = 0 then []
103+
else if (step > 0 && start >= stop) || (step < 0 && start <= stop) then []
104+
else start :: range ~step (start + step) stop
105+
in
106+
match upto with
107+
| None ->
108+
List.map (fun i -> Output.return (`Int i)) (range 1 from)
109+
|> Output.collect
110+
| Some upto ->
111+
List.map (fun i -> Output.return (`Int i)) (range ?step from upto)
112+
|> Output.collect
113+
114+
let split expr json =
115+
match json with
116+
| `String s ->
117+
let* rcase =
118+
match expr with
119+
| Literal (String s) -> Output.return s
120+
| _ -> Error "split input should be a string"
121+
in
122+
Output.return
123+
(`List (Str.split (Str.regexp rcase) s |> List.map (fun s -> `String s)))
124+
| _ -> Error "input should be a JSON string"
125+
126+
let join expr json =
127+
let* rcase =
128+
match expr with
129+
| Literal (String s) -> Output.return s
130+
| _ -> Error "join input should be a string"
131+
in
132+
match json with
133+
| `List l ->
134+
Output.return
135+
(`String
136+
(List.map (function `String s -> s | _ -> "") l
137+
|> String.concat rcase))
138+
| _ -> Error "input should be a list"
139+
69140
let length (json : Json.t) =
70141
match json with
71142
| `List list -> Output.return (`Int (list |> List.length))
@@ -230,10 +301,20 @@ let rec compile expression json : (Json.t list, string) result =
230301
| Length -> length json
231302
| Not -> not json
232303
| Map expr -> map expr json
233-
| Addition (left, right) -> operation left right add json
234-
| Subtraction (left, right) -> operation left right sub json
235-
| Multiply (left, right) -> operation left right mult json
236-
| Division (left, right) -> operation left right div json
304+
| Operation (left, op, right) -> (
305+
match op with
306+
| Add -> operation left right add json
307+
| Sub -> operation left right sub json
308+
| Mult -> operation left right mult json
309+
| Div -> operation left right div json
310+
| Gt -> operation left right gt json
311+
| Ge -> operation left right gte json
312+
| St -> operation left right lt json
313+
| Se -> operation left right lte json
314+
| Eq -> operation left right eq json
315+
| Neq -> operation left right notEq json
316+
| And -> operation left right and_ json
317+
| Or -> operation left right or_ json)
237318
| Literal literal -> (
238319
match literal with
239320
| Bool b -> Output.return (`Bool b)
@@ -248,20 +329,36 @@ let rec compile expression json : (Json.t list, string) result =
248329
match res with
249330
| `Bool b -> ( match b with true -> Output.return json | false -> empty)
250331
| _ -> Error (make_error "select" res))
251-
| Greater (left, right) -> operation left right gt json
252-
| GreaterEqual (left, right) -> operation left right gte json
253-
| Lower (left, right) -> operation left right lt json
254-
| LowerEqual (left, right) -> operation left right lte json
255-
| Equal (left, right) -> operation left right eq json
256-
| NotEqual (left, right) -> operation left right notEq json
257-
| And (left, right) -> operation left right and_ json
258-
| Or (left, right) -> operation left right or_ json
259332
| List exprs ->
260333
Output.collect (List.map (fun expr -> compile expr json) exprs)
261334
|> Result.map (fun x -> [ `List x ])
262335
| Comma (leftR, rightR) ->
263336
Result.bind (compile leftR json) (fun left ->
264337
Result.bind (compile rightR json) (fun right -> Ok (left @ right)))
338+
| Object list -> handle_objects list json
339+
| Has e -> (
340+
match e with
341+
| Literal ((String _ | Number _) as e) -> has json e
342+
| _ -> Error (show_expression e ^ " is not allowed"))
343+
| In e -> in_ json e
344+
| Range (from, upto, step) -> range ?step from upto
345+
| Reverse -> (
346+
match json with
347+
| `List l -> Output.return (`List (List.rev l))
348+
| _ -> Error (make_error "reverse" json))
349+
| Split expr -> split expr json
350+
| Join expr -> join expr json
351+
| Abs -> (
352+
match json with
353+
| `Int n -> Output.return (`Int (if n < 0 then -n else n))
354+
| `Float j -> Output.return (`Float (if j < 0. then -.j else j))
355+
| _ -> Error (make_error "reverse" json))
356+
| IfThenElse (cond, if_branch, else_branch) -> (
357+
let* cond = compile cond json in
358+
match cond with
359+
| `Bool b ->
360+
if b then compile if_branch json else compile else_branch json
361+
| json -> Error (make_error "if condition should be a bool" json))
265362
| _ -> Error (show_expression expression ^ " is not implemented")
266363

267364
and operation leftR rightR op json =
@@ -276,3 +373,35 @@ and map (expr : expression) (json : Json.t) =
276373
|> Result.map (fun x -> [ `List x ])
277374
| `List _list -> Error (make_empty_list_error "map")
278375
| _ -> Error (make_error "map" json)
376+
377+
and handle_objects list json =
378+
List.map
379+
(fun (left_expr, right_expr) ->
380+
match (left_expr, right_expr) with
381+
| Literal (String n), None ->
382+
(* Search for this key in JSON *)
383+
let r =
384+
match json with
385+
| `Null -> Output.return (`Assoc [ (n, `Null) ])
386+
| `Assoc l -> (
387+
match List.assoc_opt n l with
388+
| None -> Output.return (`Assoc [ (n, `Null) ])
389+
| Some v -> Output.return (`Assoc [ (n, v) ]))
390+
| _ -> Error (Json.show json ^ " is not implemented")
391+
in
392+
r
393+
| Literal (String key), Some right_expr -> (
394+
match right_expr with
395+
| Key (search_val, _) -> (
396+
match json with
397+
| `Assoc l -> (
398+
match List.assoc_opt search_val l with
399+
| None -> Output.return (`Assoc [ (key, `Null) ])
400+
| Some v -> Output.return (`Assoc [ (key, v) ]))
401+
| _ -> assert false)
402+
| rexp ->
403+
let* rexp = compile rexp json in
404+
Output.return (`Assoc [ (key, rexp) ]))
405+
| _ -> Error (show_expression left_expr ^ " is not implemented"))
406+
list
407+
|> Output.collect

0 commit comments

Comments
 (0)