All Downloads are FREE. Search and download functionalities are using the official Maven repository.

JavaScript.src.antlr4.Parser.js Maven / Gradle / Ivy

There is a newer version: 4.13.2
Show newest version
// [The "BSD license"]
//  Copyright (c) 2012 Terence Parr
//  Copyright (c) 2012 Sam Harwell
//  Copyright (c) 2014 Eric Vergnaud
//  All rights reserved.
//
//  Redistribution and use in source and binary forms, with or without
//  modification, are permitted provided that the following conditions
//  are met:
//
//  1. Redistributions of source code must retain the above copyright
//     notice, this list of conditions and the following disclaimer.
//  2. Redistributions in binary form must reproduce the above copyright
//     notice, this list of conditions and the following disclaimer in the
//     documentation and/or other materials provided with the distribution.
//  3. The name of the author may not be used to endorse or promote products
//     derived from this software without specific prior written permission.
//
//  this SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
//  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
//  OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
//  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
//  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
//  NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
//  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
//  THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
//  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
//  this SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

var Token = require('./Token').Token;
var ParseTreeListener = require('./tree/Tree').ParseTreeListener;
var Recognizer = require('./Recognizer').Recognizer;
var DefaultErrorStrategy = require('./error/ErrorStrategy').DefaultErrorStrategy;
var ATNDeserializer = require('./atn/ATNDeserializer').ATNDeserializer;
var ATNDeserializationOptions = require('./atn/ATNDeserializationOptions').ATNDeserializationOptions;

function TraceListener(parser) {
	ParseTreeListener.call(this);
    this.parser = parser;
	return this;
}

TraceListener.prototype = Object.create(ParseTreeListener);
TraceListener.prototype.constructor = TraceListener;

TraceListener.prototype.enterEveryRule = function(ctx) {
	console.log("enter   " + this.parser.ruleNames[ctx.ruleIndex] + ", LT(1)=" + this.parser._input.LT(1).text);
};

TraceListener.prototype.visitTerminal = function( node) {
	console.log("consume " + node.symbol + " rule " + this.parser.ruleNames[this.parser._ctx.ruleIndex]);
};

TraceListener.prototype.exitEveryRule = function(ctx) {
	console.log("exit    " + this.parser.ruleNames[ctx.ruleIndex] + ", LT(1)=" + this.parser._input.LT(1).text);
};

// this is all the parsing support code essentially; most of it is error
// recovery stuff.//
function Parser(input) {
	Recognizer.call(this);
	// The input stream.
	this._input = null;
	// The error handling strategy for the parser. The default value is a new
	// instance of {@link DefaultErrorStrategy}.
	this._errHandler = new DefaultErrorStrategy();
	this._precedenceStack = [];
	this._precedenceStack.push(0);
	// The {@link ParserRuleContext} object for the currently executing rule.
	// this is always non-null during the parsing process.
	this._ctx = null;
	// Specifies whether or not the parser should construct a parse tree during
	// the parsing process. The default value is {@code true}.
	this.buildParseTrees = true;
	// When {@link //setTrace}{@code (true)} is called, a reference to the
	// {@link TraceListener} is stored here so it can be easily removed in a
	// later call to {@link //setTrace}{@code (false)}. The listener itself is
	// implemented as a parser listener so this field is not directly used by
	// other parser methods.
	this._tracer = null;
	// The list of {@link ParseTreeListener} listeners registered to receive
	// events during the parse.
	this._parseListeners = null;
	// The number of syntax errors reported during parsing. this value is
	// incremented each time {@link //notifyErrorListeners} is called.
	this._syntaxErrors = 0;
	this.setInputStream(input);
	return this;
}

Parser.prototype = Object.create(Recognizer.prototype);
Parser.prototype.contructor = Parser;

// this field maps from the serialized ATN string to the deserialized {@link
// ATN} with
// bypass alternatives.
//
// @see ATNDeserializationOptions//isGenerateRuleBypassTransitions()
//
Parser.bypassAltsAtnCache = {};

