/* * Copyright (C) 2011 Laurent Caillette * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.novelang.parser.antlr; import java.util.Iterator; import java.util.List; import org.antlr.runtime.CommonToken; import org.antlr.runtime.MismatchedTokenException; import org.antlr.runtime.RecognitionException; import org.antlr.runtime.Token; import org.antlr.runtime.tree.RewriteEarlyExitException; import org.antlr.runtime.tree.Tree; import org.antlr.runtime.tree.TreeAdaptor; import org.novelang.common.Location; import org.novelang.common.LocationFactory; import org.novelang.common.Problem; import org.novelang.logger.Logger; import org.novelang.logger.LoggerFactory; import org.novelang.parser.NoUnescapedCharacterException; import org.novelang.parser.SourceUnescape; import org.novelang.parser.antlr.delimited.BlockDelimiter; import org.novelang.parser.antlr.delimited.BlockDelimiterSupervisor; import org.novelang.parser.antlr.delimited.DefaultBlockDelimiterSupervisor; /** * Holds stuff which is not convenient to code inside ANTLR grammar because of code generation. * * @author Laurent Caillette */ public class GrammarDelegate extends ProblemDelegate implements BlockDelimiterSupervisor, TokenNameProvider, ParserDelegate { private static final Logger LOGGER = LoggerFactory.getLogger( GrammarDelegate.class ); /** * With this constructor the {@code LocationFactory} gives only partial information. * Its use is reserved to ANTLR parser, which needs a default {@code GrammarDelegate} * for running in the debugger. */ public GrammarDelegate() { } public GrammarDelegate( final LocationFactory locationFactory ) { super( locationFactory ) ; } // ================ // Character escape // ================ @Override public String unescapeCharacter( final String escaped, final int line, final int column ) { try { return "" + SourceUnescape.unescapeCharacter( escaped ) ; } catch( NoUnescapedCharacterException e ) { final Location location = locationFactory.createLocation( line, column ) ; problems.add( Problem.createProblem( "Cannot unescape: '" + escaped + "'", location ) ) ; return "<unescaped:" + escaped + ">" ; } } // ==================== // Parser rules logging // ==================== private static final Logger parserLogger = LoggerFactory.getLogger( GrammarDelegate.class ) ; private int loggingRuleDepth = 0 ; @Override public void traceIn( final String s, final int ruleIndex ) { if( parserLogger.isDebugEnabled() ) { String indent = "" ; for( int i = 0 ; i < loggingRuleDepth ; i++ ) { indent += ". " ; } parserLogger.debug( indent + s ) ; } loggingRuleDepth ++ ; } @Override public void traceOut( final String s, final int ruleIndex ) { loggingRuleDepth -- ; } // ========== // Delimiters // ========== private final BlockDelimiterSupervisor blockDelimiterSupervisor = new DefaultBlockDelimiterSupervisor( locationFactory ) ; @Override public void startDelimitedText( final BlockDelimiter blockDelimiter, final Token startToken ) { blockDelimiterSupervisor.startDelimitedText( blockDelimiter, startToken ) ; } @Override public void reachEndDelimiter( final BlockDelimiter blockDelimiter ) { blockDelimiterSupervisor.reachEndDelimiter( blockDelimiter ) ; } @Override public void endDelimitedText( final BlockDelimiter blockDelimiter ) { blockDelimiterSupervisor.endDelimitedText( blockDelimiter ) ; } @Override public void reportMissingDelimiter( final BlockDelimiter blockDelimiter, final MismatchedTokenException mismatchedTokenException ) throws MismatchedTokenException { blockDelimiterSupervisor.reportMissingDelimiter( blockDelimiter, mismatchedTokenException ) ; } @Override public void enterBlockDelimiterBoundary( final Token location ) { blockDelimiterSupervisor.enterBlockDelimiterBoundary( location ) ; } @Override public Iterable< Problem > leaveBlockDelimiterBoundary() { final Iterable< Problem > boundaryProblems = blockDelimiterSupervisor.leaveBlockDelimiterBoundary() ; report( boundaryProblems ) ; return boundaryProblems; } // ================================ // Configuration done by the parser // ================================ private String[] tokenNames = null ; @Override public void setTokenNames( final String[] tokenNames ) { this.tokenNames = tokenNames ; } private TreeAdaptor adaptor = null ; @Override public void setAdaptor( final TreeAdaptor adaptor ) { this.adaptor = adaptor ; } // ================= // TokenNameProvider // ================= @Override public String getTokenName( final int imaginaryTokenIndex ) { return tokenNames[ imaginaryTokenIndex ] ; } // ======================== // Enhanced error reporting // ======================== @Override public void report( final RecognitionException exception ) { problems.add( ParsingProblems.createProblem( locationFactory, exception, tokenNames ) ) ; } // ============= // Tree creation // ============= @Override public Location createLocation( final Token token ) { return locationFactory.createLocation( token.getLine(), token.getCharPositionInLine() ) ; } @Override public Tree createTree( final int imaginaryTokenIdentifier, final String tokenPayload ) { return new CustomTree( new CommonToken( imaginaryTokenIdentifier ), getLocationFactory().createLocation(), tokenPayload ) ; } @Override public Tree createTree( final String tokenPayload ) { return new CustomTree( new CommonToken( -1, tokenPayload ), getLocationFactory().createLocation() ) ; } @Override public Tree createTree( final int imaginaryTokenIdentifier, final Location location, final String tokenPayload ) { return new CustomTree( new CommonToken( imaginaryTokenIdentifier ), location, tokenPayload ) ; } @Override public Object createTree( final int imaginaryTokenIdentifier, final Location location, final List list_p ) { final Object root_1 = createRoot( imaginaryTokenIdentifier, location ) ; final Iterator stream_p = list_p.iterator() ; if( !( stream_p.hasNext() ) ) { throw new RewriteEarlyExitException(); } while( stream_p.hasNext() ) { final Object tree = stream_p.next(); if( tree != null ) { adaptor.addChild( root_1, tree ) ; } } return root_1 ; } @Override public Object createTree( final int imaginaryTokenIdentifier, final Location location, final Object... trees ) { final Object root_1 = createRoot( imaginaryTokenIdentifier, location ) ; for( final Object tree : trees ) { if( tree != null ) { adaptor.addChild( root_1, tree ) ; } } return root_1 ; } @Override public Object createTree( final int imaginaryTokenIdentifier, final Location location, final String payload, final Object... trees ) { // final CustomTree customTree = new CustomTree( ); final Object root_1 = createTree( imaginaryTokenIdentifier, location, payload ) ; for( final Object tree : trees ) { if( tree != null ) { adaptor.addChild( root_1, tree ) ; } } return root_1 ; } @Override public Object createRoot( final int imaginaryTokenIdentifier, final Location location ) { final CustomTree customTree = ( CustomTree ) adaptor.create( imaginaryTokenIdentifier, tokenNames[ imaginaryTokenIdentifier ] ) ; customTree.setLocation( location ) ; return customTree ; } }