File: query.ml

package info (click to toggle)
ben 0.7.0%2Bdeb8u1
  • links: PTS, VCS
  • area: main
  • in suites: jessie
  • size: 460 kB
  • ctags: 776
  • sloc: ml: 3,584; makefile: 86; ansic: 39
file content (169 lines) | stat: -rw-r--r-- 5,965 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
(**************************************************************************)
(*  Copyright © 2009 Stéphane Glondu <steph@glondu.net>                   *)
(*                                                                        *)
(*  This program is free software: you can redistribute it and/or modify  *)
(*  it under the terms of the GNU Affero General Public License as        *)
(*  published by the Free Software Foundation, either version 3 of the    *)
(*  License, or (at your option) any later version, with the additional   *)
(*  exemption that compiling, linking, and/or using OpenSSL is allowed.   *)
(*                                                                        *)
(*  This program is distributed in the hope that it will be useful, but   *)
(*  WITHOUT ANY WARRANTY; without even the implied warranty of            *)
(*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *)
(*  Affero General Public License for more details.                       *)
(*                                                                        *)
(*  You should have received a copy of the GNU Affero General Public      *)
(*  License along with this program.  If not, see                         *)
(*  <http://www.gnu.org/licenses/>.                                       *)
(**************************************************************************)

open Printf
open Benl_error
open Benl_types
open Benl_base

type t = Benl_types.expr

let rec simplify query = match query with
  | EMatch (field, EString package) ->
    begin match (Benl_core.simple_split '|' package) with
    | package::[] -> query
    | packages ->
      let packages = List.map Re_pcre.quote packages in
      let r_string = String.concat "|" packages in
      let rex = Re_pcre.regexp (Printf.sprintf "\b(%s)\b" r_string) in
      EMatch (field, ERegexp (package, rex))
    end
  | EMatch (_, (EDep _ | ERegexp _)) -> query
  | Etrue | Efalse | ESource | EVersion _ | EString _ | ERegexp _ | EDep _ -> query
  | EMatch (f, e) -> EMatch (f, simplify e)
  | EList l -> begin match l with
    | [] -> Etrue
    | h::[] -> simplify h
    | _::_ -> EList (List.map simplify l)
  end
  | ENot e -> begin match (simplify e) with
    | Etrue -> Efalse
    | Efalse -> Etrue
    | e -> ENot e
  end
  | EOr (e1, e2) -> begin match (simplify e1, simplify e2) with
    | Efalse, e | e, Efalse -> e
    | Etrue, _ | _, Etrue -> Etrue
    | e1, e2 -> EOr (e1, e2)
  end
  | EAnd (e1, e2) -> begin match (simplify e1, simplify e2) with
    | Efalse, e | e, Efalse -> Efalse
    | Etrue, e | e, Etrue -> e
    | e1, e2 -> EAnd (e1, e2)
  end

let of_expr x = simplify x

let of_string s =
  let lexbuf = Lexing.from_string s in
  simplify (Benl_parser.full_expr Benl_lexer.token lexbuf)

let parens show expr =
  if show
  then sprintf "(%s)" expr
  else expr

let rec to_string_b ?(escape = true) last_op = function
  | EMatch (f, ERegexp r) ->
      sprintf ".%s ~ %s" f (string_of_regexp r)
  | ENot e ->
      sprintf "!%s" (to_string_b "!" e)
  | Etrue -> sprintf "true"
  | Efalse -> sprintf "false"
  | EAnd (e1, e2) ->
    parens
      (last_op <> "&" && last_op <> "")
      (sprintf "%s & %s" (to_string_b "&" e1) (to_string_b "&" e2))
  | EOr (e1, e2) ->
    parens
      (last_op <> "|" && last_op <> "")
      (sprintf "%s | %s" (to_string_b "|" e1) (to_string_b "|" e2))
  | EList xs ->
      sprintf "[%s]" (String.concat "; " (List.map (to_string_b "") xs))
  | ESource -> "source"
  | EString x -> string_of_string escape x
  | EVersion (cmp, x) ->
    parens (last_op <> "") (sprintf ".%s \"%s\"" (string_of_cmp cmp) x)
  | EMatch (field, EDep (package, cmp, ref_version)) ->
    parens (last_op <> "") (sprintf ".%s ~ \"%s\" %s \"%s\""
                field
                package
                (string_of_cmp cmp)
                ref_version)
  | EMatch (field, EString package) ->
    parens (last_op <> "") (sprintf ".%s ~ \"%s\"" field package)
  | x -> raise (Unexpected_expression "<unable to convert to string>")

let to_string ?(escape = true) = to_string_b ~escape ""

let rec eval kind pkg = function
  | EMatch (field, ERegexp (r, rex)) ->
      begin try
        let value = Package.get field pkg in
        ignore (Re_pcre.exec ~rex value);
        true
      with Not_found ->
        false
      end
  | Etrue -> true
  | Efalse -> false
  | ESource ->
      kind = `source
  | EOr (e1, e2) ->
      eval kind pkg e1 || eval kind pkg e2
  | EAnd (e1, e2) ->
      eval kind pkg e1 && eval kind pkg e2
  | ENot e ->
      not (eval kind pkg e)
  | EVersion (cmp, ref_version) ->
      let value = Package.get "version" pkg in
      version_compare cmp value ref_version
  | EMatch (field, EDep (package, cmp, refv)) ->
    let deps = Package.dependencies field pkg in
    List.exists
      (fun x ->
        x.Package.dep_name = package && begin
          match x.Package.dep_version with
            | None -> false
            | Some (rcmp, rrefv) ->
              match rcmp, cmp with
                | Ge, Ge | Gt, Ge | Gt, Gt -> Version.compare rrefv refv >= 0
                | Ge, Gt -> Version.compare rrefv refv > 0
                | _, _ -> false  (* FIXME: missing cases *)
        end)
      deps
  | EMatch (field, EString package) ->
    begin try
      let deps = Package.dependencies field pkg in
      List.exists
        (fun x -> x.Package.dep_name = package)
        deps
    with Not_found ->
      false
    end
  | x ->
      raise (Unexpected_expression (to_string x))

let eval_source x = eval `source x
let eval_binary x = eval `binary x

let rec fields accu = function
  | EMatch (f, _) ->
      Fields.add f accu
  | ENot e ->
      fields accu e
  | Etrue | Efalse -> accu
  | EAnd (e1, e2) | EOr (e1, e2) ->
      fields (fields accu e1) e2
  | EList xs ->
      List.fold_left fields accu xs
  | ESource | EString _ | ERegexp _ | EDep _ ->
      accu
  | EVersion _ ->
      Fields.add "version" accu