Commit ac5969e1 authored by Arpad Lovassy's avatar Arpad Lovassy
Browse files

Added parser logger to Tools


Signed-off-by: Arpad Lovassy's avatarzlovarp <arpad.lovassy@semcon.com>
parent c8f9d90a
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="bin"/>
</classpath>
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>org.eclipse.titan.parserutils</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: TITAN Parser Utils
Bundle-SymbolicName: org.eclipse.titan.parserutils
Bundle-Version: 8.2.0._20220504-1000
Require-Bundle: org.antlr.runtime;bundle-version="4.7.1",
org.eclipse.equinox.app,
org.eclipse.core.variables,
org.eclipse.core.runtime;bundle-version="3.5.0"
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-ClassPath: .
Bundle-ActivationPolicy: lazy
Export-Package: org.eclipse.titan.parserutils,
org.eclipse.titan.parserutils.log
Bundle-Vendor: Eclipse Titan Project
/******************************************************************************
* Copyright (c) 2000-2022 Ericsson Telecom AB
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
******************************************************************************/
package org.eclipse.titan.parserutils.log;
/**
* Simple printer, that prints on the standard output stream
* @author Arpad Lovassy
*/
public class ConsolePrinter implements IPrinter {
@Override
public void print(final String aMsg) {
System.out.print( aMsg );
}
@Override
public void println() {
System.out.println();
}
@Override
public void println(final String aMsg) {
System.out.println( aMsg );
}
}
/******************************************************************************
* Copyright (c) 2000-2022 Ericsson Telecom AB
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
******************************************************************************/
package org.eclipse.titan.parserutils.log;
/**
* Used by {@link ParserLogger} for logging to different consoles
* @author Arpad Lovassy
*/
public interface IPrinter {
void print( final String aMsg );
void println();
void println( final String aMsg );
}
/******************************************************************************
* Copyright (c) 2000-2022 Ericsson Telecom AB
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
******************************************************************************/
package org.eclipse.titan.parserutils.log;
import java.util.ArrayList;
import java.util.List;
/**
* Simple class which stores the interval related data extracted from source files by the proper lexer/parser. Can be used to quickly find the
* smallest enclosing interval of an offset.
*
* @author Kristof Szabados
* */
public class Interval {
public enum interval_type {
/**
* multi line comment (usually starting with /* end ending with
* *\/).
*/
MULTILINE_COMMENT,
/** single line comments. */
SINGLELINE_COMMENT,
/** block statement (usually between {} ). */
NORMAL,
/** array indexing (usually between [] ). */
INDEX,
/** parameter encloser ( usually between () ). */
PARAMETER
}
private int startOffset;
private int startLine;
private int endOffset;
private int endLine;
private final interval_type type;
private int depth;
private boolean erroneous;
private List<Interval> subIntervals;
private final Interval parent;
public Interval(final Interval parent, final interval_type type) {
this.parent = parent;
if (parent != null) {
parent.addSubIntervall(this);
depth = parent.getDepth() + 1;
} else {
depth = 0;
}
startOffset = -1;
startLine = -1;
endOffset = -1;
endLine = -1;
this.type = type;
erroneous = false;
subIntervals = null;
}
/**
* @return the type of the interval.
* */
public final interval_type getType() {
return type;
}
/**
* @return whether this interval is a comment type one or not.
**/
public final boolean isComment() {
return interval_type.MULTILINE_COMMENT.equals(type) || interval_type.SINGLELINE_COMMENT.equals(type);
}
public final boolean getErroneous() {
return erroneous;
}
public final void setErroneous() {
erroneous = true;
}
/**
* Returns the parent of the interval.
*
* @return the parent of the interval or null if it is the root interval
* */
public final Interval getParent() {
return parent;
}
/**
* Add a child interval to this interval.
*
* @param child the child interval to be added.
* */
private void addSubIntervall(final Interval child) {
if (subIntervals == null) {
subIntervals = new ArrayList<Interval>();
}
subIntervals.add(child);
}
/**
* @return the direct subintervals of this interval.
* */
public final List<Interval> getSubIntervals() {
if (subIntervals == null) {
return new ArrayList<Interval>();
}
return subIntervals;
}
/**
* @return the starting offset of the interval.
* */
public final int getStartOffset() {
return startOffset;
}
/**
* Sets the starting offset of the interval.
*
* @param offset the offset to be used as starting offset
* */
public final void setStartOffset(final int offset) {
startOffset = offset;
}
/**
* @return the starting line of the interval.
* */
public final int getStartLine() {
return startLine;
}
/**
* Sets the starting line of the interval.
*
* @param line the line number to be used as starting line
* */
public final void setStartLine(final int line) {
startLine = line;
}
/**
* @return the ending offset of the interval.
* */
public final int getEndOffset() {
return endOffset;
}
/**
* Sets the ending offset of the interval.
*
* @param offset the offset to be set as endoffset
* */
public final void setEndOffset(final int offset) {
endOffset = offset;
}
/**
* @return the ending line of the interval.
* */
public int getEndLine() {
return endLine;
}
/**
* Sets the ending line of the interval.
*
* @param line the line number to be used as ending line
* */
public final void setEndLine(final int line) {
endLine = line;
}
/**
* Calculates the depth of the actual interval in the actual hierarchy it is in.
*
* @return the depth of the interval in the interval hierarchy
* */
public final int getDepth() {
return depth;
}
/**
* Finds the smallest enclosing interval to an offset. For this recursively walks the subtrees of the actual interval.
*
* @param offset the offset
*
* @return the smallest enclosing interval
* */
public final Interval getSmallestEnclosingInterval(final int offset) {
return getSmallestEnclosingInterval(offset, offset);
}
/**
* Finds the smallest enclosing interval to a range. For this recursively walks the subtrees of the actual interval.
*
* @param startOffset the starting offset of the range
* @param endOffset the ending offset of the range
*
* @return the smallest enclosing interval
* */
public final Interval getSmallestEnclosingInterval(final int startOffset, final int endOffset) {
if (subIntervals == null) {
return this;
}
//if the enclosing bracket "}" is missing, then the endOffset of the last subInterval is -1
//handle as infinity
final int lastEndOffset = subIntervals.get(subIntervals.size() - 1).getEndOffset();
if (!subIntervals.isEmpty() && subIntervals.get(0).getStartOffset() <= startOffset
&& (endOffset <= lastEndOffset || lastEndOffset == -1)) {
int lowLimit = 0;
int highLimit = subIntervals.size();
int middle;
Interval testedInterval;
while (true) {
middle = (lowLimit + highLimit) / 2;
testedInterval = subIntervals.get(middle);
if (endOffset < testedInterval.startOffset) {
highLimit = middle;
} else if (startOffset > testedInterval.endOffset && testedInterval.endOffset != -1) {
lowLimit = middle;
} else if (testedInterval==this){
return this;//to avoid infinite loop
} else {
return testedInterval.getSmallestEnclosingInterval(startOffset, endOffset);
}
if (middle == (lowLimit + highLimit) / 2) {
return this;
}
}
}
return this;
}
/**
* Finds the offset of the parameters pair. For this recursively walks the subtrees of the actual interval.
*
* @param offset the offset on which the original element is
*
* @return the offset of the parameters pair, or -1 if it can not be found
* */
public final int getPairLocation(final int offset) {
if (startOffset == offset) {
return endOffset;
} else if (endOffset == offset) {
return startOffset;
} else if (subIntervals != null && !subIntervals.isEmpty()) {
return findPairInSubIntervals(offset);
}
return -1;
}
private int findPairInSubIntervals(final int offset) {
int lowLimit = 0;
int highLimit = subIntervals.size();
int middle;
Interval testedInterval;
while (true) {
middle = (lowLimit + highLimit) / 2;
testedInterval = subIntervals.get(middle);
if (offset < testedInterval.startOffset) {
highLimit = middle;
} else if (offset > testedInterval.endOffset) {
lowLimit = middle;
} else {
return testedInterval.getPairLocation(offset);
}
if (middle == (lowLimit + highLimit) / 2) {
return -1;
}
}
}
}
/******************************************************************************
* Copyright (c) 2000-2022 Ericsson Telecom AB
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
******************************************************************************/
package org.eclipse.titan.parserutils.log;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.NoViableAltException;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.Vocabulary;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNodeImpl;
/**
* USED FOR LOGGING PURPOSES.<br>
* Logger utility to print parse tree and interval tree.
* @author Arpad Lovassy
*/
public final class ParserLogger {
/** date format in ISO 8601 */
private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private static IPrinter sPrinter = new ConsolePrinter();
private ParserLogger() {
// intentionally private to disable instantiation
}
private static void setPrinter( final IPrinter aPrinter ) {
sPrinter = aPrinter;
}
/**
* Logs a parse tree. (General version)
* @param aRoot parse tree
* @param aParser parser to get rule names
* @param aPrinter printer
* @param aDescription description of the parsing type, for the logging (for example: TTCN-3, Cfg, ASN.1)
*/
public static synchronized void log( final ParseTree aRoot,
final Parser aParser,
final IPrinter aPrinter,
final String aDescription ) {
if ( !aParser.getBuildParseTree() ) {
// Parse tree logging is not requested
return;
}
setPrinter( aPrinter );
aPrinter.println( "---- parse tree" );
aPrinter.println( DATE_FORMAT.format( new Date() ) ) ;
aPrinter.println( "Parser type: " + aDescription + ", Prediction mode: " + aParser.getInterpreter().getPredictionMode() );
aPrinter.println( "Call stack: " + printCallStackReverse( 4 ) );
final TokenStream tokenStream = aParser.getTokenStream();
if ( ! ( tokenStream instanceof CommonTokenStream ) ) {
aPrinter.println("ERROR: tokenStream is not CommonTokenStream");
return;
}
final CommonTokenStream commonTokenStream = (CommonTokenStream)tokenStream;
final List<Token> tokens = commonTokenStream.getTokens();
log( aRoot, aParser, tokens );
}
/**
* Logs a parse tree
* @param aRoot parse tree
* @param aParser parser to get rule names
* @param aTokens token list to get tokens by index (for getting tokens of a rule)
*/
public static void log( final ParseTree aRoot,
final Parser aParser,
final List<Token> aTokens ) {
try {
log( aRoot, aParser, aTokens, new ArrayList<Boolean>(), false, false );
} catch( Exception e ) {
final StringWriter sw = new StringWriter();
final PrintWriter pw = new PrintWriter( sw );
e.printStackTrace( pw );
println( sw.toString() ); // stack trace as a string
}
}
/**
* Logs a parse tree.
* Internal version.
* RECURSIVE
* @param aRoot parse tree
* @param aParser parser to get rule name
* @param aTokens token list to get tokens by index (for getting tokens of a rule)
* @param aLevel a list, that tells, if tree section is drawn for that level (parent).
* If the parent of the given level is already the last child, tree sections are not drawn below.
* NOTE: indentation level is aLevel.size()
* @param aParentOneChild parent has 1 child
* @param aLastChild this node is the last child of its parent
*/
private static void log( final ParseTree aRoot,
final Parser aParser,
final List<Token> aTokens,
final List<Boolean> aLevel,
final boolean aParentOneChild,
final boolean aLastChild ) {
if ( aRoot == null ) {
println( "ERROR: ParseTree root is null" );
return;
}
if ( !aParser.getBuildParseTree() ) {
println( "ERROR: ParseTree is not build. Call Parser.setBuildParseTree( true ); BEFORE parsing. Or do NOT call Parser.setBuildParseTree( false );" );
return;
}
if ( aRoot instanceof ParserRuleContext ) {
final ParserRuleContext rule = (ParserRuleContext)aRoot;
final String ruleInfo = getRuleInfo( rule, aParser );
if ( aParentOneChild ) {
printArrow( ruleInfo );
} else {
printIndent( ruleInfo, aLevel );
}
final int count = rule.getChildCount();
final boolean oneChild = count == 1 && rule.exception == null;
if ( !oneChild ) {
println( ": '" + getEscapedRuleText( rule, aTokens ) + "'" + getExceptionInfo( rule, aParser ) );
}
for( int i = 0; i < count; i++ ) {
final ParseTree child = rule.getChild( i );
List<Boolean> level = aLevel;
if ( !aParentOneChild ) {
level = new ArrayList<Boolean>( aLevel );
if ( aLastChild ) {
level.set( level.size() - 1, false );
}
level.add( true );
}
log( child, aParser, aTokens, level, oneChild, i == count - 1 );
}
} else if ( aRoot instanceof TerminalNodeImpl ) {
final TerminalNodeImpl tn = (TerminalNodeImpl)aRoot;
final String tokenInfo = getTokenInfo( tn.getSymbol(), aParser );
if ( aParentOneChild ) {
printArrow( tokenInfo );
} else {
printIndent( tokenInfo, aLevel );
}
if ( tn.parent == null ) {
print(", parent == null <-------------------------------------------------------------- ERROR");
}
println();
} else {
final String parseTreeInfo = "Custom ParseTree: '" + getEscapedText( aRoot.getText() ) + "'";
if ( aParentOneChild ) {
println( parseTreeInfo );
} else {
printIndent( parseTreeInfo, aLevel );
}
if ( aRoot.getParent() == null ) {
print(", parent == null <-------------------------------------------------------------- ERROR");
}
println();
}
}
/**
* Rule exception info in string format for logging purpose
* @param aRule rule
* @param aParser parser to get token name
* @return exception stack trace + some other info from the exception object
*/
private static String getExceptionInfo( final ParserRuleContext aRule, final Parser aParser ) {
final RecognitionException e = aRule.exception;
if ( e == null ) {
return "";
}
final StringBuilder sb = new StringBuilder();
sb.append(", ERROR: mismatch, expected