package net.jangaroo.jooc;
import java_cup.runtime.Symbol;
import net.jangaroo.jooc.api.CompileLog;
import net.jangaroo.jooc.config.SemicolonInsertionMode;
import java.util.regex.Pattern;
public class JooParser extends parser {
class FatalSyntaxError extends RuntimeException {
FatalSyntaxError(String msg) {
super(msg);
}
}
private SemicolonInsertionMode semicolonInsertionMode;
private CompileLog log;
private boolean eofSeen = false;
// pattern for line terminator characters according to ECMA-262:
private final static Pattern LINE_TERMINATORS_PATTERN = Pattern.compile("[\n\r\u2028\u2029]");
private Scanner scanner;
public JooParser(Scanner scanner) {
super(scanner);
this.scanner = scanner;
}
public void setCompileLog(CompileLog log) {
this.log = log;
}
public void setSemicolonInsertionMode(SemicolonInsertionMode semicolonInsertionMode) {
this.semicolonInsertionMode = semicolonInsertionMode;
}
private boolean containsLineTerminator(String text) {
return LINE_TERMINATORS_PATTERN.matcher(text).find();
}
private boolean insertVirtualToken(int token, String text) {
if (eofSeen) {
return false;
}
JooSymbol currentToken = (JooSymbol)cur_token;
eofSeen = currentToken.sym == sym.EOF;
if (token == sym.SEMICOLON) {
if (currentToken.isSemicolonInsertedBefore()) {
// we already tried semicolon insertion here, avoid infinite loop
unrecovered_syntax_error(cur_token);
}
boolean isBraceOrEof = cur_token.sym == sym.RBRACE || cur_token.sym == sym.EOF;
if (!isBraceOrEof) {
checkSemicolonInsertionMode();
}
}
if (currentToken.isVirtual() && currentToken.sym == token) {
// avoid infinite loop, but isn't it a parser bug?
report_error("Internal parser error: infinite loop during virtual token insertion: " + scanner.getSymbolAbbreviation(cur_token.sym), cur_token);
return false;
}
JooSymbol virtualToken = new JooSymbol(token, currentToken.getFileName(), currentToken.getLine(), currentToken.getColumn(), "", text);
virtualToken.setVirtual(true);
currentToken.setSemicolonInsertedBefore(token == sym.SEMICOLON);
cur_token = virtualToken;
scanner.pushback(currentToken);
return true;
}
private boolean replaceToken(int token) {
JooSymbol currentToken = (JooSymbol)cur_token;
cur_token = new JooSymbol(token, currentToken.getFileName(), currentToken.getLine(),
currentToken.getColumn(), currentToken.getWhitespace(), currentToken.getText());
return true;
}
private int getActionFromTable(int sym) {
return get_action(((Symbol)stack.peek()).parse_state, sym);
}
private boolean isActionDefinedFor(int sym) {
return getActionFromTable(sym) != 0;
}
private boolean isShiftActionDefinedFor(int sym) {
return getActionFromTable(sym) > 0;
}
protected boolean error_recovery(boolean debug) {
String whitespace = ((JooSymbol)cur_token).getWhitespace();
boolean isPostfixOp = cur_token.sym == sym.PLUSPLUS || cur_token.sym == sym.MINUSMINUS;
// check for regular expression start
if ((cur_token.sym == sym.DIV || cur_token.sym == sym.DIVEQ) && isActionDefinedFor(sym.REGEXP_START)) {
// this is only the case if we are not just before parsing an expression statement
scanner.startRegexp((JooSymbol)cur_token);
return replaceToken(sym.REGEXP_START);
}
// check for Type start
if (cur_token.sym == sym.MULTEQ && isActionDefinedFor(sym.TYPE_START)) {
scanner.startType((JooSymbol)cur_token);
return replaceToken(sym.TYPE_START);
}
// mimic ECMA-262 grammar precondition: token notin { '{', 'function' } for expression statements
if (cur_token.sym == sym.LBRACE && isActionDefinedFor(sym.LBRACE_EXPR)) {
// this is only the case if we are not just before parsing an expression statement
return replaceToken(sym.LBRACE_EXPR);
}
if (cur_token.sym == sym.FUNCTION && isActionDefinedFor(sym.FUNCTION_EXPR)) {
// this is only the case if we are not just before parsing an expression statement
return replaceToken(sym.FUNCTION_EXPR);
}
// disambiguate metadata annotations and array literals (both start with '[')
if (cur_token.sym == sym.LBRACK && isActionDefinedFor(sym.LBRACK_EXPR)) {
return replaceToken(sym.LBRACK_EXPR);
}
// try semicolon insertion
boolean isShiftActionDefinedForNoLineTerminatorHere = isShiftActionDefinedFor(sym.NO_LINE_TERMINATOR_HERE);
if (isShiftActionDefinedForNoLineTerminatorHere || (isPostfixOp && isActionDefinedFor(sym.NO_LINE_TERMINATOR_HERE_POSTFIX_OP))) {
// ECMA-262 restricted production
if (containsLineTerminator(whitespace)) {
// avoid syntax error later on if there is no action for semicolon
if (isActionDefinedFor(sym.SEMICOLON)) {
return insertVirtualToken(sym.SEMICOLON, ";");
} else if (isPostfixOp) {
report_fatal_error("postfix operator should be on same line (semicolon insertion would produce syntax error)", cur_token);
}
}
// no semicolon to insert, proceed as if a NO_LINE_TERMINATOR_HERE(_POSTFIX_OP) token has been seen
return insertVirtualToken(isShiftActionDefinedForNoLineTerminatorHere
? sym.NO_LINE_TERMINATOR_HERE
: sym.NO_LINE_TERMINATOR_HERE_POSTFIX_OP,
"[no line terminator here]");
}
if (isActionDefinedFor(sym.SEMICOLON)) {
boolean isBraceOrEof = cur_token.sym == sym.RBRACE || cur_token.sym == sym.EOF;
if (isBraceOrEof || containsLineTerminator(whitespace)) {
return insertVirtualToken(sym.SEMICOLON, ";");
}
}
return false;
}
private void checkSemicolonInsertionMode() {
switch (semicolonInsertionMode) {
case ERROR:
log.error((JooSymbol)cur_token, "automatic semicolon insertion required by language spec, but forbidden by jooc semicolonInsertionMode");
break;
case WARN:
log.warning((JooSymbol)cur_token, "automatic semicolon insertion");
break;
}
}
public void report_error(String message, Object info) {
if (info instanceof JooSymbol) {
log.error((JooSymbol)info, message);
} else {
log.error("Error: " + message);
}
}
public void unrecovered_syntax_error(Symbol cur_token) {
report_fatal_error("Syntax error: " + scanner.getSymbolAbbreviation(cur_token.sym), cur_token);
}
public void report_fatal_error(String message, Object info) {
report_error(message, info);
done_parsing();
throw new FatalSyntaxError("Fatal Syntax Error");
}
public void syntax_error(Symbol cur_token) {
//ignore, we try to recover with SEMICOLON insertion
//if that fails, unrecovered_syntax_error() will be called and report the error
}
}