Рабочий пример wikitext-to-HTML в ANTLR 3
Я пытаюсь реализовать переводчик wikitext-to-HTML в ANTLR 3, но я застреваю.
Вы знаете рабочий пример, который я могу проверить? Я попробовал грамматику MediaWiki ANTLR и грамматику Wiki Creole, но не могу заставить их генерировать лексер и парсер в ANTLR 3.
Вот ссылки на две грамматики, которые я пробовал использовать:
- http://www.mediawiki.org/wiki/Markup_spec/ANTLR
- http://www.wikicreole.org/wiki/EBNFGrammarForCreole1.0
Я не могу заставить ни одного из этих двух генерировать мой Java Lexer и Parser. (Я использую ANTLR3 в качестве плагина Eclipse). MediaWiki требует много времени для сборки, а затем в какой-то момент выдает исключение OutOfMemory. В другом есть ошибки, которые я не знаю, как отлаживать.
РЕДАКТИРОВАТЬ: Хорошо, у меня есть очень базовая грамматика:
grammar wikitext;
options {
//output = AST;
//ASTLabelType = CommonTree;
output = template;
language = Java;
}
document: line (NL line?)*;
line: horizontal_line | list | heading | paragraph;
/* horizontal line */
horizontal_line: HRLINE;
/* lists */
list: unordered_list | ordered_list;
unordered_list: '*'+ content;
ordered_list: '#'+ content;
/* Headings */
heading: heading1 | heading2 | heading3 | heading4 | heading5 | heading6;
heading1: H1 plain H1;
heading2: H2 plain H2;
heading3: H3 plain H3;
heading4: H4 plain H4;
heading5: H5 plain H5;
heading6: H6 plain H6;
/* Paragraph */
paragraph: content;
content: (formatted | link)+;
/* links */
link: external_link | internal_link;
external_link: '[' external_link_uri ('|' external_link_title)? ']';
internal_link: '[[' internal_link_ref ('|' internal_link_title)? ']]' ;
external_link_uri: CHARACTER+;
external_link_title: plain;
internal_link_ref: plain;
internal_link_title: plain;
/* bold & italic */
formatted: bold_italic | bold | italic | plain;
bold_italic: BOLD_ITALIC plain BOLD_ITALIC;
bold: BOLD plain BOLD;
italic: ITALIC plain ITALIC;
/* Plain text */
plain: (CHARACTER | SPACE)+;
/**
* LEXER RULES
* --------------------------------------------------------------------------
*/
HRLINE: '---' '-'+;
H1: '=';
H2: '==';
H3: '===';
H4: '====';
H5: '=====';
H6: '======';
BOLD_ITALIC: '\'\'\'\'\'';
BOLD: '\'\'\'';
ITALIC: '\'\'';
NL: '\r'?'\n';
CHARACTER : '!' | '"' | '#' | '$' | '%' | '&'
| '*' | '+' | ',' | '-' | '.' | '/'
| ':' | ';' | '?' | '@' | '\\' | '^' | '_' | '`' | '~'
| '0'..'9' | 'A'..'Z' |'a'..'z'
| '\u0080'..'\u7fff'
| '(' | ')'
| '\'' | '<' | '>' | '=' | '[' | ']' | '|'
;
SPACE: ' ' | '\t';
Мне не совсем понятно, как можно было бы выводить HTML. Я изучал StringTemplate, но я не понимаю, как структурировать мои шаблоны. В частности, какой шаблон идет в грамматике. Можете ли вы помочь мне с коротким примером?
1 ответ
Хорошо, после вашего редактирования у меня есть пара рекомендаций.
Как я сказал в комментариях, написание грамматики для такого языка практически невозможно. По крайней мере, пытаясь сделать это за один раз, то есть. Единственный способ увидеть эту работу - это сделать это с несколькими синтаксическими анализаторами, где первый "этап синтаксического анализа" будет очень "грубо" анализировать источник вики. Например: table
будет маркирован как: TABLE : '{|' .* '|}'
а затем вы создадите другой парсер, который правильно анализирует эту таблицу. Выполнение этого в одном парсере приведет к множеству двусмысленностей в ваших правилах парсера IMO.
Что касается испускания HTML-кода, то "правильный" способ сделать это действительно с StringTemplate, но, учитывая тот факт, что вы довольно плохо знакомы с самой ANTLR, я бы упростил задачу. Вы можете создать атрибут StringBuilder в своем классе анализатора, который будет собирать весь ваш HTML-код при разборе исходного файла. Вы можете встроить код в правила ANTLR, обернув его {
а также }
,
Вот короткая демонстрация:
grammar T;
@parser::members {
// an attribute that is only available in your
// parser (so only in parser rules!)
protected StringBuilder htmlBuilder = new StringBuilder();
}
// Parser rules
parse
: atom+ EOF
;
atom
: header
| Any {htmlBuilder.append($Any.text);} // append the text from 'Any' token
;
header
: H3 h3Content H3 {htmlBuilder.append("<h3>" + $h3Content.text + "</h3>");}
| H2 h2Content H2 {htmlBuilder.append("<h2>" + $h2Content.text + "</h2>");}
| H1 h1Content H1 {htmlBuilder.append("<h1>" + $h1Content.text + "</h1>");}
;
h3Content : ~H3*; // match any token except H3, zero or more times
h2Content : ~H2*; // " H2 "
h1Content : ~H1*; // " H1 "
// Lexer rules
H3 : '===';
H2 : '==';
H1 : '=';
// Fall through rule: if non of the above
// lexer rules matched, this one will.
Any
: .
;
Из этой грамматики вы генерируете парсер и лексер:
java -cp antlr-3.2.jar org.antlr.Tool T.g
а затем создайте небольшой класс для проверки вашего парсера:
import org.antlr.runtime.*;
public class Main {
public static void main(String[] args) throws Exception {
// the source to be parsed
String source =
"= header 1 = \n"+
" \n"+
"some text here \n"+
" \n"+
"=== header level 3 === \n"+
" \n"+
"and some more text ";
ANTLRStringStream in = new ANTLRStringStream(source);
TLexer lexer = new TLexer(in);
CommonTokenStream tokens = new CommonTokenStream(lexer);
TParser parser = new TParser(tokens);
// invoke the start-rule in your parser
parser.parse();
// print the contents of your parser's StringBuilder
System.out.println(parser.htmlBuilder);
}
}
и затем скомпилируйте все ваши исходные файлы:
javac -cp antlr-3.2.jar *.java
и, наконец, запустить свой основной класс
// *nix & MacOS
java -cp .:antlr-3.2.jar Main
// Windows
java -cp .;antlr-3.2.jar Main
который выведет на консоль следующее:
<h1> header 1 </h1>
some text here
<h3> header level 3 </h3>
and some more text
Но, опять же, если вы свободны в выборе другого языка для разбора, я бы сделал это и забыл о разборе этой ужасной вики-штуки.
В любом случае, что бы вы ни делали: удачи!