corrispondenti coppie di tag a Cima di grammatica
Domanda
Non voglio una ripetizione di la risposta Cthulhu , ma voglio abbinare coppie di apertura e chiusura tag HTML usando Cima di albero. Utilizzando questa grammatica , posso abbinare i tag di apertura e tag di chiusura , ma ora voglio una regola per legarli entrambi insieme. Ho provato quanto segue, ma usando questo rende la mia parser andare avanti per sempre (loop infinito):
rule html_tag_pair
html_open_tag (!html_close_tag (html_tag_pair / '' / text / newline /
whitespace))+ html_close_tag <HTMLTagPair>
end
I stava cercando di fondare questo fuori della parentesi esempio ricorsivo e l'esempio lookahead negativo sulla pagina Treetop Github . Le altre regole che ho a cui fa riferimento sono i seguenti:
rule newline
[\n\r] {
def content
:newline
end
}
end
rule tab
"\t" {
def content
:tab
end
}
end
rule whitespace
(newline / tab / [\s]) {
def content
:whitespace
end
}
end
rule text
[^<]+ {
def content
[:text, text_value]
end
}
end
rule html_open_tag
"<" html_tag_name attribute_list ">" <HTMLOpenTag>
end
rule html_empty_tag
"<" html_tag_name attribute_list whitespace* "/>" <HTMLEmptyTag>
end
rule html_close_tag
"</" html_tag_name ">" <HTMLCloseTag>
end
rule html_tag_name
[A-Za-z0-9]+ {
def content
text_value
end
}
end
rule attribute_list
attribute* {
def content
elements.inject({}){ |hash, e| hash.merge(e.content) }
end
}
end
rule attribute
whitespace+ html_tag_name "=" quoted_value {
def content
{elements[1].content => elements[3].content}
end
}
end
rule quoted_value
('"' [^"]* '"' / "'" [^']* "'") {
def content
elements[1].text_value
end
}
end
Lo so che avrò bisogno per consentire la corrispondenza singola apertura o la chiusura di tag, ma se un paio di tag HTML esiste, mi piacerebbe metterli insieme come una coppia. Sembrava più pulito di fare questo da loro corrispondenza con la mia grammatica, ma forse c'è un modo migliore?
Soluzione
È possibile farlo solo utilizzando una regola separata per ogni coppia di tag HTML, o utilizzando un predicato semantico. Cioè, salvando il tag di apertura (in un sempred), quindi accettare (in un'altra sempred) un tag di chiusura solo se è lo stesso tag. Questo è molto più difficile da fare in Cima di quanto dovrebbe essere, perché non c'è posto comodo per salvare il contesto e non si può sbirciare lo stack parser, ma è possibile.
A proposito, lo stesso problema si verifica nel parsing confini MIME (e in Markdown). Non ho controllato l'implementazione di Mikel in ActionMailer (probabilmente usa un parser nidificato Mime per questo), ma è possibile in Cima di albero.
http://github.com/cjheath /activefacts/blob/master/lib/activefacts/cql/parser.rb risparmio contesto in un flusso di input falso - è possibile vedere quali metodi si deve sostenere - perché "input" è disponibile su tutte SyntaxNodes. Ho un diverso tipo di ragione per usare sempreds lì, ma alcune delle tecniche sono applicabili.
Altri suggerimenti
Ecco una molto semplice grammatica che utilizza un predicato semantica per abbinare il tag di chiusura al tag di partenza.
grammar SimpleXML
rule document
(text / tag)*
end
rule text
[^<]+
end
rule tag
"<" [^>]+ ">" (text / tag)* "</" [^>]+ &{|seq| seq[1].text_value == seq[5].text_value } ">"
end
end