aboutsummaryrefslogblamecommitdiffstats
path: root/infrastructure/rhino1_7R1/src/org/mozilla/javascript/InformativeParser.java
blob: c73db34e72fd437e4e9de4b4c8dad8c29f06d998 (plain) (tree)
































































































































































































































                                                                                  
package org.mozilla.javascript;

import java.io.IOException;

/**
 *  Subclass of Rhino's Parser that saves information about the token stream
 *  and error message to allow more helpful error messages.
 *
 * @author David Greenspan for AppJet
 */

/*
  This class is written with speed in mind, to some extent.  Rhino's tokenizer
  is pretty efficient, and we wouldn't want to slow it down by, for example,
  creating a TokenInfo object on the heap for every token seen.
 */

/*APPJET*/
public class InformativeParser extends Parser {

    public static class InformativeEvaluatorException extends EvaluatorException {
	final ParseErrorInfo pei;
	
	InformativeEvaluatorException(String errorMessage,
				      String sourceName, int lineNumber,
				      String lineSource, int columnNumber,
				      ParseErrorInfo peInfo) {
	    super(errorMessage, sourceName,
		  lineNumber, lineSource, columnNumber);
	    pei = peInfo;
	}

	public ParseErrorInfo getParseErrorInfo() {
	    return pei;
	}
    }

    public static class ParseErrorInfo {
	ParseErrorInfo() {}

	String messageId = null;
	String messageArg = null;
	
	final int tokenMaxHistory = 10;
	// ring buffers
	final int[] tokenTypes = new int[tokenMaxHistory];
	final String[] tokenStrings = new String[tokenMaxHistory];
	final double[] tokenNumbers = new double[tokenMaxHistory];
	final int[] tokenLineNumbers = new int[tokenMaxHistory];
	final int[] tokenLineOffsets = new int[tokenMaxHistory];
	int nextBufPos = 0;
	int historyLength = 0;
	boolean tokenPeeking = false;
	int peekSlot;

	void reportPeekToken(int type, String str, double num, int lineno,
			     int lineOffset) {
	    if (! tokenPeeking) {
		peekSlot = nextBufPos;
		tokenTypes[nextBufPos] = type;
		tokenStrings[nextBufPos] = str;
		tokenNumbers[nextBufPos] = num;
		tokenLineNumbers[nextBufPos] = lineno;
		tokenLineOffsets[nextBufPos] = lineOffset;
		
		nextBufPos++;
		if (nextBufPos == tokenMaxHistory) nextBufPos = 0;
		if (historyLength < tokenMaxHistory) historyLength++;
		tokenPeeking = true;
	    }
	}

	void reportConsumeToken() {
	    tokenPeeking = false;
	}

	private TokenInfo backToken(int n) {
	    // 0 is most recent token added to history
	    if (n >= historyLength) return null;
	    int i = (nextBufPos - 1 - n);
	    while (i < 0) i += tokenMaxHistory;
	    return new TokenInfo(tokenTypes[i], tokenStrings[i],
				 tokenNumbers[i], tokenLineNumbers[i],
				 tokenLineOffsets[i]);
	}
	
	public String getMessageId() { return messageId; }
	public String getMessageArg() { return messageArg; }
	public TokenInfo getPeekToken() {
	    if (tokenPeeking) return backToken(0);
	    return null;
	}
	public TokenInfo getPrevToken(int n) {
	    // 1 = last non-peek token seen, 2 = before that, etc.
	    if (! tokenPeeking) n--;
	    return backToken(n);
	}
	public TokenInfo getPrevToken() {
	    return getPrevToken(1);
	}
    }

    public static class TokenInfo {
	private int type, lineno, lineOffset;
	private String str;
	private double num;
	TokenInfo(int type, String str, double num, int lineno,
		  int lineOffset) {
	    this.type = type; this.str = str; this.num = num;
	    this.lineno = lineno; this.lineOffset = lineOffset;
	}
	public int getType() { return type; }
	public int getLineNumber() { return lineno; }
	public int getLineOffset() { return lineOffset; }
	public double getNumber() { return num; }
	public String getString() { return str; }
    }
    
    ParseErrorInfo info = new ParseErrorInfo();

    void doErrorReporterError(String message, String sourceURI, int line,
			      String lineText, int lineOffset) {
	
	throw new InformativeEvaluatorException(message, sourceURI, line,
						lineText, lineOffset, info);
	
    }
    
    public InformativeParser(CompilerEnvirons compilerEnv) {
	// we override most calls to the parent's ErrorReporter anyway
	super(compilerEnv, DefaultErrorReporter.instance);
    }

    @Override int peekToken() throws IOException {
	int tt = super.peekToken();
	info.reportPeekToken(tt, ts.getString(), ts.getNumber(),
			     ts.getLineno(), ts.getOffset());
	return tt;
    }
    @Override void consumeToken() {
	super.consumeToken();
	info.reportConsumeToken();
    }

    @Override void addWarning(String messageId, String messageArg)
    {
	info.messageId = messageId;
	info.messageArg = messageArg;
	
        String message = ScriptRuntime.getMessage1(messageId, messageArg);
        if (compilerEnv.reportWarningAsError()) {
            ++syntaxErrorCount;
            doErrorReporterError(message, sourceURI, ts.getLineno(),
				 ts.getLine(), ts.getOffset());
        }
	else { /* don't report */ }
    }
    
    @Override void addError(String messageId)
    {
	info.messageId = messageId;
	
	++syntaxErrorCount;
        String message = ScriptRuntime.getMessage0(messageId);
        doErrorReporterError(message, sourceURI, ts.getLineno(),
			     ts.getLine(), ts.getOffset());
    }

    @Override void addError(String messageId, String messageArg)
    {
	info.messageId = messageId;
	info.messageArg = messageArg;
	
	++syntaxErrorCount;
        String message = ScriptRuntime.getMessage1(messageId, messageArg);
        doErrorReporterError(message, sourceURI, ts.getLineno(),
			     ts.getLine(), ts.getOffset());
    }

    @Override protected Decompiler createDecompiler(CompilerEnvirons env) {
	return new MyDecompiler();
    }
    
    public static final ErrorReporter THROW_INFORMATIVE_ERRORS
	= new ErrorReporter() {
		public void warning(String message, String sourceURI, int line,
				    String lineText, int lineOffset) {
		    DefaultErrorReporter.instance.warning
			(message, sourceURI, line, lineText, lineOffset);
		}
		public void error(String message, String sourceURI, int line,
				  String lineText, int lineOffset) {
		    DefaultErrorReporter.instance.error
			(message, sourceURI, line, lineText, lineOffset);
		}
		public EvaluatorException runtimeError(String message,
						       String sourceURI,
						       int line, String lineText,
						       int lineOffset) {
		    return DefaultErrorReporter.instance.runtimeError
			(message, sourceURI, line, lineText, lineOffset);
		}
		
	    };
    
    public static Parser makeParser(CompilerEnvirons compilerEnv,
				    ErrorReporter errorReporter) {
	if (errorReporter == THROW_INFORMATIVE_ERRORS) {
	    return new InformativeParser(compilerEnv);
	}
	else {
	    return new Parser(compilerEnv, errorReporter);
	}
    }

    private class MyDecompiler extends Decompiler {
	@Override void addRegexp(String regexp, String flags) {
	    super.addRegexp(regexp, flags);
	    String str = '/'+regexp+'/'+flags;
	    info.reportPeekToken(Token.REGEXP, str, ts.getNumber(),
				 ts.getLineno(), ts.getOffset());
	    info.reportConsumeToken();
	}
    }
}