Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: Общие вопросы > ANTLR. Многострочные комментарии. Обработка в коде


Автор: PenDaLegi 14.12.2010, 21:36
Всем доброго времени суток!

Используя http://www.antlr.org/ пытаюсь разработать лексер (для начала только лексер) для подсветки синтаксиса. Столкнулся с такой бедой. После генерации лексера почти все работает так как нужно, однако как только ввожу символы многострочного комментария валится экзепшн. Ниже определение комментариев в граммере: 

Код

TOKEN_COMMENT1  : '(*' ( . )* '*)';


Код

TOKEN_COMMENT2  : '{'  ( . )* '}'  ;


Все это добро разрабатывается, как модуль к NetBeans. Вот вырезка кода лексера

Код


import org.antlr.runtime.*;
import org.netbeans.spi.lexer.Lexer;
import org.netbeans.spi.lexer.LexerRestartInfo;

public class MyLexer  implements Lexer<MyTokenId> {
    private LexerRestartInfo<MyTokenId> info;

    private MyScriptLexer lexer;

    public MyLexer(LexerRestartInfo<MyTokenId> info) {
        this.info = info;
        AntlrCharStream charStream = new AntlrCharStream(info.input(), "My Script Editor");
        lexer = new MyScriptLexer(charStream);
    }