// reset the parser's state//
Parser.prototype.reset = function() {
	if (this._input !== null) {
		this._input.seek(0);
	}
	this._errHandler.reset(this);
	this._ctx = null;
	this._syntaxErrors = 0;
	this.setTrace(false);
	this._precedenceStack = [];
	this._precedenceStack.push(0);
	if (this._interp !== null) {
		this._interp.reset();
	}
};

// Match current input symbol against {@code ttype}. If the symbol type
// matches, {@link ANTLRErrorStrategy//reportMatch} and {@link //consume} are
// called to complete the match process.
//
// 

If the symbol type does not match, // {@link ANTLRErrorStrategy//recoverInline} is called on the current error // strategy to attempt recovery. If {@link //getBuildParseTree} is // {@code true} and the token index of the symbol returned by // {@link ANTLRErrorStrategy//recoverInline} is -1, the symbol is added to // the parse tree by calling {@link ParserRuleContext//addErrorNode}.

// // @param ttype the token type to match // @return the matched symbol // @throws RecognitionException if the current input symbol did not match // {@code ttype} and the error strategy could not recover from the // mismatched symbol Parser.prototype.match = function(ttype) { var t = this.getCurrentToken(); if (t.type === ttype) { this._errHandler.reportMatch(this); this.consume(); } else { t = this._errHandler.recoverInline(this); if (this.buildParseTrees && t.tokenIndex === -1) { // we must have conjured up a new token during single token // insertion // if it's not the current symbol this._ctx.addErrorNode(t); } } return t; }; // Match current input symbol as a wildcard. If the symbol type matches // (i.e. has a value greater than 0), {@link ANTLRErrorStrategy//reportMatch} // and {@link //consume} are called to complete the match process. // //

If the symbol type does not match, // {@link ANTLRErrorStrategy//recoverInline} is called on the current error // strategy to attempt recovery. If {@link //getBuildParseTree} is // {@code true} and the token index of the symbol returned by // {@link ANTLRErrorStrategy//recoverInline} is -1, the symbol is added to // the parse tree by calling {@link ParserRuleContext//addErrorNode}.

// // @return the matched symbol // @throws RecognitionException if the current input symbol did not match // a wildcard and the error strategy could not recover from the mismatched // symbol Parser.prototype.matchWildcard = function() { var t = this.getCurrentToken(); if (t.type > 0) { this._errHandler.reportMatch(this); this.consume(); } else { t = this._errHandler.recoverInline(this); if (this._buildParseTrees && t.tokenIndex === -1) { // we must have conjured up a new token during single token // insertion // if it's not the current symbol this._ctx.addErrorNode(t); } } return t; }; Parser.prototype.getParseListeners = function() { return this._parseListeners || []; }; // Registers {@code listener} to receive events during the parsing process. // //

