(* This CDuce script produces CDuce web site. *) (** Command line **) let (input, php) = match argv with | [ "-php" s ] -> (s, `true) | [ s ] -> (s, `false) | _ -> raise "Please specify an input file on the command line";; (** Output types **) include "xhtml-strict.cd";; (* XHTML 1 Strict DTD *) include "xhtml-categ.cd";; (* Categories (Inline, ...) from this DTD *) (** Input types **) type Page = [ String <banner>[InlineText*] Item* ];; type External = <external {|href=String; title=String; name=String |}>[];; type Item = <box title=String; subtitle=?String; link=String>Content | <meta>Content | <left>Content | Page | External;; type Author = <author>String;; type Paper = <paper file=?String>[ <title>String Author+ <comment>[InlineText*] <abstract>Content ];; type Slides = <slides file=String>[ <title>String Author+ <comment>[InlineText*] ];; type Link = <link url=String; title=String>[ InlineText* ];; type Content = [ ( <p {||}>[InlineText*] | <ul {||}>[<li {||}>Content +] | <section title=String>Content | <sample highlight=?"true"|"false">String | Xtable | Paper | Slides | Link | <boxes-toc>[] | <pages-toc>[] | <site-toc>[] | <local-links href=String>[] | <two-columns>[ <left>Content <right>Content ] | InlineText )* ];; type InlineText = Char | <(`b|`i|`tt|`em) {||}>[InlineText*] | <code>String | <local href=String>String | Xa | Ximg | Xbr ;; (** Generic purpose functions **) (* Recursive inclusion of XML files and verbatim text files *) let fun load_include (String -> [Any*]) name -> (* let _ = print [ 'Loading ' !name '... \n' ] in *) xtransform [ (load_xml name) ] with | <include file=(s & String)>[] -> load_include s | <include-verbatim file=(s & String)>[] -> load_file s;; (* Highlighting text between {{...}} *) let fun highlight (String -> [ (Char | Xvar | Xi)* ] ) | [ '{{' h ::(Char *?) '}}' ; rest ] -> [ <var class="highlight">h; highlight rest ] | [ '{/{' h ::(Char *?) '}}' ; rest ] -> [ <i>h; highlight rest ] | [ c; rest ] -> [ c; highlight rest ] | [] -> [];; (* Split a comma-separated string *) let fun split_comma (String -> [String*]) | [ x::(Char*?) ',' ; rest ] -> (x, split_comma rest) | s -> [ s ];; (** Ugly hack to introduce PHP code ... The idea is to produce first an XML document with a distinguished element. The function patch_css search for the textual representation of this element and replace it with the PHP code. **) let css : String = ['<link rel="stylesheet" href="cduce.css" type="text/css">'];; let fun protect_quote (s : String) : String = transform s with '"' -> [ '\\"' ] | c -> [c];; let php_css : String = if php then [' <?php $browser = getenv("HTTP_USER_AGENT"); if (preg_match("/Mozilla/i", "$browser") && !preg_match("/Mozilla\\/5.0/i", "$browser")) { echo "<blink>For better presentation use a more recent version of your browser, like Netscape 6</blink>"; } else { echo "' !(protect_quote css) '"; } ?> '] else css;; let fun patch_css (String -> String) | [ a::_*? '<meta content="css"/>'; rem ] -> a @ php_css @ rem | s -> s;; (** Internal types **) type Path = [ { url = String; title = String }* ];; type Tree = { name = String; url = String; title = String; children = [Tree*] } ;; let fun url_of_name (String -> String) "index" -> "/" | s -> s @ ".html";; let fun authors ([Author+] -> String) | [ <author>a ] -> a | [ <author>a1 <author>a2 ] -> a1 @ ", and " @ a2 | [ <author>a; rem ] -> a @ ", " @ authors rem;; let fun find_local_link (sitemap : [Tree*], l : String) : Tree = match sitemap with | (h,t) -> if (h . name = l) then h else (try find_local_link (t,l) with `Not_found -> find_local_link (h . children,l)) | [] -> raise `Not_found;; let fun local_link (sitemap : Tree, l : String, txt : String) : Inline = try let h = find_local_link ([sitemap],l) in let txt = if txt = "" then h . title else txt in <a href=(h . url)>txt with `Not_found -> raise [ 'Local link not found: ' !l ];; let fun compute_sitemap ((Page|External) -> Tree) <page name=name>[ <title>title (c::(Page|External) | _)* ] -> let children = map c with p -> compute_sitemap p in { name = name; url = url_of_name name; title = title; children =children } |<external name=name; href=h; title=t>[] -> { name = name; url = h; title = t; children = [] };; let fun display_sitemap (h : Tree) : Xli = let ch = map h . children with x -> display_sitemap x in let ch = match ch with [] -> [] | l -> [ <ul>l ] in <li>[ <a href=(h . url)>(h . title); ch ];; let fun link_to (Page -> Xa) <page name=n>[<title>t ; _ ] -> <a href=(url_of_name n)>t;; (* Main transformation function *) let fun gen_page (prev : Page|[], page : Page, next : Page|[], path : Path, sitemap : Tree) : [] = match page with <page name=name>[ <title>title <banner>banner ; items ] -> let fun text (t : [InlineText*]) : Inlines = map t with | <code>x -> <b>[ <tt>(highlight x) ] | <local href=l>txt -> local_link (sitemap,l,txt) | <(tag & (`b|`i|`tt|`em))>x -> <(tag)>(text x) (* | <a href=url>_ & z -> let [] = print [ 'Link: ' !url '\n'] in z *) | z -> z in let fun content (t : Content) : Flow = transform t with | <section title=title>c -> [ <h4>title !(content c) ] | <paper (r)>[ <title>tit aut::Author* <comment>com <abstract>ab ] -> [ (match r with { file = f } -> <a href=f>tit | _ -> <b>tit) '. ' !(authors aut) '. ' !(text com) <div class="abstract">[ 'Abstract:' !(content ab) ] ] | <slides file=f>[ <title>tit aut::Author* <comment>com ] -> [ <a href=f>tit '. ' !(authors aut) '. ' !(text com) ] | <sample highlight="false">s -> [ <div class="code">[ <pre>s ] ] | <sample>s -> [ <div class="code">[ <pre>(highlight s) ] ] | <link url=url; title=title>com -> [ <a href=url>title '. ' !(text com) ] | <ul>lis -> [ <ul>(map lis with <li>x -> <li>(content x)) ] | Xtable & x -> [ x ] | <p>x -> [ <p>(text x) ] | <pages-toc>[] -> let toc = transform items with | Page & p -> [ <li>[ (link_to p) ] ] | <external href=l; title=t>[] -> [ <li>[ <a href=l>t ] ] in (match toc with [] -> [] | lis -> [ <ul>lis ]) | <boxes-toc>[] -> let toc = transform items with <box title=t; link=l>_ -> [ <li>[ <a href=('#',l)>t ] ] in (match toc with [] -> [] | lis -> [ <ul>lis ]) | <site-toc>[] -> [ <ul>[ (display_sitemap sitemap) ] ] | <local-links href=s>[] -> (match (split_comma s) with | [] -> [] | l -> let l = map l with x -> <li>[ (local_link(sitemap,x,"")) ] in [ <ul>l ]) | <two-columns>[ <left>x <right>y ] -> [ <table width="100%">[ <tr>[ <td valign="top">(content x) <td valign="top">(content y) ] ] ] | t -> text [ t ] in let main : Flow = transform items with | <box (r)>c -> [ <div class="box">[ <h2>(r . title) !(match r with { subtitle = t } -> [<b>t] | _ -> []) <a name=r . link>[] !(content c) ] ] | <meta>c -> [ <div class="meta">(content c) ] in let navig : Flow = transform items with | <left>c -> [<div class="box">(content c)] in let dpath : Inlines = transform path with | { url = f; title = t } -> [ <a href=f>t ' :: '] in let npath = path @ [ { url = url_of_name name; title = title } ] in let subpages = transform items with p & Page -> [ p ] in let next = gen_page_seq (page, subpages, next, npath, sitemap) in let next = match next with [] -> [] | p -> [' Next : ' (link_to p)] in let prev = match prev with [] -> [] | p -> [' Prev : ' (link_to p)] in let html : Xhtml = <html>[ <head>[ <title>[ 'CDuce: ' !title ] <meta content="text/html; charset=iso-8859-1"; http-equiv="Content-Type">[] <meta content="css">[] (* Placeholder for PHP code *) ] <body>[ <div class="title">[ <h1>(text banner) <p>[ <b>"You're here: " !dpath !title ] <p>[ !prev !next ] ] <div id="Sidelog">navig <div id="Content">main ] ] in let txt : String = [ '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"' ' "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">' !(patch_css (print_xml html)) ] in dump_to_file (name @ (if php then ".html.php" else ".html")) txt;; let fun gen_page_seq (prev : Page|[], pages : [Page*], next : Page|[], path : Path, sitemap : Tree) : Page|[] = match pages with | [ p1 p2 ; _ ] & [ _; rest ] -> let [] = gen_page (prev,p1,p2, path, sitemap) in let _ = gen_page_seq (p1, rest, next, path, sitemap) in p1 | [ p ] -> let [] = gen_page (prev,p,next, path, sitemap) in p | [] -> next;; (* Entry point *) match load_include input with | [ Page & p ] -> gen_page ([],p,[], [], compute_sitemap p) | _ -> raise ("Invalid input document " @ input);;