URL: https://linuxfr.org/users/epeios/journaux/code-natif-et-node-js-parser-et-preprocesseur-xml Title: Code natif et Node.js - parser et préprocesseur XML Authors: Claude SIMON Date: 2017年09月01日T16:31:51+02:00 License: CC By-SA Tags: nodejs, epeios, xpp, xppq, xml, parser et préprocesseur Score: 10 Histoire de diversifier mon activité, et aussi parce que j'aime bien me lancer des défis, j'ai décidé de me mettre à *[Node.js](http://fr.wikipedia.org/wiki/Node.js)*. Cependant, compte tenu de ma faible appétence pour *Javascript*, lui préférant de loin *C++*, j'ai bien entendu cherché un moyen de coder pour *Node.js* avec mon langage fétiche. Et c'est tout à fait possible, grâce aux *[addon](http://nodejs.org/api/addons.html)s*. Bon, ce n'était pas vraiment une surprise, n'ayant jusqu'à présent jamais rencontré d'environnement d'exécution logiciel qui ne puisse s'interfacer, à minima, avec du *C*. Me voici donc en train de coder mon premier *addon* pour *Node.js*, et de découvrir en passant les joies de la programmation asynchrone. Ce faisant, je me suis, comme à mon habitude, crée quelques bibliothèques pour me faciliter le développement des prochains *addons*, et j'avais acquis suffisamment d'aisance dans le domaine pour proposer avec sérénité cette nouvelle compétence à mes clients . Néanmoins, une chose me chagrinait. Par rapport à mon environnement de développement *C++* usuel, celui nécessité pour le développement d'un *addon* faisait un peu usine à gaz. Entre les fichiers `package.json` et `binding.gyp`, la commande `node(-pre)-gyp` pour générer les fichiers automatisant la génération de l'*addon* proprement dit, les dépendances aux différentes bibliothèques (*node*, *v8*, *libuv*...), je ne pouvais plus utiliser les outils que j'avais amoureusement développés pour me faciliter la gestion mes projets *C++*. J'ai donc décidé de développer un *addon* universel qui aurait pour rôle d'officier comme *wrapper* pour des bibliothèques dynamiques qui contiendraient le véritable code des mes différents *addons*. Ces bibliothèques seront totalement indépendants de *Node.js*, le *wrapper*, commun à toutes ces bibliothèques, se chargeant de faire le lien entre ces bibliothèques et *Node.js*. Pour me faire la main, j'ai développé un *addon* *Node.js* proposant un *parser* et un préprocesseur *XML*. Je l'ai placé sur [npm](http://npmjs.org/), où vous le trouverez à l'adresse [http://www.npmjs.com/package/xppq](http://www.npmjs.com/package/xppq). Comme tout paquet présent sur *npm*, vous pouvez l'installer en lançant `npm install xppq`. N'hésitez pas, tout ce petit monde est publié sous licence libre, et il y en a pour tout les goûts : *GNU/Linux* et autres systèmes *POSIX*, dont *macOS*, ainsi que *Windows*, que ce soit sur plateforme *x86* ou *ARM*. Oui, ça peut même être installé sur *Raspberry Pi* et consorts... À noter que, excepté pour *Windows*, l'installation de cet *addon* nécessite un environnement de développement *C++*. Ben oui, c'est quand même du natif ! Une fois installé, vous pouvez le tester en lançant `npm explore xppq -- node test.js`. Vous obtiendrez le résultat du *parsage* (?) d'un fichier *XML* de démonstration après passage par le préprocesseur, à savoir : ``` Start tag: 'SomeTag' Attribute: 'AnAttribute' = 'SomeAttributeValue' Start tag: 'SomeOtherTag' Attribute: 'AnotherAttribute' = 'AnotherAttributeValue' Value: 'TagValue' End tag: 'SomeOtherTag' Start tag: 'YetAnotherTag' Attribute: 'YetAnotherAttribute' = 'YetAnotherAttributeValue' Value: 'Some macro content !' End tag: 'YetAnotherTag' End tag: 'SomeTag' ``` En lançant `npm explore xppq -- node test.js 0` (notez le `0` à la fin), le contenu du fichier de démonstration s'affichera, à savoir : ```xml TagValue Some macro content ! ``` En remplaçant le `0` par un chiffre entre `1` et `4`, différents tests seront lancés, comme le *piping* du préprocesseur, ou l'application d'un *callback* sur la sortie du préprocesseur, le préprocesseur étant en fait implémenté sous forme de [stream](http://nodejs.org/api/stream.html) *Node.js*. Voici encore un aperçu du contenu du fichier [XPPq.js](http://q37.info/s/s/xppq-node/xppq.js), qui charge le *wrapper*, lui-même chargeant la bibliothèques dynamique contenant le *parser* et le préprocesseur, avec l’encapsulation permettant d'y accéder à partir de *Javascript* : ```javascript "use strict" var affix = "xppq"; var njsq = null; var componentPath = null; var componentFilename = null; var path = require("path"); njsq = require('njsq'); componentPath = __dirname; componentFilename = path.join(componentPath, affix + "njs").replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/ /g, "\\ "); njsq.register(componentFilename); module.exports = njsq; module.exports.returnArgument = (text) => { return njsq._wrapper( 0, text ) }; const stream = require('stream'); class Stream extends stream.Readable { constructor(stream, options) { super(options); stream.on('readable', () => { var chunk = stream.read(); if (chunk == null) njsq._wrapper(5, stream); else njsq._wrapper(4, stream, chunk); }); njsq._wrapper( 7, stream, this ); } _read(size) { njsq._wrapper( 6, this ); } } // If modified, modify also 'parser.cpp'. var tokens = { ERROR: 0, START_TAG: 1, ATTRIBUTE: 2, VALUE: 3, END_TAG: 4 }; module.exports = njsq; module.exports.Stream = Stream; module.exports.parse = (stream, callback) => { stream.on('readable', () => { var chunk = stream.read(); if ( chunk == null ) njsq._wrapper( 2, stream ); else njsq._wrapper(1, stream, chunk); } ); njsq._wrapper(3, stream, callback) }; module.exports.tokens = tokens; ``` Et encore le fichier [test.js](https://q37.info/s/s/xppq-node/test.js), qui montre des exemples de mise en œuvre du *parser* et du préprocesseur : ```javascript "use strict" const fs = require('fs'); const stream = require('stream'); const xppq = require('./XPPq.js'); var indentLevel = 0; function write(text) { process.stdout.write(text); } function indent(level) { while (level--) write(' '); } function callback(token, tag, attribute, value) { switch (token) { case xppq.tokens.ERROR: write(">>> ERROR: '" + value + "'\n"); break; case xppq.tokens.START_TAG: indent(indentLevel); write("Start tag: '" + tag + "'\n"); indentLevel++; break; case xppq.tokens.ATTRIBUTE: indent(indentLevel); write("Attribute: '" + attribute + "' = '" + value + "'\n"); break; case xppq.tokens.VALUE: indent(indentLevel); write("Value: '" + value.trim() + "'\n"); break; case xppq.tokens.END_TAG: indentLevel--; indent(indentLevel); write("End tag: '" + tag + "'\n"); break; default: throw ("Unknown token !!!"); break; } } const file = __dirname + '/demo.xml'; var test = 4; // Default test id. var arg = process.argv[2]; if (arg != undefined) test = Number(arg); console.log( xppq.componentInfo() ); console.log( xppq.wrapperInfo()); console.log( xppq.returnArgument('Basic test : this text comes from the addon (native code), and is written from Javascript.' ) ); console.log( ' ---------------' ); switch (test) { case 0: console.log("No treatment ; to see the original file.\n"); fs.createReadStream(file).pipe(process.stdout); break; case 1: console.log("Piping the preprocessing stream.\n"); new xppq.Stream(fs.createReadStream(file)).on('error', (err) => console.error('\n>>> ERROR : ' + err + '\n')).pipe(process.stdout); break; case 2: console.log("Using the preprocessing stream with a callback, which transforms to lower case.\n"); new xppq.Stream(fs.createReadStream(file)).on('data', (chunk) => write(chunk.toString().toLowerCase())).on('error', (err) => console.error('\n>>> ERROR : ' + err + '\n')); break; case 3: console.log("XML parsing WITHOUT preprocessing.\n"); xppq.parse(fs.createReadStream(file), callback); break; case 4: console.log("XML parsing WITH preprocessing.\n"); xppq.parse(new xppq.Stream(fs.createReadStream(file)).on('error', (err) => console.error('>>> ERROR : ' + err)), callback); break; default: console.error("'" + arg + "' is not a valid test id ; must be '0' to '4'."); break; } ``` Compte tenu de la nature *Libre* de *Node.js*, ainsi que du paquet *npm* présenté ici, celui-ci intéressera peut-être quelques développeurs *Node.js* de passage. Par ailleurs, je suis preneur de tous commentaires concernant ce paquet, notamment touchant à sa conformité aux règles de l'art (à part le fait qu'il soit codé en *C++* au lieu de *Javascript*, évidemment). Pour terminer, le traditionnel badge *npm* du paquet, donnant accès à toutes les informations le concernant : [![NPM](https://nodei.co/npm/xppq.png)](https://nodei.co/npm/xppq/)

AltStyle によって変換されたページ (->オリジナル) /