From 392c637565743b8a85a050971c2de1cc7c9319bd Mon Sep 17 00:00:00 2001 From: Hanzhou Shi Date: Fri, 24 Feb 2017 23:14:24 -0800 Subject: [PATCH 1/2] Implements #1665 for Swift target. Slightly different because #899 was not addressed in Swift target. --- .../Sources/Antlr4/ANTLRErrorStrategy.swift | 7 +- runtime/Swift/Sources/Antlr4/Parser.swift | 79 ++++++++++------ .../Sources/Antlr4/ParserRuleContext.swift | 93 +++++++++++++------ 3 files changed, 119 insertions(+), 60 deletions(-) diff --git a/runtime/Swift/Sources/Antlr4/ANTLRErrorStrategy.swift b/runtime/Swift/Sources/Antlr4/ANTLRErrorStrategy.swift index ffa726461..395d99b7e 100644 --- a/runtime/Swift/Sources/Antlr4/ANTLRErrorStrategy.swift +++ b/runtime/Swift/Sources/Antlr4/ANTLRErrorStrategy.swift @@ -72,11 +72,12 @@ public protocol ANTLRErrorStrategy { /// the parsing process func sync(_ recognizer: Parser) throws // RecognitionException; - /// Tests whether or not {@code recognizer} is in the process of recovering + /// Tests whether or not recognizer} is in the process of recovering /// from an error. In error recovery mode, {@link org.antlr.v4.runtime.Parser#consume} adds /// symbols to the parse tree by calling - /// {@link org.antlr.v4.runtime.ParserRuleContext#addErrorNode(org.antlr.v4.runtime.Token)} instead of - /// {@link org.antlr.v4.runtime.ParserRuleContext#addChild(org.antlr.v4.runtime.Token)}. + /// {@link Parser#createErrorNode(ParserRuleContext, Token)} then + /// {@link ParserRuleContext#addErrorNode(ErrorNode)} instead of + /// {@link Parser#createTerminalNode(ParserRuleContext, Token)}. /// /// - parameter recognizer: the parser instance /// - returns: {@code true} if the parser is currently recovering from a parse diff --git a/runtime/Swift/Sources/Antlr4/Parser.swift b/runtime/Swift/Sources/Antlr4/Parser.swift index f4360c743..7aa4e1fa8 100644 --- a/runtime/Swift/Sources/Antlr4/Parser.swift +++ b/runtime/Swift/Sources/Antlr4/Parser.swift @@ -1,18 +1,15 @@ -/* Copyright (c) 2012-2016 The ANTLR Project. All rights reserved. - * Use of this file is governed by the BSD 3-clause license that - * can be found in the LICENSE.txt file in the project root. - */ - - -/** This is all the parsing support code essentially; most of it is error recovery stuff. */ -//public abstract class Parser : Recognizer { +/// +/// Copyright (c) 2012-2016 The ANTLR Project. All rights reserved. +/// Use of this file is governed by the BSD 3-clause license that +/// can be found in the LICENSE.txt file in the project root. +/// import Foundation +/// This is all the parsing support code essentially; most of it is error recovery stuff. open class Parser: Recognizer { public static let EOF: Int = -1 public static var ConsoleError = true - //false public class TraceListener: ParseTreeListener { var host: Parser @@ -62,10 +59,7 @@ open class Parser: Recognizer { public func exitEveryRule(_ ctx: ParserRuleContext) { - //TODO: check necessary -// if (ctx.children is ArrayList) { -// (ctx.children as ArrayList).trimToSize(); -// } + // TODO: Print exit info. } } @@ -75,10 +69,8 @@ open class Parser: Recognizer { * * @see org.antlr.v4.runtime.atn.ATNDeserializationOptions#isGenerateRuleBypassTransitions() */ - //private let bypassAltsAtnCache : Dictionary = - // WeakHashMap(); MapTable - private let bypassAltsAtnCache: HashMap = HashMap() + /** * The error handling strategy for the parser. The default value is a new * instance of {@link org.antlr.v4.runtime.DefaultErrorStrategy}. @@ -86,7 +78,6 @@ open class Parser: Recognizer { * @see #getErrorHandler * @see #setErrorHandler */ - public var _errHandler: ANTLRErrorStrategy = DefaultErrorStrategy() /** @@ -177,14 +168,15 @@ open class Parser: Recognizer { * strategy to attempt recovery. If {@link #getBuildParseTree} is * {@code true} and the token index of the symbol returned by * {@link org.antlr.v4.runtime.ANTLRErrorStrategy#recoverInline} is -1, the symbol is added to - * the parse tree by calling {@link org.antlr.v4.runtime.ParserRuleContext#addErrorNode}.

