]> matita.cs.unibo.it Git - helm.git/commitdiff
....
authorEnrico Tassi <enrico.tassi@inria.fr>
Thu, 24 Sep 2009 11:50:42 +0000 (11:50 +0000)
committerEnrico Tassi <enrico.tassi@inria.fr>
Thu, 24 Sep 2009 11:50:42 +0000 (11:50 +0000)
helm/software/matita/nlibrary/topology/convert.awk
helm/software/matita/nlibrary/topology/grafite.lang [new file with mode: 0644]
helm/software/matita/nlibrary/topology/igft.ma
helm/software/matita/nlibrary/topology/preamble.xml
helm/software/matita/nlibrary/topology/sh_gedit.css [new file with mode: 0644]
helm/software/matita/nlibrary/topology/sh_grafite.js [new file with mode: 0644]
helm/software/matita/nlibrary/topology/sh_main.js [new file with mode: 0644]

index 68bcf8a6260599bc629c02f7081213fbe93635ec..757872324810787f3ff6a8cc89337fb4959a61b3 100644 (file)
@@ -9,6 +9,7 @@ BEGIN {
        match($0, "screenshot *\"([^\"]+)\"", data);
        key = data[1];
        refs[key] = key ".png"; 
+       $0 = gensub(/\(\*\* *screenshot[^*]*\*\)/,"",$0);
        }
 
 # literate programming
diff --git a/helm/software/matita/nlibrary/topology/grafite.lang b/helm/software/matita/nlibrary/topology/grafite.lang
new file mode 100644 (file)
index 0000000..93fabcc
--- /dev/null
@@ -0,0 +1,16 @@
+preproc = "napply|ncases|nelim|nletin|ncut|nauto|nwhd|nnormalize|nassumption|ngeneralize|nchange|nrewrite|nlapply"
+
+number = 
+'\<[+-]?((0x[[:xdigit:]]+)|(([[:digit:]]*\.)?
+[[:digit:]]+([eE][+-]?[[:digit:]]+)?))u?((int(?:8|16|32|64))|L)?\>'
+
+string delim "\"" "\"" escape "\\"
+
+keyword = "match|with|naxiom|nlemma|ntheorem|nrecord|for|ninductive|ncoinductive|ndefinition|nlet|rec|corec|notation|interpretation|nqed|include"
+
+comment delim "(*" "*)" multiline nested 
+
+type = '\<(?:CProp|Type|Prop)\>'
+
+symbol = "[","]","\|","$","@","{","}"
+
index 3c97b9c25000735971c09018c1a1f4c42a6e86a3..1cb932184a64e9fc03d69969a52865a18e0018c1 100644 (file)
@@ -12,6 +12,12 @@ the formalization of the paper
 
 by S.Berardi and S. Valentini. The tutorial is by Enrico Tassi. 
 
+The tutorial spends a considerable amount of effort in defining 
+notations that resemble the ones used in the original paper. We believe
+this a important part of every formalization, not only for the estetic 
+point of view, but also from the practical point of view. Being 
+consistent allows to follow the paper in a pedantic way. 
+
 Orientering
 -----------
 
@@ -40,6 +46,7 @@ some notation attached to them:
 
 - A ∪ B `A \cup B`
 - A ∩ B `A \cap B` 
+- A ≬ B `A \between B`
 - x ∈ A `x \in A` 
 - Ω^A, that is the type of the subsets of A, `\Omega ^ A` 
 
@@ -219,38 +226,186 @@ as subscript), separating them with a comma followed by a little space.
 The first (technical) definition
 --------------------------------
 
+Before defining the cover relation as an inductive predicate, one
+has to notice that the infinity rule uses, in its hypotheses, the 
+cover relation between two subsets, while the inductive predicate 
+we are going to define relates an element and a subset.
+
+An option would be to unfold the definition of cover between subsets,
+but we prefer to define the abstract notion of cover between subsets
+(so that we can attach a (ambiguous) notation to it).
+
+Anyway, to ease the understaing of the definition of the cover relation 
+between subsets, we first define the inductive predicate unfolding the 
+definition, and we later refine it with.
+
+DOCEND*)
+
+ninductive xcover (A : Ax) (U : Ω^A) : A → CProp[0] ≝ 
+| xcreflexivity : ∀a:A. a ∈ U → xcover A U a
+| xcinfinity    : ∀a:A.∀i:𝐈 a. (∀y.y ∈ 𝐂 a i → xcover A U y) → xcover A U a.
+
+(*DOCBEGIN
+
+We defined the xcover (x will be removed in the final version of the 
+definition) as an inductive predicate. The arity of the inductive
+predicate has to be carefully analyzed:
+
+>  (A : Ax) (U : Ω^A) : A → CProp[0]
 
+The syntax separates with `:` abstractions that are fixed for every
+constructor (introduction rule) and abstractions that can change. In that 
+case the parameter `U` is abstracted once and forall in front of every 
+constructor, and every occurrence of the inductive predicate is applied to
+`U` in a consistent way. Arguments abstracted on the right of `:` are not
+constant, for example the xcinfinity constructor introduces `a ◃ U`,
+but under the assumption that (for every y) `y ◃ U`. In that rule, the left
+had side of the predicate changes, thus it has to be abstrated (in the arity
+of the inductive predicate) on the right of `:`.
+
+DOCEND*)
+
+(* ncheck xcreflexivity. *)
+
+(*DOCBEGIN
+
+We want now to abstract out `(∀y.y ∈ 𝐂 a i → xcover A U y)` and define
+a notion `cover_set` to which a notation `𝐂 a i ◃ U` can be attached.
+
+This notion has to be abstracted over the cover relation (whose
+type is the arity of the inductive `xcover` predicate just defined).
+
+Then it has to be abstracted over the arguments of that cover relation,
+i.e. the axiom set and the set U, and the subset (in that case `𝐂 a i`)
+sitting on the left hand side of `◃`. 
 
 DOCEND*)
 
 ndefinition cover_set : 
