A arte de analisar 2 ou transliteração de sua própria marcação

+ BÔNUS: inclusão mútua de classes em C ++


Olá Habr! Este artigo é uma continuação direta do artigo The Art of Parsing ou DOM com nossas próprias mãos , onde analisamos um documento HTML e construímos com base em uma árvore de sintaxe abstrata (AST) com acesso a qualquer elemento por meio de indexação usando apenas a biblioteca C ++ padrão, ou seja, aprendemos a analisar por conta própria Coisas semelhantes a XML. Deixe-me lembrá-lo de que o processo de análise, ou análise / análise, consiste em duas etapas: análise lexical (análise do texto em tokens) e construção do AST. Se examinarmos o primeiro em detalhes, com exemplos e códigos-fonte, a descrição do segundo parecerá uma boneca de borboleta vazia, que possui apenas uma concha, e o autor extraiu um excelente conteúdo antes da publicação. Havia uma razão para o HTML construir uma árvore ser realmente simples, você precisa de apenas 4 classes: uma tag vazia, bloco, nó de texto e a raiz do documento herdada do bloco. Hoje vamos deixar essa simplicidade para trás e construir uma árvore em que as propriedades dos elementos, vazios e em blocos, não estarão contidas nos atributos das tags, mas diretamente nas classes, e para isso você precisará criar muitas classes. Realmente muito. Nós criaremos não a partir de simples linguagens de marcação conhecidas, mas criaremos as nossas, com as regras mostradas na imagem abaixo do corte. Além disso, no final, traduziremos, ou, mais corretamente, traduziremos o documento com o artigo anterior, marcado em nossa linguagem, em HTML, e como bônus, responderei aos programadores iniciantes em C ++ uma pergunta trivial, mas difícil de encontrar: como incorporar classes entre si?



Notas gramaticais


Antes de irmos diretamente para a construção de uma árvore, vamos refrescar nossa memória e esclarecer alguns dos detalhes do trabalho preliminar. Você ainda se lembra que toda a sintaxe da linguagem precisa ser escrita na forma de uma das gramáticas formais sem contexto, por exemplo, BNF? Mas é difícil para programadores iniciantes dominá-los imediatamente e, além disso, nem todas as regras possíveis com essas gramáticas podem ser descritas. Nesses casos, se você estiver parado e não puder formular uma certa regra na forma correta, poderá anotá-la como comentários na linguagem humana natural, por exemplo, assim:

... <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 

Tradução: o final do link ">" e o elemento de imagem / linha "]" não podem seguir imediatamente o início de uma linha ou documento .

Ou seja, se no início da linha o lexer encontrar "]" ou ">", devemos instruí-lo a ignorar o significado especial desses caracteres e trabalhar com eles como no texto simples. Essa maneira de adicionar comentários à gramática não é a única; você pode fazer do seu jeito. No final, o arquivo com a descrição da sintaxe não é um documento final, ninguém obriga a seguir todas as regras e é importante que você se sinta à vontade para trabalhar com ele. O principal é não esquecer os comentários feitos e refleti-los nas seções corretas do código.

Vejamos uma descrição completa desse idioma:

 <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 

Na última vez, foi necessário escrever os terminais e verificar cada caractere recebido quanto à conformidade com um deles. Mas então os terminais eram de um caractere! Agora, além de destacar os terminais, é necessário dividi-los em chaves - ou seja, caracteres. Por que as "chaves"? Eles são a chave para o lexer. Como resultado de todas as ações, as seguintes linhas aparecerão no arquivo de gramática:

 (* TERMINALS *) "___...", "\n", "\n\n", "> ", ">>...", "###...[", "*[", "%[", "_[", "^[", "~[", "&[", "+[", "=[", "`[", "]", "<<", "[<", ">", " @... ", "\n@...\n", " @...\n", "\n@... ", "***... ", "+++... ", "123.56. " (* KEYS *) "_", "\n" ">", "#", "*", "%", "^", "~", "&", "+", "=", "`", "<", "[", "]", " ", "@", "1..9", ".", <END> 

Pilha de tipos esperados de tokens