+ * the parse tree by calling {@link #createErrorNode(ParserRuleContext, Token)} then + * {@link ParserRuleContext#addErrorNode(ErrorNode)}.

* * @param ttype the token type to match * @return the matched symbol * @throws org.antlr.v4.runtime.RecognitionException if the current input symbol did not match * {@code ttype} and the error strategy could not recover from the * mismatched symbol - *///; RecognitionException + */ @discardableResult public func match(_ ttype: Int) throws -> Token { var t: Token = try getCurrentToken() @@ -196,7 +188,7 @@ open class Parser: Recognizer { if _buildParseTrees && t.getTokenIndex() == -1 { // we must have conjured up a new token during single token insertion // if it's not the current symbol - _ctx!.addErrorNode(t) + _ctx!.addErrorNode(createErrorNode(parent: _ctx!, t: t)) } } return t @@ -212,7 +204,8 @@ open class Parser: Recognizer { * strategy to attempt recovery. If {@link #getBuildParseTree} is * {@code true} and the token index of the symbol returned by * {@link org.antlr.v4.runtime.ANTLRErrorStrategy#recoverInline} is -1, the symbol is added to - * the parse tree by calling {@link org.antlr.v4.runtime.ParserRuleContext#addErrorNode}.

+ * the parse tree by calling {@link #createErrorNode(ParserRuleContext, Token)} then + * {@link ParserRuleContext#addErrorNode(ErrorNode)}.

* * @return the matched symbol * @throws org.antlr.v4.runtime.RecognitionException if the current input symbol did not match @@ -230,7 +223,7 @@ open class Parser: Recognizer { if _buildParseTrees && t.getTokenIndex() == -1 { // we must have conjured up a new token during single token insertion // if it's not the current symbol - _ctx!.addErrorNode(t) + _ctx!.addErrorNode(createErrorNode(parent: _ctx!, t: t)) } } @@ -562,11 +555,11 @@ open class Parser: Recognizer { * * * If the parser is not in error recovery mode, the consumed symbol is added - * to the parse tree using {@link org.antlr.v4.runtime.ParserRuleContext#addChild(org.antlr.v4.runtime.Token)}, and + * to the parse tree using {@link ParserRuleContext#addChild(TerminalNode)}, and * {@link org.antlr.v4.runtime.tree.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 org.antlr.v4.runtime.ParserRuleContext#addErrorNode(org.antlr.v4.runtime.Token)}, and + * added to the parse tree using {@link #createErrorNode(ParserRuleContext, Token)} then + * {@link ParserRuleContext#addErrorNode(ErrorNode)} and * {@link org.antlr.v4.runtime.tree.ParseTreeListener#visitErrorNode} is called on any parse * listeners. */ @@ -583,14 +576,14 @@ open class Parser: Recognizer { if _buildParseTrees || hasListener { if _errHandler.inErrorRecoveryMode(self) { - let node: ErrorNode = _ctx.addErrorNode(o) + let node: ErrorNode = _ctx.addErrorNode(createErrorNode(parent: _ctx, t: o)) if let _parseListeners = _parseListeners { for listener: ParseTreeListener in _parseListeners { listener.visitErrorNode(node) } } } else { - let node: TerminalNode = _ctx.addChild(o) + let node: TerminalNode = _ctx.addChild(createTerminalNode(parent: _ctx, t: o)) if let _parseListeners = _parseListeners { for listener: ParseTreeListener in _parseListeners { listener.visitTerminal(node) @@ -600,6 +593,38 @@ open class Parser: Recognizer { } return o } + + /** How to create a token leaf node associated with a parent. + * Typically, the terminal node to create is not a function of the parent + * but this method must still set the parent pointer of the terminal node + * returned. I would prefer having {@link ParserRuleContext#addAnyChild(ParseTree)} + * set the parent pointer, but the parent pointer is implementation dependent + * and currently there is no setParent() in {@link TerminalNode} (and can't + * add method in Java 1.7 without breaking backward compatibility). + * + * @since 4.6.1 + */ + public func createTerminalNode(parent: ParserRuleContext, t: Token) -> TerminalNode { + let node = TerminalNodeImpl(t); + node.parent = parent; + return node; + } + + /** How to create an error node, given a token, associated with a parent. + * Typically, the error node to create is not a function of the parent + * but this method must still set the parent pointer of the terminal node + * returned. I would prefer having {@link ParserRuleContext#addAnyChild(ParseTree)} + * set the parent pointer, but the parent pointer is implementation dependent + * and currently there is no setParent() in {@link ErrorNode} (and can't + * add method in Java 1.7 without breaking backward compatibility). + * + * @since 4.6.1 + */ + public func createErrorNode(parent: ParserRuleContext, t: Token) -> ErrorNode { + let node = ErrorNode(t); + node.parent = parent; + return node; + } internal func addContextToParseTree() { diff --git a/runtime/Swift/Sources/Antlr4/ParserRuleContext.swift b/runtime/Swift/Sources/Antlr4/ParserRuleContext.swift index 13c91d6ef..a39aa8ea1 100644 --- a/runtime/Swift/Sources/Antlr4/ParserRuleContext.swift +++ b/runtime/Swift/Sources/Antlr4/ParserRuleContext.swift @@ -105,51 +105,84 @@ open class ParserRuleContext: RuleContext { open func exitRule(_ listener: ParseTreeListener) { } - /** Does not set parent link; other add methods do that */ + /** Add a parse tree node to this as a child. Works for + * internal and leaf nodes. Does not set parent link; + * other add methods must do that. Other addChild methods + * call this. + * + * We cannot set the parent pointer of the incoming node + * because the existing interfaces do not have a setParent() + * method and I don't want to break backward compatibility for this. + * + * @since 4.6.1 + */ @discardableResult - open func addChild(_ t: TerminalNode) -> TerminalNode { + open func addAnyChild(_ t: T) -> T { if children == nil { - children = Array() + children = [T]() } children!.append(t) return t } + @discardableResult open func addChild(_ ruleInvocation: RuleContext) -> RuleContext { - if children == nil { - children = Array() - } - children!.append(ruleInvocation) - return ruleInvocation + return addAnyChild(ruleInvocation) + } + + @discardableResult + open func addChild(_ t: TerminalNode) -> TerminalNode { + return addAnyChild(t) + } + + /** Add an error node child. @since 4.6.1 */ + @discardableResult + open func addErrorNode(_ errorNode: ErrorNode) -> ErrorNode { + return addAnyChild(errorNode) } + /** Add a child to this node based upon matchedToken. It + * creates a TerminalNodeImpl rather than using + * {@link Parser#createTerminalNode(ParserRuleContext, Token)}. I'm leaving this + * in for compatibility but the parser doesn't use this anymore. + */ + @available(*, deprecated) + open func addChild(_ matchedToken: Token) -> TerminalNode { + let t: TerminalNodeImpl = TerminalNodeImpl(matchedToken) + addAnyChild(t) + t.parent = self + return t + } + + /** Add a child to this node based upon badToken. It + * creates a ErrorNodeImpl rather than using + * {@link Parser#createErrorNode(ParserRuleContext, Token)}. I'm leaving this + * in for compatibility but the parser doesn't use this anymore. + */ + @discardableResult + @available(*, deprecated) + open func addErrorNode(_ badToken: Token) -> ErrorNode { + let t: ErrorNode = ErrorNode(badToken) + addAnyChild(t) + t.parent = self + return t + } + + // public void trace(int s) { + // if ( states==null ) states = new ArrayList(); + // states.add(s); + // } + /** Used by enterOuterAlt to toss out a RuleContext previously added as * we entered a rule. If we have # label, we will need to remove * generic ruleContext object. - */ + */ open func removeLastChild() { - children?.removeLast() - //children.remove(children.size()-1); - } - -// public void trace(int s) { -// if ( states==null ) states = new ArrayList(); -// states.add(s); -// } - - open func addChild(_ matchedToken: Token) -> TerminalNode { - let t: TerminalNodeImpl = TerminalNodeImpl(matchedToken) - addChild(t) - t.parent = self - return t - } - @discardableResult - open func addErrorNode(_ badToken: Token) -> ErrorNode { - let t: ErrorNode = ErrorNode(badToken) - addChild(t) - t.parent = self - return t + if children != nil { + children!.remove(at: children!.count-1) + } } + override /** Override to make type more specific */ From 38c3aaae8ff5ac4ea11af42e9ce5c5c4774d64f6 Mon Sep 17 00:00:00 2001 From: Hanzhou Shi Date: Fri, 24 Feb 2017 23:54:25 -0800 Subject: [PATCH 2/2] Implements #1674 "augment TerminalNode with setParent()" for Swift target. --- runtime/Swift/Sources/Antlr4/Parser.swift | 22 +++----------- .../Sources/Antlr4/ParserRuleContext.swift | 29 ++++++++++++++----- .../Sources/Antlr4/tree/TerminalNode.swift | 14 +++++++++ .../Antlr4/tree/TerminalNodeImpl.swift | 5 ++++ 4 files changed, 44 insertions(+), 26 deletions(-) diff --git a/runtime/Swift/Sources/Antlr4/Parser.swift b/runtime/Swift/Sources/Antlr4/Parser.swift index 7aa4e1fa8..7b6522523 100644 --- a/runtime/Swift/Sources/Antlr4/Parser.swift +++ b/runtime/Swift/Sources/Antlr4/Parser.swift @@ -595,35 +595,21 @@ open class Parser: Recognizer { } /** How to create a token leaf node associated with a parent. - * Typically, the terminal node to create is not a function of the parent - * but this method must still set the parent pointer of the terminal node - * returned. I would prefer having {@link ParserRuleContext#addAnyChild(ParseTree)} - * set the parent pointer, but the parent pointer is implementation dependent - * and currently there is no setParent() in {@link TerminalNode} (and can't - * add method in Java 1.7 without breaking backward compatibility). + * Typically, the terminal node to create is not a function of the parent. * * @since 4.6.1 */ public func createTerminalNode(parent: ParserRuleContext, t: Token) -> TerminalNode { - let node = TerminalNodeImpl(t); - node.parent = parent; - return node; + return TerminalNodeImpl(t); } /** How to create an error node, given a token, associated with a parent. - * Typically, the error node to create is not a function of the parent - * but this method must still set the parent pointer of the terminal node - * returned. I would prefer having {@link ParserRuleContext#addAnyChild(ParseTree)} - * set the parent pointer, but the parent pointer is implementation dependent - * and currently there is no setParent() in {@link ErrorNode} (and can't - * add method in Java 1.7 without breaking backward compatibility). + * Typically, the error node to create is not a function of the parent. * * @since 4.6.1 */ public func createErrorNode(parent: ParserRuleContext, t: Token) -> ErrorNode { - let node = ErrorNode(t); - node.parent = parent; - return node; + return ErrorNode(t); } internal func addContextToParseTree() { diff --git a/runtime/Swift/Sources/Antlr4/ParserRuleContext.swift b/runtime/Swift/Sources/Antlr4/ParserRuleContext.swift index a39aa8ea1..a71f93c19 100644 --- a/runtime/Swift/Sources/Antlr4/ParserRuleContext.swift +++ b/runtime/Swift/Sources/Antlr4/ParserRuleContext.swift @@ -72,6 +72,14 @@ open class ParserRuleContext: RuleContext { /** COPY a ctx (I'm deliberately not using copy constructor) to avoid * confusion with creating node with parent. Does not copy children. + * + * This is used in the generated parser code to flip a generic XContext + * node for rule X to a YContext for alt label Y. In that sense, it is + * not really a generic copy function. + * + * If we do an error sync() at start of a rule, we might add error nodes + * to the generic XContext so this function must copy those nodes to + * the YContext as well else they are lost! */ open func copyFrom(_ ctx: ParserRuleContext) { self.parent = ctx.parent @@ -81,13 +89,12 @@ open class ParserRuleContext: RuleContext { self.stop = ctx.stop // copy any error nodes to alt label node - if ctx.children != nil{ + if ctx.children != nil { self.children = Array() // reset parent pointer for any error nodes - for child: ParseTree in ctx.children! { - if child is ErrorNode{ - self.children?.append(child) - ( (child as! ErrorNode)).parent = self + for child: ParseTree in ctx.children! { + if child is ErrorNode { + addChild(child as! ErrorNode) } } } @@ -130,14 +137,20 @@ open class ParserRuleContext: RuleContext { return addAnyChild(ruleInvocation) } + /** Add a token leaf node child and force its parent to be this node. */ @discardableResult open func addChild(_ t: TerminalNode) -> TerminalNode { + t.setParent(self) return addAnyChild(t) } - /** Add an error node child. @since 4.6.1 */ + /** Add an error node child and force its parent to be this node. + * + * @since 4.6.1 + */ @discardableResult open func addErrorNode(_ errorNode: ErrorNode) -> ErrorNode { + errorNode.setParent(self) return addAnyChild(errorNode) } @@ -150,7 +163,7 @@ open class ParserRuleContext: RuleContext { open func addChild(_ matchedToken: Token) -> TerminalNode { let t: TerminalNodeImpl = TerminalNodeImpl(matchedToken) addAnyChild(t) - t.parent = self + t.setParent(self) return t } @@ -164,7 +177,7 @@ open class ParserRuleContext: RuleContext { open func addErrorNode(_ badToken: Token) -> ErrorNode { let t: ErrorNode = ErrorNode(badToken) addAnyChild(t) - t.parent = self + t.setParent(self) return t } diff --git a/runtime/Swift/Sources/Antlr4/tree/TerminalNode.swift b/runtime/Swift/Sources/Antlr4/tree/TerminalNode.swift index f3d41f648..0542673dd 100644 --- a/runtime/Swift/Sources/Antlr4/tree/TerminalNode.swift +++ b/runtime/Swift/Sources/Antlr4/tree/TerminalNode.swift @@ -9,4 +9,18 @@ public class TerminalNode: ParseTree { fatalError() } + + /** Set the parent for this leaf node. + * + * Technically, this is not backward compatible as it changes + * the interface but no one was able to create custom + * TerminalNodes anyway so I'm adding as it improves internal + * code quality. + * + * @since 4.6.1 + */ + public func setParent(_ parent: RuleContext) { + RuntimeException(" must overriden !") + fatalError() + } } diff --git a/runtime/Swift/Sources/Antlr4/tree/TerminalNodeImpl.swift b/runtime/Swift/Sources/Antlr4/tree/TerminalNodeImpl.swift index a53f8a6ed..7c03aeee9 100644 --- a/runtime/Swift/Sources/Antlr4/tree/TerminalNodeImpl.swift +++ b/runtime/Swift/Sources/Antlr4/tree/TerminalNodeImpl.swift @@ -27,6 +27,11 @@ public class TerminalNodeImpl: TerminalNode { return parent } + override + public func setParent(_ parent: RuleContext) { + self.parent = parent + } + override public func getPayload() -> AnyObject { return symbol