Skip to content

Commit 8109d0a

Browse files
authored
Merge pull request #50 from ProgramingIsTheFuture/new_features
new functions + more tests and fixing range
2 parents 1c6549c + 5a0912d commit 8109d0a

File tree

8 files changed

+138
-89
lines changed

8 files changed

+138
-89
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ The report shows that **query-json** is between 2x and 5x faster than **jq** in
125125
#### [Builtin operators and functions](https://stedolan.github.io/jq/manual/v1.6/#Builtinoperatorsandfunctions)
126126
127127
- Abs: `abs`
128+
- Add: `add`
128129
- Addition: `+`
129130
- Subtraction: `-`
130131
- Multiplication, division, modulo: `*`, `/`, and `%`

source/Ast.ml

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

8+
type builtin = Add | Abs [@@deriving show { with_path = false }]
9+
810
type op =
911
| Add
1012
| Sub
@@ -82,7 +84,6 @@ type expression =
8284
| Some_ of expression (* some, Some_ to not collide with option *)
8385
| Find of expression (* find(x) *)
8486
(* operations *)
85-
| Abs
8687
| Operation of expression * op * expression
8788
(* Generic *)
8889
| Length (* length *)
@@ -102,4 +103,6 @@ type expression =
102103
| Break (* break *)
103104
(* Conditionals *)
104105
| Not (* not *)
106+
(* builtin *)
107+
| Fun of builtin
105108
[@@deriving show { with_path = false }]

source/Compiler.ml

Lines changed: 105 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -39,10 +39,9 @@ let make_error (name : string) (json : Json.t) =
3939
let itemName = get_field_name json in
4040
make_error_wrong_operation name itemName json
4141

42-
let empty = Ok []
43-
4442
module Output = struct
4543
let return x = Ok [ x ]
44+
let empty = Ok []
4645