Da última vez, novamente, tudo era mais simples, tínhamos apenas 10 tipos de tokens, sem contar o final, e havia menos chance de ficarmos confusos nesse zoológico de tokens. Agora, obviamente, existem mais tipos. Deixe-me lembrá-lo de que a tarefa do lexer é deixar o analisador com o mínimo de trabalho possível, idealmente, apenas construindo uma árvore. Portanto, o conjunto de tipos de tokens deve refletir sua essência com a maior precisão possível. No primeiro artigo, dei um exemplo de um bom conjunto, neste, darei um "anti-exemplo". Veja os terminais que iniciam os elementos de texto embutido (negrito - negrito, itálico - itálico etc.)? Poderíamos analisá-los em um par de fichas: o mestre ("*", "%" etc.) e o escravo ("[") e passá-lo na forma para o analisador. É fácil adivinhar que é melhor fazer uma definição exata de um elemento de texto no nível lexer, ou seja, defina "* [" como "bold_start", "% [" como "italic_start" etc.) Quanto mais tipos e com mais precisão eles se refletem - melhor. Além disso, o segundo é mais importante que o primeiro. Por exemplo, poderíamos analisar a notificação para o símbolo "@" e o nome de usuário, mas obviamente é melhor deixá-los combinados em um token.
Bem, decidimos os tipos. Por onde começar o procedimento para analisar texto em tokens? Como então, comece do começo. O que pode seguir imediatamente o início do documento analisado? Não se apresse para dobrar os dedos. Ao contrário do HTML, todos os 22 tipos aqui podem começar. Está tudo bem, armado com a unificação de bits, escrevemos assim:

 curr_token_type = TEXT | UNDERLINE | TITLE_START | QUOTE_START | CITE | BOLD_START | ... 

e na função de processamento de símbolos:

 case TEXT | UNDERLINE | TITLE_START | QUOTE_START | CITE | ... 

Se você não entender o que está em jogo, leia o primeiro artigo .

Não tenha medo do tipo genérico longo de token esperado. O primeiro caractere da string reduz imediatamente seu comprimento para 2-4 tipos. Como nossos terminais são de vários caracteres, a definição é baseada nas chaves.

É simples, veja você mesmo:

 if (c == '_') { buffer.push_back('_'); curr_token_type = TEXT | UNDERLINE | UNDERLINED_START; 

O sublinhado determinou imediatamente o token em construção para um dos três tipos: texto sem formatação, linha horizontal ou o início do texto sublinhado ("_ [").

Voltando ao problema, como acompanhar todos os tipos genéricos e lembre-se de processá-los todos? Pegue uma pilha ... em um notebook! É isso mesmo, anote todos os tipos genéricos que aparecem após "curr_token_type = ..." na lista e, depois de processar um, retire o outro da lista do final. Você pode organizar o trabalho com a lista e, como na fila, isso não importa muito. O principal é que você não esquece quais tipos já foram processados ​​e quais ainda precisam ser processados.

Árvore de classes


Finalmente, chegamos à análise. Aqui você precisa determinar as classes de nós (nós) da futura árvore da mesma maneira que determinamos com os tipos de tokens. Para fazer isso, abra o notebook novamente e escreva o seguinte:

 Node { Node * parent, Node_type type } #- Root { Root_item[] children, ulong children_count } 

Então, definimos a classe base futura de todos os nós e sua derivada - a raiz da árvore, ou seja, o próprio documento. Um documento (consulte o BPF acima) consiste em dois tipos de nós: uma seção e uma linha horizontal (sublinhado). Definimos a classe base Root_item para eles e descrevemos cada um da mesma maneira que descrevemos a raiz. Além disso, aqui, no bloco de notas, indicamos imediatamente todos os outros campos das classes, se houver. Para a raiz, esse é o número de "filhos" - isto é, seções internas e linhas horizontais. A seção consiste em elementos para os quais definiremos a classe base Div e assim por diante, movendo-se recursivamente pela gramática, determinaremos todas as classes necessárias. Antes de escrever o código, definimos aqui toda a inclusão de cabeçalhos. É simples: todos os descendentes diretos de classes generalizadas básicas devem ser incluídos nas classes que os contêm.

Denotamos essas dependências na forma de listas após a rede e obtemos o seguinte documento:

 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 } #- 

Aqui marquei "# -" a ausência de dependências e removi a inclusão de classes em si mesmas.
Percebemos que todas as classes de formatação internas (negrito, itálico, ...) dependem uma da outra e, além disso, da classe Link, que também depende delas! Em uma posição semelhante estão Unordered_list e Ordered_list. Incluir cabeçalhos um no outro não apenas levará a ignorar um deles, como esperado, mas também não passará na validação pelo pré-processador, e a inclusão unidirecional não nos permitirá declarar dentro da classe incluída a função de abrir o elemento de classe e retornar o link para ele. Como ser Existem duas maneiras.

Inclusão de aulas uma na outra


Primeiro, observe as classes Negrito, Itálico e assim por diante no Monospace. Eles são parecidos. Tanto que eles podem ser combinados em uma classe "Inline". Talvez essa decisão levante dúvidas. Isso também me causou, mas na prática, a diferença entre eles afetou apenas a forma de apresentação na forma de uma árvore no terminal e as tags em HTML. Se você perceber que algumas classes contêm os mesmos campos, têm as mesmas dependências e geralmente têm uma descrição semelhante na gramática formal, fique à vontade para combiná-las. Então você facilita as coisas para você e o processador.

