Over a million developers have joined DZone.

Karel ANTLR Integrated into the NetBeans Platform

· Java Zone

Easily build powerful user management, authentication, and authorization into your web and mobile applications. Download this Forrester report on the new landscape of Customer Identity and Access Management, brought to you in partnership with Stormpath.

Milos Silhanek gave me an ANTLR file defining the Karel programming language. Let's now create an editor for it.

  1. First of all, let's look at the ANTLR file:
    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. Next, let's empower NetBeans IDE to understand ANTLR files. Here's the NetBeans ANTLR Editor plugin, but note that it doesn't work out of the box for NetBeans IDE 7.0 and 7.0.1. I had to check out the hg repo from Kenai, then remove the "Editor Library" module, re-add it, and then was able to build it. Note that this plugin uses the deprecated Schliemann (GLF) framework, but that's not a problem, just install the plugin into the IDE and you're good to go. Nice syntax coloring is your reward for this step, as well as a Navigator:

    As you can imagine, the plugin shown in action above is useful if you're creating ANTLR files (or, maybe, editing someone's existing ANTLR file), not so much for using or processing an ANTLR file, which is actually what we're doing in this article.


  3. Now we'll follow these steps to attach and use the ANTLR tool. I found those instructions to be very good indeed. I created a new Java Class Library, generated the parser and lexer as described in those steps and then just followed everything else described here. I ended up with an application consisting of two modules:

    Then I ran the application and the Options window showed my fonts & colors:

    And here's the syntax coloring in action. Note that I don't really understand the Karel syntax, so just threw a few things together into this file:

That's all. Note that Milos Silhanek has a pretty extensive Karel support plugin for NetBeans IDE here:

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

However, I started this article from scratch just to see how much work is involved in integrating an ANTLR file, as a first step, into the NetBeans Platform, i.e., a first step implies syntax coloring. And it turned out to not be much work at all, thanks to the various resources available, as pointed out above.

 

The Java Zone is brought to you by Stormpath—offering a complete, pre-built User Management API for building web and mobile applications, and APIs. Download our new whitepaper: "Build Versus Buy: Customer Identity Management for Web and Mobile Applications".

Topics:

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}