+ BONUS: inclusion mutuelle des classes les unes dans les autres en C ++
Bonjour, Habr! Cet article est une continuation directe de l'article The
Art of Parsing ou DOM de nos propres mains , oĂą nous avons analysĂ© un document HTML et construit sur sa base un arbre de syntaxe abstraite (AST) avec accès Ă n'importe quel Ă©lĂ©ment grâce Ă l'indexation en utilisant uniquement la bibliothèque C ++ standard, en d'autres termes, nous avons appris Ă
analyser par nous- mĂŞmes Des trucs comme XML. Permettez-moi de vous rappeler que le processus d'analyse, ou
analyse / analyse, se compose de deux étapes:
l'analyse lexicale (analyse du texte en jetons) et la construction d'un AST. Si nous avons examiné le premier en détail, avec des exemples et des codes source, la description du second ressemble à une poupée papillon vide, qui n'a qu'une coquille, et l'auteur a extrait un excellent contenu avant sa publication. Il y avait une raison, pour HTML, il est vraiment facile de construire une arborescence, vous n'avez besoin que de 4 classes: une balise vide, un bloc, un nœud de texte et la racine du document héritée du bloc. Aujourd'hui, nous laisserons une telle simplicité derrière et construirons un arbre où les propriétés des éléments, à la fois vides et bloqués, ne seront pas contenues dans les attributs des balises, mais directement dans les classes, et pour cela, vous devrez créer beaucoup de classes. Vraiment beaucoup. Nous ne construirons pas à partir de langages de balisage simples et bien connus, mais créerons le nôtre, avec les règles indiquées dans l'image sous la coupe. De plus, à la fin, nous allons traduire, ou, plus correctement,
traduire le document avec l'article précédent, balisé dans notre langue, en HTML, et en prime, je répondrai aux programmeurs C ++ novices à une question triviale mais difficile à trouver: comment incorporer des classes les unes aux autres?