-  ∀c: ∀A:Ax.Ω^A → A → CProp[0]. ∀A:Ax.∀C,U:Ω^A. CProp[0] 
+  ∀cover: ∀A:Ax.Ω^A → A → CProp[0]. ∀A:Ax.∀C,U:Ω^A. CProp[0] 
 ≝ 
-  λc: ∀A:Ax.Ω^A → A → CProp[0]. λA,C,U.∀y.y ∈ C → c A U y.
+  λcover.                           λA,    C,U.     ∀y.y ∈ C → cover A U y.
 
-ndefinition cover_set_interactive : 
-  ∀c: ∀A:Ax.Ω^A → A → CProp[0]. ∀A:Ax.∀C,U:Ω^A. CProp[0]. 
-#cover; #A; #C; #U; napply (∀y:A.y ∈ C → ?); napply cover;
-##[ napply A;
-##| napply U;
-##| napply y;
-##]
-nqed.
+(*DOCBEGIN
+
+The `ndefinition` command takes a name, a type and body (of that type).
+The type can be omitted, and in that case it is inferred by the system.
+If the type is given, the system uses it to infer implicit arguments
+of the body. In that case all types are left implicit in the body.
+
+We now define the notation `a ◃ b`. Here the keywork `hvbox`
+and `break` tell the system how to wrap text when it does not
+fit the screen (they can be safely ignore for the scope of
+this tutorial). we also add an interpretation for that notation, 
+where the (abstracted) cover relation is implicit. The system
+will not be able to infer it from the other arguments `C` and `U`
+and will thus prompt the user for it. This is also why we named this 
+interpretation `covers set temp`: we will later define another 
+interpretation in which the cover relation is the one we are going to 
+define.
+
+DOCEND*)
 
