/[svn]/web/ocaml.xml
ViewVC logotype

Contents of /web/ocaml.xml

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1815 - (show annotations)
Tue Jul 10 19:24:58 2007 UTC (5 years, 10 months ago) by abate
File MIME type: text/xml
File size: 42933 byte(s)
[r2005-10-31 12:34:19 by afrisch] Empty log message

Original author: afrisch
Date: 2005-10-31 12:34:19+00:00
1 <?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
2 <page name="ocaml">
3
4 <title>OCamlDuce</title>
5
6 <left>
7 <local-links href="index,documentation"/>
8 <p>On this page:</p>
9 <boxes-toc/>
10 </left>
11
12 <box>
13
14 <p>
15 OCamlDuce is a merger between <a
16 href="http://caml.inria.fr/">OCaml</a> and
17 <local href="index">CDuce</local>. It comes as a modified
18 version of OCaml which integrates CDuce features: expressions, types,
19 patterns.
20 </p>
21
22 <p>
23 OCamlDuce is distributed under the same licenses as Objective Caml:
24 the Q Public License version 1.0 for the Compiler, and the LGPL
25 version 2 for the Library. The extension has been written by Alain
26 Frisch. Parts of the CDuce implementation, by the same author, have
27 been reused.
28 </p>
29
30 <p>
31 The theory behind OCamlDuce's type system is described in a <a
32 href="http://cristal.inria.fr/~frisch/ocamlcduce/">technical
33 report</a>.
34 </p>
35
36 </box>
37
38 <box title="Download and installation" link="install">
39
40 <p>
41 Currently, OCamlDuce
42 is based on OCaml 3.08.4 and on a CVS snapshots
43 of CDuce (between 0.3.92 and the head).
44 </p>
45
46 <ul>
47 <li><a
48 href="http://pauillac.inria.fr/~frisch/ocamlcduce/download/ocamlduce-3.08.4pl5.tar.gz">Compiler,
49 version 3.08.4, patch level 5</a></li>
50 </ul>
51
52 <p>
53 There are two different installation modes:
54 </p>
55
56 <ul>
57 <li><b>Stand-alone mode</b>. OCamlDuce is used as a drop-in
58 replacement for OCaml. The build procedure is unchanged:
59 <tt>./configure &amp;&amp; make world &amp;&amp; make install</tt>.
60 The tools are named <tt>ocaml, ocamlc, ocamlopt</tt>, ...
61 The standard library is extended with the <tt>num</tt> library
62 and the <tt>Ocamlduce</tt> module.
63 </li>
64
65 <li><b>Package mode</b>. OCamlDuce is installed on top of an existing
66 OCaml installation (whose version number must match), without touching
67 it. The build
68 procedure is: <tt>./configure &amp;&amp; make all &amp;&amp; make opt
69 &amp;&amp; make install</tt>. The <tt>configure</tt> script should be called with
70 the same arguments as the ones used when you built OCaml. For instance,
71 the <tt>LIBDIR</tt> argument is used to find OCaml standard library.
72 The tools names are changed to <tt>ocamlduce, ocamlducec,
73 ocamlduceopt</tt>, ... They use the existing standard library.
74 In addition, a library <tt>ocamlduce.cma</tt> is built.
75 It depends on the <tt>nums.cma</tt> library. The <tt>install</tt>
76 target implements a <tt>Findlib</tt>-based installation. It registers
77 a package named <tt>ocamlduce</tt> and it puts the tools
78 in the package sub-directory (the <tt>BINDIR</tt> and <tt>LIBDIR</tt>
79 arguments to <tt>configure</tt> are not used). The toplevel
80 can be called by <tt>ocamlfind ocamlduce/ocamlduce -I `ocamlfind query ocamlduce`</tt>.
81 </li>
82 </ul>
83
84 </box>
85
86 <box title="Ports and packages" link="ports">
87
88 <section title="GODI">
89 <p>
90 GODI users can choose any of the two installation modes.
91 In order to upgrade an existing installation so as to use
92 OCamlDuce in place of OCaml, they must add this
93 line to their <tt>etc/godi.conf</tt> file:
94 </p>
95 <sample>
96 GODI_BUILD_SITES += http://pauillac.inria.fr/~frisch/ocamlcduce/godi
97 </sample>
98 <p>
99 and force a recompilation of the <tt>godi-ocaml-src</tt>
100 and <tt>godi-ocaml</tt> packages. The alternative is to install
101 OCamlDuce
102 as a GODI package over an existing installation. You don't need
103 to touch the <tt>etc/godi.conf</tt> file. The package
104 name is <tt>godi-ocamlduce</tt>. In order to use the new compilers
105 and tools, you can make the environment variable
106 <tt>OCAMLFIND_CONF</tt> point to the
107 <tt>$GODI/etc/findlib-ocamlduce.conf</tt> file and then
108 uses e.g. <tt>ocamlfind ocamlc -package ocamlduce</tt>.
109 </p>
110 </section>
111
112 <section title="DarwinPorts and OpenBSD">
113
114 <p>
115 Anil Madhavapeddy contributed two ports of OCamlDuce for DarwinPorts
116 (in dports/lang/ocamlduce) and for OpenBSD (in ports/lang/ocamlduce).
117 </p>
118
119 </section>
120
121 </box>
122
123 <box title="Overview" link="overview">
124
125 <p>
126 The goal of the OCamlDuce project is to extend the OCaml language with features
127 to make it easier to write safe and efficient complex applications
128 that need to deal with XML documents. In particular, it relies
129 on a notion of types and patterns to guarantee statically
130 that all the possible input documents are correctly processed, and
131 that only valid output documents are produced.
132 </p>
133
134 <p>
135 In a nutshell, OCamlDuce extends OCaml with a new kind of values
136 (<em>x-values</em>) to represent XML documents, fragments, tags, Unicode
137 strings. In order to describe these values, it also extends the type algebra
138 with so-called <em>x-types</em>. The philosophy behind these types is that they
139 represent <em>set of x-values</em>. They can be very precise: indeed,
140 each value can be seen as a singleton type (a set with a single
141 value), and it is possible to form Boolean combinations of x-types
142 (intersection, union, difference).
143 </p>
144
145 <p>
146 OCamlDuce's type system can be understood as a refinement of OCaml.
147 For each sub-expression which is inferred to be of the x-kind (using
148 OCaml unification based type-system), OCamlDuce will try to infer to
149 best possible sound x-type. Here, best means smallest for the natural
150 subtyping relation (set inclusion). The inference algorithm is
151 actually a data-flow analysis: the x-type will collect all the values
152 that can be produced by the expression, considering all the possible
153 data-flow in the program. It it sometimes necessary to provide
154 explicit type annotations to help the type checker infer this type, in
155 particular when you define recursive functions or when you use
156 iterators.
157 </p>
158
159 <p>
160 Subtyping is implicit for x-types: if an expression is inferred to be
161 of x-type <code>t</code>, which is a subtype of <code>s</code>, then
162 it is possible to use this expression in any context which expects a
163 value of type <code>s</code>.
164 </p>
165
166 </box>
167
168 <box title="Getting started" link="start">
169
170 <p>
171 Most of the new language features are enclosed within double curly braces
172 <code>{{ON}}{{...}}</code>. For instance, the following code sample
173 defines a value <code>x</code> as an XML element (with tag
174 <code>a</code>, an attribute <code>href</code>, and a simple
175 string as content):
176 </p>
177
178 <sample><![CDATA[{{ON}}
179 # let x = {{ <a href="http://www.cduce.org">['CDuce'] }};;
180 val x : {{<a href=[ 'http://www.cduce.org' ]>[ 'CDuce' ]}} =
181 {{<a href="http://www.cduce.org">[ 'CDuce' ]}}
182 ]]></sample>
183
184 <p>
185 What appears between the curly braces is called an x-expression.
186 Similarly, there are x-types (as seen above), and also x-patterns.
187 The delimiters <code>{{ON}}{{...}}</code> are only used
188 for syntactical reasons, to avoid clashed between OCaml and CDuce
189 syntaxes and lexical conventions. As a matter of fact,
190 an OCaml expression need not be a syntactical x-expression
191 (delimited by double curly braces) to evaluate to an x-value.
192 For instance, once <code>x</code> has been declared as above,
193 the expression <code>x</code> evaluates to an x-value.
194 </p>
195
196
197 <p>
198 It is possible to use an arbitrary
199 OCaml expression as part of an x-expression: it must simply be
200 protected by a new pair of double curly braces. For instance, there is
201 no <code>if-then-else</code> construction for x-expressions, but you
202 can write:
203 </p>
204
205 <sample><![CDATA[{{ON}}
206 # {{ <a href={{if true then {{"a"}} else {{"z"}}}}>[] }};;
207 - : {{<a href=[ 'a' | 'z' ]>[ ]}} = {{<a href="a">[ ]}}
208 ]]></sample>
209
210 <p>
211 Only the highlighted parts are parsed as x-expressions. The
212 <code>if-then-else</code> sub-expression is parsed as an OCaml
213 expression, but its type is an x-type (namely <code>{{ON}}{{[ 'a' |
214 'z' ]}}</code>).
215 </p>
216
217 </box>
218
219 <box title="X-values" link="values">
220
221 <p>
222 X-values are intended to represent XML documents and fragments
223 thereof: elements, tags, text, sequences. In this section, we
224 present the x-value algebra, the syntax of the corresponding
225 x-expression constructors and the associated x-types.
226 </p>
227
228 <p>
229 There are three kinds of atomic kind of x-values:
230 </p>
231 <ul>
232 <li>Unicode characters;</li>
233 <li>qualified names;</li>
234 <li>arbitrarily large integers.</li>
235 </ul>
236
237 <section title="Characters">
238
239 <p>
240 X-characters are different from OCaml characters. They can represent
241 the range of Unicode codepoints defined in the XML specification.
242 Character literals are delimited by single quotes. The escape
243 sequences \n, \r, \t, \b, \', \&quot;, \\ are recognized as usual. The
244 numerical escape sequence are written <code>\n;</code> where n is an integer
245 literal (note the extra semi-colon). The source code is interpreted as
246 being encoded in iso-8859-1. As a consequence, Unicode characters which are not
247 part of the Latin1 character set must be introduced with this
248 numerical escape mechanism. The x-types for x-characters are:
249 </p>
250 <ul>
251 <li>singletons;</li>
252 <li>intervals, written <code>c -- d</code>, where <code>c</code> and
253 <code>d</code> are literals (example: <code>{{ON}}type t = {{ 'a'--'z'
254 }}</code>);</li>
255 <li>the type of all x-characters, written <code>Char</code>;</li>
256 <li>the type of all Latin1 characters, written <code>Latin1Char</code>
257 (defined as <code>\0; -- \255;</code>).</li>
258 </ul>
259
260 </section>
261
262 <section title="Integers">
263
264 <p>
265 X-integers are arbitrarily large. Literals must be written in decimal.
266 Negative literals must be in parenthesis. E.g.: <code>(-3)</code>.
267 The x-types for x-integers are:
268 </p>
269 <ul>
270 <li>singletons;</li>
271 <li>intervals, written <code>i -- j</code>, where <code>i</code> and
272 <code>j</code> are literals (example: <code>{{ON}}type t = {{ 10--20
273 }}</code>); it is possible to replace <code>i</code> or <code>j</code>
274 with <code>**</code> to define open-ended intervals, e.g.
275 <code>{{ON}}type pos = {{ 1 -- ** }}</code>;
276 </li>
277 <li>the type of all x-integers, written <code>Int</code>;</li>
278 <li>the type of all the integers which can be represented by a
279 signed 32 (resp. 64) bit machine word, written <code>Int32</code> (resp.
280 <code>Int64</code>).</li>
281 </ul>
282
283 </section>
284
285 <section title="Qualified names">
286
287 <p>
288 Qualified names are intended to represent XML tag names. Conceptually,
289 they are made of a namespace URI and a local name. Since URIs tends
290 to be long, literals are of the form <code>`prefix:local</code>
291 where <code>local</code> is the local name and <code>prefix</code>
292 is an <em>namespace prefix</em> bound to some URI (in the scope of the
293 literal). The local name follows the definitions from
294 the XML Namespaces specification; a dot character must be protected
295 by a backslash and non-Latin1 characters are written as character
296 literals <code>\n;</code>. <a href="#ns">See below</a> for a
297 explanation on how to bind prefixes to URIs. To refer
298 to the default namespace (or the absence of namespace if not default
299 has been defined), the syntax is simply <code>`local</code>.
300 The x-types for qualified names are:
301 </p>
302 <ul>
303 <li>singletons;</li>
304 <li>the type of all qualified names, written <code>Atom</code>;</li>
305 <li>the type of all qualified names from a specified namespace,
306 written <code>`ns:*</code>.</li>
307 </ul>
308 </section>
309
310 <section title="Records">
311
312 <p>
313 X-records are mainly used to represent the set of attributes of an XML
314 element. An x-record is a binding from a finite set of <em>labels</em>
315 to x-values. Labels follows the same syntax as for qualified names
316 without the leading backquote. However, if the namespace prefix is not
317 given, the default namespace does not apply (the namespace URI is
318 empty). The syntax for record x-expressions is <code> { l1=e1
319 ... ln=en }</code> where the <code>li</code> are labels and the
320 <code>ei</code> are x-expressions. Fields can also be separated with a
321 semi-colon. It is legal to omit the expression for a field; the label is then
322 taken as the content of the field (a value with this name must be
323 defined in the current scope), e.g.: <code>{{ON}}let x = ... and y = ...
324 in {{ {x y z=3} }}</code> is equivalent to <code>{{ON}}let x = ... and
325 y = ... in {{ {x=x y=y z=3} }}</code>. The types for x-records specify
326 which labels are authorized/mandatory, and what the types of the
327 corresponding fields are. There are two kind of record x-types:
328 </p>
329
330 <ul>
331 <li>
332 Closed record types, which only allow a finite number of fields:
333 <code>{ l1=t1 ... ln=tn }</code>;
334 </li>
335 <li>
336 Open record types, which allow additional fields (with arbitrary
337 type):
338 <code>{ l1=t1 ... ln=tn .. }</code> (the final two colons are
339 in the syntax).
340 </li>
341 </ul>
342
343 <p>
344 In both cases, it is possible to make one of
345 the fields optional by changing = to =?.
346 </p>
347
348 <p>
349 The x-type of all x-record is thus <code>{ .. }</code>,
350 and the x-type of x-records with maybe a field <code>l</code>
351 of type <code>Int</code> and maybe arbitrary other fields is
352 <code>{ l=?Int .. }</code>.
353 </p>
354
355 </section>
356
357 <section title="Sequences">
358
359 <p>
360 X-sequences are finite and ordered collections of x-values.
361 The syntax for a sequence x-expression in
362 <code>[ e1 ... en ]</code> (note that elements are <em>not</em> separated
363 by semi-colons as in OCaml list). Each item <code>ei</code>
364 can either be:
365 </p>
366 <ul>
367 <li>an x-expression;</li>
368 <li><code>!e</code> where <code>e</code> is an x-expression which
369 evaluates to a sequence (whose content is inserted in the sequence
370 which is currently defined); e.g.
371 <code>let x = [ 2 3 ] in [ 1 !x 4 ]</code> is equivalent to
372 <code>[ 1 2 3 4 ]</code>;</li>
373 <li>a string literal delimited by simple quotes; e.g.
374 <code>[ 'abc' ]</code> is equivalent to <code>[ 'a' 'b' 'c' ]</code>.</li>
375 </ul>
376
377 <p>
378 X-types for sequences are of the form <code>[R]</code>
379 where <code>R</code> is a regular expression over x-types which
380 describe the possible contents of the sequences. The possible
381 forms of regular expressions are:
382 </p>
383
384 <ul>
385 <li><code>t</code> (one single element of x-type <code>t</code>)</li>
386 <li><code>R*</code> (zero or more repetitions)</li>
387 <li><code>R+</code> (one or more repetitions)</li>
388 <li><code>R?</code> (zero or one repetition)</li>
389 <li><code>R1 R2</code> (sequence)</li>
390 <li><code>R1|R2</code> (alternation)</li>
391 <li><code>(R)</code></li>
392 <li><code>/t</code> (guard: the tail of the sequence must comply with
393 <code>t</code>).</li>
394 <li><code>PCDATA</code> (equivalent to Char*).</li>
395 </ul>
396
397 <note>sequence are actually encoded with embedded pairs and a
398 terminator, and sequences types are encoded with product types and
399 recursive types. The encoding is available to the programmer
400 but not described in this manual.
401 </note>
402
403 </section>
404
405 <section title="Strings">
406
407 <p>
408 Strings are nothing but sequences of characters. There are two
409 predefined types <code>String</code> and <code>Latin1</code>
410 (defined as <code>[ Char* ]</code> and <code>[ Latin1Char* ]</code>).
411 </p>
412
413 <p>
414 A string literal <code>[ '...' ]</code> can also be written
415 <code>"..." </code> (without the square brackets). Note that simple
416 (resp. double) quotes need to be escaped only when the string is
417 delimited with double (resp. simple) quotes.
418 </p>
419
420 </section>
421
422 <section title="XML elements">
423
424 <p>
425 An XML element is a triple of x-values. The syntax for
426 the corresponding x-expression constructor is
427 <code><![CDATA[<(e1) (e2)>e3]]></code>. When <code>e1</code> is a
428 qualified name literal, it is possible to omit the leading
429 backquote and the surrounding parentheses. Similarly,
430 when <code>e2</code> is an x-record literal, it is possible
431 to omit the curly braces and the parentheses. For instance,
432 one can simply write <code><![CDATA[<a href="abc">['def']]]></code>
433 instead of <code><![CDATA[<(`a) ({href="abc"})>['def']]]></code>.
434 </p>
435
436 <p>
437 XML element x-type are written <code><![CDATA[<(t1) (t2)>t3]]></code>,
438 and the same simplifications applies. For instance, if
439 the namespace prefix <code>ns</code> has been defined,
440 the following is a legal x-type <code><![CDATA[<ns:* ..>[]]]></code>;
441 it describes XML elements whose tag is in the namespace bound to
442 <code>ns</code>, with an empty content, and with an arbitrary set of
443 attributes. An underscore in place of <code>(t1)</code> is
444 equivalent to <code>(Atom)</code> (any tag).
445 </p>
446
447 </section>
448
449 </box>
450
451 <box title="X-expressions" link="expr">
452
453 <p>
454 In the previous section, we have seen the syntax for x-values
455 constructors (constant literals, sequence, record, element constructors).
456 In this section, we describe the other kinds of x-expressions.
457 </p>
458
459 <section title="Binary infix operators">
460
461 <p>
462 The arithmetic operators on integers follow the usual precedence.
463 They are written <code>+,*,-,div,mod</code> (they are all infix).
464 </p>
465
466 <p>
467 Record concatenation: <code>e1 ++ e2</code>. The x-expressions
468 <code>e1</code> and <code>e2</code> must evaluate to x-records.
469 The result is obtained by concatening them. If a field with the same
470 label is present in both records, the right-most one is selected.
471 </p>
472
473 <p>
474 Sequence concatenation: <code>e1 @ e2</code>, equivalent
475 to <code>[!e1 !e2]</code>.
476 </p>
477
478 </section>
479
480 <section title="Projections, filtering">
481
482 <p>
483 If the x-expression <code>e</code> evaluates to a record or an XML
484 element, the construction <code>e.l</code> will extract the value of
485 field or attribute <code>l</code>. Similarly, the construction
486 <code>e.?l</code> will extract the value of field or attribute
487 <code>l</code> if present, and return the empty sequence
488 <code>[]</code> otherwise.
489 </p>
490
491 <p>
492 If the x-expression <code>e</code> evaluates to a record,
493 the construction <code>e -. l</code> will produce a new record
494 where the field <code>l</code> has been removed (if present).
495 </p>
496
497 <p>
498 If the x-expression <code>e</code> evaluates to an x-sequence,
499 the construction <code>e/</code> will result in a new x-sequence
500 obtained by taking in order all the children of the XML elements
501 from the sequence <code>e</code>. For instance, the x-expression
502 <code><![CDATA[[<a>[ 1 2 3 ] 4 5 <b>[ 6 7 8 ] ]/]]></code>
503 evaluates to the x-value <code>[ 1 2 3 6 7 8 ]</code>.
504 </p>
505
506 <p>
507 If the x-expression <code>e</code> evaluates to an x-sequence,
508 the construction <code>e.(t)</code> (where <code>t</code> is an
509 x-type) will result in a new x-sequence
510 obtained by filtering <code>e</code> to keep only the elements
511 of type <code>t</code>. For instance, the x-expression
512 <code><![CDATA[[<a>[ 1 2 3 ] 4 5 <b>[ 6 7 8 ] ].(Int)]]></code>
513 evaluates to the x-value <code>[ 4 5 ]</code>.
514 </p>
515 </section>
516
517 <section title="Dynamic type checking">
518
519 <p>
520 If <code>e</code> is an x-expression and <code>t</code> is an x-type,
521 the construction <code>(e :? t)</code> returns the same
522 result as <code>e</code> if it has type <code>t</code>, and otherwise
523 raises a <code>Failure</code> exception whose argument explains
524 why this is not the case.
525 </p>
526
527 <sample><![CDATA[{{ON}}
528 # let f (x : {{ Any }}) = {{ (x :? <a>[ Int* ] ) }} in
529 f {{ <a>[ 1 2 '3' ] }};;
530 Exception:
531 Failure
532 "Value <a>[ 1 2 '3' ] does not match type <a>[ Int* ]\nValue '3' does not match type Int\n".
533 ]]></sample>
534 </section>
535
536 <section title="Pattern matching">
537
538 <p>
539 OCamlDuce comes with a powerful pattern matching operation.
540 X-patterns are described <a href="#patterns">below</a>.
541 The syntax for the pattern matching operation is:
542 <code>match e with p1 -> e1 | ... | pn -> en</code>.
543 The type-system ensures exhaustivivity for the pattern matching
544 and infers precise types for the capture variables.
545 It is also possile to use x-pattern matching as a regular
546 OCaml expression; x-patterns must be surrounded by {{..}}, e.g.:
547 match e with {{p1}} -> e1 | ... | {{pn}} -> en
548 function {{p1}} -> e1 | ... | {{pn}} -> en
549 </p>
550
551 <p>
552 Pattern matching follows is first-match policy. The first pattern
553 that succeeds triggers the corresponding branch.
554 </p>
555
556 <note>
557 currently it is impossible to mix normal OCaml patterns and x-patterns
558 in a single pattern matching.
559 </note>
560
561 </section>
562
563 <section title="Local binding">
564
565 <p>
566 The x-expression <code>let p=e1 in e2</code> is equivalent to
567 <code>match e1 with p -> e2</code>. There is also an local binding
568 with an x-pattern in OCaml expressions: <code>let {{p}}=e1 in
569 e2</code>.
570 </p>
571
572 </section>
573
574
575 <section title="Iterators">
576
577 <p>
578 OCamlDuce comes with a sequence iterator
579 <code>map e with p1 -> e1 | ... | pn -> en</code> and
580 a tree iterator
581 <code>map* e with p1 -> e1 | ... | pn -> en</code>.
582 </p>
583
584 <p>
585 For both constructions, the argument must evaluate to a sequence.
586 The <code>map</code> iterator applies the patterns to each element
587 of this sequence in turns and produces a new sequence by concatenating
588 all the results (all the right-hand sides must thus produce a
589 sequence). The set of patterns must be exhaustive for all the possible
590 elements of the input sequence.
591 </p>
592
593 <p>
594 The tree iterator is similar except that the patterns need not be
595 exhaustive. If some element of the input sequence is not matched,
596 it is simply copied into the result unless it is an XML element. In
597 this case, the transformation is applied recursively to its content.
598 </p>
599
600 </section>
601
602 <section title="OCaml constructions">
603
604 <p>
605 As a convenience, some of the OCaml expression constructors
606 are allowed as x-expressions (without a need to go back to OCaml
607 with double curly braces): (unqualified) value identifiers and
608 function calls.
609 </p>
610
611 </section>
612
613 </box>
614
615 <box title="More on x-types" link="types">
616
617 <p>
618 We have seen how to write simple x-types. We can then combine
619 them with Boolean connectives:
620 </p>
621
622 <ul>
623 <li><code>t1 &amp; t2</code>: intersection;</li>
624 <li><code>t1 | t2</code>: union;</li>
625 <li><code>t1 - t2</code>: difference.</li>
626 </ul>
627
628 <p>
629 The empty x-type is written <code>Empty</code> (it contains no value),
630 and the universal x-type is written <code>Any</code> (it contains
631 all the x-values) or <code>_</code>.
632 </p>
633
634 <p>
635 When an x-type has been bound to some OCaml identifier
636 (<code>{{ON}}type t = {{...}}</code>), it is possible to use
637 this identifier in another x-type. Recursive definitions
638 are allowed:
639 </p>
640
641 <sample><![CDATA[{{ON}}
642 type t1 = {{ <a>[ t2* ] }}
643 and t2 = {{ <b>[ t1* ] }}
644 ]]></sample>
645
646 <p>
647 Note that x-values are always finite and acyclic. The type checker
648 detects type definition which would yield empty types:
649 </p>
650
651 <sample><![CDATA[{{ON}}
652 # type t = {{ <a>[ t+ ] }};;
653 This definition yields an empty type
654 ]]></sample>
655
656 <p>
657 If <code>t1</code> and <code>t2</code> are record x-types,
658 we can combine them with the infix <code>++</code> operator, which
659 mimics the corresponding operator on expressions (record
660 concatenation). Similarly, we can use the infix <code>@</code>
661 concatenation operator on sequence x-types.
662 </p>
663
664 </box>
665
666 <box title="X-patterns" link="patterns">
667
668 <p>
669 X-patterns follow the same syntax as X-types. In particular,
670 any X-type is a valid X-pattern. In addition to X-types constructors,
671 X-patterns can have:
672 </p>
673
674 <ul>
675 <li>capture variables (lowercase OCaml identifiers);</li>
676 <li>constant bindings <code>(x := c)</code> where x is a capture
677 variable and c is
678 a literal x-constant (this pattern always succeeds and returns the
679 binding x->c).</li>
680 </ul>
681
682 <p>
683 Here is a brief description of the semantics of patterns. Given
684 an input value, a pattern can either succeed or fail. If it succeeds,
685 it also produces a bindings from the capture variables in the pattern
686 to x-values.
687 </p>
688
689 <ul>
690
691 <li>A pattern which is just a type (no capture variable) succeeds if
692 and only if the value has the type.</li>
693
694 <li>A pattern <code>p1 | p2</code> succeeds if either <code>p1</code>
695 or <code>p2</code> succeed, and returns the corresponding binding; if
696 both patterns succeeds, <code>p1</code> wins. It is required that
697 <code>p1</code> and <code>p2</code> have the same sets of capture
698 variables. </li>
699
700 <li>A pattern <code>p1 &amp; p2</code> succeeds if both <code>p1</code>
701 and <code>p2</code> succeed, and returns the concatenation of the two
702 bindings. It is required that <code>p1</code> and <code>p2</code> have
703 <em>disjoint</em> sets of capture variables. </li>
704
705 </ul>
706
707 <p>
708 In record x-patterns, it is possible to omit the <code>=p</code> part
709 of a field. The content is then replaced with the label name
710 considered as a capture variable (or as a previously defined type).
711 E.g. <code>{ x y=p }</code> is
712 equivalent to <code>{ x=x y=p }</code>.</p>
713
714 <p>It is also possible to add an "else" clause:
715 <code>{ x = (a,_)|(a:=3) }</code>
716 will accept any record with atmost the field <code>x</code>. If the content
717 is a pair, the capture variable a will be bound to its component;
718 otherwise, it is set to <code>3</code>.</p>
719
720 <p>
721 In regular expressions, it is possible to extract whole subsequences
722 with the notation <code>x::R</code>, e.g.: <code>[ _* x::Int+ _* ]</code>
723 </p>
724
725 <p>
726 If the same sequence capture variable appears several times (or below a
727 repetition) in a regexp, it is bound to the concatenation of all
728 matched subsequences. E.g.: <code>[ (x::Int | _)* ]</code> will
729 collect in <code>x</code> all the elements of type <code>Int</code> from
730 a sequence. It is not legal to have repeated simple capture variables.
731 </p>
732
733 <p>
734 The regexp operators <code>+,*,?</code> are greedy by default (they match as long
735 as possible). They admit non-greedy variants <code>+?,*?,??</code>.
736 </p>
737 </box>
738
739 <box title="Namespace bindings" link="ns">
740
741 <p>
742 The binding of namespace prefixes to URIs
743 can be done either by toplevel phrases (structure items) or
744 by local declarations:
745 </p>
746
747 <sample>{{ON}}
748 # {{ namespace ns = "http://..." }};;
749 # let x = {{ `ns: x }};;
750 val x : {{`ns:x}} = {{`ns:x}}
751 # let x = {{ let namespace ns = "http://..." in `ns:x }};;
752 val x : {{`ns:x}} = {{`ns:x}}
753 </sample>
754
755 <p>The toplevel definitions can also appear in module interfaces
756 (signatures). A toplevel prefix binding is not exported by a module: its scope
757 is limited to the current structure or signature. It is possible
758 to specify a default namespace, and to reset it:
759 </p>
760
761 <sample>{{ON}}
762 # {{ namespace "http://..." }};;
763 # {{ `x }};;
764 - : {{`ns1:x}} = {{`ns1:x}}
765 # {{ namespace "" }};;
766 # {{ `x }};;
767 - : {{`x}} = {{`x}}
768 </sample>
769
770 <p>
771 Note that the value pretty-printer invented some prefix
772 for the namespace URI. The default prefix declaration also have a
773 local form <code> let namespace "..." in ... </code>.
774 </p>
775
776 </box>
777
778 <box title="More on type-checking" link="typecheck">
779
780 <section title="Type inference">
781
782 <p>
783 As we said above, the programmer is sometimes required to provide type
784 annotations. To know where to put these annotation, it is necessary to
785 get a basic understanding of how type-checking works.
786 </p>
787
788 <p>
789 The OCaml type-checker is run first to detect which sub-expressions
790 are of the x-kind. A second ML type-checking pass is then done to
791 introduce subsumption (implicit subtyping) steps where allowed. After
792 these two passes, the OCamlDuce type checker obtains a data-flow summary of
793 x-values in the whole compilation unit. This is a directed graph,
794 whose edges represent either simple data-flow or complex operation
795 on x-values. The nodes of the graph can be thought as x-type
796 variables. A data-flow edge corresponds to a subtyping constraints,
797 and an operation edge corresponds to a symbolic constraints which
798 mimics the corresponding operation on values.
799 </p>
800
801 <p>
802 Some of the nodes are given an explicit type by the programmer,
803 through type annotations (on expressions or function arguments)
804 or the other usual mechanism in ML (data type declarations,
805 signatures, ...).
806 </p>
807
808 <p>
809 Also, if there is a loop with only subtyping edges in the graph,
810 all the nodes on the loop are merged together.
811 </p>
812
813 <p>
814 After this operation, the graph is required to be acyclic (assuming
815 that the nodes with an explicit type are removed from the graph). It
816 is the responsibility of the programmer to provide enough type
817 annotation to achieve this property. Otherwise, a type error
818 is issued.
819 </p>
820
821 <sample><![CDATA[{{ON}}
822 # let rec f x = match x with 0 -> {{ [] }} | n -> {{ f {{n-1}} @ ['.'] }};;
823 Cycle detected: cannot type-check
824 # let rec f x : {{ String }} = match x with 0 -> {{ [] }} | n -> {{ f {{n-1}} @ ['.'] }};;
825 val f : int -> {{String}} = <fun>]]>
826 </sample>
827
828 <p>
829 In the example above, there is a cycle between the result type for
830 <code>f</code> and the type for the sub-expression <code>{{ON}}f
831 {{n-1}}</code>. It is here broken with a type annotation on the result; it could
832 have been broken by a type annotation on the expression <code>{{ON}}f
833 {{n-1}}</code>, or on the function <code>f</code> itself, or by a
834 module signature.
835 </p>
836
837 <p>
838 Let us study another simple example:
839 </p>
840
841 <sample>{{ON}}
842 # let f x = {{ x + 1 }} in f {{ 2 }}, f {{ 3 }};;
843 - : {{3--4}} * {{3--4}} = ({{3}}, {{4}})
844 </sample>
845
846 <p>
847 The type-checkers detects that the two x-values <code>2</code> and
848 <code>3</code> can flow to the argument of <code>f</code>. Its body
849 is thus type-checked with the assumption that <code>x</code> has type
850 <code>2--3</code>. The computed result type is then <code>3--4</code>.
851 </p>
852
853
854 <p>
855 The type-inference process described above is global by nature. The
856 acyclicity condition is only imposed after a whole compilation unit
857 has been type-checked by OCaml (and the information from the module
858 interface as been integrated). When a type variable is inferred to
859 be of the x-kind, it is never generalized. As a consequence, there
860 is no parametric polymorphism on x-types.
861 </p>
862
863 <p>
864 In the toplevel, type-checking is done after each phrase. Consider
865 the following session:
866 </p>
867
868 <sample><![CDATA[{{ON}}
869 # let f x = {{ x + 1 }};;
870 val f : {{Empty}} -> {{Empty}} = <fun>
871 # let a = f {{ 2 }};;
872 Subtyping failed 2 <= Empty
873 Sample:
874 2
875 ]]></sample>
876
877 <p>
878 The function <code>f</code> is inferred to have type
879 <code>{{ON}}{{Empty}} -> {{Empty}}</code> because when the first
880 phrase is type-checked, the data-flow graph says that no value
881 can flow to <code>x</code>, and thus the input type is empty
882 (and similarly for the result type). If the two phrases
883 were type-checked together (which would be the case it they had
884 been compiled by the compiler, not in the toplevel), the type checker
885 would have correctly inferred that the input type for <code>f</code>
886 must contain <code>2</code>.
887 </p>
888
889 </section>
890
891 <section title="Implicit subtyping">
892
893 <p>
894 Coercion from an x-type to a super type is automatic in OCamlDuce.
895 However, this automatic subsumption does not carry over to OCaml
896 type constructor, even if there are covariant. Consider:
897 </p>
898
899 <sample><![CDATA[{{ON}}
900 # let f (x : {{ Int }} * {{ Int }}) = 1;;
901 val f : {{Int}} * {{Int}} -> int = <fun>
902 # let g (x : {{ 0 }} * {{ 0 }}) = f x;;
903 This expression has type {{0}} * {{0}} but is here used with type
904 {{Int}} * {{Int}}
905 # let g (x : {{ 0 }} * {{ 0 }}) = let a,b = x in f (a,b);;
906 val g : {{0}} * {{0}} -> int = <fun>
907 # let g (x : {{ 0 }} * {{ 0 }}) = f (x :> {{ Int }} * {{ Int }});;
908 val g : {{0}} * {{0}} -> int = <fun>
909 ]]></sample>
910
911 <p>
912 The first attempt to define <code>g</code> fails because the type for
913 <code>x</code> is not an x-type and thus subsumption does not
914 apply. In the second attempt, we extract the two components of the
915 pair; since they are inferred to be x-values, subtyping applies to
916 both of them. Thus, when the pair <code>(a,b)</code> is reconstructed,
917 it is legal to unify its type with the input type of <code>f</code>.
918 The third definition for <code>g</code> gives an alternative solution:
919 using explicit OCaml type coercions.
920 </p>
921
922 </section>
923
924 </box>
925
926 <box title="Exchanging values" link="transl">
927
928 <p>
929 OCamlDuce strongly seperates regular OCaml values from the new
930 x-values. They have different syntax, expressions, types, patterns,
931 and even type-checking algorithms. This strong segregation is key point
932 which allowed a simple integration between very different type
933 systems.
934 </p>
935
936 <p>
937 At some point, it is still necessary to cross the frontier and
938 translate OCaml values to x-values or the opposite.
939 </p>
940
941 <p>
942 Fortunately, OCamlDuce provides automatic translations in both
943 directions. Instead of double curly braces, you can
944 enclose x-expressions in curly brace+colon <code>{: ... :}</code>
945 (here, the <code>...</code> is an x-expression).
946 The effect is to translate the result of the x-expression
947 (which must be an x-value) to an OCaml value. Similarly,
948 in an x-expression, you can obtain the x-translation of
949 an OCaml value with the same syntax <code>{: ... :}</code>
950 (here, the <code>...</code> is an OCaml expression).
951 </p>
952
953 <p>
954 Here is how the translation works. To each OCaml type <code>t</code>,
955 we associate an x-type <code>T(t)</code> and a pair of translation
956 function between <code>t</code> and <code>T(t)</code>.
957 Actually, not all the features are supported. For instance,
958 free type variables, abstract types, object types, non-regular
959 recursive types cannot be translated. In particular, since
960 type variables are not allowed, the OCaml type must be fully known.
961 </p>
962
963 <p>
964 The translation for an OCaml type <code>t</code> is defined by structural
965 induction on <code>t</code>. Sum types are
966 translated to union types: a constant constructor <code>A</code> is
967 translated to the qualified name <code>`A</code>; a non-constant
968 constructor <code>A of t1 * ... * tn</code> is translated to
969 <code>&lt;A>[ T(t1) ... T(tn) ]</code>. Closed polymorphic variants
970 have the same translation. Record types are translated to closed
971 record x-types. Some other translations:
972 </p>
973
974 <table border="1">
975 <tr><th>Caml type t</th> <th>X-type T(t)</th></tr>
976 <tr><td><code>int</code></td> <td><code>Int</code></td></tr>
977 <tr><td><code>int32</code></td> <td><code>Int32</code></td></tr>
978 <tr><td><code>int64</code></td> <td><code>Int64</code></td></tr>
979 <tr><td><code>string</code></td> <td><code>Latin1</code></td></tr>
980 <tr><td><code>t list</code></td> <td><code>[T(t)*]</code></td></tr>
981 <tr><td><code>t array</code></td> <td><code>[T(t)*]</code></td></tr>
982 <tr><td><code>unit</code></td> <td><code>[]</code></td></tr>
983 <tr><td><code>char</code></td> <td><code>Latin1Char</code></td></tr>
984 <tr><td><code>{{t}}</code></td> <td><code>t</code></td></tr>
985 </table>
986
987 <p>
988 Here is an example:
989 </p>
990
991 <sample>{{ON}}
992 # let f (x : {{ Int }}) = {{ x + 1 }} in List.map f {: [ 1 2 3 ] :};;
993 - : {{Int}} list = [{{2}}; {{3}}; {{4}}]
994 </sample>
995
996 <p>
997 In this example, the result type of the translation is inferred
998 to be <code>{{ON}}{{ Int }} list</code> (because the type for
999 <code>f</code> is given). The corresponding x-type
1000 is <code>{{ON}}{{ [Int*] }}</code>.
1001 </p>
1002
1003 </box>
1004
1005 <box title="The standard library" link="stdlib">
1006
1007 <p>
1008 In OCamlDuce, the Num library from OCaml is included in the standard
1009 library. In addition, there are two new module called
1010 <code>Ocamlduce</code> and <code>Cduce_types</code> in the standard library.
1011 </p>
1012
1013 <p>
1014 The module <code>Cduce_types</code> gives access to the internal
1015 representation of x-values. It is currently undocumented.
1016 </p>
1017
1018 <p>
1019 The module <code>Ocamlduce</code> provides several useful
1020 functionality x-values. See the <a href="http://yquem.inria.fr/~frisch/ocamlcduce/doc/ocamlduce/Ocamlduce.html">ocamldoc</a> generated
1021 documentation for a description of its interface.
1022 </p>
1023
1024 </box>
1025
1026 <box title="Marshaling" link="marshal">
1027
1028 <p>
1029 OCamlDuce use some tricks on its internal representation of x-values
1030 to reduce memory usage and improve performance. You need to pay
1031 special attention if you want to use OCaml serialization functions
1032 (module <code>Marshal</code>, functions
1033 <code>input_value/output_value</code>) on x-values. In addition to
1034 your values, you also need to save and restore some piece of internal data
1035 using the functions <code>Cduce_types.Value.extract_all</code> and
1036 <code>Cduce_types.Value.intract_all</code>. Of course, this also
1037 applies if the value to be serialized contains deeply nested x-values.
1038 </p>
1039
1040 <p>
1041 Here are generic
1042 serialization/deserializations functions that illustrate how to do it:
1043 </p>
1044
1045 <sample>
1046 let my_output_value oc v =
1047 let p = Cduce_types.Value.extract_all () in
1048 output_value oc (p,v)
1049
1050 let my_input_value ic =
1051 let (p,v) = input_value ic in
1052 Cduce_types.Value.intract_all p;
1053 v
1054 </sample>
1055
1056 </box>
1057
1058 <box title="Performance" link="perf">
1059
1060 <section title="Strings">
1061
1062 <p>
1063 OCaml users might be surprised by the fact that x-strings are simply
1064 represented as sequences in OCamlDuce. Does this mean that they are
1065 actually stored in memory as linked list? Certainly not! The internal
1066 representation of sequence values uses several tricks to improve
1067 performance and memory usage. In particular, a special form in the
1068 representation can store strings as byte buffers, as in OCaml.
1069 It an XML document is loaded, or if a Caml string is converted
1070 to an x-value, this compact representation will be used.
1071 </p>
1072
1073 </section>
1074
1075 <section title="Concatenation">
1076
1077 <p>
1078 Similarly, OCaml users might be relectutant to use the sequence
1079 concatenation <code>@</code> on sequences. In OCaml, the complexity
1080 of this operator is linear in the size of its first argument (which
1081 need to be copied). OCamlDuce use a special form in its internal
1082 representation to store concatenation in a lazy way. The concatenation
1083 will really by computed only when the value is accessed. This means
1084 that it's perfectly ok to build a long sequence by adding
1085 new elements at the end one by one, as long as you don't
1086 simultaneously inspect the sequence.
1087 </p>
1088
1089 </section>
1090
1091 <section title="Pattern matching">
1092
1093 <p>
1094 Another point which is worth knowing when programming in OCamlDuce
1095 is that patterns can be written in a declarative style without
1096 affective performance. The compiler uses static type information
1097 about matched values to produce efficient code for pattern matching.
1098 To illustrate this, consider the following sample:
1099 </p>
1100
1101 <sample><![CDATA[{{ON}}
1102 x.ml:
1103
1104 type a = {{ <a>[ a* ] }}
1105 type b = {{ <b>[ b* ] }}
1106
1107 let f : {{ a|b }} -> int = function {{ a }} -> 0 | {{ _ }} -> 1
1108 ]]></sample>
1109
1110 <sample><![CDATA[{{ON}}
1111 y.ml:
1112
1113 type a = {{ <a>[ a* ] }}
1114 type b = {{ <b>[ b* ] }}
1115
1116 let f : {{ a|b }} -> int = function {{ <a>_ }} -> 0 | {{ _ }} -> 1
1117 ]]></sample>
1118
1119 <p>
1120 The two functions have exactly the same semantics, but the first
1121 implementation is more declarative: it uses type checks to distinguish
1122 between <code>a</code> and <code>b</code> instead of saying
1123 <em>how</em> to distinguish between these two types. Imagine
1124 that the definition of these types change to:
1125 </p>
1126
1127 <sample><![CDATA[{{ON}}
1128 type a = {{ <x kind="a">[ a* ] }}
1129 type b = {{ <x kind="b">[ b* ] }}
1130 ]]></sample>
1131
1132 <p>
1133 Then the first implementation still works as expected, but the
1134 second one needs to be rewritten.</p>
1135
1136 <p>Now one might believe that the second implementation is more
1137 efficient because it tells the compiler to check only the root tag,
1138 whereas the first implementation would force
1139 the compiler to produce code to check that all tags in the tree
1140 are <code>a</code>s. But this is not what happens! Actually,
1141 you can check that the compiler will produce exactly the same code
1142 for both implementations. It considers the static type information
1143 about the argument of the pattern matching (here, the input type
1144 of the function), and computes an efficient way to evaluate
1145 patterns for the values of this type.
1146 </p>
1147
1148 </section>
1149
1150 <section title="The map iterator">
1151
1152 <p>
1153 The <code>map ... with ...</code> iterator is implemented in a
1154 tail-recursive way. You can safely use it on very long sequences.
1155 </p>
1156
1157 </section>
1158
1159 </box>
1160
1161 <box title="OCaml and OCamlDuce" link="ocaml">
1162
1163 <p>
1164 Since the 3.08.4 release, OCamlDuce is binary compatible with the corresponding
1165 OCaml release. This means that OCamlDuce can use OCaml-generated
1166 <tt>.cmi</tt> files and that it produces an OCaml-compatible
1167 <tt>.cmi</tt> file if the interface does not use any x-type
1168 (this file is equal to what would have been obtained by using OCaml).
1169 </p>
1170
1171 <p>
1172 It is thus possible to use existing libraries which were compiled for
1173 OCaml 3.08.4. It is also possible to use OCamlDuce to compile
1174 some modules and use them in an OCaml project provided their interface
1175 is pure OCaml.
1176 </p>
1177
1178
1179 </box>
1180
1181 <box title="Code samples" link="code">
1182
1183 <section title="Parsing XML files">
1184
1185 <p>
1186 OCamlDuce does not come with any built-in XML parser. However,
1187 the <a href="http://yquem.inria.fr/~frisch/ocamlcduce/doc/ocamlduce/Ocamlduce.Load.html"><code>Ocamlduce.Load</code></a> module in the standard library
1188 makes it easy to plug existing XML parsers. Here is some
1189 code which demonstrate how to do that with three of
1190 the most popular OCaml XML parser libraries:
1191 </p>
1192
1193 <ul>
1194 <li><a
1195 href="http://yquem.inria.fr/~frisch/ocamlcduce/samples/pxp/">PXP</a></li>
1196 <li><a
1197 href="http://yquem.inria.fr/~frisch/ocamlcduce/samples/expat/">Expat</a></li>
1198 <li><a href="http://yquem.inria.fr/~frisch/ocamlcduce/samples/xmllight/">Xml-light</a></li>
1199 </ul>
1200
1201 </section>
1202
1203 <section title="Converting DTD to OCamlDuce types">
1204
1205 <p>
1206 This <a href="http://yquem.inria.fr/~frisch/ocamlcduce/samples/dtd2types/">tool</a> produces a set of OCamlDuce type declarations
1207 from a DTD. It requires PXP.
1208 </p>
1209
1210 <note>This application does not use any of the new features, but it
1211 can be useful in the development of OCamlDuce applications.
1212 </note>
1213
1214 </section>
1215
1216 <section title="Parsing XML Schema, producing valid XHTML output">
1217
1218 <p>
1219 This <a
1220 href="http://yquem.inria.fr/~frisch/ocamlcduce/samples/schema/">application</a>
1221 parses XML Schema Definitions (.xsd files), and produces summaries
1222 (toplevel declaration names) in XHTML. OCamlDuce type system ensures
1223 that the parser is coherent with the input XML type (any valid XML
1224 Schema is accepted) and that the printer is coherent with the output
1225 XML type (it is necessarily a valid XHTML document).
1226 </p>
1227
1228 <p>
1229 Of course, for such a simple transformation, parsing the XML document
1230 into an internal representation is not necessary. A direct XML-to-XML
1231 transformation would be easy to write. We wanted to illustrate
1232 a complex parsing of XML.
1233 </p>
1234
1235 <p>
1236 It it interesting to introduce errors in the parser
1237 <code>schema_loader.ml</code> or the printer
1238 <code>dump_schema.ml</code> and see how the type system catches them.
1239 </p>
1240
1241 <note>
1242 The application uses XML Light to parse XML document.
1243 </note>
1244
1245 <note>
1246 Some features of XML Schema are not parsed, such as
1247 <code>redefine</code> elements or substitution groups.
1248 </note>
1249
1250 <note>
1251 To compile the application with the provided Makefile,
1252 you must make the environment variable <code>OCAMLFIND_CONF</code>
1253 point to the <code>$GODI/etc/findlib-ocamlduce.conf</code> file.
1254 </note>
1255
1256 </section>
1257
1258 <section title="String regular expressions">
1259
1260 <p>
1261 OCamlDuce supports regular expression types and patterns, not only
1262 for sequences of XML elements, but also for strings. The following
1263 example shows how to use regular expressions to split a string
1264 of the form <code>name1=val1,...,namen=valn</code> with
1265 <code>n>0</code> into
1266 a list of pairs <code>[ (name1,val1); ...; (namen,valn) ]</code>.
1267 The <code>*?</code> operator in regular expressions means ``ungreedy
1268 match'' (match the shortest possible subsequence). The last
1269 pattern describes precisely strings which are not matched by
1270 the other cases. It would be possible to replace it with
1271 the wildcard <code>_</code>.
1272 </p>
1273
1274 <sample><![CDATA[{{ON}}
1275 let rec split (s : {{ String }}) =
1276 match s with
1277 | {{ [ n::_*? '=' v::_*? ',' rest::_* ] }} -> (n,v)::(split rest)
1278 | {{ [ n::_*? '=' v::_*? ] }} -> [ (n,v) ]
1279 | {{ Any - [ _* '=' _* ] }} -> failwith "split"
1280 ]]></sample>
1281
1282 </section>
1283
1284 </box>
1285
1286 <box title="Applications in OCamlDuce" link="appli">
1287
1288 <ul>
1289 <li><a
1290 href="http://anil.recoil.org/projects/review2atom.html">Review2Atom</a>
1291 by Anil Madhavapeddy: translates paper review files in XML format into
1292 an Atom feed suitable for aggregation.
1293 </li>
1294 </ul>
1295
1296 </box>
1297
1298
1299 </page>

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