/* * [The "BSD license"] * Copyright (c) 2013 Terence Parr * Copyright (c) 2013 Sam Harwell * 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. */ using System; using System.Collections; using System.Collections.Generic; using Antlr4.Runtime; using Antlr4.Runtime.Atn; using Antlr4.Runtime.Dfa; using Antlr4.Runtime.Misc; using Antlr4.Runtime.Tree; using Sharpen; namespace Antlr4.Runtime { /// This is all the parsing support code essentially; most of it is error recovery stuff. /// /// This is all the parsing support code essentially; most of it is error recovery stuff. /// public abstract class Parser : Recognizer { public class TraceListener : IParseTreeListener { public virtual void EnterEveryRule(ParserRuleContext ctx) { System.Console.Out.WriteLine("enter " + this._enclosing.RuleNames[ctx.GetRuleIndex ()] + ", LT(1)=" + this._enclosing._input.Lt(1).Text); } public virtual void ExitEveryRule(ParserRuleContext ctx) { System.Console.Out.WriteLine("exit " + this._enclosing.RuleNames[ctx.GetRuleIndex ()] + ", LT(1)=" + this._enclosing._input.Lt(1).Text); } public virtual void VisitErrorNode(IErrorNode node) { } public virtual void VisitTerminal(ITerminalNode node) { ParserRuleContext parent = (ParserRuleContext)((IRuleNode)node.Parent).RuleContext; IToken token = node.Symbol; System.Console.Out.WriteLine("consume " + token + " rule " + this._enclosing.RuleNames [parent.GetRuleIndex()]); } internal TraceListener(Parser _enclosing) { this._enclosing = _enclosing; } private readonly Parser _enclosing; } public class TrimToSizeListener : IParseTreeListener { public static readonly Parser.TrimToSizeListener Instance = new Parser.TrimToSizeListener (); public virtual void VisitTerminal(ITerminalNode node) { } public virtual void VisitErrorNode(IErrorNode node) { } public virtual void EnterEveryRule(ParserRuleContext ctx) { } public virtual void ExitEveryRule(ParserRuleContext ctx) { if (ctx.children is ArrayList) { ((List)ctx.children).TrimExcess(); } } } /// The error handling strategy for the parser. /// /// The error handling strategy for the parser. The default value is a new /// instance of /// DefaultErrorStrategy /// . /// /// ErrorHandler() /// ErrorHandler(IAntlrErrorStrategy) /// [NotNull] protected internal IAntlrErrorStrategy _errHandler = new DefaultErrorStrategy(); /// The input stream. /// The input stream. /// InputStream() /// SetInputStream(ITokenStream) protected internal ITokenStream _input; protected internal readonly List _precedenceStack; /// /// The /// ParserRuleContext /// object for the currently executing rule. /// This is always non-null during the parsing process. /// protected internal ParserRuleContext _ctx; /// /// Specifies whether or not the parser should construct a parse tree during /// the parsing process. /// /// /// Specifies whether or not the parser should construct a parse tree during /// the parsing process. The default value is /// true /// . /// /// BuildParseTree() /// BuildParseTree(bool) protected internal bool _buildParseTrees = true; /// /// When /// Trace(bool) /// (true) /// is called, a reference to the /// TraceListener /// is stored here so it can be easily removed in a /// later call to /// Trace(bool) /// (false) /// . The listener itself is /// implemented as a parser listener so this field is not directly used by /// other parser methods. /// private Parser.TraceListener _tracer; /// /// The list of /// Antlr4.Runtime.Tree.IParseTreeListener /// /// listeners registered to receive /// events during the parse. /// /// AddParseListener(Antlr4.Runtime.Tree.IParseTreeListener) /// [Nullable] protected internal IList _parseListeners; /// The number of syntax errors reported during parsing. /// /// The number of syntax errors reported during parsing. This value is /// incremented each time /// NotifyErrorListeners(string) /// is called. /// protected internal int _syntaxErrors; public Parser(ITokenStream input) { { _precedenceStack = new List(); _precedenceStack.Add(0); } SetInputStream(input); } /// reset the parser's state public virtual void Reset() { if (((ITokenStream)InputStream) != null) { ((ITokenStream)InputStream).Seek(0); } _errHandler.Reset(this); _ctx = null; _syntaxErrors = 0; Trace = false; _precedenceStack.Clear(); _precedenceStack.Add(0); ATNSimulator interpreter = Interpreter; if (interpreter != null) { interpreter.Reset(); } } /// /// Match current input symbol against /// ttype /// . If the symbol type /// matches, /// IAntlrErrorStrategy.ReportMatch(Parser) /// /// and /// Consume() /// are /// called to complete the match process. ///

/// If the symbol type does not match, /// IAntlrErrorStrategy.RecoverInline(Parser) /// /// is called on the current error /// strategy to attempt recovery. If /// BuildParseTree() /// is /// true /// and the token index of the symbol returned by /// IAntlrErrorStrategy.RecoverInline(Parser) /// /// is -1, the symbol is added to /// the parse tree by calling /// ParserRuleContext.AddErrorNode(IToken) /// /// . ///

/// the token type to match /// the matched symbol /// /// if the current input symbol did not match /// ttype /// and the error strategy could not recover from the /// mismatched symbol /// /// [NotNull] public virtual IToken Match(int ttype) { IToken t = CurrentToken; if (t.Type == ttype) { _errHandler.ReportMatch(this); Consume(); } else { t = _errHandler.RecoverInline(this); if (_buildParseTrees && t.TokenIndex == -1) { // we must have conjured up a new token during single token insertion // if it's not the current symbol _ctx.AddErrorNode(t); } } return t; } /// Match current input symbol as a wildcard. /// /// Match current input symbol as a wildcard. If the symbol type matches /// (i.e. has a value greater than 0), /// IAntlrErrorStrategy.ReportMatch(Parser) /// /// and /// Consume() /// are called to complete the match process. ///

/// If the symbol type does not match, /// IAntlrErrorStrategy.RecoverInline(Parser) /// /// is called on the current error /// strategy to attempt recovery. If /// BuildParseTree() /// is /// true /// and the token index of the symbol returned by /// IAntlrErrorStrategy.RecoverInline(Parser) /// /// is -1, the symbol is added to /// the parse tree by calling /// ParserRuleContext.AddErrorNode(IToken) /// /// . /// /// the matched symbol /// /// if the current input symbol did not match /// a wildcard and the error strategy could not recover from the mismatched /// symbol /// /// [NotNull] public virtual IToken MatchWildcard() { IToken t = CurrentToken; if (t.Type > 0) { _errHandler.ReportMatch(this); Consume(); } else { t = _errHandler.RecoverInline(this); if (_buildParseTrees && t.TokenIndex == -1) { // we must have conjured up a new token during single token insertion // if it's not the current symbol _ctx.AddErrorNode(t); } } return t; } ///

/// Track the /// ParserRuleContext /// objects during the parse and hook /// them up using the /// ParserRuleContext.children /// list so that it /// forms a parse tree. The /// ParserRuleContext /// returned from the start /// rule represents the root of the parse tree. ///

/// Note that if we are not building parse trees, rule contexts only point /// upwards. When a rule exits, it returns the context but that gets garbage /// collected if nobody holds a reference. It points upwards but nobody /// points at it. ///

/// When we build parse trees, we are adding all of these contexts to /// ParserRuleContext.children /// list. Contexts are then not candidates /// for garbage collection. ///

/// /// Gets whether or not a complete parse tree will be constructed while /// parsing. /// /// /// Gets whether or not a complete parse tree will be constructed while /// parsing. This property is /// true /// for a newly constructed parser. /// /// /// /// true /// if a complete parse tree will be constructed while /// parsing, otherwise /// false /// public virtual bool BuildParseTree { get { return _buildParseTrees; } set { bool buildParseTrees = value; this._buildParseTrees = buildParseTrees; } } /// Trim the internal lists of the parse tree during parsing to conserve memory. /// /// /// Trim the internal lists of the parse tree during parsing to conserve memory. /// This property is set to /// false /// by default for a newly constructed parser. /// /// /// /// true /// to trim the capacity of the /// ParserRuleContext.children /// list to its size after a rule is parsed. /// /// /// /// true /// if the /// ParserRuleContext.children /// list is trimmed /// using the default /// TrimToSizeListener /// during the parse process. /// public virtual bool TrimParseTree { get { return ParseListeners.Contains(Parser.TrimToSizeListener.Instance); } set { bool trimParseTrees = value; if (trimParseTrees) { if (TrimParseTree) { return; } AddParseListener(Parser.TrimToSizeListener.Instance); } else { RemoveParseListener(Parser.TrimToSizeListener.Instance); } } } public virtual IList ParseListeners { get { IList listeners = _parseListeners; if (listeners == null) { return Sharpen.Collections.EmptyList(); } return listeners; } } /// /// Registers /// 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 /// Antlr4.Runtime.Tree.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.
  • ///
///
/// the listener to add /// /// if /// /// listener is /// null /// public virtual void AddParseListener(IParseTreeListener listener) { if (listener == null) { throw new ArgumentNullException("listener"); } if (_parseListeners == null) { _parseListeners = new List(); } this._parseListeners.AddItem(listener); } /// /// Remove /// listener /// from the list of parse listeners. ///

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

/// AddParseListener(Antlr4.Runtime.Tree.IParseTreeListener) /// /// the listener to remove public virtual void RemoveParseListener(IParseTreeListener listener) { if (_parseListeners != null) { if (_parseListeners.Remove(listener)) { if (_parseListeners.IsEmpty()) { _parseListeners = null; } } } } /// Remove all parse listeners. /// Remove all parse listeners. /// AddParseListener(Antlr4.Runtime.Tree.IParseTreeListener) /// public virtual void RemoveParseListeners() { _parseListeners = null; } /// Notify any parse listeners of an enter rule event. /// Notify any parse listeners of an enter rule event. /// AddParseListener(Antlr4.Runtime.Tree.IParseTreeListener) /// protected internal virtual void TriggerEnterRuleEvent() { foreach (IParseTreeListener listener in _parseListeners) { listener.EnterEveryRule(_ctx); _ctx.EnterRule(listener); } } /// Notify any parse listeners of an exit rule event. /// Notify any parse listeners of an exit rule event. /// AddParseListener(Antlr4.Runtime.Tree.IParseTreeListener) /// protected internal virtual void TriggerExitRuleEvent() { // reverse order walk of listeners for (int i = _parseListeners.Count - 1; i >= 0; i--) { IParseTreeListener listener = _parseListeners[i]; _ctx.ExitRule(listener); listener.ExitEveryRule(_ctx); } } /// Gets the number of syntax errors reported during parsing. /// /// Gets the number of syntax errors reported during parsing. This value is /// incremented each time /// NotifyErrorListeners(string) /// is called. /// /// NotifyErrorListeners(string) public virtual int NumberOfSyntaxErrors { get { return _syntaxErrors; } } public virtual ITokenFactory GetTokenFactory() { return _input.TokenSource.TokenFactory; } public virtual IAntlrErrorStrategy ErrorHandler { get { return _errHandler; } set { IAntlrErrorStrategy handler = value; this._errHandler = handler; } } public override IIntStream InputStream { get { return _input; } } /// Set the token stream and reset the parser. /// Set the token stream and reset the parser. public virtual void SetInputStream(ITokenStream input) { this._input = null; 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. /// /// /// Match needs to return the current input symbol, which gets put /// into the label for the associated token ref; e.g., x=ID. /// public virtual IToken CurrentToken { get { return _input.Lt(1); } } public void NotifyErrorListeners(string msg) { NotifyErrorListeners(CurrentToken, msg, null); } public virtual void NotifyErrorListeners(IToken offendingToken, string msg, RecognitionException e) { _syntaxErrors++; int line = -1; int charPositionInLine = -1; if (offendingToken != null) { line = offendingToken.Line; charPositionInLine = offendingToken.Column; } IAntlrErrorListener listener = ((IParserErrorListener)GetErrorListenerDispatch ()); listener.SyntaxError(this, offendingToken, line, charPositionInLine, msg, e); } /// /// Consume and return the /// /// #getCurrentToken /// current symbol /// /// . ///

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

        /// A B
        /// ^
        /// 
/// If the parser is not in error recovery mode, the consumed symbol is added /// to the parse tree using /// ParserRuleContext.AddChild(IToken) /// /// , and /// Antlr4.Runtime.Tree.IParseTreeListener.VisitTerminal(Antlr4.Runtime.Tree.ITerminalNode) /// /// is called on any parse listeners. /// If the parser is in error recovery mode, the consumed symbol is /// added to the parse tree using /// ParserRuleContext.AddErrorNode(IToken) /// /// , and /// Antlr4.Runtime.Tree.IParseTreeListener.VisitErrorNode(Antlr4.Runtime.Tree.IErrorNode) /// /// is called on any parse /// listeners. ///
public virtual IToken Consume() { IToken o = CurrentToken; if (o.Type != Eof) { ((ITokenStream)InputStream).Consume(); } bool hasListener = _parseListeners != null && !_parseListeners.IsEmpty(); if (_buildParseTrees || hasListener) { if (_errHandler.InErrorRecoveryMode(this)) { IErrorNode node = _ctx.AddErrorNode(o); if (_parseListeners != null) { foreach (IParseTreeListener listener in _parseListeners) { listener.VisitErrorNode(node); } } } else { ITerminalNode node = _ctx.AddChild(o); if (_parseListeners != null) { foreach (IParseTreeListener listener in _parseListeners) { listener.VisitTerminal(node); } } } } return o; } protected internal virtual void AddContextToParseTree() { ParserRuleContext parent = (ParserRuleContext)_ctx.parent; // add current context to parent if we have a parent if (parent != null) { parent.AddChild(_ctx); } } /// Always called by generated parsers upon entry to a rule. /// /// Always called by generated parsers upon entry to a rule. Access field /// _ctx /// get the current context. /// public virtual void EnterRule(ParserRuleContext localctx, int state, int ruleIndex ) { State = state; _ctx = localctx; _ctx.start = _input.Lt(1); if (_buildParseTrees) { AddContextToParseTree(); } if (_parseListeners != null) { TriggerEnterRuleEvent(); } } public virtual void EnterLeftFactoredRule(ParserRuleContext localctx, int state, int ruleIndex) { State = state; if (_buildParseTrees) { ParserRuleContext factoredContext = (ParserRuleContext)_ctx.GetChild(_ctx.ChildCount - 1); _ctx.RemoveLastChild(); factoredContext.parent = localctx; localctx.AddChild(factoredContext); } _ctx = localctx; _ctx.start = _input.Lt(1); if (_buildParseTrees) { AddContextToParseTree(); } if (_parseListeners != null) { TriggerEnterRuleEvent(); } } public virtual void ExitRule() { _ctx.stop = _input.Lt(-1); // trigger event on _ctx, before it reverts to parent if (_parseListeners != null) { TriggerExitRuleEvent(); } State = _ctx.invokingState; _ctx = (ParserRuleContext)_ctx.parent; } public virtual void EnterOuterAlt(ParserRuleContext localctx, int altNum) { // if we have new localctx, make sure we replace existing ctx // that is previous child of parse tree if (_buildParseTrees && _ctx != localctx) { ParserRuleContext parent = (ParserRuleContext)_ctx.parent; if (parent != null) { parent.RemoveLastChild(); parent.AddChild(localctx); } } _ctx = localctx; } public virtual void EnterRecursionRule(ParserRuleContext localctx, int ruleIndex, int precedence) { _precedenceStack.Add(precedence); _ctx = localctx; _ctx.start = _input.Lt(1); if (_parseListeners != null) { TriggerEnterRuleEvent(); } } // simulates rule entry for left-recursive rules /// /// Like /// EnterRule(ParserRuleContext, int, int) /// /// but for recursive rules. /// public virtual void PushNewRecursionContext(ParserRuleContext localctx, int state , int ruleIndex) { ParserRuleContext previous = _ctx; previous.parent = localctx; previous.invokingState = state; previous.stop = _input.Lt(-1); _ctx = localctx; _ctx.start = previous.start; if (_buildParseTrees) { _ctx.AddChild(previous); } if (_parseListeners != null) { TriggerEnterRuleEvent(); } } // simulates rule entry for left-recursive rules public virtual void UnrollRecursionContexts(ParserRuleContext _parentctx) { _precedenceStack.Pop(); _ctx.stop = _input.Lt(-1); ParserRuleContext retctx = _ctx; // save current ctx (return value) // unroll so _ctx is as it was before call to recursive method if (_parseListeners != null) { while (_ctx != _parentctx) { TriggerExitRuleEvent(); _ctx = (ParserRuleContext)_ctx.parent; } } else { _ctx = _parentctx; } // hook into tree retctx.parent = _parentctx; if (_buildParseTrees && _parentctx != null) { // add return ctx into invoking rule's tree _parentctx.AddChild(retctx); } } public virtual ParserRuleContext GetInvokingContext(int ruleIndex) { ParserRuleContext p = _ctx; while (p != null) { if (p.GetRuleIndex() == ruleIndex) { return p; } p = (ParserRuleContext)p.parent; } return null; } public virtual ParserRuleContext Context { get { return _ctx; } } public override bool Precpred(RuleContext localctx, int precedence) { return precedence >= _precedenceStack.Peek(); } public override IAntlrErrorListener GetErrorListenerDispatch() { return new ProxyParserErrorListener(GetErrorListeners()); } public virtual bool InContext(string context) { // TODO: useful in parser? return false; } /// /// Checks whether or not /// 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);
        /// 
///
/// the symbol type to check /// /// /// true /// if /// symbol /// can follow the current state in /// the ATN, otherwise /// false /// . /// public virtual bool IsExpectedToken(int symbol) { // return getInterpreter().atn.nextTokens(_ctx); ATN atn = Interpreter.atn; ParserRuleContext ctx = _ctx; ATNState s = atn.states[State]; IntervalSet following = atn.NextTokens(s); if (following.Contains(symbol)) { return true; } // System.out.println("following "+s+"="+following); if (!following.Contains(TokenConstants.Epsilon)) { return false; } while (ctx != null && ctx.invokingState >= 0 && following.Contains(TokenConstants .Epsilon)) { ATNState invokingState = atn.states[ctx.invokingState]; RuleTransition rt = (RuleTransition)invokingState.Transition(0); following = atn.NextTokens(rt.followState); if (following.Contains(symbol)) { return true; } ctx = (ParserRuleContext)ctx.parent; } if (following.Contains(TokenConstants.Epsilon) && symbol == TokenConstants.Eof) { return true; } return false; } /// /// Computes the set of input symbols which could follow the current parser /// state and context, as given by /// Recognizer<Symbol, ATNInterpreter>.State() /// /// and /// Context() /// , /// respectively. /// /// Antlr4.Runtime.Atn.ATN.GetExpectedTokens(int, RuleContext) /// [NotNull] public virtual IntervalSet GetExpectedTokens() { return Atn.GetExpectedTokens(State, Context); } [NotNull] public virtual IntervalSet GetExpectedTokensWithinCurrentRule() { ATN atn = Interpreter.atn; ATNState s = atn.states[State]; return atn.NextTokens(s); } public virtual ParserRuleContext RuleContext { get { // /** Compute the set of valid tokens reachable from the current // * position in the parse. // */ // public IntervalSet nextTokens(@NotNull RuleContext ctx) { // ATN atn = getInterpreter().atn; // ATNState s = atn.states.get(ctx.s); // if ( s == null ) return null; // return atn.nextTokens(s, ctx); // } return _ctx; } } /// /// Return List<String> of the rule names in your parser instance /// leading up to a call to the current rule. /// /// /// 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. /// public virtual IList GetRuleInvocationStack() { return GetRuleInvocationStack(_ctx); } public virtual IList GetRuleInvocationStack(RuleContext p) { string[] ruleNames = RuleNames; IList stack = new List(); while (p != null) { // compute what follows who invoked us int ruleIndex = p.GetRuleIndex(); if (ruleIndex < 0) { stack.AddItem("n/a"); } else { stack.AddItem(ruleNames[ruleIndex]); } p = p.parent; } return stack; } /// For debugging and other purposes. /// For debugging and other purposes. public virtual IList GetDFAStrings() { IList s = new List(); for (int d = 0; d < _interp.atn.decisionToDFA.Length; d++) { DFA dfa = _interp.atn.decisionToDFA[d]; s.AddItem(dfa.ToString(TokenNames, RuleNames)); } return s; } /// For debugging and other purposes. /// For debugging and other purposes. public virtual void DumpDFA() { bool seenOne = false; for (int d = 0; d < _interp.atn.decisionToDFA.Length; d++) { DFA dfa = _interp.atn.decisionToDFA[d]; if (!dfa.IsEmpty()) { if (seenOne) { System.Console.Out.WriteLine(); } System.Console.Out.WriteLine("Decision " + dfa.decision + ":"); System.Console.Out.Write(dfa.ToString(TokenNames, RuleNames)); seenOne = true; } } } public virtual string SourceName { get { return _input.SourceName; } } /// /// During a parse is sometimes useful to listen in on the rule entry and exit /// events as well as token matches. /// /// /// 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. /// public virtual bool Trace { get { foreach (object o in ParseListeners) { if (o is Parser.TraceListener) { return true; } } return false; } set { bool trace = value; if (!trace) { RemoveParseListener(_tracer); _tracer = null; } else { if (_tracer != null) { RemoveParseListener(_tracer); } else { _tracer = new Parser.TraceListener(this); } AddParseListener(_tracer); } } } } }