/* Copyright (C) 2004 Cédric Lemaire

This script is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

To contact the author: codeworker@free.fr
*/


/**[description]
[title]Ecriture de documents en [i]CWcode[/i][line/]Manuel d'utilisation[/title]

[docinfo,author="Cédric Lemaire",creation="23sep2004",keywords="CWcode,mise en forme de documents,document formate,conversion de documents en HTML"/]
[synopsis]Ce manuel précise comment écrire un document en [i]CWcode[/i]. Le [i]CWcode[/i] s'apparente au [i]BBcode[/i], à ceci
près qu'il se destine à la mise en forme de documents, plutôt qu'à l'écriture dans des forums. A l'heure actuelle, seule la
production de documents HTML selon le style de [url=http://www.developpez.com]Developpez.com[/url] est supportée.[/synopsis]

[chapter]Pourquoi écrire un document en [i]CWcode[/i] ?[/chapter]

Le [i]CWcode[/i] permet d'écrire un document dans un éditeur de texte classique, indépendamment de son aspect visuel. Il s'agit
principalement de taper du contenu, avant de se concentrer sur la mise en forme. A partir d'un document écrit en [i]CWcode[/i],
on veut pouvoir réaliser facilement une nouvelle déclinaison visuelle du contenu. Le plus facile est de réaliser une
déclinaison en [i]HTML[/i].

Pour l'instant, le [i]CWcode[/i] est livré avec une conversion en [i]HTML[/i] dont le rendu est semblable à celui proposé par
[url=http://www.developpez.com]Developpez.com[/url]. Il est possible d'adapter un convertisseur à la charte graphique de
n'importe quel site Web. Les convertisseurs se construisent dans le langage de script CodeWorker, un outil de parsing et un
générateur de code, disponible sous licence [i]LGPL[/i] à l'adresse [url]http://www.codeworker.org[/url].

La souplesse d'adaptation du rendu visuel n'est pas le seul critère qui peut pousser à utiliser le [i]CWcode[/i]. Ce dernier
se prête bien à l'écriture de documentations informatique ou de tutoriels, incluant des échantillons de code notamment. Il
offre la possibilité d'exécuter les codes sources qu'il contient, et d'inclure les résultats de ces exécutions. Vous
garantissez ainsi à la fois que le code source a une syntaxe correcte et fonctionne, mais en plus que le résultat coïncide
bien avec ce que le code est sensé faire (voir le chapitre consacré à l'[reference=insertion_de_code_source]insertion de code source[/reference]).

Ce document a été entièrement réalisé en [i]CWcode[/i]. Vous lisez ici une représentation visuelle possible. On pourrait en
inventer une infinité, dans des formats autres que HTML.

[chapter]La syntaxe du [i]CWcode[/i][/chapter]

Le terme [i]CWcode[/i] est un clin d'oeil au [i]BBcode[/i], ce langage de mise en forme du texte, très répandu sur les forums des sites
Web. Son principe est le même : on utilise des balises pour appliquer des caractéristiques au texte : nouveau chapître, passage
en italique, présentation de code source.

Une balise terminale marque la fin du champ d'application de la balise initiale dans le texte. Une balise initiale est un
mot-clé placé entre crochets. Une balise terminale ressemble à une balise initiale, si ce n'est que le mot-clé est précédé
du symbole [b]/[/b]. Parfois, des options sont spécifiées à l'intérieur d'une balise terminale. Elles se présentent sous la
forme d'un ensemble de propriétés séparées par des virgules. Une propriété est un nom suivi d'une valeur, les deux séparés
par une virgule. Une valeur peut être placée entre guillemets.

Exemples de balises :
[list]
[*] [code]Cette phrase a [b][[/b][b]i][/b]quatre mots en italique[b][[/b][b]/i][/b], pas un de plus.[/code]
[*] [code][b][[/b][b]code,save="test.cpp"][/b]vitesse = 45;[b][[/b][b]/code][/b][/code]
[*] [code][b][[/b][b]chapter=force_coriolis][/b]La force de Coriolis[b][[/b][b]/chapter][/b][/code]
[/list]

Une ligne vide est comptée comme un saut de paragraphe. En revanche, plusieurs lignes vides consécutives sont comptées pour un
seul saut de paragraphe. Un saut de ligne n'a pas d'effet, à moins qu'il ne soit placé derrière un point, point
d'interrogation, point d'exclamation ou deux-points.
[/description]
 **/


// on récupère la grammaire du CWcode : toutes les règles de production
// ne sont pas à redéfinir
#include "CWcode.cwp"