Notes de grammaire
Avant de passer directement à la construction d'un arbre, rafraîchissons notre mémoire et clarifions certains détails du travail préliminaire. Vous souvenez-vous encore que toute la syntaxe du langage doit être écrite sous la forme d'une des grammaires formelles sans contexte, par exemple BNF? Mais il est difficile pour les programmeurs débutants de les maîtriser immédiatement, et d'ailleurs, toutes les règles possibles avec ces grammaires ne peuvent pas être décrites. Dans de tels cas, si vous êtes dans une impasse et ne formulez pas une certaine règle sous la forme correcte, vous pouvez l'écrire sous forme de remarques en langage humain naturel, par exemple, comme ceci:
... <ordered_list_item> = <number_marker> <div> <number_marker> = <number> "." {<number> "."} " " <number> = <digit> {<digit>} !! link ending ">" and image/span ending "]" can't follow "\n" or document start
Traduction: la
fin du lien ">" et l'élément image / inline "]" ne peuvent pas suivre immédiatement le début d'une ligne ou d'un document .
Autrement dit, si au début de la ligne le lexer rencontre "]" ou ">", nous devons lui demander d'ignorer la signification spéciale de ces caractères et de travailler avec eux comme du texte brut. Cette façon d'ajouter des commentaires à la grammaire n'est pas la seule; vous pouvez le faire à votre façon. En fin de compte, le fichier avec la description de la syntaxe n'est pas un papier à termes, personne ne vous oblige à suivre toutes les règles et il est seulement important qu'il vous soit pratique de travailler avec. L'essentiel est de ne pas oublier les commentaires faits et de les refléter dans les bonnes sections du code.
Regardons une description complète de cette langue:
<article> = {<article_item>} <article_item> = <underline> | <section> (* ARTICLE ITEMS *) <underline> = "___" {"_"} "\n" <section> = <div> {<div>} <div> = <paragraphs> | <title> | <quote> | <cite> | <unordered_list> | <ordered_list> (* SECTION ITEMS *) <paragraphs> = <paragraph> {"\n" <paragraph>} <paragraph> = <span> {<span>} ("\n" | <END>) <span> = <bold> | <italic> | <underlined> | <overlined> | <throwlined> | <subscript> | <superscript> | <marked> | <monospace> | <text> | <image> | <link> | <notification> <title> = <number signs> <left_angle_bracket> {<span>} <right_angle_bracket> ("\n" | <END>) <number signs> "######" | "#####" | "####" | "###" | "##" | "#" <quote> = "> " {<span>} ("\n" | <END>) <cite> = ">>" <number> ("\n" | <END>) <number> = <digit> {<digit>} (* PARAGRAPH ITEMS *) <bold> = "*[" {<span>} "]" <italic> = "%[" {<span>} "]" <underlined> = "_[" {<span>} "]" <overlined> = "^[" {<span>} "]" <throwlined> = "~[" {<span>} "]" <subscript> = "&[" {<span>} "]" <superscript> = "+[" {<span>} "]" <marked> = "=[" {<span>} "]" <monospace> = "`[" {<span>} "]" <text> = <textline> "\n" {<textline> "\n"} <textline> = <symbol> {<symbol>} <symbol> = /^[\n]/ <link> = "<<" <text> ">" {<span>} ">" <image> = "[<" <text> ">" [<text>] "]" <notification> = (" " | "\n") "@" <word> (" " | "\n" | <END>) <word> = (<letter> | <digit>) {<letter> | <digit>} <letter> = "a" | "b" | "c" | "d" | ... | "_" | "-" <digit> = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" (* LISTS *) <unordered_list> = <unordered_list_item> {<unordered_list_item>} <ordered_list> = <ordered_list_item> {<ordered_list_item>} <unordered_list_item> = <marker> <div> <marker> = ("*" {"*"}) | ("+" {"+"}) " " <ordered_list_item> = <number_marker> <div> <number_marker> = <number> "." {<number> "."} " " <number> = <digit> {<digit>} !! link ending ">" and image/span ending "]" can't follow "\n" or document start
La dernière fois, il était nécessaire d'écrire les terminaux et de vérifier la conformité de chaque caractère entrant avec l'un d'entre eux. Mais alors les terminaux étaient à un seul caractère! Maintenant, en plus de mettre en évidence les terminaux, il est nécessaire de les diviser eux-mêmes en
clés , c'est-à -dire en caractères. Pourquoi les "clés"? Ils sont la clé du lexer. À la suite de toutes les actions, les lignes suivantes apparaîtront dans le fichier de grammaire:
(* TERMINALS *) "___...", "\n", "\n\n", "> ", ">>...", "###...[", "*[", "%[", "_[", "^[", "~[", "&[", "+[", "=[", "`[", "]", "<<", "[<", ">", " @... ", "\n@...\n", " @...\n", "\n@... ", "***... ", "+++... ", "123.56. " (* KEYS *) "_", "\n" ">", "#", "*", "%", "^", "~", "&", "+", "=", "`", "<", "[", "]", " ", "@", "1..9", ".", <END>
Pile de types de jetons attendus
La dernière fois, encore une fois, tout était plus simple, nous n'avions que 10 types de jetons, sans compter la fin, et il y avait moins de chance de se confondre dans ce zoo de jetons. Maintenant, il y a évidemment plus de types. Permettez-moi de vous rappeler que la tâche du lexer est de laisser à l'analyseur le moins de travail possible, idéalement, uniquement en construisant un arbre. Par conséquent, l'ensemble des types de jetons doit refléter leur essence aussi précisément que possible. Dans le premier article, j'ai donné un exemple de bon ensemble, je vais le donner avec un "anti-exemple". Voir les terminaux commençant des éléments de texte en ligne (gras - gras, italique - italique, etc.)? Nous pourrions les analyser en une paire de jetons: le maître ("*", "%", etc.) et l'esclave ("[") et le transmettre sous la forme à l'analyseur. Il est facile de deviner qu'il vaut mieux faire une définition exacte d'un élément de texte au niveau lexer, c'est-à -dire définir "* [" comme "bold_start", "% [" comme "italic_start", etc. Plus il y a de types et plus ils se reflètent avec précision - mieux c'est. De plus, le second est plus important que le premier. Par exemple, nous pourrions analyser la notification pour le symbole "@" et le nom d'utilisateur, mais évidemment, il est préférable de les laisser combinés en un seul jeton.
Eh bien, nous avons décidé des types. Par où commencer la procédure d'analyse du texte en jetons? Comme alors, recommencez depuis le début. Qu'est-ce qui peut suivre immédiatement le début du document analysé? Ne vous précipitez pas pour plier vos doigts. Contrairement au HTML, les 22 types ici peuvent commencer. C'est bon, armé d'un peu d'unification, nous écrivons comme ceci:
curr_token_type = TEXT | UNDERLINE | TITLE_START | QUOTE_START | CITE | BOLD_START | ...
et dans la fonction de traitement des symboles:
case TEXT | UNDERLINE | TITLE_START | QUOTE_START | CITE | ...
Si vous ne comprenez pas ce qui est en jeu, lisez le
premier article .
N'ayez pas peur du long type générique de jeton attendu. Le premier caractère de la chaîne réduit immédiatement sa longueur à 2 à 4 types. Nos terminaux étant multi-caractères, la définition est basée sur les clés.
C'est simple, voyez par vous-mĂŞme:
if (c == '_') { buffer.push_back('_'); curr_token_type = TEXT | UNDERLINE | UNDERLINED_START;
Le trait de soulignement a immédiatement déterminé le jeton en cours de construction selon l'un des trois types suivants: texte brut, ligne horizontale ou début du texte souligné ("_ [").
Pour en revenir au problème, comment garder une trace de tous les types génériques et ne pas oublier de les traiter tous? Obtenez une pile ... dans un cahier! C'est vrai, notez tous les types génériques qui apparaissent après "curr_token_type = ..." dans la liste, et après avoir traité l'un, prenez l'autre de la liste de la fin. Vous pouvez organiser le travail avec la liste et comme avec la file d'attente, cela n'a pas beaucoup d'importance. L'essentiel est que vous n'oublierez pas quels types sont déjà traités et lesquels doivent encore être traités.
Arbre de classe
Enfin, nous sommes arrivés à l'analyse. Ici, vous devez déterminer les classes de nœuds (nœuds) du futur arbre de la même manière que nous avons déterminé avec les types de jetons. Pour ce faire, ouvrez à nouveau le bloc-notes et écrivez ce qui suit:
Node { Node * parent, Node_type type } #- Root { Root_item[] children, ulong children_count }
Nous avons donc défini la future classe de base de tous les nœuds et sa dérivée - la racine de l'arbre, c'est-à -dire le document lui-même. Un document (voir le BPF ci-dessus) se compose de deux types de nœuds: une section et une ligne horizontale (souligné). Nous leur définissons la classe de base Root_item et les décrivons de la même manière que nous avons décrit la racine. De plus, ici, dans le bloc-notes, nous indiquons immédiatement tous les autres champs des classes, le cas échéant. Pour la racine, c'est le nombre "d'enfants" - c'est-à -dire sections internes et lignes horizontales. La section se compose d'éléments pour lesquels nous définirons la classe de base Div et ainsi de suite, en parcourant récursivement la grammaire, nous déterminerons toutes les classes nécessaires. Avant d'écrire le code, nous définissons ici toute l'inclusion des en-têtes. C'est simple: tous les descendants directs des classes généralisées de base doivent être inclus dans les classes qui les contiennent.
Nous dénotons ces dépendances sous forme de listes après le treillis, et nous obtenons le document suivant:
Node { Node * parent, Node_type type } #- Root { Root_item[] children, ulong children_count } #Underline, #Section Root_item {} #- Underline {} Section { Div[] children, ulong children_count } #Paragraph, #Title, #Quote, #Cite, #Unordered_list, #Ordered_list Div {} #- Paragraph { Span[] children, ulong children_count } #Bold, #Italic, #Underlined, #Overlined, #Throwlined, #Subscript, #Superscript, #Marked, #Monospace, #Text, #Image, #Link, #Notification Title { char level, Span[] children, ulong children_count } #Bold, #Italic, #Underlined, #Overlined, #Throwlined, #Subscript, #Superscript, #Marked, #Monospace, #Text, #Image, #Link, #Notification Quote { Span[] children, ulong children_count } #Bold, #Italic, #Underlined, #Overlined, #Throwlined, #Subscript, #Superscript, #Marked, #Monospace, #Text, #Image, #Link, #Notification Cite { ulong number } #- Unordered_list { Div } #Paragraph, #Title, #Quote, #Cite, #Ordered_list Ordered_list { Div } #Paragraph, #Title, #Quote, #Cite, Unordered list Span {} #- Bold { Span[] children, ulong children_count } #Italic, #Underlined, #Overlined, #Throwlined, #Subscript, #Superscript, #Marked, #Monospace, #Text, #Image, #Link, #Notification Italic { Span[] children, ulong children_count } #Bold, #Underlined, #Overlined, #Throwlined, #Subscript, #Superscript, #Marked, #Monospace, #Text, #Image, #Link, #Notification Underlined { Span[] children, ulong children_count } #Bold, #Italic, #Overlined, #Throwlined, #Subscript, #Superscript, #Marked, #Monospace, #Text, #Image, #Link, #Notification Overlined { Span[] children, ulong children_count } #Bold, #Italic, #Underlined, #Throwlined, #Subscript, #Superscript, #Marked, #Monospace, #Text, #Image, #Link, #Notification Throwlined { Span[] children, ulong children_count } #Bold, #Italic, #Underlined, #Overlined, #Subscript, #Superscript, #Marked, #Monospace, #Text, #Image, #Link, #Notification Subscript { Span[] children, ulong children_count } #Bold, #Italic, #Underlined, #Overlined, #Throwlined, #Superscript, #Marked, #Monospace, #Text, #Image, #Link, #Notification Superscript { Span[] children, ulong children_count } #Bold, #Italic, #Underlined, #Overlined, #Throwlined, #Subscript, #Marked, #Monospace, #Text, #Image, #Link, #Notification Marked { Span[] children, ulong children_count } #Bold, #Italic, #Underlined, #Overlined, #Throwlined, #Subscript, #Superscript, #Monospace, #Text, #Image, #Link, #Notification Monospace { Span[] children, ulong children_count } #Bold, #Italic, #Underlined, #Overlined, #Throwlined, #Subscript, #Superscript, #Marked, #Text, #Image, #Link, #Notification Text { string text } #- Image { string src, string alt } #- Link { string URL, Span[] children, ulong children_count } #Bold, #Italic, #Underlined, #Overlined, #Throwlined, #Subscript, #Superscript, #Marked, #Monospace, #Text, #Image, #Notification Notification { string user } #-
Ici, j'ai marqué "# -" l'absence de dépendances et supprimé l'inclusion des classes en elles-mêmes.
On remarque que toutes les classes de formatage intégrées (Gras, Italique, ...) sont dépendantes les unes des autres et, en plus, de la classe Link, qui en dépend aussi! Dans une position similaire se trouvent Unordered_list et Ordered_list. L'inclusion d'en-têtes les uns dans les autres conduira non seulement à ignorer l'un d'eux, comme on pourrait s'y attendre, mais ne passera pas non plus la validation par le préprocesseur, et l'inclusion unilatérale ne nous permettra pas de déclarer à l'intérieur de la classe incluse la fonction d'ouvrir l'élément de classe et de lui renvoyer le lien. Comment être Il y a deux façons.
Inclusion de classes les unes dans les autres
Tout d'abord, regardez les classes Bold, Italic et ainsi de suite sur Monospace. Ils se ressemblent. A tel point qu'ils peuvent être combinés en une seule classe "Inline". Cette décision soulèvera peut-être des doutes. Cela m'a également causé, mais dans la pratique, la différence entre eux n'a affecté que la forme de présentation sous forme d'arbre dans le terminal et les balises en HTML. Si vous voyez que certaines classes contiennent les mêmes champs, ont les mêmes dépendances et ont généralement une description similaire dans la grammaire formelle, n'hésitez pas à les combiner. Vous simplifiez ainsi la tâche pour vous et le processeur.
Mais une telle astuce ne fonctionnera pas avec la classe Link, car elle contient un champ supplémentaire - la chaîne URL. Nous utiliserons la deuxième méthode.
Est-ce que tout le monde sait que la séparation des classes en déclarations et définitions est une bonne forme en programmation C ++? Dans l'en-tête avec l'extension .h ou .hpp - déclaration, dans la source avec l'extension .cpp - définition, non? Et maintenant, je me tourne vers les nouveaux venus pour la programmation: asseyez-vous et attachez vos ceintures de sécurité, car ce sera désagréable. Après tout, ce que nous prescrivons dans un fichier avec l'extension .h n'est rien de plus qu'une
définition de classe. Et dans le fichier .cpp, il y a déjà une
implémentation des méthodes de cette classe. Je ne comprends pas? Nous avons été trompés à l'école. Une classe est déclarée en tant que fonction, sur une seule ligne, si elle ne contient pas de modèles.
C'est encore plus simple qu'une fonction, car elle n'a pas d'arguments. Voici une
déclaration de classe typique:
class MyClass;
Et c'est tout! Et les déclarations de champs et de méthodes sont déjà sa
définition .
Nous en profiterons. Nous incluons le titre de la classe Inline dans le titre de la classe Link et y déclarons la classe Link elle-même avant de
définir la classe Inline. Le fichier inline.h devrait ressembler à ceci:
#ifndef INLINE_H #define INLINE_H #include "AST/span.h" #include "AST/text.h" #include "AST/image.h" #include "AST/notification.h" class Link; class Inline : public Span { public: static const unsigned long MAX_CHILDREN_COUNT = 0xFFFFFF; private: Span ** children; unsigned long children_count; unsigned long extended; void extend(); public: Inline(const Node * parent, const Node_type &type); Inline(const Span * span); ~Inline(); Inline * add_text(const string &text); Inline * add_image(const string &src, const string &alt); Inline * add_notification(const string &user); Link * open_link(const string &URL); ...
La classe Inline ne sait toujours rien de la classe Link, de ses champs et de ses méthodes, mais elle est sûre de son existence. Par conséquent, nous pouvons déclarer des méthodes qui renvoient un
pointeur sur un objet de la classe Link, ou l'accepter comme argument. Le mot
pointeur n'a pas été sélectionné au hasard, la classe Inline ne sait pas encore comment construire des objets de type Link, car elle n'a pas accès à son constructeur, mais elle peut fonctionner avec tous les pointeurs, car ils ont tous la même interface. Mais nous n'avons pas besoin d'objets ici. Mais dans l'implémentation de la méthode open_link, un objet de type Link est créé et un pointeur est renvoyé, ce qui signifie qu'au moment où le préprocesseur entre dans cette méthode, le constructeur et les autres méthodes Link dont la méthode open_link peut avoir besoin doivent être déclarés. Ici, nous profitons de la division du code source en fichiers séparés avec en-têtes et implémentation. Le fichier inline.h est inclus dans le fichier inline.cpp («undercloud»), mais le fichier link.h n'est pas inclus dans inline.h. Donc, l'inclure dans inline.cpp sera la première inclusion du préprocesseur. Ensuite, le fichier inline.cpp démarre comme ceci:
#include "inline.h" #include "link.h" ...
Je répète tout ce qui précède. Le titre de la classe Ah est inclus dans le titre de la classe Bh comme d'habitude, et la classe B est déclarée avant la classe A et nous incluons son titre dans la source A.cpp. Cette méthode n'est pas la seule, mais la plus simple, à mon avis.
Je note que cette inclusion mutuelle des classes n'empêche pas la classe B d'hériter de la classe A, si nous écrivions sa
déclaration avant la
définition de la classe A. C'est exactement ce que j'ai fait, héritant Ordered_list de Unordered_list.
Construire un arbre
Nous sommes donc arrivés à la construction d'un arbre de syntaxe abstraite. Dans le dernier article, la fonction tient sur 50 lignes. Spoiler: cette fois, il est passé à près de 1400. Le principe de fonctionnement est le même: nous vérifions le type de chaque jeton et, selon lui, exécutons une certaine section de code, stockant un nœud d'arbre ouvert en mémoire. Mais si pour analyser du HTML, presque toutes les sections contenaient une et une seule des trois commandes: ajouter un nœud vide à l'intérieur de l'ouverture, ouvrir un nouveau nœud dans l'ouverture et fermer le nœud ouvert, en retournant son parent, alors l'action souhaitée ici dépend également du type de nœud ouvert. Par exemple, si le jeton "ligne horizontale" est entré en traitement et que le nœud ouvert est la racine du document, alors tout ce qui est nécessaire est d'ajouter une ligne à ce nœud ouvert en utilisant la conversion et la fonction avec le nom conditionnel add_line (), quelque chose comme ceci:
if (type == Node::ROOT) static_case<Root*>(open_node)->add_line();
Mais si le nœud ouvert est un paragraphe (paragraphe), vous devez d'abord le fermer ainsi que tous les ancêtres possibles (listes à puces et numérotées) jusqu'à ce que le nœud ouvert devienne du type "section", puis le fermer également:
else if (type == Node::PARAGRAPH) { open_node = static_cast<Paragraph*>(open_node)->close(); while (open_node->get_type() != Node::SECTION) { if (open_node->get_type() == Node::UNORDERED_LIST) open_node = static_cast<Unordered_list*>(open_node)->close(); else if (open_node->get_type() == Node::UNORDERED_LIST) open_node = static_cast<Unordered_list*>(open_node)->close(); else if (open_node->get_type() == Node::PARAGRAPH) open_node = static_cast<Paragraph*>(open_node)->close(); } open_node = static_cast<Section*>(open_node)->close(); open_node = tree->add_line(); }
Si le nœud ouvert est une légende de l'image, la ligne horizontale casse complètement la grammaire et une exception doit être levée, et si le nœud ouvert n'est pas un lien et que le jeton entrant ">" est de type "LINK_FINISH", il ne doit pas être traité comme la fin du lien, mais comment texte, etc.
Ainsi, l'arborescence commutateur / boîtier, qui vérifie le type du jeton entrant, doit contenir un autre commutateur / boîtier, qui vérifie le type du nœud ouvert. Au début, il est difficile de s'attaquer à une telle construction, mais il n'est pas nécessaire de partir du début, de la première condition. Vous pouvez créer un document standard délimité par votre langue / contenant un script dans votre langue et implémenter des conditions tout au long du document, en vérifiant le résultat en sortant une arborescence pseudographique sur le terminal. J'ai pris l'article précédent comme un tel document, le tout premier jeton reçu est le début du titre. Nous traitons donc le jeton avec le type TITLE_START. Voici le texte de l'en-tête et le crochet carré de fermeture, nous traitons les jetons de types TEXT et SPAN_OR_IMAGE_FINISH.
Après cela, nous aurons déjà un tel mini-arbre:
<article> | +-<section> | +-<h1> | +-" DOM "
En cours de route, vous remarquerez que certaines classes incluent les mêmes méthodes avec les mêmes algorithmes. Par exemple, les classes de paragraphe Paragraphe et les guillemets Quote ouvrent les liens de la même manière et leur ajoutent du texte. Dans de tels cas, la meilleure solution lors de la refactorisation consiste à créer une classe avec ces méthodes et à en hériter les nœuds nécessaires. J'ai essayé de l'implémenter, mais mes compétences n'étaient pas suffisantes, et je me suis perdu dans l'ambiguïté lors du casting, donc je donne juste les résultats du lexer et de l'analyseur:
L'article lui-même @2che >>442964 #[ DOM ] , ! markdown, , — « », . , , , , LibreOffice Writer, %[ ], — %[]. , «parser example», «html to DOM», «how to parse html» . , , , flex, bison, llvm yacc. , , (gumbo, jsoup, rapidjson, Qt .) , C++ , . , AST ( ), , , . , — — , . . , . , , . HTML, . , , %[ ] — . : 1. *[ ] — , . 2. *[ ] — %[ ] (AST — abstract syntax tree), %[ ] (DOM — document object model). . , IDE , . - — %[ - ()] %[ -]. , . : > `[<> = <_1> <_> <_2>] , . , , «» .. ? : %[] %[]. *[] — , : > `[<_1> = <> (<_> | <_>) <>] *[] , . : > `[<> = <_1> "+" <_2> <_1> = <> ("*" | "/") <>] "+", "*", "/" — . , , — . <<https://ru.wikipedia.org/wiki/%D0%A4%D0%BE%D1%80%D0%BC%D0%B0_%D0%91%D1%8D%D0%BA%D1%83%D1%81%D0%B0_%E2%80%94_%D0%9D%D0%B0%D1%83%D1%80%D0%B0>> <<https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D1%81%D1%88%D0%B8%D1%80%D0%B5%D0%BD%D0%BD%D0%B0%D1%8F_%D1%84%D0%BE%D1%80%D0%BC%D0%B0_%D0%91%D1%8D%D0%BA%D1%83%D1%81%D0%B0_%E2%80%94_%D0%9D%D0%B0%D1%83%D1%80%D0%B0>>. — , . , . , , , . , , , . , . HTML5 : > `[stub] , ( , .. , ). : , , ? , , . . , disassemble(ifsteam &file) , process(const char &c), . , process switch, . : switch , . , , . , : , , , HTML ( PHP, "<?… ?>". case. ? . ( — , — ). (1, 2, 4, 8 .). : 0001, 0010, 0100 .., . , . : > `[enum Token_type { END = 1, TEXT = 2, OPENING_BLOCK_TAG_NAME = 4, CLOSING_BLOCK_TAG_NAME = 8, EMPTY_TAG_NAME = 16, COMMENT = 32, MACRO_TAG = 64, ATTRIBUTE_NAME = 128, UNQUOTED_ATTRIBUTE_VALUE = 256, SINGLE_QUOTED_ATTRIBUTE_VALUE = 512, DOUBLE_QUOTED_ATTRIBUTE_VALUE = 1024 };] process: > `[stub] switch ( ), case . , : , , (), . . () , «» .. gedit: [<https://hsto.org/webt/72/fw/tw/72fwtwt_waeie4ulzftkxua356w.png>] , . disassemble: > `[stub] TEXT, END ( , ). HTML- , - PHP, "[ "<_>": <_> ]". : > =[ `[stub]] =[ `[stub]] — . , -. DOM, . HTML-? — , , — «Node», «Block» (, ) «Root». , , , <p>, <li>, <strong> , . . , — : , , . , , Node, , . %[ ]. : > `[stub] ! , : `[| +--<ROOT> | +--<!DOCTYPE> | +--<html> | +--<head> | | | +--<meta> | | | +--<meta> | | | +--<meta> | | | +--<meta> | | | +--<meta> | | | +--<meta> | | | +--<meta> | | | +--<meta> | | | +--<meta> | | | +--<title> | | | +--<link> | | | +--<link> | | | +--<COMMENT> | +--<body> | +--<header> | | | +--<div> | +--<nav> | | | +--<ul> | | | +--<li> | | | | | +--<a> | | | +--<li> | | | | | +--<a> | | | +--<li> | | | +--<a> | +--<main> | | | +--<MACRO> | +--<footer> | +--<hr> | +--<small>] , DOM, jQuery, Jsoup, beautifulsoup Gumbo , , , <style> <script>, . , . . PS <<https://gitlab.com/2che/nyHTML>>. , , .
Jetons du lexer 0: ["2che": NOTIFICATION]
1: ["
": NEWLINE]
2: ["442964": CITE]
3: ["# [": TITLE_START]
4: ["L'art d'analyser ou de faire soi-mĂŞme DOM": TEXTE]
5: ["]": SPAN_OR_IMAGE_FINISH]
6: ["
": DOUBLE_NEWLINE]
7: [ ", ! markdown, , — « », . , , , , LibreOffice Writer, " : TEXT ]
8: [ "%[" : ITALIC_START ]
9: [ " " : TEXT ]
10: [ "]" : SPAN_OR_IMAGE_FINISH ]
11: [ ", — " : TEXT ]
12: [ "%[" : ITALIC_START ]
13: [ "" : TEXT ]
14: [ "]" : SPAN_OR_IMAGE_FINISH ]
15: [ ". , «parser example», «html to DOM», «how to parse html» . , , , flex, bison, llvm yacc. , , (gumbo, jsoup, rapidjson, Qt .) , C++ , . , AST ( ), , , ." : TEXT ]
16: [ "
" : DOUBLE_NEWLINE ]
17: [ " , — — , . . , . , , . HTML, ." : TEXT ]
18: [ "
" : DOUBLE_NEWLINE ]
19: [ " , , " : TEXT ]
20: [ "%[" : ITALIC_START ]
21: [ " " : TEXT ]
22: [ "]" : SPAN_OR_IMAGE_FINISH ]
23: [ " — . :" : TEXT ]
24: [ "
" : NEWLINE ]
25: [ "1. " : ORDERED_LIST_ITEM_MARKER ]
26: [ "*[" : BOLD_START ]
27: [ " " : TEXT ]
28: [ "]" : SPAN_OR_IMAGE_FINISH ]
29: [ " — , ." : TEXT ]
30: [ "
" : NEWLINE ]
31: [ "2. " : ORDERED_LIST_ITEM_MARKER ]
32: [ "*[" : BOLD_START ]
33: [ " " : TEXT ]
34: [ "]" : SPAN_OR_IMAGE_FINISH ]
35: [ " — " : TEXT ]
36: [ "%[" : ITALIC_START ]
37: [ " " : TEXT ]
38: [ "]" : SPAN_OR_IMAGE_FINISH ]
39: [ " (AST — abstract syntax tree), " : TEXT ]
40: [ "%[" : ITALIC_START ]
41: [ " " : TEXT ]
42: [ "]" : SPAN_OR_IMAGE_FINISH ]
43: [ " (DOM — document object model)." : TEXT ]
44: [ "
" : DOUBLE_NEWLINE ]
45: [ " . , IDE , . - — " : TEXT ]
46: [ "%[" : ITALIC_START ]
47: [ " - ()" : TEXT ]
48: [ "]" : SPAN_OR_IMAGE_FINISH ]
49: [ " " : TEXT ]
50: [ "%[" : ITALIC_START ]
51: [ " -" : TEXT ]
52: [ "]" : SPAN_OR_IMAGE_FINISH ]
53: [ ". , . :" : TEXT ]
54: [ "
" : NEWLINE ]
55: [ "> " : QUOTE_START ]
56: [ "`[" : MONOSPACE_START ]
57: [ "<" : TEXT ]
58: [ ">" : LINK_FINISH ]
59: [ " = <_1" : TEXT ]
60: [ ">" : LINK_FINISH ]
61: [ " <_" : TEXT ]
62: [ ">" : LINK_FINISH ]
63: [ " <_2" : TEXT ]
64: [ ">" : LINK_FINISH ]
65: [ "]" : SPAN_OR_IMAGE_FINISH ]
66: [ "
" : DOUBLE_NEWLINE ]
67: [ " , . , , «» .." : TEXT ]
68: [ "
" : DOUBLE_NEWLINE ]
69: [ " ?" : TEXT ]
70: [ "
" : DOUBLE_NEWLINE ]
71: [ " : " : TEXT ]
72: [ "%[" : ITALIC_START ]
73: [ "" : TEXT ]
74: [ "]" : SPAN_OR_IMAGE_FINISH ]
75: [ " " : TEXT ]
76: [ "%[" : ITALIC_START ]
77: [ "" : TEXT ]
78: [ "]" : SPAN_OR_IMAGE_FINISH ]
79: [ ". " : TEXT ]
80: [ "*[" : BOLD_START ]
81: [ "" : TEXT ]
82: [ "]" : SPAN_OR_IMAGE_FINISH ]
83: [ " — , :" : TEXT ]
84: [ "
" : NEWLINE ]
85: [ "> " : QUOTE_START ]
86: [ "`[" : MONOSPACE_START ]
87: [ "<_1" : TEXT ]
88: [ ">" : LINK_FINISH ]
89: [ " = <" : TEXT ]
90: [ ">" : LINK_FINISH ]
91: [ " (<_" : TEXT ]
92: [ ">" : LINK_FINISH ]
93: [ " | <_" : TEXT ]
94: [ ">" : LINK_FINISH ]
95: [ ") <" : TEXT ]
96: [ ">" : LINK_FINISH ]
97: [ "]" : SPAN_OR_IMAGE_FINISH ]
98: [ "
" : DOUBLE_NEWLINE ]
99: [ "*[" : BOLD_START ]
100: [ "" : TEXT ]
101: [ "]" : SPAN_OR_IMAGE_FINISH ]
102: [ " , . :" : TEXT ]
103: [ "
" : NEWLINE ]
104: [ "> " : QUOTE_START ]
105: [ "`[" : MONOSPACE_START ]
106: [ "<" : TEXT ]
107: [ ">" : LINK_FINISH ]
108: [ " = <_1" : TEXT ]
109: [ ">" : LINK_FINISH ]
110: [ " "+" <_2" : TEXT ]
111: [ ">" : LINK_FINISH ]
112: [ "
" : NEWLINE ]
113: [ "<_1" : TEXT ]
114: [ ">" : LINK_FINISH ]
115: [ " = <" : TEXT ]
116: [ ">" : LINK_FINISH ]
117: [ " ("*" | "/") <" : TEXT ]
118: [ ">" : LINK_FINISH ]
119: [ "]" : SPAN_OR_IMAGE_FINISH ]
120: [ "
" : DOUBLE_NEWLINE ]
121: [ " "+", "*", "/" — ." : TEXT ]
122: [ "
" : NEWLINE ]
123: [ " , , — ." : TEXT ]
124: [ "
" : DOUBLE_NEWLINE ]
125: [ " " : TEXT ]
126: [ "<<" : LINK_START ]
127: [ "https://ru.wikipedia.org/wiki/%D0%A4%D0%BE%D1%80%D0%BC%D0%B0_%D0%91%D1%8D%D0%BA%D1%83%D1%81%D0%B0_%E2%80%94_%D0%9D%D0%B0%D1%83%D1%80%D0%B0" : TEXT ]
128: [ ">" : LINK_FINISH ]
129: [ "" : TEXT ]
130: [ ">" : LINK_FINISH ]
131: [ " " : TEXT ]
132: [ "<<" : LINK_START ]
133: [ "https://ru.wikipedia.org/wiki/%D0%A0%D0%B0%D1%81%D1%88%D0%B8%D1%80%D0%B5%D0%BD%D0%BD%D0%B0%D1%8F_%D1%84%D0%BE%D1%80%D0%BC%D0%B0_%D0%91%D1%8D%D0%BA%D1%83%D1%81%D0%B0_%E2%80%94_%D0%9D%D0%B0%D1%83%D1%80%D0%B0" : TEXT ]
134: [ ">" : LINK_FINISH ]
135: [ "" : TEXT ]
136: [ ">" : LINK_FINISH ]
137: [ ". — , . , . , , , . , , , . , . HTML5 :" : TEXT ]
138: [ "
" : NEWLINE ]
139: [ "> " : QUOTE_START ]
140: [ "`[" : MONOSPACE_START ]
141: [ "stub" : TEXT ]
142: [ "]" : SPAN_OR_IMAGE_FINISH ]
143: [ "
" : DOUBLE_NEWLINE ]
144: [ " , ( , .. , ). : , , ? , , . . , disassemble(ifsteam &file) , process(const char &c), . , process switch, . : switch , . , , . , : , , , HTML ( PHP, "<?… ?" : TEXT ]
145: [ ">" : LINK_FINISH ]
146: [ "". case. ? . ( — , — ). (1, 2, 4, 8 .). : 0001, 0010, 0100 .., . , . :" : TEXT ]
147: [ "
" : NEWLINE ]
148: [ "> " : QUOTE_START ]
149: [ "`[" : MONOSPACE_START ]
150: [ "enum Token_type {" : TEXT ]
151: [ "
" : NEWLINE ]
152: [ " END = 1, TEXT = 2," : TEXT ]
153: [ "
" : NEWLINE ]
154: [ " OPENING_BLOCK_TAG_NAME = 4, CLOSING_BLOCK_TAG_NAME = 8, EMPTY_TAG_NAME = 16, COMMENT = 32, MACRO_TAG = 64," : TEXT ]
155: [ "
" : NEWLINE ]
156: [ " ATTRIBUTE_NAME = 128, UNQUOTED_ATTRIBUTE_VALUE = 256, SINGLE_QUOTED_ATTRIBUTE_VALUE = 512, DOUBLE_QUOTED_ATTRIBUTE_VALUE = 1024" : TEXT ]
157: [ "
" : NEWLINE ]
158: [ "};" : TEXT ]
159: [ "]" : SPAN_OR_IMAGE_FINISH ]
160: [ "
" : DOUBLE_NEWLINE ]
161: [ " process:" : TEXT ]
162: [ "
" : NEWLINE ]
163: [ "> " : QUOTE_START ]
164: [ "`[" : MONOSPACE_START ]
165: [ "stub" : TEXT ]
166: [ "]" : SPAN_OR_IMAGE_FINISH ]
167: [ "
" : DOUBLE_NEWLINE ]
168: [ " switch ( ), case . , : , , (), . . () , «» .. gedit:" : TEXT ]
169: [ "
" : NEWLINE ]
170: [ "[<" : IMAGE_START ]
171: [ "https://hsto.org/webt/72/fw/tw/72fwtwt_waeie4ulzftkxua356w.png" : TEXT ]
172: [ ">" : LINK_FINISH ]
173: [ "]" : SPAN_OR_IMAGE_FINISH ]
174: [ "
" : DOUBLE_NEWLINE ]
175: [ " , . disassemble:" : TEXT ]
176: [ "
" : NEWLINE ]
177: [ "> " : QUOTE_START ]
178: [ "`[" : MONOSPACE_START ]
179: [ "stub" : TEXT ]
180: [ "]" : SPAN_OR_IMAGE_FINISH ]
181: [ "
" : DOUBLE_NEWLINE ]
182: [ " TEXT, END ( , )." : TEXT ]
183: [ "
" : DOUBLE_NEWLINE ]
184: [ " HTML- , - PHP, "[ "<_" : TEXT ]
185: [ ">" : LINK_FINISH ]
186: [ "": <_" : TEXT ]
187: [ ">" : LINK_FINISH ]
188: [ " " : TEXT ]
189: [ "]" : SPAN_OR_IMAGE_FINISH ]
190: [ "". :" : TEXT ]
191: [ "
" : NEWLINE ]
192: [ "> " : QUOTE_START ]
193: [ "=[" : MARKED_START ]
194: [ " " : TEXT ]
195: [ "`[" : MONOSPACE_START ]
196: [ "stub" : TEXT ]
197: [ "]" : SPAN_OR_IMAGE_FINISH ]
198: [ "]" : SPAN_OR_IMAGE_FINISH ]
199: [ "
" : NEWLINE ]
200: [ "=[" : MARKED_START ]
201: [ " " : TEXT ]
202: [ "`[" : MONOSPACE_START ]
203: [ "stub" : TEXT ]
204: [ "]" : SPAN_OR_IMAGE_FINISH ]
205: [ "]" : SPAN_OR_IMAGE_FINISH ]
206: [ "
" : DOUBLE_NEWLINE ]
207: [ " — . , -. DOM, ." : TEXT ]
208: [ "
" : DOUBLE_NEWLINE ]
209: [ " HTML-?" : TEXT ]
210: [ "
" : DOUBLE_NEWLINE ]
211: [ " — , , — «Node», «Block» (, ) «Root». , , , <p" : TEXT ]
212: [ ">" : LINK_FINISH ]
213: [ ", <li" : TEXT ]
214: [ ">" : LINK_FINISH ]
215: [ ", <strong" : TEXT ]
216: [ ">" : LINK_FINISH ]
217: [ " , . . , — : , , . , , Node, , . " : TEXT ]
218: [ "%[" : ITALIC_START ]
219: [ " " : TEXT ]
220: [ "]" : SPAN_OR_IMAGE_FINISH ]
221: [ "." : TEXT ]
222: [ "
" : DOUBLE_NEWLINE ]
223: [ " :" : TEXT ]https://gitlab.com/2che/markedit
224: [ "
" : NEWLINE ]
225: [ "> " : QUOTE_START ]
226: [ "`[" : MONOSPACE_START ]
227: [ "stub" : TEXT ]
228: [ "]" : SPAN_OR_IMAGE_FINISH ]
229: [ "
" : DOUBLE_NEWLINE ]
230: [ " ! , :" : TEXT ]
231: [ "
" : NEWLINE ]
232: [ "`[" : MONOSPACE_START ]
233: [ "| " : TEXT ]
234: [ "
" : NEWLINE ]
235: [ "+--<ROOT" : TEXT ]
236: [ ">" : LINK_FINISH ]
237: [ "
" : NEWLINE ]
238: [ " | " : TEXT ]
239: [ "
" : NEWLINE ]
240: [ " +--<!DOCTYPE" : TEXT ]
241: [ ">" : LINK_FINISH ]
242: [ "
" : NEWLINE ]
243: [ " | " : TEXT ]
244: [ "
" : NEWLINE ]
245: [ " +--<html" : TEXT ]
246: [ ">" : LINK_FINISH ]
247: [ "
" : NEWLINE ]
248: [ " | " : TEXT ]
249: [ "
" : NEWLINE ]
250: [ " +--<head" : TEXT ]
251: [ ">" : LINK_FINISH ]
252: [ "
" : NEWLINE ]
253: [ " | | " : TEXT ]
254: [ "
" : NEWLINE ]
255: [ " | +--<meta" : TEXT ]
256: [ ">" : LINK_FINISH ]
257: [ "
" : NEWLINE ]
258: [ " | | " : TEXT ]
259: [ "
" : NEWLINE ]
260: [ " | +--<meta" : TEXT ]
261: [ ">" : LINK_FINISH ]
262: [ "
" : NEWLINE ]
263: [ " | | " : TEXT ]
264: [ "
" : NEWLINE ]
265: [ " | +--<meta" : TEXT ]
266: [ ">" : LINK_FINISH ]
267: [ "
" : NEWLINE ]
268: [ " | | " : TEXT ]
269: [ "
" : NEWLINE ]
270: [ " | +--<meta" : TEXT ]
271: [ ">" : LINK_FINISH ]
272: [ "
" : NEWLINE ]
273: [ " | | " : TEXT ]
274: [ "
" : NEWLINE ]
275: [ " | +--<meta" : TEXT ]
276: [ ">" : LINK_FINISH ]
277: [ "
" : NEWLINE ]
278: [ " | | " : TEXT ]
279: [ "
" : NEWLINE ]
280: [ " | +--<meta" : TEXT ]
281: [ ">" : LINK_FINISH ]
282: [ "
" : NEWLINE ]
283: [ " | | " : TEXT ]
284: [ "
" : NEWLINE ]
285: [ " | +--<meta" : TEXT ]
286: [ ">" : LINK_FINISH ]
287: [ "
" : NEWLINE ]
288: [ " | | " : TEXT ]
289: [ "
" : NEWLINE ]
290: [ " | +--<meta" : TEXT ]
291: [ ">" : LINK_FINISH ]
292: [ "
" : NEWLINE ]
293: [ " | | " : TEXT ]
294: [ "
" : NEWLINE ]
295: [ " | +--<meta" : TEXT ]
296: [ ">" : LINK_FINISH ]
297: [ "
" : NEWLINE ]
298: [ " | | " : TEXT ]
299: [ "
" : NEWLINE ]
300: [ " | +--<title" : TEXT ]
301: [ ">" : LINK_FINISH ]
302: [ "
" : NEWLINE ]
303: [ " | | " : TEXT ]
304: [ "
" : NEWLINE ]
305: [ " | +--<link" : TEXT ]
306: [ ">" : LINK_FINISH ]
307: [ "
" : NEWLINE ]
308: [ " | | " : TEXT ]
309: [ "
" : NEWLINE ]
310: [ " | +--<link" : TEXT ]
311: [ ">" : LINK_FINISH ]
312: [ "
" : NEWLINE ]
313: [ " | | " : TEXT ]
314: [ "
" : NEWLINE ]
315: [ " | +--<COMMENT" : TEXT ]
316: [ ">" : LINK_FINISH ]
317: [ "
" : NEWLINE ]
318: [ " | " : TEXT ]
319: [ "
" : NEWLINE ]
320: [ " +--<body" : TEXT ]
321: [ ">" : LINK_FINISH ]
322: [ "
" : NEWLINE ]
323: [ " | " : TEXT ]
324: [ "
" : NEWLINE ]
325: [ " +--<header" : TEXT ]
326: [ ">" : LINK_FINISH ]
327: [ "
" : NEWLINE ]
328: [ " | | " : TEXT ]
329: [ "
" : NEWLINE ]
330: [ " | +--<div" : TEXT ]
331: [ ">" : LINK_FINISH ]
332: [ "
" : NEWLINE ]
333: [ " | " : TEXT ]
334: [ "
" : NEWLINE ]
335: [ " +--<nav" : TEXT ]
336: [ ">" : LINK_FINISH ]
337: [ "
" : NEWLINE ]
338: [ " | | " : TEXT ]
339: [ "
" : NEWLINE ]
340: [ " | +--<ul" : TEXT ]
341: [ ">" : LINK_FINISH ]
342: [ "
" : NEWLINE ]
343: [ " | | " : TEXT ]
344: [ "
" : NEWLINE ]
345: [ " | +--<li" : TEXT ]
346: [ ">" : LINK_FINISH ]
347: [ "
" : NEWLINE ]
348: [ " | | | " : TEXT ]
349: [ "
" : NEWLINE ]
350: [ " | | +--<a" : TEXT ]
351: [ ">" : LINK_FINISH ]
352: [ "
" : NEWLINE ]
353: [ " | | " : TEXT ]
354: [ "
" : NEWLINE ]
355: [ " | +--<li" : TEXT ]
356: [ ">" : LINK_FINISH ]
357: [ "
" : NEWLINE ]
358: [ " | | | " : TEXT ]
359: [ "
" : NEWLINE ]
360: [ " | | +--<a" : TEXT ]
361: [ ">" : LINK_FINISH ]
362: [ "
" : NEWLINE ]
363: [ " | | " : TEXT ]
364: [ "
" : NEWLINE ]
365: [ " | +--<li" : TEXT ]
366: [ ">" : LINK_FINISH ]
367: [ "
" : NEWLINE ]
368: [ " | | " : TEXT ]
369: [ "
" : NEWLINE ]
370: [ " | +--<a" : TEXT ]
371: [ ">" : LINK_FINISH ]
372: [ "
" : NEWLINE ]
373: [ " | " : TEXT ]
374: [ "
" : NEWLINE ]
375: [ " +--<main" : TEXT ]
376: [ ">" : LINK_FINISH ]
377: [ "
" : NEWLINE ]
378: [ " | | " : TEXT ]
379: [ "
" : NEWLINE ]
380: [ " | +--<MACRO" : TEXT ]
381: [ ">" : LINK_FINISH ]
382: [ "
" : NEWLINE ]
383: [ " | " : TEXT ]
384: [ "
" : NEWLINE ]
385: [ " +--<footer" : TEXT ]
386: [ ">" : LINK_FINISH ]
387: [ "
" : NEWLINE ]
388: [ " | " : TEXT ]
389: [ "
" : NEWLINE ]
390: [ " +--<hr" : TEXT ]
391: [ ">" : LINK_FINISH ]
392: [ "
" : NEWLINE ]
393: [ " | " : TEXT ]
394: [ "
" : NEWLINE ]
395: [ " +--<small" : TEXT ]
396: [ ">" : LINK_FINISH ]
397: [ "]" : SPAN_OR_IMAGE_FINISH ]
398: [ "
" : NEWLINE ]
399: [ " " : TEXT ]
400: [ "
" : NEWLINE ]
401: [ ", DOM, jQuery, Jsoup, beautifulsoup Gumbo , , , <style" : TEXT ]
402: [ ">" : LINK_FINISH ]
403: [ " <script" : TEXT ]
404: [ ">" : LINK_FINISH ]
405: [ ", . , . ." : TEXT ]
406: [ "
" : DOUBLE_NEWLINE ]
407: [ "PS " : TEXT ]
408: [ "<<" : LINK_START ]
409: [ "https://gitlab.com/2che/nyHTML" : TEXT ]
410: [ ">" : LINK_FINISH ]
411: [ "" : TEXT ]
412: [ ">" : LINK_FINISH ]
413: [ ". , , ." : TEXT ]
414: [ "
" : NEWLINE ]
415: [ "" : END ]
Arbre de syntaxe <pre><article> | +-<section> | +-<p> | | | +-@2che | | | +-"\n" | +->>442964 | +-<h1> | | | +-" DOM " | +-<p> | | | +-", ! ..." | | | +-<i> | | | | | +-" " | | | +-", — " | | | +-<i> | | | | | +-"" | | | +-". , ..." | +-<p> | | | +-" , — — ..." | +-<p> | | | +-" , , " | | | +-<i> | | | | | +-" " | | | +-" — ..." | | | +-"\n" | | | +-<b> | | | | | +-" " | | | +-" — , ..." | | | +-"\n" | | | +-<b> | | | | | +-" " | | | +-" — " | | | +-<i> | | | | | +-" " | | | +-" (AST — abstract syntax tree), " | | | +-<i> | | | | | +-" " | | | +-" (DOM — document object model)." | +-<p> | | | +-" . , ..." | | | +-<i> | | | | | +-" - ()" | | | +-" " | | | +-<i> | | | | | +-" -" | | | +-". , . ..." | | | +-"\n" | +-<blockquote> | | | +-<pre> | | | +-"<" | | | +-">" | | | +-" = <_1" | | | +-">" | | | +-" <_" | | | +-">" | | | +-" <_2" | | | +-">" | +-<p> | | | +-" , ..." | +-<p> | | | +-" ?" | +-<p> | | | +-" ..." | | | +-<i> | | | | | +-"" | | | +-" " | | | +-<i> | | | | | +-"" | | | +-". " | | | +-<b> | | | | | +-"" | | | +-" — , :" | | | +-"\n" | +-<blockquote> | | | +-<pre> | | | +-"<_1" | | | +-">" | | | +-" = <" | | | +-">" | | | +-" (<_" | | | +-">" | | | +-" | <_" | | | +-">" | | | +-") <" | | | +-">" | +-<p> | | | +-<b> | | | | | +-"" | | | +-" , . ..." | | | +-"\n" | +-<blockquote> | | | +-<pre> | | | +-"<" | | | +-">" | | | +-" = <_1" | | | +-">" | | | +-" "+" <_2" | | | +-">" | | | +-"\n" | | | +-"<_1" | | | +-">" | | | +-" = <" | | | +-">" | | | +-" ("*" | "/") <" | | | +-">" | +-<p> | | | +-" "+", "*", "/" — ." | | | +-"\n" | | | +-" , ..." | +-<p> | | | +-" " | | | +-<a> | | | | | +-"" | | | +-" " | | | +-<a> | | | | | +-"" | | | +-". — ..." | | | +-"\n" | +-<blockquote> | | | +-<pre> | | | +-"stub" | +-<p> | | | +-" , ..." | | | +-">" | | | +-"". case. ..." | | | +-"\n" | +-<blockquote> | | | +-<pre> | | | +-"enum Token_type {" | | | +-"\n" | | | +-" END = 1, TEXT = 2," | | | +-"\n" | | | +-" OPENING_BLOCK_TAG_NAME = 4, CLOSING_BLOCK_TAG_NAME = 8, EMPTY_TAG_NAME = 16, COMMENT = 32, MACRO..." | | | +-"\n" | | | +-" ATTRIBUTE_NAME = 128, UNQUOTED_ATTRIBUTE_VALUE = 256, SINGLE_QUOTED_ATTRIBUTE_VALUE = 512, DOUBL..." | | | +-"\n" | | | +-"};" | +-<p> | | | +-" process:" | | | +-"\n" | +-<blockquote> | | | +-<pre> | | | +-"stub" | +-<p> | | | +-" switch ( ..." | | | +-"\n" | +-<p> | | | +-<img /> | +-<p> | | | +-" , ..." | | | +-"\n" | +-<blockquote> | | | +-<pre> | | | +-"stub" | +-<p> | | | +-" ..." | +-<p> | | | +-" HTML- ..." | | | +-">" | | | +-"": <_" | | | +-">" | | | +-" " | | | +-"]" | | | +-"". :" | | | +-"\n" | +-<blockquote> | | | +-<mark> | | | | | +-" " | | | | | +-<pre> | | | | | +-"stub" | | | +-"\n" | | | +-<mark> | | | +-" " | | | +-<pre> | | | +-"stub" | +-<p> | | | +-" — ..." | +-<p> | | | +-" HTML-..." | +-<p> | | | +-" — , ..." | | | +-">" | | | +-", <li" | | | +-">" | | | +-", <strong" | | | +-">" | | | +-" , . ..." | | | +-<i> | | | | | +-" " | | | +-"." | +-<p> | | | +-" :" | | | +-"\n" | +-<blockquote> | | | +-<pre> | | | +-"stub" | +-<p> | | | +-" ! , ..." | | | +-"\n" | | | +-<pre> | | | | | +-"| " | | | | | +-"\n" | | | | | +-"+--<ROOT" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | " | | | | | +-"\n" | | | | | +-" +--<!DOCTYPE" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | " | | | | | +-"\n" | | | | | +-" +--<html" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | " | | | | | +-"\n" | | | | | +-" +--<head" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | | " | | | | | +-"\n" | | | | | +-" | +--<meta" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | | " | | | | | +-"\n" | | | | | +-" | +--<meta" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | | " | | | | | +-"\n" | | | | | +-" | +--<meta" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | | " | | | | | +-"\n" | | | | | +-" | +--<meta" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | | " | | | | | +-"\n" | | | | | +-" | +--<meta" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | | " | | | | | +-"\n" | | | | | +-" | +--<meta" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | | " | | | | | +-"\n" | | | | | +-" | +--<meta" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | | " | | | | | +-"\n" | | | | | +-" | +--<meta" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | | " | | | | | +-"\n" | | | | | +-" | +--<meta" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | | " | | | | | +-"\n" | | | | | +-" | +--<title" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | | " | | | | | +-"\n" | | | | | +-" | +--<link" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | | " | | | | | +-"\n" | | | | | +-" | +--<link" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | | " | | | | | +-"\n" | | | | | +-" | +--<COMMENT" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | " | | | | | +-"\n" | | | | | +-" +--<body" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | " | | | | | +-"\n" | | | | | +-" +--<header" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | | " | | | | | +-"\n" | | | | | +-" | +--<div" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | " | | | | | +-"\n" | | | | | +-" +--<nav" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | | " | | | | | +-"\n" | | | | | +-" | +--<ul" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | | " | | | | | +-"\n" | | | | | +-" | +--<li" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | | | " | | | | | +-"\n" | | | | | +-" | | +--<a" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | | " | | | | | +-"\n" | | | | | +-" | +--<li" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | | | " | | | | | +-"\n" | | | | | +-" | | +--<a" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | | " | | | | | +-"\n" | | | | | +-" | +--<li" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | | " | | | | | +-"\n" | | | | | +-" | +--<a" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | " | | | | | +-"\n" | | | | | +-" +--<main" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | | " | | | | | +-"\n" | | | | | +-" | +--<MACRO" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | " | | | | | +-"\n" | | | | | +-" +--<footer" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | " | | | | | +-"\n" | | | | | +-" +--<hr" | | | | | +-">" | | | | | +-"\n" | | | | | +-" | " | | | | | +-"\n" | | | | | +-" +--<small" | | | | | +-">" | | | +-"\n" | | | +-" " | | | +-"\n" | | | +-", ..." | | | +-">" | | | +-" <script" | | | +-">" | | | +-", . ..." | +-<p> | +-"PS " | +-<a> | | | +-"" | +-". , , ..." | +-"\n" </pre>
Tout va bien, mais il y a trop de nœuds de texte dans une chaîne les uns après les autres. De plus, j'aimerais que les guillemets consécutifs soient combinés en un seul. Pour ce faire, nous devons à nouveau parcourir l'arbre et effectuer la concaténation , c'est-à -dire l'adhésion d'éléments homogènes les uns aux autres. Je n’expliquerai pas les détails de ce processus, je vais joindre les sources, mais pour le moment, regardez commentArbre après concaténation: <pre><article> | +-<section> | +-<p> | | | +-@2che | | | +-"\n" | +->>442964 | +-<h1> | | | +-" DOM " | +-<p> | | | +-", ! ..." | | | +-<i> | | | | | +-" " | | | +-", — " | | | +-<i> | | | | | +-"" | | | +-". , ..." | +-<p> | | | +-" , — — ..." | +-<p> | | | +-" , , " | | | +-<i> | | | | | +-" " | | | +-" — ..." | | | +-<b> | | | | | +-" " | | | +-" — , ..." | | | +-<b> | | | | | +-" " | | | +-" — " | | | +-<i> | | | | | +-" " | | | +-" (AST — abstract syntax tree), " | | | +-<i> | | | | | +-" " | | | +-" (DOM — document object model)." | +-<p> | | | +-" . , ..." | | | +-<i> | | | | | +-" - ()" | | | +-" " | | | +-<i> | | | | | +-" -" | | | +-". , . ..." | +-<blockquote> | | | +-<pre> | | | +-"<> = <_1> <_> <_2>" | +-<p> | | | +-" , ..." | +-<p> | | | +-" ?" | +-<p> | | | +-" ..." | | | +-<i> | | | | | +-"" | | | +-" " | | | +-<i> | | | | | +-"" | | | +-". " | | | +-<b> | | | | | +-"" | | | +-" — , :\n" | +-<blockquote> | | | +-<pre> | | | +-"<_1> = <> (<_> | <_>) < ..." | +-<p> | | | +-<b> | | | | | +-"" | | | +-" , . ..." | +-<blockquote> | | | +-<pre> | | | +-"<> = <_1> "+" <_2>\n<_1> = <..." | +-<p> | | | +-" "+", "*", "/" — .\n ..." | +-<p> | | | +-" " | | | +-<a> | | | | | +-"" | | | +-" " | | | +-<a> | | | | | +-"" | | | +-". — ..." | +-<blockquote> | | | +-<pre> | | | +-"stub" | +-<p> | | | +-" , ..." | +-<blockquote> | | | +-<pre> | | | +-"enum Token_type {\n END = 1, TEXT = 2,\n OPENING_BLOCK_TAG_NAME = 4, CLOSING_BLOCK_TAG_NAME = ..." | +-<p> | | | +-" process:\n" | +-<blockquote> | | | +-<pre> | | | +-"stub" | +-<p> | | | +-" switch ( ..." | +-<p> | | | +-<img /> | +-<p> | | | +-" , ..." | +-<blockquote> | | | +-<pre> | | | +-"stub" | +-<p> | | | +-" ..." | +-<p> | | | +-" HTML- ..." | +-<blockquote> | | | +-<mark> | | | | | +-" " | | | | | +-<pre> | | | | | +-"stub" | | | +-"\n" | | | +-<mark> | | | +-" " | | | +-<pre> | | | +-"stub" | +-<p> | | | +-" — ..." | +-<p> | | | +-" HTML-..." | +-<p> | | | +-" — , ..." | | | +-<i> | | | | | +-" " | | | +-"." | +-<p> | | | +-" :\n" | +-<blockquote> | | | +-<pre> | | | +-"stub" | +-<p> | | | +-" ! , ..." | | | +-<pre> | | | | | +-"| \n+--<ROOT>\n | \n +--<!DOCTYPE>\n | \n +--<html>\n | \n +--<head>\n | | \n | +--<..." | | | +-"\n \n, ..." | +-<p> | +-"PS " | +-<a> | | | +-"" | +-". , , ..." </pre>
— HTML-. : ( html body, head) . : to_HTML() , , , , . , , Inline ( ):
string Inline::to_HTML (const unsigned int &level) { string HTML;
C’est tout.
J'espère que maintenant, après avoir lu les deux articles, vous pouvez facilement implémenter un traducteur pour votre langage de balisage ou de programmation. Si vous avez des questions, posez-les dans les commentaires. Et voici les sources . Succès.PS J'ai oublié de mentionner le blindage . Il est implémenté simplement: si le caractère suivant dans la procédure d'analyse lexicale est la barre oblique inverse ("\"), il est ignoré et le caractère suivant est traité, mais en plus de cela, la valeur booléenne true est envoyée à la fonction de traitement de caractère, donnant la commande d'échappement. Ensuite, si ce symbole, par exemple, est "[", sa signification spéciale est ignorée et il joint simplement le jeton en cours de construction en tant que texte. Sinon, false est transmis à la fonction et le caractère est traité comme d'habitude.