    public org.netbeans.api.lexer.Token<MyTokenId> nextToken() {
        Token token = lexer.nextToken();
        if (token.getType() != MyScriptLexer.EOF) {
            MyTokenId tokenId = MyLanguageHierarchy.getToken(token.getType());
            return info.tokenFactory().createToken(tokenId);
        }
        return null;
    }
...


Проблема, насколько мне удалось понять, связана с тем, что лексер фиксирует начало комментария, но не может найти его конец. в результате валится экзепшн:

Цитата

SEVERE [global]
java.lang.IllegalStateException: Lexer com.lexer.MyLexer@c91010
  returned null token but lexerInput.readLength()=91
  lexer-state: null
  tokenStartOffset=14, readOffset=105, lookaheadOffset=106
  Chars: "(*)\nprocedure simple(temp1:integer;temp2,temp3:string);\nbegin\n    //do somthing\nend;\n\nEnd.\n" - these characters need to be tokenized.
Fix the lexer to not return null token in this state.


При попытке ввести сочетание "(*" вот в этом коде
Код

(*
procedure simple(temp1:integer;temp2,temp3:string);
begin
    //do somthing
end;

End.


Если же предварительно где-то ввести символы закрывающие многострочный комментарий, то ошибки не возникает

Ну а вопрос в том, можно ли как-то изменить определение многострочного комментария в граммере ANTLR чтобы не писать код обработки в классе лексера.

Пересмотрел много буржуйных ресурсов, однако так и не удалось найти конкретного решения этой проблемы.
Надеюсь, хотя и не сильно, что кто-то сталкивался с подобной ошибкой при разработкой синтаксических анализаторов используя ANTLR =)

Автор: dobrolub 14.12.2010, 23:16
глянь на определение для COMMENT_1/2 в pascal.g : http://www.antlr.org/grammar/1279217060704/pascal3.zip

Код

COMMENT_1
        : '(*'
           (
          // ( options { generateAmbigWarnings=false; }                                                        
           :    {input.LA(2) != ')' }? '*'
           |    '\r' '\n'   //  {newline();}                                                                   
           |    '\r'        //  {newline();}                                                                   
           |    '\n'        //  {newline();}                                                                   
           |   ~('*' | '\n' | '\r')
           )*
          '*)'
        {$channel=HIDDEN;}
    ;

COMMENT_2
        :  '{'
           (
           // ( options {generateAmbigWarnings=false;}                                                         
            :   '\r' '\n'      // {newline();}                                                                 
            |   '\r'            //{newline();}                                                                 
            |   '\n'            //{newline();}                                                                 
            |   ~('}' | '\n' | '\r')
            )*
           '}'
        {$channel=HIDDEN;}
    ;


Автор: PenDaLegi 14.12.2010, 23:53
Это уже пробовал =( Кроме того нынешняя мода ANTLR говорит что такие описания комментариев уже устарели http://www.antlr.org/wiki/pages/viewpage.action?pageId=1573

В выше приведенном мною примере убраны дополнительные опции. Но я пробовал всячески, пока для всех вариантов исход один и тот же - падает исключение.

Проблему удалось временно решить доработав код лексера, но, на мой взгляд решение не очень правильное. Правильнее было бы корректно определить  комментарии именно в граммере. Ниже используемая заглушка:

Код

    public org.netbeans.api.lexer.Token<MyTokenId> nextToken() {
        Token token = lexer.nextToken();
        if (token.getType() != MyScriptLexer.EOF) {
            MyTokenId tokenId = MyLanguageHierarchy.getToken(token.getType());
            return info.tokenFactory().createToken(tokenId);
        }
        LexerInput input = info.input();
        StringBuffer sb = new StringBuffer(lexer.getText());
        int indexOfOpen = sb.toString().split("\\{").length - 1;
        int indexOfClos = sb.toString().split("}").length - 1;
        if (indexOfOpen > indexOfClos) {
             return info.tokenFactory().createToken(MyTokenId.TOKEN_COMMENT2);
        }
        indexOfOpen = sb.toString().split("\\(*").length - 1;
        indexOfClos = sb.toString().split("\\*"+"\\)").length - 1;
        if (indexOfOpen > indexOfClos) {
            return info.tokenFactory().createToken(MyTokenId.TOKEN_COMMENT1);
        }
        return null;
    }




PS. Собственно http://wiki.netbeans.org/New_Language_Support_Tutorial_Antlr есть туториал как добавить поддержку нового языка. Изначально код брал там.

Автор: ivg 15.12.2010, 07:10
Код

TOKEN_COMMENT1  : '(*' (options {greedy=false;} : . )* '*)';
?

Автор: aleksandy 15.12.2010, 08:45
Не знаю поможет или нет, но такой регэксп не учитывает много нюансов. 
Код

"\\(\\*.*\\*\\)"

Вот так можно найти pascal-style комментарии.
Код

    // Самая сложная часть всего регэкспа - это регэксп для многострочного комментария.
    // Разберемся, что означает это монструозное выражение
    // 1 \\(\\* - начало комментария
    // 2 [^*]*\\*+ - текст комментария до одного или нескольких символов "*"
    // 3 (?:[^/*][^*]*\\*+)* - группа, повторяющаяся 0 или более число раз:
    //  3.1 [^\\)*] - символ отличный от закрывающей скобки и не являющийся "*"
    //  3.2 то же, что и (2)
    // 4 \\) - конец комментария
    private final static String COMMENTS_FIND_REGEXP = "(" + // начало группы
                                                       "'[^']*?'" + // предопределенная строка
                                                       ")" + // конец группы
                                                       "|" + // или
                                                       "\\(\\*[^*]*\\*+(?:[^\\(*][^*]*\\*+)*\\)"; 


Монстра взял из книжки Дж.Фридла "Регулярные выражения"

Автор: PenDaLegi 15.12.2010, 11:23
Цитата(aleksandy @ 15.12.2010,  08:45)
Не знаю поможет или нет, но такой регэксп не учитывает много нюансов. 
Код

"\\(\\*.*\\*\\)"

Вот так можно найти pascal-style комментарии.
Код

    // Самая сложная часть всего регэкспа - это регэксп для многострочного комментария.
    // Разберемся, что означает это монструозное выражение
    // 1 \\(\\* - начало комментария
    // 2 [^*]*\\*+ - текст комментария до одного или нескольких символов "*"
    // 3 (?:[^/*][^*]*\\*+)* - группа, повторяющаяся 0 или более число раз:
    //  3.1 [^\\)*] - символ отличный от закрывающей скобки и не являющийся "*"
    //  3.2 то же, что и (2)
    // 4 \\) - конец комментария
    private final static String COMMENTS_FIND_REGEXP = "(" + // начало группы
                                                       "'[^']*?'" + // предопределенная строка
                                                       ")" + // конец группы
                                                       "|" + // или
                                                       "\\(\\*[^*]*\\*+(?:[^\\(*][^*]*\\*+)*\\)"; 


Монстра взял из книжки Дж.Фридла "Регулярные выражения"

aleksandy: спасибо, очень даже пригодится

Добавлено через 4 минуты и 3 секунды
Цитата(ivg @ 15.12.2010,  07:10)
Код

TOKEN_COMMENT1  : '(*' (options {greedy=false;} : . )* '*)';
?

Да, определение данного токена не фиксирует конца кооментария. Лексер ищет символы конца комментария "*)" натыкается на EOF и вместо токена комментария регистрируется токен конца файла.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)