// recopie implicite du document CWcode, avec transformation des caractères
// spéciaux du HTML dans le fichier cible
#implicitCopy(composeHTMLLikeString)

// Elimine toutes les balises contenues dans le texte, et retourne le
// texte brut correspondant
function removeLayout(sText) {
    return translateString({
            #implicitCopy
            remove ::=
                [
                    ->[
                        #explicitCopy
                        '[' ->(:sText)']'
                        => if sText == "line/" {@; @}
                    ]

                ]*
                ->#empty
                ;
        }, project, sText);
}

// Retourne le numéro de la section en cours, construite à partir
// du numéro de section précédent. Utile pour numéroter la structure
// du document (chapitres, sections, sous-sections).
function incrementSection(sNumber : value) {
    local iIndex = $sNumber.findLastString('.') + 1$;
    return sNumber.leftString(iIndex) + $sNumber.subString(iIndex) + 1$;
}

// Prend du texte (du code source à inclure, bien souvent), et transforme
// les retour-chariots, espaces et tabulations en équivalents HTML
// (respectivement : '<br/>', '&nbsp;', répétition de '&nbsp;' modulo 4).
function formatCode(sCode : value) {
    return translateString({
            #implicitCopy
            text_code ::=
                    [
                            ['\r']? '\n' => {@<br/>@}
                        |
                            #explicitCopy ' ' => {@&nbsp;@}
                        |
                            #explicitCopy '\t'
                            => {
                                local iPosition = $countInputCols() - 1$;
                                do {
                                    @&nbsp;@
                                    increment(iPosition);
                                } while $(iPosition % 4) != 0$;
                            }
                        |
                            #readChar    
                    ]*
                    ;
        }, project, sCode);
}

//--------------------------------------------------------------------------
//        Coloration syntaxique en fonction du type de code source
//--------------------------------------------------------------------------

// Code source non typé : simple mise en forme du texte.
function highlightScript<"">(sScript : value) {
    return formatCode(composeHTMLLikeString(sScript));
}

// Coloration syntaxique d'un script CodeWorker, dont le sous-type
// a déjà été précisé (.cwp, .cws, .cwt ou sans sous-type).
function highlightCWScript(script : node, sScript : value) {
    insert script.embeddedScript = true;
    return translateString("CWScript2HTML.cwp", script, sScript);
}

// Coloration syntaxique d'un script dont on ne connaît pas le
// sous-type.
function highlightScript<"CodeWorker">(sScript : value) {
    local script;
    return highlightCWScript(script, sScript);
}

// Coloration syntaxique d'un script CodeWorker standard.
function highlightScript<"CodeWorker.cws">(sScript : value) {
    local script = "procedural";
    return highlightCWScript(script, sScript);
}

// Coloration syntaxique d'un script CodeWorker 'modèle de génération'.
function highlightScript<"CodeWorker.cwt">(sScript : value) {
    local script = "template-based";
    return highlightCWScript(script, sScript);
}

// Coloration syntaxique d'un script CodeWorker de parsing.
function highlightScript<"CodeWorker.cwp">(sScript : value) {
    local script = "extended-BNF";
    return highlightCWScript(script, sScript);
}

// Coloration syntaxique des mot-clés du C.
function keyword<"C">(sId : value) {
    return sId in    {"auto""register""static""extern""typedef""void""char""short""int""long""float",
                     "double""signed""unsigned""struct""union""const""volatile""sizeof""enum""case",
                     "default""switch""if""else""while""do""for""goto""continue""break""return"};
}

// Coloration syntaxique des mot-clés du C++.
function keyword<"C++">(sId : value) {
    return keyword<"C">(sId) || sId in {"virtual""class""public""protected""private""throw""template""try""catch"};
}

// Coloration syntaxique des mot-clés du Java.
function keyword<"Java">(sId : value) {
    return sId in    {"package""import""class""interface""public""protected""private""abstract""static""void""byte",
                     "extends""implements""char""short""int""long""float""double""case""default""switch""if",
                     "else""while""do""for""goto""continue""break""return""throw""throws""try""catch""finally""final"};
}

// Coloration syntaxique des mot-clés de l'IDL.
function keyword<"IDL">(sId : value) {
    return sId in    {"abstract""any""attribute""boolean""case""char""component""const""consults",
                     "context""custom""default""double""emits""enum""eventtype""exception""factory",
                     "FALSE""finder""fixed""float""getraises""home""import""in""inout""interface",
                     "local""long""manages""module""multiple""native""Object""octet""oneway""out",
                     "primarykey""private""provides""public""publishes""raises""readonly""sequence",
                     "setraises""short""string""struct""supports""switch""TRUE""truncatable""typedef",
                     "typeid""typeprefix""union""unsigned""uses""ValueBase""valuetype""wchar""wstring"};
}

