Aqui uma gramática "livre de contexto" para reconhecer XMLs em geral (sem entrar na especificidade dos XSDs).
A tag de identificação por hora tá sendo ignorada, a que tem a forma <?xml ... ?>
, mas seria só adicionar ela.
Para facilitar a escrita da gramática livre de contexto, estou usando aqui uma estratégia de "passagem de parâmetro".
Isso facilita a indicação, por exemplo, de tags que se fecham: <p></p>
, por exemplo, é derivado de TREE_NODE("p")
.
Para os casos de passagem de parâmetro, o "tipo" tá indicado. Por exemplo, a produção de TREE_NODE(TAG_NAME ::= IDENTIFICADOR_TAG)
indica que o parâmetro TAG_NAME
é feito como a produção IDENTIFICADOR_TAG
.
Para produção vazia, estou usando ε
para não deixar ambiguidades
ROOT_NODE ::= COMMENTS? TAG_NODE COMMENTS?
TAG_NODE ::= TREE_NODE | SELF_CONTAINED_NODE
SELF_CONTAINED_NODE ::= "<" IDENTIFICADOR_TAG TAG_ATTRS "/>"
TREE_NODE(TAG_NAME ::= IDENTIFICADOR_TAG) ::= "<" TAG_NAME TAG_ATTRS ">" NODES "</" TAG_NAME "/>"
NODES ::= ε | NODE | NODE NODES
NODE ::= TAG_NODE | TEXT_NODE | COMMENT_NODE
COMMENTS ::= ε | COMMENT_NODE | COMMENT_NODE COMMENTS
TAG_ATTRS ::= ε | TAG_ATTR | TAG_ATTR TAG_ATTRS
TAG_ATTR ::= ATTR_IDENTIFIER "=" "\"" ATTR_VALUE "\""
TEXT_NODE ::= /* texto aceito dentro do XML, não tem `<` nem `>` e tem os escapes XML como `>` */
ATTR_IDENTIFIER ::= /* identificador de atributo, provável que seja algo como `[][A-Za-z0-9_{}():]+` */
ATTR_VALUE ::= /* valor de um atributo; basicamente permite tudo, exceto quebras de linhas e aspas não escapadas; aceita algumas sequências de escapes */
IDENTIFICADOR_TAG ::= /* identificador de tag, creio que seja `[][A-Za-z0-9_{}():]+` */