Статьи

Karel ANTLR, интегрированный в платформу NetBeans

Милош Силханек дал мне файл ANTLR, определяющий язык программирования Karel . Давайте теперь создадим редактор для этого.

  1. Прежде всего, давайте посмотрим на файл ANTLR:

    grammar AntlrKarel;
    
    
    options {
        language = Java;
        output = AST;  
        k=1;                   // character lookahead
    }
     
    tokens {
            KWD_STEP;
            KWD_LEFT;
            KWD_RIGHT;
            KWD_PUT;
            KWD_PICK;
            KWD_STOP;
            KWD_IF;
            KWD_WHILE;
            KWD_REPEAT;
            KWD_ELSE;
    	KWD_END;
            KWD_TIMES;
            KWD_DEFINE;
    	KWD_AS;
    	KWD_IS;
    	KWD_NOT;
    	KWD_WALL;
    	KWD_SIGN;
    	KWD_NORTH;
    	KWD_EAST;
    	KWD_SOUTH;
    	KWD_WEST;
            IDENTIFIER;
            NUMBER;
            LINECOMMENT;
            WS;
            THEN;
            BLOCK;
            COND;
            CALL;
    }
    
    
    @header {
    package cz.uf.karel.core.lang;
    }
    
    @lexer::header {
    package cz.uf.karel.core.lang;
    
    import cz.uf.karel.core.KarelKeyWords;
    import java.util.Locale;
    import java.util.HashMap;
    import java.util.Map;
    }
    
    @lexer::members {
    
        private List syntaxErrors = new ArrayList(); 
    
      
    
            public AntlrKarelLexer(CharStream input, RecognizerSharedState state
                    , Locale locale) {
                super(input, state); 
             }
    
      
        public List getSyntaxErrors() {
            return syntaxErrors;
        }
    
        @Override
        public String getErrorMessage(RecognitionException e, String[] tokenNames) {
                String message = super.getErrorMessage(e, tokenNames);
                SyntaxError syntaxError = new SyntaxError();
                syntaxError.exception = e;
                syntaxError.message = message;
                syntaxError.line = e.line;
                syntaxError.charPositionInLine = e.charPositionInLine;
                syntaxErrors.add(syntaxError);
                return message;
        }
    
        @Override
        public void emitErrorMessage(String msg) {
            super.emitErrorMessage(msg);
        }
    
    /*@Override
    public void reportError(RecognitionException e) {
      throw e;
    }*/
    }
    
    @parser::members {
    
        private List syntaxErrors = new ArrayList();
        private boolean incomment = false;
    
        public List getSyntaxErrors() {
            return syntaxErrors;
        }
    
        @Override
        public String getErrorMessage(RecognitionException e, String[] tokenNames) {
                String message = super.getErrorMessage(e, tokenNames);
                SyntaxError syntaxError = new SyntaxError();
                syntaxError.exception = e;
                syntaxError.message = message;
                syntaxError.line = e.line;
                syntaxError.charPositionInLine = e.charPositionInLine;
                syntaxErrors.add(syntaxError);
                return message;
        }
    
    
        @Override
        public void emitErrorMessage(String msg) {
            super.emitErrorMessage(msg);
        }
    
    /*
        protected void mismatch(IntStream input, int ttype, BitSet follow)
            throws RecognitionException
        {
            throw new MismatchedTokenException(ttype, input);
        }
    
        @Override
        public Object recoverFromMismatchedSet(IntStream input,
                            RecognitionException e,
                                BitSet follow)
                throws RecognitionException
        {
            throw e;
        }
    }
    
        // Alter code generation so catch-clauses get replace with
        // this action.
        @rulecatch {
            catch (RecognitionException e) {
                throw e;
        }
        */
    }
    
    
    /* ********-******************    PARSER   ******************************* */
    
    
    
    parseSource 	:	 ( statement | definition )*;
    
    statement 	:
                satomic
      	  | swhile
      	  | srepeat
              | sif
              | linecomment
              | command  // TODO change token.type=CALL ?
    ;
    
    definition
    	:
                        KWD_DEFINE name KWD_AS
    	              block
    	            KWD_END
                  ->
                     ^(KWD_DEFINE name
    	              ^(block)
    	          )
                  ;
    
    
    swhile
    	:	 KWD_WHILE conditionexpr
    	                 block
    	         KWD_END
                  ->
                     ^(KWD_WHILE conditionexpr block)
                 ;
    
    srepeat
    	:	 KWD_REPEAT  NUMBER    KWD_TIMES
    	                   block
    	         KWD_END
                     -> ^(KWD_REPEAT NUMBER block)
                 ;
    
    sif
    	:	 KWD_IF conditionexpr
    	              b1=block
    	         (KWD_ELSE
    	              b2=block
    	          )?
    	         KWD_END
    
                       -> ^( KWD_IF conditionexpr ^(THEN $b1) ^(KWD_ELSE $b2)? )
                     ;
    
    block 	:	 (statement)*
                   ->  ^( BLOCK (statement)* )
                   ;
    
    conditionexpr
    	:	 condprefix condition
                       -> ^(COND condprefix condition)
                    ;
    
    condprefix
    	:	 ( KWD_IS | KWD_NOT );
    
    condition
    	:
    	(
              KWD_WALL
    	| KWD_SIGN
    	| KWD_EAST
    	| KWD_SOUTH
            | KWD_WEST
    	| KWD_NORTH
    	)
    	;
      
     
    linecomment
    	:
                 LINECOMMENT^
    //               -> ^(LINECOMMENT
    //              { $LINECOMMENT.setText($LINECOMMENT.getText().trim()); }
    //                )
                   ;
    
    
    command
    	:	 IDENTIFIER
                     -> ^(CALL IDENTIFIER)
                    ;
    
    name
    	:	 IDENTIFIER
                     -> ^(IDENTIFIER)
                    ;
     
    
    satomic :
               KWD_STEP
             | KWD_LEFT
             | KWD_RIGHT
             | KWD_PUT
             | KWD_PICK
             | KWD_STOP
               ;
    
    KWD_STEP   : 'KWD_STEP';
    KWD_LEFT   : 'KWD_LEFT';
    KWD_RIGHT  : 'KWD_RIGHT';
    KWD_PUT    : 'KWD_PUT';
    KWD_PICK   : 'KWD_PICK';
    KWD_STOP   : 'KWD_STOP';
    
    KWD_DEFINE : 'KWD_DEFINE';
    KWD_IF     : 'KWD_IF';
    KWD_WHILE  : 'KWD_WHILE';
    KWD_REPEAT : 'KWD_REPEAT';
    KWD_ELSE   : 'KWD_ELSE';
    KWD_TIMES  : 'KWD_TIMES';
    KWD_AS     : 'KWD_AS'; 
    KWD_END    : 'KWD_END';
    
    KWD_IS     : 'KWD_IS';
    KWD_NOT    : 'KWD_NOT';
    
    KWD_WALL   : 'KWD_WALL';
    KWD_SIGN   : 'KWD_SIGN';
    
    KWD_NORTH  : 'KWD_NORTH';
    KWD_EAST   : 'KWD_EAST';
    KWD_SOUTH  : 'KWD_SOUTH';
    KWD_WEST   : 'KWD_WEST';
    
    IDENTIFIER  :	('a'..'z'|'A'..'Z'|'_'|'?') ('a'..'z'|'A'..'Z'|'0'..'9'|'_'|'-')*
        ;
    
    NUMBER  :	('0'..'9')+
        ;
    
    LINECOMMENT options {greedy=false;}
        :   '#' (~('\n'|'\r')*) 
        ;
        
    WS  :   ( ' '
            | '\t'
            | '\r'
            | '\n'
            ) {$channel=HIDDEN;}
        ;
    
    
    
    
    /*   Tree parser   */
    
    
    
    
  2. Далее давайте расширим возможности IDE NetBeans для понимания файлов ANTLR. Вот плагин NetBeans ANTLR Editor , но учтите, что он не работает «из коробки» для IDE NetBeans 7.0 и 7.0.1. Я должен был проверить репозиторий hg из Kenai , затем удалить модуль «Библиотека редактора», повторно добавить его, а затем смог собрать его. Обратите внимание, что этот плагин использует устаревшую платформу Шлимана (GLF), но это не проблема, просто установите плагин в IDE, и все готово. Хорошая окраска синтаксиса — ваша награда за этот шаг, а также навигатор:

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

  3. Теперь мы выполним эти шаги, чтобы присоединить и использовать инструмент ANTLR. Я нашел эти инструкции действительно очень хорошими. Я создал новую библиотеку классов Java, сгенерировал парсер и лексер, как описано в этих шагах, а затем просто следовал всему остальному, описанному здесь . Я закончил с приложением, состоящим из двух модулей:

    Затем я запустил приложение, и в окне «Параметры» были показаны мои шрифты и цвета:

    И вот раскраска синтаксиса в действии. Обратите внимание, что я не совсем понимаю синтаксис Karel, поэтому просто бросил несколько вещей в этот файл:

Это все. Обратите внимание, что Милош Силханек имеет довольно обширный плагин поддержки Karel для IDE NetBeans здесь:

http://kenai.com/projects/karelnb/sources/mercurial/show

Тем не менее, я начал эту статью с нуля, просто чтобы посмотреть, сколько работы требуется для интеграции файла ANTLR, как первого шага, в платформу NetBeans, то есть первый шаг подразумевает окрашивание синтаксиса. И, как указывалось выше, оказалось, что работы совсем немного, благодаря различным доступным ресурсам.