// Coloration syntaxique d'un code source XML.
// On réalise un parseur XML simplifié.
function highlightScript<"XML">(sScript : value) {
    return translateString({
        #implicitCopy(composeHTMLLikeString)
        xml2HTML ::=
            [
                ->[
                    => local iPosition = getOutputLocation();
                    [
                            '<'
                            #ignore(blanks)
                            tag
                            [attribute]*
                            ['/']?
                            '>'
                            => {
                                insertText(iPosition, "<span class=\"xml_balise\">");
                                @</span>@
                            }
                        |
                            '<' '/'
                            #ignore(blanks)
                            tag
                            '>'
                            => {
                                insertText(iPosition, "<span class=\"xml_balise\">");
                                @</span>@
                            }
                        |
                            "<!--" [whitespace | ~"-->"]* "-->"
                            => {
                                insertText(iPosition, "<span class=\"xml_comment\">");
                                @</span>@
                            }
                        |
                            whitespace
                    ]
                ]
            ]*
            ;
        tag ::=
            => local iPosition = getOutputLocation();
            #readIdentifier #!ignore ['.' | ':' | #readIdentifier]*
            => {
                insertText(iPosition, "<span class=\"php_type\">");
                @</span>@
            }
            ;
        attribute ::= tag ['=' #continue #skipIgnore(blanksvalue]?;
        value ::=
            => local iPosition = getOutputLocation();
            [
                    '"' ->'"'
                |
                    "'" ->"'"
                |
                    [~['>' | whitespace]]*
            ]
            => {
                insertText(iPosition, "<span class=\"php_ch\">");
                @</span>@
            }
            ;
        whitespace ::=
                    ['\r']? '\n' => {@<br/>@}
                |
                    #explicitCopy ' ' => {@&nbsp;@}
                |
                    #explicitCopy '\t'
                    => {
                        local iPosition = $countInputCols() - 1$;
                        do {
                            @&nbsp;@
                            increment(iPosition);
                        } while $(iPosition % 4) != 0$;
                    }
                ;
    }, project, sScript);
}

// Coloration syntaxique du HTML. On reprend le parseur XML précédent.
function highlightScript<"HTML">(sScript : value) {
    return highlightScript<"XML">(sScript);
}

// Coloration syntaxique d'un code source C, C++, Java, IDL. Se contente
// de détecter les mot-clés du langage et de les colorer.
function highlightScript<T>(sScript : value) {
    return translateString({
        #implicitCopy(composeHTMLLikeString)
        cpp2HTML ::=
                [
                    => local iPosition  = getOutputLocation();
                    [
                            '#'
                            #readIdentifier
                            => {
                                insertText(iPosition, "<span class=\"cpp_define\">");
                                @</span>@
                            }
                        |
                            #readIdentifier:sId
                            [
                                #check(keyword<this>(sId))
                                => {
                                    insertText(iPosition, "<span class=\"csharp_type\">");
                                    @</span>@
                                }
                            ]?

                        |
                            #readCString
                            => {
                                insertText(iPosition, "<span class=\"csharp_ch\">");
                                @</span>@
                            }
                        |
                            #readCChar
                        |
                            "//" [~[['\r']? '\n']]*
                            => {
                                insertText(iPosition, "<span class=\"cpp_comment\">");
                                @</span>@
                            }
                        |
                            "/*" [whitespace | ~"*/"]* "*/"
                            => {
                                insertText(iPosition, "<span class=\"cpp_comment\">");
                                @</span>@
                            }
                        |
                            whitespace
                        |
                            #readChar    
                    ]
                ]*
                ;
        whitespace ::=
                    ['\r']? '\n' => {@<br/>@}
                |
                    #explicitCopy ' ' => {@&nbsp;@}
                |
                    #explicitCopy '\t'
                    => {
                        local iPosition = $countInputCols() - 1$;
                        do {
                            @&nbsp;@
                            increment(iPosition);
                        } while $(iPosition % 4) != 0$;
                    }
                ;
    }, T, sScript);
}

// Si un nom de fichier du CWcode contient un $ suivi d'un identifiant,
// il s'agit de substituer ce brin par la valeur de la variable
// d'environnement correspondante.
function normalizeFilename(sFilename : value) {
    return translateString({
                #implicitCopy
                normalize ::=
                        [
                                ->[
                                    #explicitCopy
                                    '$'
                                    #readIdentifier:sVar
                                    => {@@getEnv(sVar)@@}
                                ]

                        ]*
                        ->#empty
                        ;
            }, project, sFilename);
}

// En théorie, toute balise hérite des attributs suivants, à exécuter avant
// son action.
function preexecuteOptions(options : node) {
    foreach i in options {
        if i.key() == "reference" {
            @<a name="@i@"></a>@
        }
    }
}

// En théorie, toute balise hérite des attributs suivants, à exécuter après
// son action.
function postexecuteOption(name : value, option : node) {
    if name == "reference" return true;
    return false;
}

// Règle principale. On redéfinit la règle de tête déclarée au tout début,
// par l'inclusion de la grammaire.
#overload mainCWcode ::=
        => {
            @<html>
  <head>
    <title>@
newFloatingLocation("title");@</title>
    <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
    <meta name="generator" content="developpez-com" />
    <meta name="description" content=@
newFloatingLocation("description");@ />
    <meta name="keywords" content=@
newFloatingLocation("keywords");@ /> 
    <link rel="stylesheet" type="text/css" href="http://www.developpez.com/mainstyle2.css" />
    <link rel="stylesheet" type="text/css" media="print" href="http://www.developpez.com/template/printer.css" />
    <link rel="stylesheet" type="text/css" media="screen" href="./article.css">
    <link rel="stylesheet" type="text/css" href="./CodeWorker.css">
  </head>
  <body>
    <table cellspacing="1" width="100%" class="tbnoir"><tr><td class="fondart"><table class="cadrearticle" width="90%" cellpadding="10" cellspacing="0">
      <tr>
        <td> 
@

        }
        #super::mainCWcode
        => {
            if existFloatingLocation("TOC"falseinsertText(getFloatingLocation("TOC"), "<p/><br/>");
            @        </td>
      </tr>
    </table></td></tr></table>
@

            if this.docinfo.author {
                local iYear = formatDate(getNow(), "%Y");
                if this.docinfo.creation && this.docinfo.creation.formatDate("%Y") != iYear {
                    iYear = this.docinfo.creation.formatDate("%Y") + '-' + iYear;
                }
                @<br><table class="noteBasPage"></table><hr><div class="licence">
        &copy; @
iYear@ @composeHTMLLikeString(this.docinfo.author)@ - Tous droits r&eacute;serv&eacute;s : @composeHTMLLikeString(this.docinfo.author)@. Toute reproduction,
        utilisation ou diffusion de ce document par quelque moyen que ce soit autre que pour
        un usage personnel doit faire l'objet d'une autorisation &eacute;crite pr&eacute;alable de la part
        de : @
composeHTMLLikeString(this.docinfo.author)@ , le propri&eacute;taire des droits intellectuels.
    </div>
@

            }
@  </body>
</html>
@

        }
        ;

// Traitement du fin de ligne :
//   - juste après une ponctuation telle que '.' | ':' | '!' | '?' -> saut de ligne
//   - plusieurs sauts de ligne d'affilée -> compte pour un seul
#overload EOL ::=
        ['.' | ':' | '!' | '?']
        ['\r']? '\n'
        [
                #explicitCopy [['\r']? '\n']+
                => {@<p/>@}
            |
                => {@<br/>@}
        ]

    |
        ['\r']? '\n'
        [
            #explicitCopy [['\r']? '\n']+
            => {@<p/>@}
        ]?

        ;

// Lecture générique d'une balise CWcode, et appel de la bonne implémentation
// de règle de production 'CWcode<balise>'.
#overload readCode<T> ::=
        '[' #readIdentifier:sCode
        #check(!T || (sCode == T))
        => local options = $getOutputLocation() - sCode.length() - 1$;
        [
            => local sValue;
            [
                '='
                readValue:sValue
                => insert options[sCode] = sValue;
            ]?

            [
                ','
                #readIdentifier:sKey
                '='
                readValue:sValue
                => insert options[sKey] = sValue;
            ]*

        ]?
        [
                "/]"
                CWcode<sCode + '/'>(options)
            |
                ']'
                CWcode<sCode>(options)
        ]
        ;

// Les balises doivent traiter elles-même la détection de leur balise terminale.
// La balise terminale n'est simplement pas recopiée dans le fichier cible.
#overload endCode<T> ::= #explicitCopy "[/" #readText(T) ']';

/**[description]
[chapter]Les balises de structuration du document[/chapter]

Les documents possèdent un titre, et se conforment à une structure hiérarchique : synopsis, chapîtres, sections et
sous-sections. Ils disposent éventuellement d'informations générales sur l'auteur et sur l'historique des mises à jour.

[section]Le titre[/section]

Le titre du document se place dans la balise [code][b][[/b][b]title][/b][/code].

Exemple :
[list]
[*] [code][b][[/b][b]titre][/b]La programmation générative : perspectives[b][/[/b][b]titre][/b][/code]
[/list]
[/description]
 **/

#overload CWcode<"title">(options : node::=
        #continue
        removeCodeBegin(options)
        =>{@<h1>@}
        text:sText
        =>{@</h1>@}
        => insertText(getFloatingLocation("title"), removeLayout(sText));
        endCode<"title">
        ;

/**[description]
[section]Les informations sur le document[/section]

Les informations sur le document sont placées dans la balise [code][b][[/b][b]docinfo][/b][/code].

[table]
[header][cell=2]Options[/cell][/header]
[row][cell][code][b]author[/b][/code][/cell][cell]Nom et prénom de l'auteur.[/cell][/row]
[row][cell][code][b]creation[/b][/code][/cell][cell]Date de création du document.[/cell][/row]
[row][cell][code][b]keywords[/b][/code][/cell][cell]Liste de mot-clés, séparés par une virgule.[/cell][/row]
[row][cell][code][b]profileId[/b][/code][/cell][cell]ID du profil de l'auteur chez [url=http://www.developpez.com]Developpez.com[/url].[/cell][/row]
[row][cell][code][b]profileName[/b][/code][/cell][cell]Abréviation du nom de l'auteur chez [url=http://www.developpez.com]Developpez.com[/url].[/cell][/row]
[/table]

Les mot-clés sont injectés directement dans les méta-informations de l'entête du fichier HTML cible.

[/description]
 **/

#overload CWcode<"docinfo/">(options : node::=
        removeCodeBegin(options)
        => {
            foreach i in options {
                switch(i.key()) {
                    case "author":
                        insert this.docinfo.author = i;
                        break;
                    case "creation":
                        insert this.docinfo.creation = i;
                        break;
                    case "profileId":
                        insert this.docinfo.author.profileId = i;
                        break;
                    case "profileName":
                        insert this.docinfo.author.profileName = i;
                        break;
                    case "keywords":
                        insert this.docinfo.keywords = i;
                        break;
                }
            }
            if this.docinfo.creation {
                @<p class="dateArticle">Date de publication : @this.docinfo.creation.formatDate("%d/%m/%Y")@</p>
@

            }
            @<p class="dateArticle">Date de mise a jour : @formatDate(getNow(), "%d/%m/%Y")@</p>
@

            if this.docinfo.author {
                @<p align="center">Par @
                if this.docinfo.author.profileId {
                    @<a class="auteur" href="http://www.developpez.net/forums/profile.php?mode=viewprofile&amp;u=@this.docinfo.author.profileId@">@
                    if this.docinfo.author.profileName {
                        @@this.docinfo.author.profileName@@
                    } else {
                        @@this.docinfo.author@@
                    }
                    @</a>@
                } else {
                    @@this.docinfo.author@@
                }
                @<br>&nbsp;</p>
@

            }
            if this.docinfo.keywords {
                insertTextToFloatingLocation("keywords""\"" + this.docinfo.keywords + "\"");
            }
        }
        ;

/**[description]
[section]Le synopsis[/section]

Le synopsis est annoncé par la balise [code][b][[/b][b]synopsis][/b][/code]. Dans la structure du document, elle se place
avant l'écriture des chapîtres. Le synopsis est injecté directement dans le champ 'description' des méta-informations
de l'entête du fichier HTML cible.

Exemple :
[list]
[*] [code][b][[/b][b]synopsis][/b]Cet article traite du ramassage des feuilles à l'approche de la mousson.[b][[/b][b]/synopsis][/b][/code]
[/list]
[/description]
 **/

#overload CWcode<"synopsis">(options : node::=
        removeCodeBegin(options)
        => {@<p class="synopsis">@}
        #super::CWcode<"synopsis">(options):sText
        => sText = removeLayout(sText);
        => sText = sText.replaceString('\r'"");
        => sText = sText.replaceString('\n'" ");
        => insertTextToFloatingLocation("description""\"" + sText + "\"");
        => {@</p>@}
        ;

/**[description]
[section]L'introduction[/section]

La balise [code][b][[/b][b]introduction][/b][/code] sert à annoncer l'introduction, mais aussi la préface ou l'avant-propos.

[table]
[header][cell=2]Options[/cell][/header]
[row][cell][code][b]introduction[/b][/code][/cell][cell]Nom à utiliser par la balise [reference=reference][code][b][[/b][b]reference][/b][/code][/reference] pour pointer ici.[