/[svn]/schema/schema_builtin.ml
ViewVC logotype

Contents of /schema/schema_builtin.ml

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1490 - (show annotations)
Tue Jul 10 18:55:12 2007 UTC (5 years, 10 months ago) by abate
File size: 24370 byte(s)
[r2005-02-25 14:46:38 by afrisch] float

Original author: afrisch
Date: 2005-02-25 14:46:39+00:00
1 open Printf
2
3 open Encodings
4 open Schema_pcre
5 open Schema_common
6 open Schema_types
7
8 (* TODO dates: boundary checks (e.g. 95/26/2003) *)
9 (* TODO a lot of almost cut-and-paste code, expecially in gFoo types validation
10 *)
11
12 (* TODO: distinguish primitive and derived types in the interface *)
13
14 (** {2 Aux/Misc stuff} *)
15
16 let xsd = Schema_xml.xsd
17 let add_xsd_prefix s = (xsd, Utf8.mk s)
18
19 let unsupported = [ "NOTATION"; "QName" ]
20
21 let is_empty s = Utf8.equal s (Utf8.mk "")
22
23 let zero = Intervals.V.zero
24 let one = (Intervals.V.succ Intervals.V.zero)
25 let minus_one = (Intervals.V.pred Intervals.V.zero)
26 let long_l = (Intervals.V.mk "-9223372036854775808")
27 let long_r = (Intervals.V.mk "9223372036854775807")
28 let int_l = (Intervals.V.mk "-2147483648")
29 let int_r = (Intervals.V.mk "2147483647")
30 let short_l = (Intervals.V.mk "-32768")
31 let short_r = (Intervals.V.mk "32767")
32 let byte_l = (Intervals.V.mk "-128")
33 let byte_r = (Intervals.V.mk "127")
34
35 let xml_S_RE = pcre_regexp "[ \\t\\r\\n]+"
36 (* split a string at XML recommendation "S" production boundaries *)
37 let split_xml_S s = pcre_split ~rex:xml_S_RE s
38 let norm_RE = pcre_regexp "[\\t\\r\\n]"
39
40 let char_of_hex =
41 let int_of_hex_char = function
42 | '0' -> 0 | '1' -> 1 | '2' -> 2 | '3' -> 3 | '4' -> 4 | '5' -> 5 | '6' -> 6
43 | '7' -> 7 | '8' -> 8 | '9' -> 9 | 'a' | 'A' -> 10 | 'b' | 'B' -> 11
44 | 'c' | 'C' -> 12 | 'd' | 'D' -> 13 | 'e' | 'E' -> 14 | 'f' | 'F' -> 15
45 | _ -> assert false
46 in
47 (* most significative, least significative *)
48 fun ms ls -> Char.unsafe_chr (int_of_hex_char ms * 16 + int_of_hex_char ls)
49
50 let strip_parens s = Pcre.replace ~pat:"[()]" s
51 let add_limits s = "^" ^ s ^ "$"
52
53 exception Error of string
54 let simple_type_error name = raise (Error name)
55
56 let qualify s = (Ns.empty, Encodings.Utf8.mk s)
57
58 (* regular expressions used to validate built-in types *)
59
60 let timezone_RE_raw = "(Z)|(([+-])?(\\d{2}):(\\d{2}))"
61 let date_RE_raw = "(\\d{4,})-(\\d{2})-(\\d{2})"
62 let time_RE_raw = "(\\d{2}):(\\d{2}):(\\d{2})"
63
64 let gYearMonth_RE_raw = sprintf "(-)?(\\d{4,})-(\\d{2})(%s)?" timezone_RE_raw
65 let gYear_RE_raw = sprintf "(-)?(\\d{4,})(%s)?" timezone_RE_raw
66 let gMonthDay_RE_raw = sprintf "--(\\d{2})-(\\d{2})(%s)?" timezone_RE_raw
67 let gDay_RE_raw = sprintf "---(\\d{2})(%s)?" timezone_RE_raw
68 let gMonth_RE_raw = "--(\\d{2})--(%s)?"
69
70 (** {2 CDuce types} *)
71
72 let positive_field = false, qualify "positive", Builtin_defs.bool
73 let year_field = false, qualify "year", Builtin_defs.int
74 let month_field = false, qualify "month", Builtin_defs.int
75 let day_field = false, qualify "day", Builtin_defs.int
76 let hour_field = false, qualify "hour", Builtin_defs.int
77 let minute_field = false, qualify "minute", Builtin_defs.int
78 let second_field = false, qualify "second", Builtin_defs.int
79 (* TODO this should be a decimal *)
80 let time_type_fields = [ hour_field; minute_field; second_field ]
81 let date_type_fields = [ year_field; month_field; day_field ]
82
83 let time_kind_field = false, qualify "time_kind", Builtin_defs.time_kind
84 let time_kind kind = (qualify "time_kind", Value.Atom (Atoms.V.mk_ascii kind))
85
86 (* TODO the constraint that at least one part should be present isn't easily
87 expressible with CDuce types *)
88 let duration_type = Types.rec_of_list' [
89 time_kind_field;
90 positive_field;
91 true, qualify "year", Builtin_defs.int;
92 true, qualify "month", Builtin_defs.int;
93 true, qualify "day", Builtin_defs.int;
94 true, qualify "hour", Builtin_defs.int;
95 true, qualify "minute", Builtin_defs.int;
96 true, qualify "second", Builtin_defs.int; (* TODO this should be a decimal *)
97 ]
98 let timezone_type = Types.rec_of_list' [
99 positive_field;
100 hour_field; minute_field
101 ]
102 let timezone_type_fields = [ true, qualify "timezone", timezone_type ]
103 let time_type = Types.rec_of_list' (time_kind_field :: time_type_fields @ timezone_type_fields)
104 let date_type = Types.rec_of_list' (time_kind_field :: positive_field :: date_type_fields)
105 let dateTime_type =
106 Types.rec_of_list' (time_kind_field :: positive_field ::
107 (date_type_fields @ time_type_fields @ timezone_type_fields))
108 let gYearMonth_type = Types.rec_of_list' [
109 positive_field; time_kind_field; year_field; month_field
110 ]
111 let gYear_type = Types.rec_of_list' [ time_kind_field; positive_field; year_field ]
112 let gMonthDay_type = Types.rec_of_list' [ time_kind_field; month_field; day_field ]
113 let gDay_type = Types.rec_of_list' [ time_kind_field; day_field ]
114 let gMonth_type = Types.rec_of_list' [ time_kind_field; month_field ]
115 let nonPositiveInteger_type = Builtin_defs.non_pos_int
116 let negativeInteger_type = Builtin_defs.neg_int
117 let nonNegativeInteger_type = Builtin_defs.non_neg_int
118 let positiveInteger_type = Builtin_defs.pos_int
119 let long_type = Builtin_defs.long_int
120 let int_type = Builtin_defs.int_int
121 let short_type = Builtin_defs.short_int
122 let byte_type = Builtin_defs.byte_int
123
124 let string_list_type = Sequence.star Builtin_defs.string
125
126 (** {2 Validation functions (string -> Value.t)} *)
127
128 let parse_sign s =
129 if Utf8.equal s (Utf8.mk "+") || Utf8.equal s (Utf8.mk "") then
130 Value.vtrue
131 else
132 Value.vfalse
133
134 let validate_integer s =
135 let s = Utf8.get_str s in
136 if (String.length s = 0) then simple_type_error "integer"
137 else
138 try Value.Integer (Intervals.V.mk s)
139 with Failure _ -> simple_type_error "integer"
140
141 let validate_decimal s =
142 let s = Utf8.get_str s in
143 try Value.float (float_of_string s)
144 with Failure _ -> simple_type_error "decimal"
145
146 let strip_decimal_RE = Pcre.regexp "\\..*$"
147
148 let parse_date =
149 let rex = Pcre.regexp (add_limits date_RE_raw) in
150 fun s ->
151 let abort () = simple_type_error "date" in
152 let subs = try pcre_extract ~rex s with Not_found -> abort () in
153 [ qualify "year", validate_integer subs.(1);
154 qualify "month", validate_integer subs.(2);
155 qualify "day", validate_integer subs.(3) ]
156
157 let parse_time =
158 let rex = Pcre.regexp (add_limits time_RE_raw) in
159 fun s ->
160 let abort () = simple_type_error "time" in
161 let subs = try pcre_extract ~rex s with Not_found -> abort () in
162 [ qualify "hour", validate_integer subs.(1);
163 qualify "minute", validate_integer subs.(2);
164 qualify "second", validate_integer subs.(3) ]
165
166 let parse_timezone =
167 let rex = Pcre.regexp (add_limits timezone_RE_raw) in
168 fun s ->
169 let abort () = simple_type_error "timezone" in
170 let subs = try pcre_extract ~rex s with Not_found -> abort () in
171 if Utf8.equal subs.(1) (Utf8.mk "Z") then
172 [qualify "positive", Value.vtrue;
173 qualify "hour", validate_integer (Utf8.mk "0");
174 qualify "minute", validate_integer (Utf8.mk "0")]
175 else
176 [qualify "positive", parse_sign subs.(3);
177 qualify "hour", validate_integer subs.(4);
178 qualify "minute", validate_integer subs.(5)]
179 (* parse a timezone from a string, if it's empty return the empty list,
180 otherwise return a list containing a pair <"timezone", timezone value> *)
181 let parse_timezone' s =
182 if is_empty s then
183 []
184 else
185 [ qualify "timezone", Value.vrecord (parse_timezone s) ]
186
187 let validate_string s = Value.string_utf8 s
188 let validate_normalizedString s =
189 validate_string (normalize_white_space `Replace s)
190 let validate_token s =
191 validate_string (normalize_white_space `Collapse s)
192 let validate_token_list s =
193 Value.sequence (List.map validate_token (split_xml_S s))
194
195 let validate_interval interval type_name s =
196 let integer =
197 let s = Utf8.get_str s in
198 if (String.length s = 0) then simple_type_error "integer"
199 else
200 try Intervals.V.mk s
201 with Failure _ -> simple_type_error "integer"
202 in
203 if Intervals.contains integer interval then
204 Value.Integer integer
205 else
206 simple_type_error type_name
207 let validate_nonPositiveInteger =
208 validate_interval (Intervals.left Intervals.V.zero) "nonPositiveInteger"
209 let validate_negativeInteger =
210 validate_interval (Intervals.left Intervals.V.minus_one) "negativeInteger"
211 let validate_nonNegativeInteger =
212 validate_interval (Intervals.right Intervals.V.zero) "nonNegativeInteger"
213 let validate_positiveInteger =
214 validate_interval (Intervals.right Intervals.V.one) "positiveInteger"
215 let validate_long = validate_interval (Intervals.bounded long_l long_r) "long"
216 let validate_int = validate_interval (Intervals.bounded int_l int_r) "int"
217 let validate_short =
218 validate_interval (Intervals.bounded short_l short_r) "short"
219 let validate_byte = validate_interval (Intervals.bounded byte_l byte_r) "byte"
220
221 let validate_bool s =
222 if Utf8.equal s (Utf8.mk "true") || Utf8.equal s (Utf8.mk "1") then
223 Value.vtrue
224 else if Utf8.equal s (Utf8.mk "false") || Utf8.equal s (Utf8.mk "0") then
225 Value.vfalse
226 else
227 simple_type_error "boolean"
228
229 let validate_duration =
230 let rex = pcre_regexp
231 "^([+-])?P((\\d+)Y)?((\\d+)M)?((\\d+)D)?(T((\\d+)H)?((\\d+)M)?((\\d+)S)?)?$"
232 in
233 fun s ->
234 let abort () = simple_type_error "duration" in
235 let subs = try pcre_extract ~rex s with Not_found -> abort () in
236 try
237 let fields =
238 time_kind "duration" ::
239 [qualify "positive", parse_sign subs.(1) ] @
240 (if is_empty subs.(3) then []
241 else [qualify "year", validate_integer subs.(3)]) @
242 (if is_empty subs.(5) then []
243 else [qualify "month", validate_integer subs.(5)]) @
244 (if is_empty subs.(7) then []
245 else [qualify "day", validate_integer subs.(7)]) @
246 (if is_empty subs.(10) then []
247 else [qualify "hour", validate_integer subs.(10)]) @
248 (if is_empty subs.(12) then []
249 else [qualify "minute", validate_integer subs.(12)]) @
250 (if is_empty subs.(14) then []
251 else [qualify "second", validate_integer subs.(14)])
252 in
253 Value.vrecord fields
254 with Error _ -> abort ()
255
256 let validate_dateTime =
257 let rex = Pcre.regexp (sprintf "^([+-])?(%s)T(%s)(%s)?$"
258 (strip_parens date_RE_raw) (strip_parens time_RE_raw)
259 (strip_parens timezone_RE_raw))
260 in
261 fun s ->
262 let abort () = simple_type_error "dateTime" in
263 let subs = try pcre_extract ~rex s with Not_found -> abort () in
264 try
265 let fields =
266 time_kind "dateTime" ::
267 [ qualify "positive", parse_sign subs.(1) ] @
268 parse_date subs.(2) @
269 parse_time subs.(3) @
270 parse_timezone' subs.(4)
271 in
272 Value.vrecord fields
273 with Error _ -> abort ()
274
275 let validate_gYearMonth =
276 let rex = Pcre.regexp (add_limits gYearMonth_RE_raw) in
277 fun s ->
278 let abort () = simple_type_error "gYearMonth" in
279 let subs = try pcre_extract ~rex s with Not_found -> abort () in
280 try
281 let fields = [
282 time_kind "gYearMonth";
283 qualify "positive", parse_sign subs.(1);
284 qualify "year", validate_integer subs.(2);
285 qualify "month", validate_integer subs.(3)
286 ] @ parse_timezone' subs.(4)
287 in
288 Value.vrecord fields
289 with Error _ -> abort ()
290
291 let validate_gYear =
292 let rex = Pcre.regexp (add_limits gYear_RE_raw) in
293 fun s ->
294 let abort () = simple_type_error "gYear" in
295 let subs = try pcre_extract ~rex s with Not_found -> abort () in
296 try
297 let fields = [
298 time_kind "gYear";
299 qualify "positive", parse_sign subs.(1);
300 qualify "year", validate_integer subs.(2);
301 ] @ parse_timezone' subs.(3)
302 in
303 Value.vrecord fields
304 with Error _ -> abort ()
305
306 let validate_gMonthDay =
307 let rex = Pcre.regexp (add_limits gMonthDay_RE_raw) in
308 fun s ->
309 let abort () = simple_type_error "gMonthDay" in
310 let subs = try pcre_extract ~rex s with Not_found -> abort () in
311 try
312 let fields = [
313 time_kind "gMonthDay";
314 qualify "month", validate_integer subs.(1);
315 qualify "day", validate_integer subs.(2);
316 ] @ parse_timezone' subs.(3)
317 in
318 Value.vrecord fields
319 with Error _ -> abort ()
320
321 let validate_gDay =
322 let rex = Pcre.regexp (add_limits gDay_RE_raw) in
323 fun s ->
324 let abort () = simple_type_error "gDay" in
325 let subs = try pcre_extract ~rex s with Not_found -> abort () in
326 try
327 let fields =
328 time_kind "gDay" ::
329 (qualify "day", validate_integer subs.(1)) ::
330 (parse_timezone' subs.(2))
331 in
332 Value.vrecord fields
333 with Error _ -> abort ()
334
335 let validate_gMonth =
336 let rex = Pcre.regexp (add_limits gMonth_RE_raw) in
337 fun s ->
338 let abort () = simple_type_error "gMonth" in
339 let subs = try pcre_extract ~rex s with Not_found -> abort () in
340 try
341 let fields =
342 time_kind "gMonth" ::
343 (qualify "month", validate_integer subs.(1)) ::
344 (parse_timezone' subs.(2))
345 in
346 Value.vrecord fields
347 with Error _ -> abort ()
348
349 let validate_time =
350 let rex = Pcre.regexp (sprintf "^(%s)(%s)?$" (strip_parens time_RE_raw)
351 (strip_parens timezone_RE_raw))
352 in
353 fun s ->
354 let abort () = simple_type_error "time" in
355 let subs = try pcre_extract ~rex s with Not_found -> abort () in
356 try
357 let fields =
358 time_kind "time" ::
359 parse_time subs.(1) @
360 (if is_empty subs.(2) then []
361 else [ qualify "timezone", Value.vrecord (parse_timezone subs.(2)) ])
362 in
363 Value.vrecord fields
364 with Error _ -> abort ()
365
366 let validate_date =
367 let rex = Pcre.regexp (sprintf "^(-)?(%s)(%s)?$" (strip_parens date_RE_raw)
368 (strip_parens timezone_RE_raw))
369 in
370 fun s ->
371 let abort () = simple_type_error "date" in
372 let subs = try pcre_extract ~rex s with Not_found -> abort () in
373 try
374 let fields =
375 time_kind "date" ::
376 [ qualify "positive", parse_sign subs.(1) ] @
377 parse_date subs.(2) @
378 (if is_empty subs.(3) then []
379 else [ qualify "timezone", Value.vrecord (parse_timezone subs.(3)) ])
380 in
381 Value.vrecord fields
382 with Error _ -> abort ()
383
384 let validate_hexBinary s =
385 let s = Utf8.get_str s in
386 let len = String.length s in
387 if len mod 2 <> 0 then
388 simple_type_error "hexBinary";
389 let res = String.create (len / 2) in
390 let rec aux idx =
391 if idx < len then begin
392 String.unsafe_set res (idx / 2)
393 (char_of_hex (String.unsafe_get s idx) (String.unsafe_get s (idx + 1)));
394 aux (idx + 2)
395 end
396 in
397 aux 0;
398 validate_string (Utf8.mk res)
399
400 let validate_base64Binary s =
401 let s = Utf8.get_str s in
402 validate_string (Utf8.mk (Netencoding.Base64.decode s))
403
404 let validate_anyURI s =
405 let s = Utf8.get_str s in
406 try
407 validate_string (Utf8.mk (Neturl.string_of_url (Neturl.url_of_string
408 Neturl.ip_url_syntax s)))
409 with Neturl.Malformed_URL -> simple_type_error "anyURI"
410
411 (** {2 API backend} *)
412
413 type t = simple_type_definition * Types.t * (Utf8.t -> Value.t)
414
415 module QTable = Hashtbl.Make(Ns.QName)
416
417 let builtins : t QTable.t = QTable.create 50
418 let reg = QTable.add builtins
419
420
421 let restrict name (base,_,_) facets cd v =
422 let name = add_xsd_prefix name in
423 let t = simple_restrict (Some name) base facets in
424 let b = (t,cd,v) in
425 reg name b;
426 b
427
428 let list name (item,_,_) cd v =
429 let name = add_xsd_prefix name in
430 let t = simple_list (Some name) item in
431 let b = (t,cd,v) in
432 reg name b;
433 b
434
435
436 let primitive name cd v =
437 let name = add_xsd_prefix name in
438 let rec t =
439 { st_name = Some name;
440 st_variety = Atomic t;
441 st_facets = no_facets;
442 st_base = None } in
443 let b = (t,cd,v) in
444 reg name b;
445 b
446
447 let alias name b =
448 let name = add_xsd_prefix name in
449 reg name b
450
451 let any_simple_type =
452 primitive "anySimpleType" Builtin_defs.string validate_string
453 let string =
454 primitive "string" Builtin_defs.string validate_string
455 let integer =
456 primitive "integer" Builtin_defs.int validate_integer
457 let _ =
458 primitive "boolean" Builtin_defs.bool validate_bool
459 let _ =
460 primitive "hexBinary" Builtin_defs.string validate_hexBinary
461 let _ =
462 primitive "base64Binary" Builtin_defs.string validate_base64Binary
463 let _ =
464 primitive "anyURI" Builtin_defs.string validate_anyURI
465 let _ =
466 primitive "duration" duration_type validate_duration
467 let _ =
468 primitive "dateTime" dateTime_type validate_dateTime
469 let _ =
470 primitive "time" time_type validate_time
471 let _ =
472 primitive "date" date_type validate_date
473 let _ =
474 primitive "gYearMonth" gYearMonth_type validate_gYearMonth
475 let _ =
476 primitive "gYear" gYear_type validate_gYear
477 let _ =
478 primitive "gMonthDay" gMonthDay_type validate_gMonthDay
479 let _ =
480 primitive "gDay" gDay_type validate_gDay
481 let _ =
482 primitive "gMonth" gMonth_type validate_gMonth
483 let decimal =
484 primitive "decimal" Builtin_defs.float validate_decimal
485
486 let _ =
487 alias "float" decimal;
488 alias "double" decimal
489
490
491 let _ =
492 List.iter (fun n -> alias n string) unsupported
493
494 (* derived builtins *)
495
496 let nonpos =
497 restrict "nonPositiveInteger" integer
498 { no_facets with maxInclusive = Some (Value.Integer zero, false) }
499 nonPositiveInteger_type validate_nonPositiveInteger
500 let _ =
501 restrict "negativeInteger" nonpos
502 { no_facets with maxInclusive = Some (Value.Integer minus_one, false) }
503 negativeInteger_type validate_negativeInteger
504 let nonneg =
505 restrict "nonNegativeInteger" integer
506 { no_facets with minInclusive = Some (Value.Integer zero, false) }
507 nonNegativeInteger_type validate_nonNegativeInteger
508 let _ =
509 restrict "positiveInteger" nonneg
510 { no_facets with minInclusive = Some (Value.Integer one, false) }
511 positiveInteger_type validate_positiveInteger
512 let long =
513 restrict "long" integer
514 { no_facets with
515 minInclusive = Some (Value.Integer long_l, false);
516 maxInclusive = Some (Value.Integer long_r, false)}
517 long_type validate_long
518 let int =
519 restrict "int" long
520 { no_facets with
521 minInclusive = Some (Value.Integer int_l, false);
522 maxInclusive = Some (Value.Integer int_r, false)}
523 int_type validate_int
524 let short =
525 restrict "short" int
526 { no_facets with
527 minInclusive = Some (Value.Integer short_l, false);
528 maxInclusive = Some (Value.Integer short_r, false)}
529 short_type validate_short
530 let _ =
531 restrict "byte" short
532 { no_facets with
533 minInclusive = Some (Value.Integer byte_l, false);
534 maxInclusive = Some (Value.Integer byte_r, false)}
535 byte_type validate_short
536 let normalized_string =
537 restrict "normalizedString" string
538 { no_facets with whiteSpace = `Replace, false }
539 Builtin_defs.string validate_normalizedString
540 let token =
541 restrict "token" normalized_string
542 { no_facets with whiteSpace = `Collapse, false }
543 Builtin_defs.string validate_token
544
545 let _ =
546 alias "language" token;
547 alias "Name" token;
548 alias "NMTOKEN" token;
549 alias "NCName" token;
550 alias "ID" token;
551 alias "IDREF" token;
552 alias "ENTITY" token
553
554 let nmtokens =
555 list "NMTOKENS" token string_list_type validate_token_list
556
557 let _ =
558 alias "IDREFS" nmtokens;
559 alias "ENTITIES" nmtokens
560
561
562
563 (** {2 Printing} *)
564
565 open Big_int
566
567 type kind =
568 Duration | DateTime | Time | Date | GYearMonth | GYear | GMonthDay | GDay |
569 GMonth
570 type timezone = bool * Intervals.V.t * Intervals.V.t
571 (* positive, hour, minute *)
572 type time_value = {
573 kind: kind option; positive: bool option; year: Intervals.V.t option;
574 month: Intervals.V.t option; day: Intervals.V.t option;
575 hour: Intervals.V.t option; minute: Intervals.V.t option;
576 second: Intervals.V.t option; timezone: timezone option
577 }
578 let null_value = {
579 kind = None; positive = None; year = None; month = None; day = None;
580 hour = None; minute = None; second = None; timezone = None
581 }
582
583 let string_of_time_type fields =
584 let fail () = raise (Error "") in
585 let parse_int = function Value.Integer i -> i | _ -> fail () in
586 let parse_timezone v =
587 let fields =
588 try
589 Value.get_fields v
590 with Invalid_argument _ -> fail ()
591 in
592 let (positive, hour, minute) = (ref true, ref zero, ref zero) in
593 List.iter
594 (fun ((ns, name), value) ->
595 if ns <> Ns.empty then fail ();
596 (match Utf8.get_str name with
597 | "positive" -> positive := (Value.equal value Value.vtrue)
598 | "hour" -> hour := parse_int value
599 | "minute" -> minute := parse_int value
600 | _ -> fail ()))
601 fields;
602 !positive, !hour, !minute
603 in
604 let parse_time_kind = function
605 | Value.Atom a ->
606 (match Utf8.get_str (snd (Atoms.V.value a)) with
607 | "duration" -> Duration | "dateTime" -> DateTime | "time" -> Time
608 | "date" -> Date | "gYearMonth" -> GYearMonth | "gYear" -> GYear
609 | "gMonthDay" -> GMonthDay | "gDay" -> GDay | "gMonth" -> GMonth
610 | _ -> fail ())
611 | _ -> fail ()
612 in
613 let parse_positive = function
614 | v when Value.equal v Value.vfalse -> false
615 | _ -> true
616 in
617 let string_of_positive v =
618 match v.positive with Some false -> "-" | _ -> ""
619 in
620 let string_of_year v =
621 match v.year with None -> fail () | Some i -> Intervals.V.to_string i
622 in
623 let string_of_month v =
624 match v.month with None -> fail () | Some i -> Intervals.V.to_string i
625 in
626 let string_of_day v =
627 match v.day with None -> fail () | Some i -> Intervals.V.to_string i
628 in
629 let string_of_hour v =
630 match v.hour with None -> fail () | Some i -> Intervals.V.to_string i
631 in
632 let string_of_minute v =
633 match v.minute with None -> fail () | Some i -> Intervals.V.to_string i
634 in
635 let string_of_second v =
636 match v.second with None -> fail () | Some i -> Intervals.V.to_string i
637 in
638 let string_of_date v =
639 sprintf "%s-%s-%s" (string_of_year v) (string_of_month v) (string_of_day v)
640 in
641 let string_of_timezone v =
642 match v.timezone with
643 | Some (positive, hour, minute) ->
644 sprintf "Z%s%s:%s" (if not positive then "-" else "")
645 (Intervals.V.to_string hour) (Intervals.V.to_string minute)
646 | None -> ""
647 in
648 let string_of_time v =
649 sprintf "%s:%s:%s" (string_of_hour v) (string_of_minute v)
650 (string_of_second v)
651 in
652 let v =
653 List.fold_left
654 (fun acc ((ns, name), value) ->
655 if ns <> Ns.empty then fail ();
656 (match Utf8.get_str name with
657 | "year" -> { acc with year = Some (parse_int value) }
658 | "month" -> { acc with month = Some (parse_int value) }
659 | "day" -> { acc with day = Some (parse_int value) }
660 | "hour" -> { acc with hour = Some (parse_int value) }
661 | "minute" -> { acc with minute = Some (parse_int value) }
662 | "second" -> { acc with second = Some (parse_int value) }
663 | "timezone" -> { acc with timezone = Some (parse_timezone value) }
664 | "time_kind" -> { acc with kind = Some (parse_time_kind value) }
665 | "positive" -> { acc with positive = Some (parse_positive value) }
666 | s -> fail ()))
667 null_value fields
668 in
669 let s =
670 match v.kind with
671 | None -> fail ()
672 | Some Duration ->
673 sprintf "%sP%s%s%s%s"
674 (string_of_positive v)
675 (match v.year with Some v -> Intervals.V.to_string v ^ "Y" | _ -> "")
676 (match v.month with Some v -> Intervals.V.to_string v ^ "M" | _ -> "")
677 (match v.day with Some v -> Intervals.V.to_string v ^ "D" | _ -> "")
678 (if v.hour = None && v.minute = None && v.second = None then
679 ""
680 else
681 "T" ^
682 (match v.hour with Some v -> Intervals.V.to_string v ^ "H" | _ ->
683 "") ^
684 (match v.minute with Some v -> Intervals.V.to_string v ^ "M" | _ ->
685 "") ^
686 (match v.second with Some v -> Intervals.V.to_string v ^ "S" | _ ->
687 ""))
688 | Some DateTime ->
689 sprintf "%s%sT%s%s" (string_of_positive v) (string_of_date v)
690 (string_of_time v) (string_of_timezone v)
691 | Some Time ->
692 sprintf "%s%s%s" (string_of_positive v) (string_of_time v)
693 (string_of_timezone v)
694 | Some Date ->
695 sprintf "%s%s%s" (string_of_positive v) (string_of_date v)
696 (string_of_timezone v)
697 | Some GYearMonth ->
698 sprintf "%s%s-%s%s" (string_of_positive v) (string_of_year v)
699 (string_of_month v) (string_of_timezone v)
700 | Some GYear ->
701 sprintf "%s%s%s" (string_of_positive v) (string_of_year v)
702 (string_of_timezone v)
703 | Some GMonthDay ->
704 sprintf "--%s%s%s" (string_of_month v) (string_of_day v)
705 (string_of_timezone v)
706 | Some GDay ->
707 sprintf "---%s%s" (string_of_day v) (string_of_timezone v)
708 | Some GMonth ->
709 sprintf "--%s--%s" (string_of_month v) (string_of_timezone v)
710 in
711 Utf8.mk s
712
713 (** {2 API} *)
714
715 let is = QTable.mem builtins
716 let iter f = QTable.iter f builtins
717
718 let get name = QTable.find builtins name
719 let simple_type (st,_,_) = st
720 let cd_type (_,t,_) = t
721 let validate (_,_,v) = v
722
723 let of_st = function
724 | { st_name = Some n } -> get n
725 | _ -> assert false
726
727

CVS Admin">CVS Admin
ViewVC Help
Powered by ViewVC 1.1.5