Mas esse truque não funcionará com a classe Link, porque contém um campo adicional - a string da URL. Vamos usar o segundo método.

Todo mundo sabe que separar classes em declarações e definições é uma boa forma de programação em C ++? No cabeçalho com a extensão .h ou .hpp - declaração, na fonte com a extensão .cpp - definição, certo? E agora recorro aos novatos em programação: sente-se e aperte o cinto, porque será desagradável. Afinal, o que prescrevemos em um arquivo com a extensão .h nada mais é do que uma definição de classe. E no arquivo .cpp, já existe uma implementação dos métodos dessa classe. Não entende? Nós fomos enganados na escola. Uma classe é declarada como uma função, em uma única linha, se não contiver modelos.

É ainda mais simples que uma função, porque não possui argumentos. Aqui está uma declaração de classe típica:

 class MyClass; 

E é isso aí! E declarações de campos e métodos já são sua definição .

Vamos tirar proveito disso. Incluímos o título da classe Inline no título da classe Link e nela declaramos a própria classe Link antes de definir a classe Inline. O arquivo inline.h deve ficar assim:

 #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); ... 

A classe Inline ainda não sabe nada sobre a classe Link, seus campos e métodos, mas sabe com certeza sobre sua existência. Portanto, podemos declarar métodos que retornam um ponteiro para um objeto da classe Link ou aceitá-lo como argumento. A palavra ponteiro não foi selecionada aleatoriamente, a classe Inline ainda não sabe como criar objetos do tipo Link, pois não tem acesso ao seu construtor, mas pode trabalhar com todos os ponteiros, porque todos eles têm a mesma interface. Mas não precisamos de objetos aqui. Mas na implementação do método open_link, um objeto do tipo Link é criado e um ponteiro para ele é retornado, o que significa que, no momento em que o pré-processador entra nesse método, o construtor e outros métodos de Link que o método open_link possa precisar devem ser declarados. Aqui aproveitamos a divisão do código fonte em arquivos separados com cabeçalhos e implementação. O arquivo inline.h está incluído no arquivo inline.cpp (“undercloud”), mas o arquivo link.h não está incluído no inline.h. Portanto, incluí-lo no inline.cpp será a primeira inclusão do pré-processador. Em seguida, o arquivo inline.cpp começará assim:

 #include "inline.h" #include "link.h" ... 

Repito todas as opções acima. O cabeçalho da classe Ah é incluído no cabeçalho da classe Bh, como de costume, e a classe B é declarada antes da classe A e incluímos seu cabeçalho na fonte A.cpp. Este método não é o único, mas o mais simples, na minha opinião.

Observo que essa inclusão mútua de classes não impede que a classe B seja herdada da classe A, se escrevermos sua declaração antes da definição da classe A. Foi exatamente isso que eu fiz, herdando Ordered_list de Unordered_list.

Construindo uma árvore


Então, chegamos à construção de uma árvore de sintaxe abstrata. No último artigo, a função coube em 50 linhas. Spoiler: desta vez, chegou a quase 1400. O princípio de operação é o mesmo: verificamos o tipo de cada token e, dependendo dele, executamos uma certa seção de código, armazenando um nó de árvore aberto na memória. Mas se para analisar HTML quase todas as seções continham um e apenas um dos três comandos: adicione um nó vazio ao aberto, abra um novo nó no aberto e feche o nó aberto, retornando seu pai, a ação desejada aqui também depende do tipo de nó aberto. Por exemplo, se o token “linha horizontal” entrou em processamento e o nó aberto é a raiz do documento, tudo o que é necessário é adicionar uma linha a esse nó aberto usando a conversão e a função com o nome condicional add_line (), algo como isto:

 if (type == Node::ROOT) static_case<Root*>(open_node)->add_line(); 

Mas se o nó aberto for um parágrafo (Parágrafo), primeiro você precisará fechá-lo e todos os ancestrais possíveis (listas com marcadores e numeradas) até que o nó aberto se torne do tipo "seção" e, em seguida, feche-o também:

 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(); } 

Se o nó aberto for uma legenda para a imagem, a linha horizontal interromperá totalmente a gramática, e uma exceção deverá ser lançada. Se o nó aberto não for um link e o token de entrada ">" tiver o tipo "LINK_FINISH", não deverá ser processado como o final do link, mas como texto etc.

Portanto, a árvore do comutador / caso, que verifica o tipo do token recebido, deve conter outra árvore do comutador / caso, que verifica o tipo do nó aberto. A princípio, é difícil lidar com essa construção, mas não é necessário começar do início, desde a primeira condição. Você pode criar um documento padrão marcado pelo seu idioma / contendo um script no seu idioma e implementar condições ao longo do documento, verificando o resultado enviando uma árvore pseudográfica para o terminal. Peguei o artigo anterior como um documento desse tipo, o primeiro token recebido é o início do título. Portanto, processamos o token com o tipo TITLE_START. A seguir, o texto do cabeçalho e o colchete de fechamento, processamos tokens dos tipos TEXT e SPAN_OR_IMAGE_FINISH.

