Legend:
Page
Library
Module
Module type
Parameter
Class
Class type
Source
Page
Library
Module
Module type
Parameter
Class
Class type
Source
cudf_parser.ml
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 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
(*****************************************************************************) (* libCUDF - CUDF (Common Upgrade Description Format) manipulation library *) (* Copyright (C) 2009-2015 Stefano Zacchiroli <zack@upsilon.cc> *) (* *) (* This library is free software: you can redistribute it and/or modify *) (* it under the terms of the GNU Lesser General Public License as *) (* published by the Free Software Foundation, either version 3 of the *) (* License, or (at your option) any later version. A special linking *) (* exception to the GNU Lesser General Public License applies to this *) (* library, see the COPYING file for more information. *) (*****************************************************************************) open ExtLib open Printf open Cudf open Cudf_types type cudf_parser = { lexbuf: Lexing.lexbuf ; fname: string ; mutable typedecl: Cudf_conf.stanza_typedecl ; priv_in_chan: in_channel option; (* in_channel to be closed upon close() invocation, to avoid leaving up to OCaml GC when to close it. Will be set only if it is Cudf_parser itself who has created the in_channel, e.g., upon Cudf_parser.from_file *) } type loc_map = (string * loc) list exception Parse_error of string * loc let parse_error loc msg = raise (Parse_error (msg, loc)) let from_in_channel ?(typedecl=Cudf_conf.stanza_typedecl) ic = { lexbuf = Lexing.from_channel ic ; typedecl = typedecl ; fname = "" ; priv_in_chan = None ; } let from_IO_in_channel ?(typedecl=Cudf_conf.stanza_typedecl) ic = let f s n = try IO.input ic s 0 n with IO.No_more_input -> 0 in { lexbuf = Lexing.from_function f; typedecl = typedecl ; fname = "" ; priv_in_chan = None ; } let from_file ?(typedecl=Cudf_conf.stanza_typedecl) fname = (* Syntax error with OCaml 3.10.2: * { from_in_channel ?typedecl (open_in fname) * with fname = fname } *) let ic = open_in fname in { lexbuf = Lexing.from_channel ic ; typedecl = typedecl ; fname = fname ; priv_in_chan = Some ic ; } let close p = match p.priv_in_chan with | None -> () | Some ic -> close_in ic let parse_stanza p = try (match Cudf_822_parser.stanza_822 Cudf_822_lexer.token_822 p.lexbuf with | Some stanza -> List.fold_right (* split loc_map from (string * loc) stanzas *) (* non tail recursive, but should be ok: stanzas are short *) (fun (k, (loc, v)) (locs, stanza) -> (k, loc) :: locs, (k, v) :: stanza) stanza ([], []) | None -> raise End_of_file) with Parse_error_822 (msg, loc) -> raise (Syntax_error (msg, loc)) let loc_lookuper locs = (fun p -> try List.assoc p locs with Not_found -> prerr_endline "non located property" ; assert false) let type_check_stanza ?locs stanza types = let lookup_loc = match locs with | None -> (fun _p -> dummy_loc) | Some locs -> loc_lookuper locs in let typed_stanza = List.map (fun (k, v) -> try let decl = List.assoc k types in let typed_v = Cudf_types_pp.parse_value (type_of_typedecl decl) v in k, typed_v with | Not_found -> parse_error (lookup_loc k) (sprintf "unexpected property \"%s\" in this stanza" k) | Cudf_types_pp.Type_error (typ, v) -> (* localize type errors *) raise (Cudf_types.Type_error (typ, v, lookup_loc k))) stanza in let defaults, missing = (* deal with missing properties *) List.fold_left (fun (defaults, missing) (name, ty1) -> match value_of_typedecl ty1, List.mem_assoc name typed_stanza with | None, true -> defaults, missing (* mandatory, present *) | None, false -> defaults, (name :: missing) (* mandatory, missing *) | Some _v, true -> defaults, missing (* optional, present *) | Some v, false -> (* optional, missing *) (name, v) :: defaults, missing) ([], []) types in if missing <> [] then begin let loc = match stanza with [] -> dummy_loc | (k,_) :: _ -> lookup_loc k in parse_error loc (sprintf "missing mandatory properties: %s" (String.concat ", " missing)) end; typed_stanza @ defaults (** Cast a typed stanza starting with "package: " to a {!Cudf.package}. ASSUMPTION: type checking of the stanza has already happend, in particular all extra properties have already been checked for allowance. *) let bless_package stanza = let p = default_package in (* assumption: should be completely overrode *) let rec aux p = function | ("package", `Pkgname v) :: tl -> aux { p with package = v } tl | ("version", `Posint v) :: tl -> aux { p with version = v } tl | ("depends", `Vpkgformula v) :: tl -> aux { p with depends = v } tl | ("conflicts", `Vpkglist v) :: tl -> aux { p with conflicts = v } tl | ("provides", `Veqpkglist v) :: tl -> aux { p with provides = v } tl | ("installed", `Bool v) :: tl -> aux { p with installed = v } tl | ("was-installed", `Bool v) :: tl -> aux { p with was_installed = v } tl | ("keep", `Enum (_, v)) :: tl -> aux { p with keep = Cudf_types_pp.parse_keep v } tl | (k, (v: typed_value)) :: tl -> aux { p with pkg_extra = (k, v) :: p.pkg_extra } tl | [] -> p in let p' = aux p stanza in { p' with pkg_extra = List.rev p'.pkg_extra } (** Cast a typed stanza starting with "preamble: " to a {!Cudf.preamble} ASSUMPTION: as per {!Cudf_parser.bless_package} above. *) let bless_preamble stanza = let p = default_preamble in (* assumption: should be completely overrode *) let rec aux p = function | ("preamble", `String v) :: tl -> aux { p with preamble_id = v } tl | ("property", `Typedecl v) :: tl -> aux { p with property = v } tl | ("univ-checksum", `String v) :: tl -> aux { p with univ_checksum = v } tl | ("status-checksum", `String v) :: tl -> aux { p with status_checksum = v } tl | ("req-checksum", `String v) :: tl -> aux { p with req_checksum = v } tl | [] -> p | _ -> assert false in aux p stanza (** Cast a typed stanza starting with "request: " to a {!Cudf.request}. ASSUMPTION: as per {!Cudf_parser.bless_package} above. *) let bless_request stanza = let r = default_request in (* assumption: should be completely overrode *) let rec aux r = function | ("request", `String v) :: tl -> aux { r with request_id = v } tl | ("install", `Vpkglist v) :: tl -> aux { r with install = v } tl | ("remove", `Vpkglist v) :: tl -> aux { r with remove = v } tl | ("upgrade", `Vpkglist v) :: tl -> aux { r with upgrade = v } tl | (k, (v: typed_value)) :: tl -> aux { r with req_extra = (k, v) :: r.req_extra } tl | [] -> r in let r' = aux r stanza in { r' with req_extra = List.rev r'.req_extra } let parse_item' p = let locs, stanza = try parse_stanza p with Syntax_error (msg, loc) -> parse_error loc msg in let lookup_loc = loc_lookuper locs in let typed_stanza = match stanza with | [] -> eprintf "empty stanza\n%!"; assert false | (postmark, _) :: _ -> (try type_check_stanza ~locs stanza (List.assoc postmark p.typedecl) with Not_found -> parse_error (lookup_loc postmark) (sprintf "Unknown stanza type, starting with \"%s\" postmark." postmark)) in let item = match typed_stanza with | [] -> assert false | ("preamble", _) :: _ -> let preamble = bless_preamble typed_stanza in p.typedecl <- (* update type declaration for "package" stanza *) (let pkg_typedecl = (List.assoc "package" p.typedecl) @ preamble.property in ("package", pkg_typedecl) :: List.remove_assoc "package" p.typedecl); `Preamble preamble | ("package", _) :: _ -> `Package (bless_package typed_stanza) | ("request", _) :: _ -> `Request (bless_request typed_stanza) | _ -> assert false in (locs, item) let parse_item p = snd (parse_item' p) let get_postmark = function | `Package pkg -> pkg.package | `Request req -> req.request_id | `Preamble pre -> pre.preamble_id let parse p = let pre, pkgs, req = ref None, ref [], ref None in let rec aux_pkg () = match parse_item' p with | _locs, `Package pkg -> pkgs := pkg :: !pkgs ; aux_pkg () | _locs, `Request req' -> req := Some req' (* stop recursion after req *) | locs, `Preamble pre -> parse_error (loc_lookuper locs pre.preamble_id) "late preamble" in let parse () = try (match parse_item p with (* parse first item *) | `Preamble pre' -> pre := Some pre' ; (try aux_pkg () with End_of_file -> ()) | `Package pkg -> pkgs := [pkg] ; (try aux_pkg () with End_of_file -> ()) | `Request req' -> req := Some req') with End_of_file -> () in (try parse () ; with Cudf_types.Type_error (typ, v, loc) -> parse_error loc (sprintf ("a value of type \"%s\" was expected, but \"%s\" has been found") (Cudf_types_pp.string_of_type typ) (Cudf_types_pp.string_of_value v))); (try (* check for forbidden trailing content *) let locs, item = parse_item' p in parse_error (loc_lookuper locs (get_postmark item)) "trailing stanzas after final request stanza" with End_of_file -> ()); (!pre, List.rev !pkgs, !req) let load p = let pre, pkgs, req = parse p in (pre, load_universe pkgs, req) let load_solution p univ = let pre, sol_pkgs, _ = parse p in let expand_package pkg = let old_pkg = try lookup_package univ (pkg.package, pkg.version) with Not_found -> parse_error dummy_loc (sprintf "unknown package (%s,%d) found in solution" pkg.package pkg.version) in { old_pkg with installed = pkg.installed } in let sol_univ = load_universe (List.map expand_package sol_pkgs) in pre, sol_univ let parser_wrapper ?typedecl fname f = let ic = open_in fname in let p = from_in_channel ?typedecl ic in finally (fun () -> close_in ic ; close p) f p let parse_from_file ?typedecl fname = parser_wrapper ?typedecl fname parse let load_from_file ?typedecl fname = parser_wrapper ?typedecl fname load let load_solution_from_file fname univ = parser_wrapper fname (fun p -> load_solution p univ)