-(* a \ltri b *)
 notation "hvbox(a break ◃ b)" non associative with precedence 45
 for @{ 'covers $a $b }.
 
 interpretation "covers set temp" 'covers C U = (cover_set ?? C U).
 
+(*DOCBEGIN
+
+We can now define the cover relation using the `◃` notation for 
+the premise of infinity. 
+
+DOCEND*)
+
 ninductive cover (A : Ax) (U : Ω^A) : A → CProp[0] ≝ 
-| creflexivity : ∀a:A. a ∈ U → cover ? U a
-| cinfinity    : ∀a:A.∀i:𝐈 a. 𝐂 a i ◃ U → cover ? U a.
+| creflexivity : ∀a. a ∈ U → cover ? U a
+| cinfinity    : ∀a. ∀i. 𝐂 a i ◃ U → cover ? U a.
+(** screenshot "cover". *) 
 napply cover;
 nqed.
 
+(*DOCBEGIN
+
+Note that the system accepts the definition
+but prompts the user for the relation the `cover_set` notion is
+abstracted on.
+
+![The system asks for a cover relation][cover]
+
+The orizontal line separates the hypotheses from the conclusion.
+The `napply cover` command tells the system that the relation
+it is looking for is exactly our first context entry (i.e. the inductive
+predicate we are defining, up to α-conversion); while the `nqed` command
+ends a definition or proof.
+
+We can now define the interpretation for the cover relation between an
+element and a subset fist, then between two subsets (but this time
+we fixed the relation `cover_set` is abstracted on).
+
+DOCEND*)
+
 interpretation "covers" 'covers a U = (cover ? U a).
-(* interpretation "covers set" 'covers a U = (cover_set cover ? a U). *)
+interpretation "covers set" 'covers a U = (cover_set cover ? a U).
+
+(*DOCBEGIN
+
+We will proceed similarly for the fish relation, but before going
+on it is better to give a short introduction to the proof mode of Matita.
+We define again the `cover_set` term, but this time we will build
+its body interactively. In λ-calculus Matita is based on, CIC, proofs
+and terms share the same syntax, and it thus possible to use the
+commands devoted to build proof term to build regular definitions.
+
+DOCEND*)
+
+
+ndefinition xcover_set : 
+  ∀c: ∀A:Ax.Ω^A → A → CProp[0]. ∀A:Ax.∀C,U:Ω^A. CProp[0]. 
+(** screenshot "xcover-set-1". *)
+(*DOCBEGIN
+The system asks for a proof of the full statement, in an empty context.
+![xcover_set proof step ][xcover-set-1]
+The `#` command in the ∀-introduction rule, it gives a name to an 
+assumption putting it in the context, and generates a λ-abstraction
+in the proof term.
+DOCEND*)
+#cover; #A; #C; #U; (** screenshot "xcover-set-2". *) 
+(*DOCBEGIN
+![xcover_set proof step ][xcover-set-2]
+We have now to provide a proposition, and we exhibit it. We left
+a part of it implicit; since the system cannot infer it it will
+ask it later. Note that the type of `∀y:A.y ∈ C → ?` is a proposition
+whenever `?` is.
+DOCEND*)
+napply (∀y:A.y ∈ C → ?); (** screenshot "xcover-set-3". *)
+(*DOCBEGIN
+![xcover_set proof step ][xcover-set-3]
+The proposition we want to provide is an application of the
+cover relation we have abstracted in the context. The command
+`napply`, if the given term has not the expected type (in that
+case it is a product versus a proposition) it applies it to as many 
+implicit arguments as necessary (in that case `? ? ?`).
+DOCEND*)
+napply cover; (** screenshot "xcover-set-4". *)
+(*DOCBEGIN
+![xcover_set proof step ][xcover-set-4]
+The system will now ask in turn the three implicit arguments 
+passed to cover. The syntax `##[` allows to start a branching
+to tackle every sub proof individually, otherwise every command
+is applied to every subrpoof. The command `##|` switches to the next
+subproof and `##]` ends the branching.  
+DOCEND*)
+##[ napply A;
+##| napply U;
+##| napply y;
+##]
+nqed.
+
+(*DOCBEGIN
+The definition of fish works exactly the same way as for cover, except 
+that it is defined as a coinductive proposition.
+DOCEND*)
 
 ndefinition fish_set ≝ λf:∀A:Ax.Ω^A → A → CProp[0].
  λA,U,V.
@@ -270,22 +425,59 @@ nqed.
 interpretation "fish set" 'fish A U = (fish_set fish ? U A).
 interpretation "fish" 'fish a U = (fish ? U a).
 
+(*DOCBEGIN
+
+Matita is able to generate elimination rules for inductive types,
+but not introduction rules for the coinductive case. 
+
+DOCEND*)
+
+(* ncheck cover_rect_CProp0. *) 
+
+(*DOCBEGIN
+
+We thus have to define the introduction rule for fish by corecursion.
+Here we again use the proof mode of Matita to exhibit the body of the
+corecursive function.
+
+DOCEND*)
+
 nlet corec fish_rec (A:Ax) (U: Ω^A)
  (P: Ω^A) (H1: P ⊆ U)
-  (H2: ∀a:A. a ∈ P → ∀j: 𝐈 a. 𝐂 a j ≬ P):
-   ∀a:A. ∀p: a ∈ P. a ⋉ U ≝ ?.
-#a; #p; napply cfish; (** screenshot "def-fish-rec". *)
-##[ napply H1; napply p;
-##| #i; ncases (H2 a p i); #x; *; #xC; #xP; @; ##[napply x]
-    @; ##[ napply xC ] napply (fish_rec ? U P); nassumption;
+  (H2: ∀a:A. a ∈ P → ∀j: 𝐈 a. 𝐂 a j ≬ P): ∀a:A. ∀p: a ∈ P. a ⋉ U ≝ ?.
+(** screenshot "def-fish-rec-1". *)
+#a; #p; napply cfish; 
+##[ (** screenshot "def-fish-rec-2". *) napply H1;
+    (** screenshot "def-fish-rec-3". *) napply p;
+##| (** screenshot "def-fish-rec-4". *) #i; ncases (H2 a p i); 
+    (** screenshot "def-fish-rec-5". *) #x; *; #xC; #xP;
+    (** screenshot "def-fish-rec-5". *) @; 
+    ##[ (** screenshot "def-fish-rec-6". *) napply x
+    ##| (** screenshot "def-fish-rec-7". *)
+        @; ##[ napply xC; 
+           ##| (** screenshot "def-fish-rec-8". *) 
+               napply (fish_rec ? U P); 
+               nassumption;
+           ##]
+    ##]
 ##]
 nqed.
 
-notation "◃U" non associative with precedence 55
-for @{ 'coverage $U }.
+(*DOCBEGIN
+![fish proof step][def-fish-rec-1]
+![fish proof step][def-fish-rec-2]
+![fish proof step][def-fish-rec-3]
+![fish proof step][def-fish-rec-4]
+![fish proof step][def-fish-rec-5]
+![fish proof step][def-fish-rec-6]
+![fish proof step][def-fish-rec-7]
+![fish proof step][def-fish-rec-8]
+DOCEND*)
 
 ndefinition coverage : ∀A:Ax.∀U:Ω^A.Ω^A ≝ λA,U.{ a | a ◃ U }.
 
+notation "◃U" non associative with precedence 55 for @{ 'coverage $U }.
+
 interpretation "coverage cover" 'coverage U = (coverage ? U).
 
 ndefinition cover_equation : ∀A:Ax.∀U,X:Ω^A.CProp[0] ≝  λA,U,X. 
@@ -430,7 +622,6 @@ naxiom setoidification :
 
 Bla Bla, 
 
-<div id="figure1" class="img" ><img src="figure1.png"/>foo</div>
 
 DOCEND*)
 
index bbba386e1034260d79ac403e174b9db59069ba3f..a85be4fc6bc6fea0f20827c7011c049950f80e61 100644 (file)
@@ -4,9 +4,9 @@
   <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
   <style type="text/css">
          pre, code { 
-           font-family: Sans
-           font-size: 1em
-           background-color:#fafaaf; 
+           font-family: monospace
+           font-size: 110%
+         } 
          pre { margin-right: 5em; margin-left: 2em; }
          img { 
            margin-left: auto; 
            border-color: grey;
          }
   </style>
+  <script type="text/javascript" src="sh_main.js"></script>
+  <script type="text/javascript" src="sh_grafite.js"></script>
+  <link type="text/css" rel="stylesheet" href="sh_gedit.css">
+  <script type="text/javascript">
+  function syntaxon(){
+    var pres=document.getElementsByTagName("pre")
+    for (i=0; i < pres.length; i++) {
+      pres[i].className="sh_grafite";
+    }
+    sh_highlightDocument();
+  }
+  </script>
  </head>
- <body>
+ <body onload="syntaxon();">
diff --git a/helm/software/matita/nlibrary/topology/sh_gedit.css b/helm/software/matita/nlibrary/topology/sh_gedit.css
new file mode 100644 (file)
index 0000000..c1529dd
--- /dev/null
@@ -0,0 +1,66 @@
+pre.sh_sourceCode {\r
+  background-color: white;\r
+  color: black;\r
+  font-style: normal;\r
+  font-weight: normal;\r
+}\r
+\r
+pre.sh_sourceCode .sh_keyword { color: brown; font-weight: bold; }           /* language keywords */\r
+pre.sh_sourceCode .sh_type { color: darkgreen; font-weight: bold; }          /* basic types */\r
+pre.sh_sourceCode .sh_usertype { color: teal; }                             /* user defined types */\r
+pre.sh_sourceCode .sh_string { color: magenta; font-family: monospace; }        /* strings and chars */\r
+pre.sh_sourceCode .sh_regexp { color: orange; font-family: monospace; }     /* regular expressions */\r
+pre.sh_sourceCode .sh_specialchar { color: pink; font-family: monospace; }  /* e.g., \n, \t, \\ */\r
+pre.sh_sourceCode .sh_comment { color: brown; font-style: italic; }         /* comments */\r
+pre.sh_sourceCode .sh_number { color: black; }                             /* literal numbers */\r
+pre.sh_sourceCode .sh_preproc { color: MediumSeaGreen; }       /* e.g., #include, import */\r
+pre.sh_sourceCode .sh_symbol { color:brown; font-weight: bold; }                            /* e.g., <, >, + */\r
+pre.sh_sourceCode .sh_function { color: black; font-weight: bold; }         /* function calls and declarations */\r
+pre.sh_sourceCode .sh_cbracket { color: red; }                              /* block brackets (e.g., {, }) */\r
+pre.sh_sourceCode .sh_todo { font-weight: bold; background-color: cyan; }   /* TODO and FIXME */\r
+\r
+/* Predefined variables and functions (for instance glsl) */\r
+pre.sh_sourceCode .sh_predef_var { color: darkblue; }\r
+pre.sh_sourceCode .sh_predef_func { color: darkblue; font-weight: bold; }\r
+\r
+/* for OOP */\r
+pre.sh_sourceCode .sh_classname { color: teal; }\r
+\r
+/* line numbers (not yet implemented) */\r
+pre.sh_sourceCode .sh_linenum { color: black; font-family: monospace; }\r
+\r
+/* Internet related */\r
+pre.sh_sourceCode .sh_url { color: blue; text-decoration: underline; font-family: monospace; }\r
+\r
+/* for ChangeLog and Log files */\r
+pre.sh_sourceCode .sh_date { color: blue; font-weight: bold; }\r
+pre.sh_sourceCode .sh_time, pre.sh_sourceCode .sh_file { color: darkblue; font-weight: bold; }\r
+pre.sh_sourceCode .sh_ip, pre.sh_sourceCode .sh_name { color: darkgreen; }\r
+\r
+/* for Prolog, Perl... */\r
+pre.sh_sourceCode .sh_variable { color: darkgreen; }\r
+\r
+/* for LaTeX */\r
+pre.sh_sourceCode .sh_italics { color: darkgreen; font-style: italic; }\r
+pre.sh_sourceCode .sh_bold { color: darkgreen; font-weight: bold; }\r
+pre.sh_sourceCode .sh_underline { color: darkgreen; text-decoration: underline; }\r
+pre.sh_sourceCode .sh_fixed { color: green; font-family: monospace; }\r
+pre.sh_sourceCode .sh_argument { color: darkgreen; }\r
+pre.sh_sourceCode .sh_optionalargument { color: purple; }\r
+pre.sh_sourceCode .sh_math { color: orange; }\r
+pre.sh_sourceCode .sh_bibtex { color: blue; }\r
+\r
+/* for diffs */\r
+pre.sh_sourceCode .sh_oldfile { color: orange; }\r
+pre.sh_sourceCode .sh_newfile { color: darkgreen; }\r
+pre.sh_sourceCode .sh_difflines { color: blue; }\r
+\r
+/* for css */\r
+pre.sh_sourceCode .sh_selector { color: purple; }\r
+pre.sh_sourceCode .sh_property { color: blue; }\r
+pre.sh_sourceCode .sh_value { color: darkgreen; font-style: italic; }\r
+\r
+/* other */\r
+pre.sh_sourceCode .sh_section { color: black; font-weight: bold; }\r
+pre.sh_sourceCode .sh_paren { color: red; }\r
+pre.sh_sourceCode .sh_attribute { color: darkgreen; }\r
diff --git a/helm/software/matita/nlibrary/topology/sh_grafite.js b/helm/software/matita/nlibrary/topology/sh_grafite.js
new file mode 100644 (file)
index 0000000..f327caa
--- /dev/null
@@ -0,0 +1,71 @@
+if (! this.sh_languages) {
+  this.sh_languages = {};
+}
+sh_languages['grafite'] = [
+  [
+    [
+      /\b(?:napply|ncases|nelim|nletin|ncut|nauto|nwhd|nnormalize|nassumption|ngeneralize|nchange|nrewrite|nlapply)\b/g,
+      'sh_preproc',
+      -1
+    ],
+    [
+      /\b[+-]?(?:(?:0x[A-Fa-f0-9]+)|(?:(?:[\d]*\.)?[\d]+(?:[eE][+-]?[\d]+)?))u?(?:(?:int(?:8|16|32|64))|L)?\b/g,
+      'sh_number',
+      -1
+    ],
+    [
+      /"/g,
+      'sh_string',
+      1
+    ],
+    [
+      /\b(?:match|with|naxiom|nlemma|ntheorem|nrecord|for|ninductive|ncoinductive|ndefinition|nlet|rec|corec|notation|interpretation|nqed|include)\b/g,
+      'sh_keyword',
+      -1
+    ],
+    [
+      /\(\*/g,
+      'sh_comment',
+      2
+    ],
+    [
+      /\b(?:CProp|Type|Prop)\b/g,
+      'sh_type',
+      -1
+    ],
+    [
+      /\[|\]|\||\$|@|\{|\}/g,
+      'sh_symbol',
+      -1
+    ]
+  ],
+  [
+    [
+      /$/g,
+      null,
+      -2
+    ],
+    [
+      /\\(?:\\|")/g,
+      null,
+      -1
+    ],
+    [
+      /"/g,
+      'sh_string',
+      -2
+    ]
+  ],
+  [
+    [
+      /\*\)/g,
+      'sh_comment',
+      -2
+    ],
+    [
+      /\(\*/g,
+      'sh_comment',
+      2
+    ]
+  ]
+];
diff --git a/helm/software/matita/nlibrary/topology/sh_main.js b/helm/software/matita/nlibrary/topology/sh_main.js
new file mode 100644 (file)
index 0000000..1fe3ea0
--- /dev/null
@@ -0,0 +1,538 @@
+/*\r
+SHJS - Syntax Highlighting in JavaScript\r
+Copyright (C) 2007, 2008 gnombat@users.sourceforge.net\r
+License: http://shjs.sourceforge.net/doc/gplv3.html\r
+*/\r
+\r
+if (! this.sh_languages) {\r
+  this.sh_languages = {};\r
+}\r
+var sh_requests = {};\r
+\r
+function sh_isEmailAddress(url) {\r
+  if (/^mailto:/.test(url)) {\r
+    return false;\r
+  }\r
+  return url.indexOf('@') !== -1;\r
+}\r
+\r
+function sh_setHref(tags, numTags, inputString) {\r
+  var url = inputString.substring(tags[numTags - 2].pos, tags[numTags - 1].pos);\r
+  if (url.length >= 2 && url.charAt(0) === '<' && url.charAt(url.length - 1) === '>') {\r
+    url = url.substr(1, url.length - 2);\r
+  }\r
+  if (sh_isEmailAddress(url)) {\r
+    url = 'mailto:' + url;\r
+  }\r
+  tags[numTags - 2].node.href = url;\r
+}\r
+\r
+/*\r
+Konqueror has a bug where the regular expression /$/g will not match at the end\r
+of a line more than once:\r
+\r
+  var regex = /$/g;\r
+  var match;\r
+\r
+  var line = '1234567890';\r
+  regex.lastIndex = 10;\r
+  match = regex.exec(line);\r
+\r
+  var line2 = 'abcde';\r
+  regex.lastIndex = 5;\r
+  match = regex.exec(line2);  // fails\r
+*/\r
+function sh_konquerorExec(s) {\r
+  var result = [''];\r
+  result.index = s.length;\r
+  result.input = s;\r
+  return result;\r
+}\r
+\r
+/**\r
+Highlights all elements containing source code in a text string.  The return\r
+value is an array of objects, each representing an HTML start or end tag.  Each\r
+object has a property named pos, which is an integer representing the text\r
+offset of the tag. Every start tag also has a property named node, which is the\r
+DOM element started by the tag. End tags do not have this property.\r
+@param  inputString  a text string\r
+@param  language  a language definition object\r
+@return  an array of tag objects\r
+*/\r
+function sh_highlightString(inputString, language) {\r
+  if (/Konqueror/.test(navigator.userAgent)) {\r
+    if (! language.konquered) {\r
+      for (var s = 0; s < language.length; s++) {\r
+        for (var p = 0; p < language[s].length; p++) {\r
+          var r = language[s][p][0];\r
+          if (r.source === '$') {\r
+            r.exec = sh_konquerorExec;\r
+          }\r
+        }\r
+      }\r
+      language.konquered = true;\r
+    }\r
+  }\r
+\r
+  var a = document.createElement('a');\r
+  var span = document.createElement('span');\r
+\r
+  // the result\r
+  var tags = [];\r
+  var numTags = 0;\r
+\r
+  // each element is a pattern object from language\r
+  var patternStack = [];\r
+\r
+  // the current position within inputString\r
+  var pos = 0;\r
+\r
+  // the name of the current style, or null if there is no current style\r
+  var currentStyle = null;\r
+\r
+  var output = function(s, style) {\r
+    var length = s.length;\r
+    // this is more than just an optimization - we don't want to output empty <span></span> elements\r
+    if (length === 0) {\r
+      return;\r
+    }\r
+    if (! style) {\r
+      var stackLength = patternStack.length;\r
+      if (stackLength !== 0) {\r
+        var pattern = patternStack[stackLength - 1];\r
+        // check whether this is a state or an environment\r
+        if (! pattern[3]) {\r
+          // it's not a state - it's an environment; use the style for this environment\r
+          style = pattern[1];\r
+        }\r
+      }\r
+    }\r
+    if (currentStyle !== style) {\r
+      if (currentStyle) {\r
+        tags[numTags++] = {pos: pos};\r
+        if (currentStyle === 'sh_url') {\r
+          sh_setHref(tags, numTags, inputString);\r
+        }\r
+      }\r
+      if (style) {\r
+        var clone;\r
+        if (style === 'sh_url') {\r
+          clone = a.cloneNode(false);\r
+        }\r
+        else {\r
+          clone = span.cloneNode(false);\r
+        }\r
+        clone.className = style;\r
+        tags[numTags++] = {node: clone, pos: pos};\r
+      }\r
+    }\r
+    pos += length;\r
+    currentStyle = style;\r
+  };\r
+\r
+  var endOfLinePattern = /\r\n|\r|\n/g;\r
+  endOfLinePattern.lastIndex = 0;\r
+  var inputStringLength = inputString.length;\r
+  while (pos < inputStringLength) {\r
+    var start = pos;\r
+    var end;\r
+    var startOfNextLine;\r
+    var endOfLineMatch = endOfLinePattern.exec(inputString);\r
+    if (endOfLineMatch === null) {\r
+      end = inputStringLength;\r
+      startOfNextLine = inputStringLength;\r
+    }\r
+    else {\r
+      end = endOfLineMatch.index;\r
+      startOfNextLine = endOfLinePattern.lastIndex;\r
+    }\r
+\r
+    var line = inputString.substring(start, end);\r
+\r
+    var matchCache = [];\r
+    for (;;) {\r
+      var posWithinLine = pos - start;\r
+\r
+      var stateIndex;\r
+      var stackLength = patternStack.length;\r
+      if (stackLength === 0) {\r
+        stateIndex = 0;\r
+      }\r
+      else {\r
+        // get the next state\r
+        stateIndex = patternStack[stackLength - 1][2];\r
+      }\r
+\r
+      var state = language[stateIndex];\r
+      var numPatterns = state.length;\r
+      var mc = matchCache[stateIndex];\r
+      if (! mc) {\r
+        mc = matchCache[stateIndex] = [];\r
+      }\r
+      var bestMatch = null;\r
+      var bestPatternIndex = -1;\r
+      for (var i = 0; i < numPatterns; i++) {\r
+        var match;\r
+        if (i < mc.length && (mc[i] === null || posWithinLine <= mc[i].index)) {\r
+          match = mc[i];\r
+        }\r
+        else {\r
+          var regex = state[i][0];\r
+          regex.lastIndex = posWithinLine;\r
+          match = regex.exec(line);\r
+          mc[i] = match;\r
+        }\r
+        if (match !== null && (bestMatch === null || match.index < bestMatch.index)) {\r
+          bestMatch = match;\r
+          bestPatternIndex = i;\r
+          if (match.index === posWithinLine) {\r
+            break;\r
+          }\r
+        }\r
+      }\r
+\r
+      if (bestMatch === null) {\r
+        output(line.substring(posWithinLine), null);\r
+        break;\r
+      }\r
+      else {\r
+        // got a match\r
+        if (bestMatch.index > posWithinLine) {\r
+          output(line.substring(posWithinLine, bestMatch.index), null);\r
+        }\r
+\r
+        var pattern = state[bestPatternIndex];\r
+\r
+        var newStyle = pattern[1];\r
+        var matchedString;\r
+        if (newStyle instanceof Array) {\r
+          for (var subexpression = 0; subexpression < newStyle.length; subexpression++) {\r
+            matchedString = bestMatch[subexpression + 1];\r
+            output(matchedString, newStyle[subexpression]);\r
+          }\r
+        }\r
+        else {\r
+          matchedString = bestMatch[0];\r
+          output(matchedString, newStyle);\r
+        }\r
+\r
+        switch (pattern[2]) {\r
+        case -1:\r
+          // do nothing\r
+          break;\r
+        case -2:\r
+          // exit\r
+          patternStack.pop();\r
+          break;\r
+        case -3:\r
+          // exitall\r
+          patternStack.length = 0;\r
+          break;\r
+        default:\r
+          // this was the start of a delimited pattern or a state/environment\r
+          patternStack.push(pattern);\r
+          break;\r
+        }\r
+      }\r
+    }\r
+\r
+    // end of the line\r
+    if (currentStyle) {\r
+      tags[numTags++] = {pos: pos};\r
+      if (currentStyle === 'sh_url') {\r
+        sh_setHref(tags, numTags, inputString);\r
+      }\r
+      currentStyle = null;\r
+    }\r
+    pos = startOfNextLine;\r
+  }\r
+\r
+  return tags;\r
+}\r
+\r
+////////////////////////////////////////////////////////////////////////////////\r
+// DOM-dependent functions\r
+\r
+function sh_getClasses(element) {\r
+  var result = [];\r
+  var htmlClass = element.className;\r
+  if (htmlClass && htmlClass.length > 0) {\r
+    var htmlClasses = htmlClass.split(' ');\r
+    for (var i = 0; i < htmlClasses.length; i++) {\r
+      if (htmlClasses[i].length > 0) {\r
+        result.push(htmlClasses[i]);\r
+      }\r
+    }\r
+  }\r
+  return result;\r
+}\r
+\r
+function sh_addClass(element, name) {\r
+  var htmlClasses = sh_getClasses(element);\r
+  for (var i = 0; i < htmlClasses.length; i++) {\r
+    if (name.toLowerCase() === htmlClasses[i].toLowerCase()) {\r
+      return;\r
+    }\r
+  }\r
+  htmlClasses.push(name);\r
+  element.className = htmlClasses.join(' ');\r
+}\r
+\r
+/**\r
+Extracts the tags from an HTML DOM NodeList.\r
+@param  nodeList  a DOM NodeList\r
+@param  result  an object with text, tags and pos properties\r
+*/\r
+function sh_extractTagsFromNodeList(nodeList, result) {\r
+  var length = nodeList.length;\r
+  for (var i = 0; i < length; i++) {\r
+    var node = nodeList.item(i);\r
+    switch (node.nodeType) {\r
+    case 1:\r
+      if (node.nodeName.toLowerCase() === 'br') {\r
+        var terminator;\r
+        if (/MSIE/.test(navigator.userAgent)) {\r
+          terminator = '\r';\r
+        }\r
+        else {\r
+          terminator = '\n';\r
+        }\r
+        result.text.push(terminator);\r
+        result.pos++;\r
+      }\r
+      else {\r
+        result.tags.push({node: node.cloneNode(false), pos: result.pos});\r
+        sh_extractTagsFromNodeList(node.childNodes, result);\r
+        result.tags.push({pos: result.pos});\r
+      }\r
+      break;\r
+    case 3:\r
+    case 4:\r
+      result.text.push(node.data);\r
+      result.pos += node.length;\r
+      break;\r
+    }\r
+  }\r
+}\r
+\r
+/**\r
+Extracts the tags from the text of an HTML element. The extracted tags will be\r
+returned as an array of tag objects. See sh_highlightString for the format of\r
+the tag objects.\r
+@param  element  a DOM element\r
+@param  tags  an empty array; the extracted tag objects will be returned in it\r
+@return  the text of the element\r
+@see  sh_highlightString\r
+*/\r
+function sh_extractTags(element, tags) {\r
+  var result = {};\r
+  result.text = [];\r
+  result.tags = tags;\r
+  result.pos = 0;\r
+  sh_extractTagsFromNodeList(element.childNodes, result);\r
+  return result.text.join('');\r
+}\r
+\r
+/**\r
+Merges the original tags from an element with the tags produced by highlighting.\r
+@param  originalTags  an array containing the original tags\r
+@param  highlightTags  an array containing the highlighting tags - these must not overlap\r
+@result  an array containing the merged tags\r
+*/\r
+function sh_mergeTags(originalTags, highlightTags) {\r
+  var numOriginalTags = originalTags.length;\r
+  if (numOriginalTags === 0) {\r
+    return highlightTags;\r
+  }\r
+\r
+  var numHighlightTags = highlightTags.length;\r
+  if (numHighlightTags === 0) {\r
+    return originalTags;\r
+  }\r
+\r
+  var result = [];\r
+  var originalIndex = 0;\r
+  var highlightIndex = 0;\r
+\r
+  while (originalIndex < numOriginalTags && highlightIndex < numHighlightTags) {\r
+    var originalTag = originalTags[originalIndex];\r
+    var highlightTag = highlightTags[highlightIndex];\r
+\r
+    if (originalTag.pos <= highlightTag.pos) {\r
+      result.push(originalTag);\r
+      originalIndex++;\r
+    }\r
+    else {\r
+      result.push(highlightTag);\r
+      if (highlightTags[highlightIndex + 1].pos <= originalTag.pos) {\r
+        highlightIndex++;\r
+        result.push(highlightTags[highlightIndex]);\r
+        highlightIndex++;\r
+      }\r
+      else {\r
+        // new end tag\r
+        result.push({pos: originalTag.pos});\r
+\r
+        // new start tag\r
+        highlightTags[highlightIndex] = {node: highlightTag.node.cloneNode(false), pos: originalTag.pos};\r
+      }\r
+    }\r
+  }\r
+\r
+  while (originalIndex < numOriginalTags) {\r
+    result.push(originalTags[originalIndex]);\r
+    originalIndex++;\r
+  }\r
+\r
+  while (highlightIndex < numHighlightTags) {\r
+    result.push(highlightTags[highlightIndex]);\r
+    highlightIndex++;\r
+  }\r
+\r
+  return result;\r
+}\r
+\r
+/**\r
+Inserts tags into text.\r
+@param  tags  an array of tag objects\r
+@param  text  a string representing the text\r
+@return  a DOM DocumentFragment representing the resulting HTML\r
+*/\r
+function sh_insertTags(tags, text) {\r
+  var doc = document;\r
+\r
+  var result = document.createDocumentFragment();\r
+  var tagIndex = 0;\r
+  var numTags = tags.length;\r
+  var textPos = 0;\r
+  var textLength = text.length;\r
+  var currentNode = result;\r
+\r
+  // output one tag or text node every iteration\r
+  while (textPos < textLength || tagIndex < numTags) {\r
+    var tag;\r
+    var tagPos;\r
+    if (tagIndex < numTags) {\r
+      tag = tags[tagIndex];\r
+      tagPos = tag.pos;\r
+    }\r
+    else {\r
+      tagPos = textLength;\r
+    }\r
+\r
+    if (tagPos <= textPos) {\r
+      // output the tag\r
+      if (tag.node) {\r
+        // start tag\r
+        var newNode = tag.node;\r
+        currentNode.appendChild(newNode);\r
+        currentNode = newNode;\r
+      }\r
+      else {\r
+        // end tag\r
+        currentNode = currentNode.parentNode;\r
+      }\r
+      tagIndex++;\r
+    }\r
+    else {\r
+      // output text\r
+      currentNode.appendChild(doc.createTextNode(text.substring(textPos, tagPos)));\r
+      textPos = tagPos;\r
+    }\r
+  }\r
+\r
+  return result;\r
+}\r
+\r
+/**\r
+Highlights an element containing source code.  Upon completion of this function,\r
+the element will have been placed in the "sh_sourceCode" class.\r
+@param  element  a DOM <pre> element containing the source code to be highlighted\r
+@param  language  a language definition object\r
+*/\r
+function sh_highlightElement(element, language) {\r
+  sh_addClass(element, 'sh_sourceCode');\r
+  var originalTags = [];\r
+  var inputString = sh_extractTags(element, originalTags);\r
+  var highlightTags = sh_highlightString(inputString, language);\r
+  var tags = sh_mergeTags(originalTags, highlightTags);\r
+  var documentFragment = sh_insertTags(tags, inputString);\r
+  while (element.hasChildNodes()) {\r
+    element.removeChild(element.firstChild);\r
+  }\r
+  element.appendChild(documentFragment);\r
+}\r
+\r
+function sh_getXMLHttpRequest() {\r
+  if (window.ActiveXObject) {\r
+    return new ActiveXObject('Msxml2.XMLHTTP');\r
+  }\r
+  else if (window.XMLHttpRequest) {\r
+    return new XMLHttpRequest();\r
+  }\r
+  throw 'No XMLHttpRequest implementation available';\r
+}\r
+\r
+function sh_load(language, element, prefix, suffix) {\r
+  if (language in sh_requests) {\r
+    sh_requests[language].push(element);\r
+    return;\r
+  }\r
+  sh_requests[language] = [element];\r
+  var request = sh_getXMLHttpRequest();\r
+  var url = prefix + 'sh_' + language + suffix;\r
+  request.open('GET', url, true);\r
+  request.onreadystatechange = function () {\r
+    if (request.readyState === 4) {\r
+      try {\r
+        if (! request.status || request.status === 200) {\r
+          eval(request.responseText);\r
+          var elements = sh_requests[language];\r
+          for (var i = 0; i < elements.length; i++) {\r
+            sh_highlightElement(elements[i], sh_languages[language]);\r
+          }\r
+        }\r
+        else {\r
+          throw 'HTTP error: status ' + request.status;\r
+        }\r
+      }\r
+      finally {\r
+        request = null;\r
+      }\r
+    }\r
+  };\r
+  request.send(null);\r
+}\r
+\r
+/**\r
+Highlights all elements containing source code on the current page. Elements\r
+containing source code must be "pre" elements with a "class" attribute of\r
+"sh_LANGUAGE", where LANGUAGE is a valid language identifier; e.g., "sh_java"\r
+identifies the element as containing "java" language source code.\r
+*/\r
+function sh_highlightDocument(prefix, suffix) {\r
+  var nodeList = document.getElementsByTagName('pre');\r
+  for (var i = 0; i < nodeList.length; i++) {\r
+    var element = nodeList.item(i);\r
+    var htmlClasses = sh_getClasses(element);\r
+    for (var j = 0; j < htmlClasses.length; j++) {\r
+      var htmlClass = htmlClasses[j].toLowerCase();\r
+      if (htmlClass === 'sh_sourcecode') {\r
+        continue;\r
+      }\r
+      if (htmlClass.substr(0, 3) === 'sh_') {\r
+        var language = htmlClass.substring(3);\r
+        if (language in sh_languages) {\r
+          sh_highlightElement(element, sh_languages[language]);\r
+        }\r
+        else if (typeof(prefix) === 'string' && typeof(suffix) === 'string') {\r
+          sh_load(language, element, prefix, suffix);\r
+        }\r
+        else {\r
+          throw 'Found <pre> element with class="' + htmlClass + '", but no such language exists';\r
+        }\r
+        break;\r
+      }\r
+    }\r
+  }\r
+}\r