Depois disso, já teremos uma mini-árvore:

 <article> | +-<section> | +-<h1> | +-"   DOM  " 

Ao longo do caminho, você notará que algumas classes incluem os mesmos métodos com os mesmos algoritmos. Por exemplo, as classes de parágrafos de parágrafos e as aspas entre aspas abrem os links da mesma maneira e adicionam texto a elas. Nesses casos, a melhor solução ao refatorar é criar uma classe com esses métodos e herdar os nós necessários. Tentei implementar isso, mas minhas habilidades não eram suficientes e fiquei confuso com a ambiguidade ao lançar, por isso, apenas dou os resultados do lexer e do analisador:

O artigo em si
 @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>>. , ,      . 

Tokens do lexer
 0: ["2che": NOTIFICAÇÃO]
1: ["
": NEWLINE]
2: ["442964": CITE]
3: ["# [": TITLE_START]
4: [“A arte de analisar ou o DOM faça você mesmo”: TEXTO]
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 ]


Árvore de sintaxe
 <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> 

Tudo está ótimo, mas há muitos nós de texto entrando em cadeia um após o outro. Além disso, gostaria que as aspas seguidas fossem combinadas em uma. Para fazer isso, precisamos percorrer a árvore novamente e executar concatenação , isto é, a adesão de elementos homogêneos entre si. Não vou explicar os detalhes desse processo, anexarei a fonte, mas, por enquanto, veja como nossos

Árvore após concatenação:
 <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> 

O último passo que resta é representar essa árvore em um formulário HTML. Tudo é simples aqui: criamos uma linha no método raiz com o início da marcação principal (abrindo elementos html e body, um bloco de cabeçalho) e começamos a anexar as linhas retornadas de elementos filhos semelhantes. Aqui estamos lidando com uma descida recursiva novamente: cada classe, quando o método virtual to_HTML () é chamado, cria uma linha, coloca sua marcação principal nela, depois chama o mesmo método em cada um de seus descendentes, concatena as linhas, completa a marcação primária e a retorna ao pai que chama. Aqui, por exemplo, se parece com este método para a classe Inline (combinando elementos inline formatados):
 string Inline::to_HTML (const unsigned int &level) { string HTML; // ******    -   ****** switch (type) { case BOLD: { HTML.append("<b>"); break; } case ITALIC: { HTML.append("<i>"); break; } case UNDERLINED: { HTML.append("<ins>"); break; } case OVERLINED: { HTML.append("<span class=\"overlined\">"); break; } case THROWLINED: { HTML.append("<del>"); break; } case SUBSCRIPT: { HTML.append("<sub>"); break; } case SUPERSCRIPT: { HTML.append("<sup>"); break; } case MARKED: { HTML.append("<mark>"); break; } case MONOSPACE: { HTML.append("<pre>"); break; } default: throw string("invalid inline type: " + type_str() + "!"); } // *** *** *** *** // ******   -    ****** for (unsigned long i(0); i < children_count; i++) HTML.append(children[i]->to_HTML(level+1)); // *** *** *** *** // ******    -   ****** switch (type) { case BOLD: { HTML.append("</b>"); break; } case ITALIC: { HTML.append("</i>"); break; } case UNDERLINED: { HTML.append("</ins>"); break; } case OVERLINED: { HTML.append("</span>"); break; } case THROWLINED: { HTML.append("</del>"); break; } case SUBSCRIPT: { HTML.append("</sub>"); break; } case SUPERSCRIPT: { HTML.append("</sup>"); break; } case MARKED: { HTML.append("</mark>"); break; } case MONOSPACE: { HTML.append("</pre>"); break; } default: throw string("invalid inline type: " + type_str() + "!"); } // *** *** *** *** return HTML; } 

Isso é tudo.Espero que agora, depois de ler os dois artigos, você possa implementar facilmente um tradutor para sua marcação ou linguagem de programação. Se você tiver alguma dúvida, pergunte nos comentários. E aqui estão as fontes . Sucessos.

PS Eu esqueci de mencionar blindagem . Ele é implementado simplesmente: se o próximo caractere no procedimento de análise lexical for a barra invertida ("\"), ele será ignorado e o próximo caractere será processado, mas, além disso, o valor booleano true será enviado à função de processamento de caracteres, dando o comando para escapar. Então, se esse símbolo, por exemplo, for "[", seu significado especial será ignorado e simplesmente unirá o token em construção como texto. Caso contrário, a função retornará false e o caractere será processado como de costume.

Source: https://habr.com/ru/post/pt444876/


All Articles