4746
let lift2 (f : 'a -> 'b -> 'c) (mx : ('a, string) result)
4847
(my : ('b, string) result) : ('c, string) result =
@@ -60,6 +59,88 @@ end
6059

6160
let ( let* ) = Output.bind
6261

62+
module Operators = struct
63+
let not (json : Json.t) =
64+
match json with
65+
| `Bool false | `Null -> Output.return (`Bool true)
66+
| _ -> Output.return (`Bool false)
67+
68+
let rec merge_map ~(eq_f : 'a -> 'a -> 'b) ~(f : 'a -> 'b)
69+
(cmp : 'a -> 'a -> int) (l1 : 'a list) (l2 : 'a list) : 'b list =
70+
match (l1, l2) with
71+
| [], l2 -> List.map f l2
72+
| l1, [] -> List.map f l1
73+
| h1 :: t1, h2 :: t2 ->
74+
let r = cmp h1 h2 in
75+
if r = 0 then eq_f h1 h2 :: merge_map ~eq_f ~f cmp t1 t2
76+
else if r < 0 then f h1 :: merge_map ~eq_f ~f cmp t1 l2
77+
else f h2 :: merge_map ~eq_f ~f cmp l1 t2
78+
79+
let rec add str (left : Json.t) (right : Json.t) :
80+
(Json.t list, string) result =
81+
match (left, right) with
82+
| `Float l, `Float r -> Output.return (`Float (l +. r))
83+
| `Int l, `Float r -> Output.return (`Float (float_of_int l +. r))
84+
| `Float l, `Int r -> Output.return (`Float (l +. float_of_int r))
85+
| `Int l, `Int r ->
86+
Output.return (`Float (float_of_int l +. float_of_int r))
87+
| `Null, `Int r | `Int r, `Null -> Output.return (`Float (float_of_int r))
88+
| `Null, `Float r | `Float r, `Null -> Output.return (`Float r)
89+
| `String l, `String r -> Output.return (`String (l ^ r))
90+
| `Null, `String r | `String r, `Null -> Output.return (`String r)
91+
| `Assoc l, `Assoc r -> (
92+
let cmp (key1, _) (key2, _) = String.compare key1 key2 in
93+
let eq_f (key, v1) (_, v2) =
94+
let* result = add str v1 v2 in
95+
Output.return (key, result)
96+
in
97+
match merge_map ~f:Output.return ~eq_f cmp l r |> Output.collect with
98+
| Ok l -> Output.return (`Assoc l)
99+
| Error e -> Error e)
100+
| `Null, `Assoc r | `Assoc r, `Null -> Output.return (`Assoc r)
101+
| `List l, `List r -> Output.return (`List (l @ r))
102+
| `Null, `List r | `List r, `Null -> Output.return (`List r)
103+
| `Null, `Null -> Output.return `Null
104+
| _ -> Error (make_error str left)
105+
106+
let apply_op str fn (left : Json.t) (right : Json.t) =
107+
match (left, right) with
108+
| `Float l, `Float r -> Output.return (`Float (fn l r))
109+
| `Int l, `Float r -> Output.return (`Float (fn (float_of_int l) r))
110+
| `Float l, `Int r -> Output.return (`Float (fn l (float_of_int r)))
111+
| `Int l, `Int r ->
112+
Output.return (`Float (fn (float_of_int l) (float_of_int r)))
113+
| _ -> Error (make_error str left)
114+
115+
let compare str fn (left : Json.t) (right : Json.t) =
116+
match (left, right) with
117+
| `Float l, `Float r -> Output.return (`Bool (fn l r))
118+
| `Int l, `Float r -> Output.return (`Bool (fn (float_of_int l) r))
119+
| `Float l, `Int r -> Output.return (`Bool (fn l (float_of_int r)))
120+
| `Int l, `Int r ->
121+
Output.return (`Bool (fn (float_of_int l) (float_of_int r)))
122+
| _ -> Error (make_error str right)
123+
124+
let condition (str : string) (fn : bool -> bool -> bool) (left : Json.t)
125+
(right : Json.t) =
126+
match (left, right) with
127+
| `Bool l, `Bool r -> Output.return (`Bool (fn l r))
128+
| _ -> Error (make_error str right)
129+
130+
let gt = compare ">" ( > )
131+
let gte = compare ">=" ( >= )
132+
let lt = compare "<" ( < )
133+
let lte = compare "<=" ( <= )
134+
let and_ = condition "and" ( && )
135+
let or_ = condition "or" ( || )
136+
let eq l r = Output.return (`Bool (l = r))
137+
let notEq l r = Output.return (`Bool (l <> r))
138+
let add = add "+"
139+
let sub = apply_op "-" (fun l r -> l -. r)
140+
let mult = apply_op "*" (fun l r -> l *. r)
141+
let div = apply_op "/" (fun l r -> l /. r)
142+
end
143+
63144
let keys (json : Json.t) =
64145
match json with
65146
| `Assoc _list ->
@@ -101,15 +182,11 @@ let range ?step from upto =
101182
let rec range ?(step = 1) start stop =
102183
if step = 0 then []
103184
else if (step > 0 && start >= stop) || (step < 0 && start <= stop) then []
104-
else start :: range ~step (start + step) stop
185+
else `Int start :: range ~step (start + step) stop
105186
in
106187
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
188+
| None -> Output.return (range 1 from)
189+
| Some upto -> Output.return (range ?step from upto)
113190

114191
let split expr json =
115192
match json with
@@ -142,48 +219,6 @@ let length (json : Json.t) =
142219
| `List list -> Output.return (`Int (list |> List.length))
143220
| _ -> Error (make_error "length" json)
144221

145-
let not (json : Json.t) =
146-
match json with
147-
| `Bool false | `Null -> Output.return (`Bool true)
148-
| _ -> Output.return (`Bool false)
149-
150-
let apply str fn (left : Json.t) (right : Json.t) =
151-
match (left, right) with
152-
| `Float l, `Float r -> Output.return (`Float (fn l r))
153-
| `Int l, `Float r -> Output.return (`Float (fn (float_of_int l) r))
154-
| `Float l, `Int r -> Output.return (`Float (fn l (float_of_int r)))
155-
| `Int l, `Int r ->
156-
Output.return (`Float (fn (float_of_int l) (float_of_int r)))
157-
| _ -> Error (make_error str left)
158-
159-
let compare str fn (left : Json.t) (right : Json.t) =
160-
match (left, right) with
161-
| `Float l, `Float r -> Output.return (`Bool (fn l r))
162-
| `Int l, `Float r -> Output.return (`Bool (fn (float_of_int l) r))
163-
| `Float l, `Int r -> Output.return (`Bool (fn l (float_of_int r)))
164-
| `Int l, `Int r ->
165-
Output.return (`Bool (fn (float_of_int l) (float_of_int r)))
166-
| _ -> Error (make_error str right)
167-
168-
let condition (str : string) (fn : bool -> bool -> bool) (left : Json.t)
169-
(right : Json.t) =
170-
match (left, right) with
171-
| `Bool l, `Bool r -> Output.return (`Bool (fn l r))
172-
| _ -> Error (make_error str right)
173-
174-
let gt = compare ">" ( > )
175-
let gte = compare ">=" ( >= )
176-
let lt = compare "<" ( < )
177-
let lte = compare "<=" ( <= )
178-
let and_ = condition "and" ( && )
179-
let or_ = condition "or" ( || )
180-
let eq l r = Output.return (`Bool (l = r))
181-
let notEq l r = Output.return (`Bool (l <> r))
182-
let add = apply "+" (fun l r -> l +. r)
183-
let sub = apply "-" (fun l r -> l -. r)
184-
let mult = apply "*" (fun l r -> l *. r)
185-
let div = apply "/" (fun l r -> l /. r)
186-
187222
let filter (fn : Json.t -> bool) (json : Json.t) =
188223
match json with
189224
| `List list -> Ok (`List (List.filter fn list))
@@ -288,15 +323,15 @@ let slice (start : int option) (finish : int option) (json : Json.t) =
288323

289324
let iterator (json : Json.t) =
290325
match json with
291-
| `List [] -> empty
326+
| `List [] -> Output.empty
292327
| `List items -> Ok items
293328
| `Assoc obj -> Ok (List.map snd obj)
294329
| _ -> Error (make_error "[]" json)
295330

296331
let rec compile expression json : (Json.t list, string) result =
297332
match expression with
298333
| Identity -> Output.return json
299-
| Empty -> empty
334+
| Empty -> Output.empty
300335
| Keys -> keys json
301336
| Key key -> member key json
302337
| Optional expr -> (
@@ -309,9 +344,10 @@ let rec compile expression json : (Json.t list, string) result =
309344
| Head -> head json
310345
| Tail -> tail json
311346
| Length -> length json
312-
| Not -> not json
347+
| Not -> Operators.not json
313348
| Map expr -> map expr json
314349
| Operation (left, op, right) -> (
350+
let open Operators in
315351
match op with
316352
| Add -> operation left right add json
317353
| Sub -> operation left right sub json
@@ -337,32 +373,44 @@ let rec compile expression json : (Json.t list, string) result =
337373
| Select conditional -> (
338374
let* res = compile conditional json in
339375
match res with
340-
| `Bool b -> ( match b with true -> Output.return json | false -> empty)
376+
| `Bool b -> (
377+
match b with true -> Output.return json | false -> Output.empty)
341378
| _ -> Error (make_error "select" res))
342379
| List exprs ->
343380
Output.collect (List.map (fun expr -> compile expr json) exprs)
344381
|> Result.map (fun x -> [ `List x ])
345382
| Comma (leftR, rightR) ->
346383
Result.bind (compile leftR json) (fun left ->
347384
Result.bind (compile rightR json) (fun right -> Ok (left @ right)))
385+
| Object [] -> Output.return (`Assoc [])
348386
| Object list -> handle_objects list json
349387
| Has e -> (
350388
match e with
351389
| Literal ((String _ | Number _) as e) -> has json e
352390
| _ -> Error (show_expression e ^ " is not allowed"))
353391
| In e -> in_ json e
354-
| Range (from, upto, step) -> range ?step from upto
392+
| Range (from, upto, step) -> Result.map List.flatten (range ?step from upto)
355393
| Reverse -> (
356394
match json with
357395
| `List l -> Output.return (`List (List.rev l))
358396
| _ -> Error (make_error "reverse" json))
359397
| Split expr -> split expr json
360398
| Join expr -> join expr json
361-
| Abs -> (
399+
| Fun Abs -> (
362400
match json with
363401
| `Int n -> Output.return (`Int (if n < 0 then -n else n))
364402
| `Float j -> Output.return (`Float (if j < 0. then -.j else j))
365403
| _ -> Error (make_error "reverse" json))
404+
| Fun Add -> (
405+
match json with
406+
| `List [] -> Output.return `Null
407+
| `List l ->
408+
List.fold_left
409+
(fun acc el ->
410+
let* acc = acc in
411+
Operators.add acc el)
412+
(Output.return `Null) l
413+
| _ -> Error (make_error "add" json))
366414
| IfThenElse (cond, if_branch, else_branch) -> (
367415
let* cond = compile cond json in
368416
match cond with

source/Parser.mly

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
%token <bool> BOOL
1212
%token NULL
1313
%token <string> IDENTIFIER
14-
%token RANGE ABS
14+
%token RANGE
1515
%token IF THEN ELSE ELIF END
1616
%token DOT
1717
%token RECURSE
@@ -83,8 +83,8 @@ sequence_expr:
8383
{ e }
8484

8585
%inline operator:
86-
| ADD {Add}
8786
| SUB {Sub}
87+
| ADD {Add}
8888
| MULT {Mult}
8989
| DIV {Div}
9090
| EQUAL {Eq}
@@ -122,7 +122,7 @@ term:
122122
{ Literal (Bool b) }
123123
| NULL
124124
{ Literal(Null) }
125-
| RANGE; OPEN_PARENT; nl = separated_nonempty_list(SEMICOLON, NUMBER); CLOSE_PARENT;
125+
| RANGE; OPEN_PARENT; nl = separated_nonempty_list(SEMICOLON, number); CLOSE_PARENT;
126126
{
127127
let nl = List.map int_of_float nl in
128128
match nl with
@@ -132,8 +132,6 @@ term:
132132
| x :: y :: z :: [] -> Range (x, Some y, Some z)
133133
| _ -> failwith "too many arguments for function range"
134134
}
135-
| ABS
136-
{ Abs }
137135
| f = FUNCTION; CLOSE_PARENT;
138136
{ failwith (f ^ "(), should contain a body") }
139137
| f = FUNCTION; cb = sequence_expr; CLOSE_PARENT;
@@ -196,6 +194,8 @@ term:
196194
| "nan" -> Nan
197195
| "is_nan" -> IsNan
198196
| "not" -> Not
197+
| "abs" -> Fun (Abs)
198+
| "add" -> Fun (Add)
199199
(* TODO: remove failwiths once we have implemented the functions *)
200200
| "if" -> failwith @@ Console.Errors.not_implemented f
201201
| "then" -> failwith @@ Console.Errors.not_implemented f

source/Tokenizer.ml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ let dot = [%sedlex.regexp? '.']
44
let digit = [%sedlex.regexp? '0' .. '9']
55
let number = [%sedlex.regexp? Plus digit, Opt '.', Opt (Plus digit)]
66
let space = [%sedlex.regexp? Plus ('\n' | '\t' | ' ')]
7-
let identifier = [%sedlex.regexp? alphabetic, Star (alphabetic | digit)]
7+
let identifier = [%sedlex.regexp? (alphabetic | '_'), Star (alphabetic | digit | '_')]
88
let not_double_quotes = [%sedlex.regexp? Compl '"']
99

1010
type token =
@@ -45,7 +45,6 @@ type token =
4545
| ELSE
4646
| ELIF
4747
| END
48-
| ABS
4948
| EOF
5049
[@@deriving show]
5150

@@ -106,7 +105,6 @@ let rec tokenize buf =
106105
| "else" -> Ok ELSE
107106
| "elif" -> Ok ELIF
108107
| "end" -> Ok END
109-
| "abs" -> Ok ABS
110108
| dot -> Ok DOT
111109
| ".." -> Ok RECURSE
112110
| '"' -> string buf

test/Test_parse.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,4 +85,5 @@ let tests =
8585
( Literal (Bool false),
8686
Literal (String "Welcome"),
8787
Literal (String "Real") ) ));
88+
case "map(add)" (Map (Fun Add));
8889
]

0 commit comments

Comments
 (0)