To support output-preserving grammar transformations (including but not // limited to left-recursion removal, automated left-factoring, and // optimized code generation), calls to listener methods during the parse // may differ substantially from calls made by // {@link ParseTreeWalker//DEFAULT} used after the parse is complete. In // particular, rule entry and exit events may occur in a different order // during the parse than after the parser. In addition, calls to certain // rule entry methods may be omitted.

// //

With the following specific exceptions, calls to listener events are // deterministic, i.e. for identical input the calls to listener // methods will be the same.

// //
    //
  • Alterations to the grammar used to generate code may change the // behavior of the listener calls.
  • //
  • Alterations to the command line options passed to ANTLR 4 when // generating the parser may change the behavior of the listener calls.
  • //
  • Changing the version of the ANTLR Tool used to generate the parser // may change the behavior of the listener calls.
  • //
// // @param listener the listener to add // // @throws NullPointerException if {@code} listener is {@code null} // Parser.prototype.addParseListener = function(listener) { if (listener === null) { throw "listener"; } if (this._parseListeners === null) { this._parseListeners = []; } this._parseListeners.push(listener); }; // // Remove {@code listener} from the list of parse listeners. // //

If {@code listener} is {@code null} or has not been added as a parse // listener, this method does nothing.

// @param listener the listener to remove // Parser.prototype.removeParseListener = function(listener) { if (this._parseListeners !== null) { var idx = this._parseListeners.indexOf(listener); if (idx >= 0) { this._parseListeners.splice(idx, 1); } if (this._parseListeners.length === 0) { this._parseListeners = null; } } }; // Remove all parse listeners. Parser.prototype.removeParseListeners = function() { this._parseListeners = null; }; // Notify any parse listeners of an enter rule event. Parser.prototype.triggerEnterRuleEvent = function() { if (this._parseListeners !== null) { var ctx = this._ctx; this._parseListeners.map(function(listener) { listener.enterEveryRule(ctx); ctx.enterRule(listener); }); } }; // // Notify any parse listeners of an exit rule event. // // @see //addParseListener // Parser.prototype.triggerExitRuleEvent = function() { if (this._parseListeners !== null) { // reverse order walk of listeners var ctx = this._ctx; this._parseListeners.slice(0).reverse().map(function(listener) { ctx.exitRule(listener); listener.exitEveryRule(ctx); }); } }; Parser.prototype.getTokenFactory = function() { return this._input.tokenSource._factory; }; // Tell our token source and error strategy about a new way to create tokens.// Parser.prototype.setTokenFactory = function(factory) { this._input.tokenSource._factory = factory; }; // The ATN with bypass alternatives is expensive to create so we create it // lazily. // // @throws UnsupportedOperationException if the current parser does not // implement the {@link //getSerializedATN()} method. // Parser.prototype.getATNWithBypassAlts = function() { var serializedAtn = this.getSerializedATN(); if (serializedAtn === null) { throw "The current parser does not support an ATN with bypass alternatives."; } var result = this.bypassAltsAtnCache[serializedAtn]; if (result === null) { var deserializationOptions = new ATNDeserializationOptions(); deserializationOptions.generateRuleBypassTransitions = true; result = new ATNDeserializer(deserializationOptions) .deserialize(serializedAtn); this.bypassAltsAtnCache[serializedAtn] = result; } return result; }; // The preferred method of getting a tree pattern. For example, here's a // sample use: // //
// ParseTree t = parser.expr();
// ParseTreePattern p = parser.compileParseTreePattern("<ID>+0",
// MyParser.RULE_expr);
// ParseTreeMatch m = p.match(t);
// String id = m.get("ID");
// 
var Lexer = require('./Lexer').Lexer; Parser.prototype.compileParseTreePattern = function(pattern, patternRuleIndex, lexer) { lexer = lexer || null; if (lexer === null) { if (this.getTokenStream() !== null) { var tokenSource = this.getTokenStream().tokenSource; if (tokenSource instanceof Lexer) { lexer = tokenSource; } } } if (lexer === null) { throw "Parser can't discover a lexer to use"; } var m = new ParseTreePatternMatcher(lexer, this); return m.compile(pattern, patternRuleIndex); }; Parser.prototype.getInputStream = function() { return this.getTokenStream(); }; Parser.prototype.setInputStream = function(input) { this.setTokenStream(input); }; Parser.prototype.getTokenStream = function() { return this._input; }; // Set the token stream and reset the parser.// Parser.prototype.setTokenStream = function(input) { this._input = null; this.reset(); this._input = input; }; // Match needs to return the current input symbol, which gets put // into the label for the associated token ref; e.g., x=ID. // Parser.prototype.getCurrentToken = function() { return this._input.LT(1); }; Parser.prototype.notifyErrorListeners = function(msg, offendingToken, err) { offendingToken = offendingToken || null; err = err || null; if (offendingToken === null) { offendingToken = this.getCurrentToken(); } this._syntaxErrors += 1; var line = offendingToken.line; var column = offendingToken.column; var listener = this.getErrorListenerDispatch(); listener.syntaxError(this, offendingToken, line, column, msg, err); }; // // Consume and return the {@linkplain //getCurrentToken current symbol}. // //

E.g., given the following input with {@code A} being the current // lookahead symbol, this function moves the cursor to {@code B} and returns // {@code A}.

// //
// A B
// ^
// 
// // If the parser is not in error recovery mode, the consumed symbol is added // to the parse tree using {@link ParserRuleContext//addChild(Token)}, and // {@link ParseTreeListener//visitTerminal} is called on any parse listeners. // If the parser is in error recovery mode, the consumed symbol is // added to the parse tree using // {@link ParserRuleContext//addErrorNode(Token)}, and // {@link ParseTreeListener//visitErrorNode} is called on any parse // listeners. // Parser.prototype.consume = function() { var o = this.getCurrentToken(); if (o.type !== Token.EOF) { this.getInputStream().consume(); } var hasListener = this._parseListeners !== null && this._parseListeners.length > 0; if (this.buildParseTrees || hasListener) { var node; if (this._errHandler.inErrorRecoveryMode(this)) { node = this._ctx.addErrorNode(o); } else { node = this._ctx.addTokenNode(o); } node.invokingState = this.state; if (hasListener) { this._parseListeners.map(function(listener) { listener.visitTerminal(node); }); } } return o; }; Parser.prototype.addContextToParseTree = function() { // add current context to parent if we have a parent if (this._ctx.parentCtx !== null) { this._ctx.parentCtx.addChild(this._ctx); } }; // Always called by generated parsers upon entry to a rule. Access field // {@link //_ctx} get the current context. Parser.prototype.enterRule = function(localctx, state, ruleIndex) { this.state = state; this._ctx = localctx; this._ctx.start = this._input.LT(1); if (this.buildParseTrees) { this.addContextToParseTree(); } if (this._parseListeners !== null) { this.triggerEnterRuleEvent(); } }; Parser.prototype.exitRule = function() { this._ctx.stop = this._input.LT(-1); // trigger event on _ctx, before it reverts to parent if (this._parseListeners !== null) { this.triggerExitRuleEvent(); } this.state = this._ctx.invokingState; this._ctx = this._ctx.parentCtx; }; Parser.prototype.enterOuterAlt = function(localctx, altNum) { localctx.setAltNumber(altNum); // if we have new localctx, make sure we replace existing ctx // that is previous child of parse tree if (this.buildParseTrees && this._ctx !== localctx) { if (this._ctx.parentCtx !== null) { this._ctx.parentCtx.removeLastChild(); this._ctx.parentCtx.addChild(localctx); } } this._ctx = localctx; }; // Get the precedence level for the top-most precedence rule. // // @return The precedence level for the top-most precedence rule, or -1 if // the parser context is not nested within a precedence rule. Parser.prototype.getPrecedence = function() { if (this._precedenceStack.length === 0) { return -1; } else { return this._precedenceStack[this._precedenceStack.length-1]; } }; Parser.prototype.enterRecursionRule = function(localctx, state, ruleIndex, precedence) { this.state = state; this._precedenceStack.push(precedence); this._ctx = localctx; this._ctx.start = this._input.LT(1); if (this._parseListeners !== null) { this.triggerEnterRuleEvent(); // simulates rule entry for // left-recursive rules } }; // // Like {@link //enterRule} but for recursive rules. Parser.prototype.pushNewRecursionContext = function(localctx, state, ruleIndex) { var previous = this._ctx; previous.parentCtx = localctx; previous.invokingState = state; previous.stop = this._input.LT(-1); this._ctx = localctx; this._ctx.start = previous.start; if (this.buildParseTrees) { this._ctx.addChild(previous); } if (this._parseListeners !== null) { this.triggerEnterRuleEvent(); // simulates rule entry for // left-recursive rules } }; Parser.prototype.unrollRecursionContexts = function(parentCtx) { this._precedenceStack.pop(); this._ctx.stop = this._input.LT(-1); var retCtx = this._ctx; // save current ctx (return value) // unroll so _ctx is as it was before call to recursive method if (this._parseListeners !== null) { while (this._ctx !== parentCtx) { this.triggerExitRuleEvent(); this._ctx = this._ctx.parentCtx; } } else { this._ctx = parentCtx; } // hook into tree retCtx.parentCtx = parentCtx; if (this.buildParseTrees && parentCtx !== null) { // add return ctx into invoking rule's tree parentCtx.addChild(retCtx); } }; Parser.prototype.getInvokingContext = function(ruleIndex) { var ctx = this._ctx; while (ctx !== null) { if (ctx.ruleIndex === ruleIndex) { return ctx; } ctx = ctx.parentCtx; } return null; }; Parser.prototype.precpred = function(localctx, precedence) { return precedence >= this._precedenceStack[this._precedenceStack.length-1]; }; Parser.prototype.inContext = function(context) { // TODO: useful in parser? return false; }; // // Checks whether or not {@code symbol} can follow the current state in the // ATN. The behavior of this method is equivalent to the following, but is // implemented such that the complete context-sensitive follow set does not // need to be explicitly constructed. // //
// return getExpectedTokens().contains(symbol);
// 
// // @param symbol the symbol type to check // @return {@code true} if {@code symbol} can follow the current state in // the ATN, otherwise {@code false}. Parser.prototype.isExpectedToken = function(symbol) { var atn = this._interp.atn; var ctx = this._ctx; var s = atn.states[this.state]; var following = atn.nextTokens(s); if (following.contains(symbol)) { return true; } if (!following.contains(Token.EPSILON)) { return false; } while (ctx !== null && ctx.invokingState >= 0 && following.contains(Token.EPSILON)) { var invokingState = atn.states[ctx.invokingState]; var rt = invokingState.transitions[0]; following = atn.nextTokens(rt.followState); if (following.contains(symbol)) { return true; } ctx = ctx.parentCtx; } if (following.contains(Token.EPSILON) && symbol === Token.EOF) { return true; } else { return false; } }; // Computes the set of input symbols which could follow the current parser // state and context, as given by {@link //getState} and {@link //getContext}, // respectively. // // @see ATN//getExpectedTokens(int, RuleContext) // Parser.prototype.getExpectedTokens = function() { return this._interp.atn.getExpectedTokens(this.state, this._ctx); }; Parser.prototype.getExpectedTokensWithinCurrentRule = function() { var atn = this._interp.atn; var s = atn.states[this.state]; return atn.nextTokens(s); }; // Get a rule's index (i.e., {@code RULE_ruleName} field) or -1 if not found.// Parser.prototype.getRuleIndex = function(ruleName) { var ruleIndex = this.getRuleIndexMap()[ruleName]; if (ruleIndex !== null) { return ruleIndex; } else { return -1; } }; // Return List<String> of the rule names in your parser instance // leading up to a call to the current rule. You could override if // you want more details such as the file/line info of where // in the ATN a rule is invoked. // // this is very useful for error messages. // Parser.prototype.getRuleInvocationStack = function(p) { p = p || null; if (p === null) { p = this._ctx; } var stack = []; while (p !== null) { // compute what follows who invoked us var ruleIndex = p.ruleIndex; if (ruleIndex < 0) { stack.push("n/a"); } else { stack.push(this.ruleNames[ruleIndex]); } p = p.parentCtx; } return stack; }; // For debugging and other purposes.// Parser.prototype.getDFAStrings = function() { return this._interp.decisionToDFA.toString(); }; // For debugging and other purposes.// Parser.prototype.dumpDFA = function() { var seenOne = false; for (var i = 0; i < this._interp.decisionToDFA.length; i++) { var dfa = this._interp.decisionToDFA[i]; if (dfa.states.length > 0) { if (seenOne) { console.log(); } this.printer.println("Decision " + dfa.decision + ":"); this.printer.print(dfa.toString(this.literalNames, this.symbolicNames)); seenOne = true; } } }; /* " printer = function() {\r\n" + " this.println = function(s) { document.getElementById('output') += s + '\\n'; }\r\n" + " this.print = function(s) { document.getElementById('output') += s; }\r\n" + " };\r\n" + */ Parser.prototype.getSourceName = function() { return this._input.sourceName; }; // During a parse is sometimes useful to listen in on the rule entry and exit // events as well as token matches. this is for quick and dirty debugging. // Parser.prototype.setTrace = function(trace) { if (!trace) { this.removeParseListener(this._tracer); this._tracer = null; } else { if (this._tracer !== null) { this.removeParseListener(this._tracer); } this._tracer = new TraceListener(this); this.addParseListener(this._tracer); } }; exports.Parser = Parser;




© 2015 - 2025 Weber Informatics LLC | Privacy Policy