You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
231 lines
8.8 KiB
231 lines
8.8 KiB
7 months ago
|
/*
|
||
|
* [The "BSD licence"]
|
||
|
* Copyright (c) 2005-2008 Terence Parr
|
||
|
* All rights reserved.
|
||
|
*
|
||
|
* Conversion to C#:
|
||
|
* Copyright (c) 2008-2009 Sam Harwell, Pixel Mine, Inc.
|
||
|
* 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.
|
||
|
*/
|
||
|
|
||
|
namespace Antlr.Runtime.Tree {
|
||
|
using System.Collections.Generic;
|
||
|
using IList = System.Collections.IList;
|
||
|
|
||
|
/** <summary>
|
||
|
* A generic list of elements tracked in an alternative to be used in
|
||
|
* a -> rewrite rule. We need to subclass to fill in the next() method,
|
||
|
* which returns either an AST node wrapped around a token payload or
|
||
|
* an existing subtree.
|
||
|
* </summary>
|
||
|
*
|
||
|
* <remarks>
|
||
|
* Once you start next()ing, do not try to add more elements. It will
|
||
|
* break the cursor tracking I believe.
|
||
|
*
|
||
|
* TODO: add mechanism to detect/puke on modification after reading from stream
|
||
|
* </remarks>
|
||
|
*
|
||
|
* <see cref="RewriteRuleSubtreeStream"/>
|
||
|
* <see cref="RewriteRuleTokenStream"/>
|
||
|
*/
|
||
|
[System.Serializable]
|
||
|
public abstract class RewriteRuleElementStream {
|
||
|
/** <summary>
|
||
|
* Cursor 0..n-1. If singleElement!=null, cursor is 0 until you next(),
|
||
|
* which bumps it to 1 meaning no more elements.
|
||
|
* </summary>
|
||
|
*/
|
||
|
protected int cursor = 0;
|
||
|
|
||
|
/** <summary>Track single elements w/o creating a list. Upon 2nd add, alloc list */
|
||
|
protected object singleElement;
|
||
|
|
||
|
/** <summary>The list of tokens or subtrees we are tracking */
|
||
|
protected IList elements;
|
||
|
|
||
|
/** <summary>Once a node / subtree has been used in a stream, it must be dup'd
|
||
|
* from then on. Streams are reset after subrules so that the streams
|
||
|
* can be reused in future subrules. So, reset must set a dirty bit.
|
||
|
* If dirty, then next() always returns a dup.
|
||
|
*
|
||
|
* I wanted to use "naughty bit" here, but couldn't think of a way
|
||
|
* to use "naughty".
|
||
|
*/
|
||
|
protected bool dirty = false;
|
||
|
|
||
|
/** <summary>The element or stream description; usually has name of the token or
|
||
|
* rule reference that this list tracks. Can include rulename too, but
|
||
|
* the exception would track that info.
|
||
|
*/
|
||
|
protected string elementDescription;
|
||
|
protected ITreeAdaptor adaptor;
|
||
|
|
||
|
public RewriteRuleElementStream(ITreeAdaptor adaptor, string elementDescription) {
|
||
|
this.elementDescription = elementDescription;
|
||
|
this.adaptor = adaptor;
|
||
|
}
|
||
|
|
||
|
/** <summary>Create a stream with one element</summary> */
|
||
|
public RewriteRuleElementStream(ITreeAdaptor adaptor, string elementDescription, object oneElement)
|
||
|
: this(adaptor, elementDescription) {
|
||
|
Add(oneElement);
|
||
|
}
|
||
|
|
||
|
/** <summary>Create a stream, but feed off an existing list</summary> */
|
||
|
public RewriteRuleElementStream(ITreeAdaptor adaptor, string elementDescription, IList elements)
|
||
|
: this(adaptor, elementDescription) {
|
||
|
this.singleElement = null;
|
||
|
this.elements = elements;
|
||
|
}
|
||
|
|
||
|
/** <summary>
|
||
|
* Reset the condition of this stream so that it appears we have
|
||
|
* not consumed any of its elements. Elements themselves are untouched.
|
||
|
* Once we reset the stream, any future use will need duplicates. Set
|
||
|
* the dirty bit.
|
||
|
* </summary>
|
||
|
*/
|
||
|
public virtual void Reset() {
|
||
|
cursor = 0;
|
||
|
dirty = true;
|
||
|
}
|
||
|
|
||
|
public virtual void Add(object el) {
|
||
|
//System.out.println("add '"+elementDescription+"' is "+el);
|
||
|
if (el == null) {
|
||
|
return;
|
||
|
}
|
||
|
if (elements != null) { // if in list, just add
|
||
|
elements.Add(el);
|
||
|
return;
|
||
|
}
|
||
|
if (singleElement == null) { // no elements yet, track w/o list
|
||
|
singleElement = el;
|
||
|
return;
|
||
|
}
|
||
|
// adding 2nd element, move to list
|
||
|
elements = new List<object>(5);
|
||
|
elements.Add(singleElement);
|
||
|
singleElement = null;
|
||
|
elements.Add(el);
|
||
|
}
|
||
|
|
||
|
/** <summary>
|
||
|
* Return the next element in the stream. If out of elements, throw
|
||
|
* an exception unless size()==1. If size is 1, then return elements[0].
|
||
|
* Return a duplicate node/subtree if stream is out of elements and
|
||
|
* size==1. If we've already used the element, dup (dirty bit set).
|
||
|
* </summary>
|
||
|
*/
|
||
|
public virtual object NextTree() {
|
||
|
int n = Count;
|
||
|
if (dirty || (cursor >= n && n == 1)) {
|
||
|
// if out of elements and size is 1, dup
|
||
|
object el = NextCore();
|
||
|
return Dup(el);
|
||
|
}
|
||
|
// test size above then fetch
|
||
|
object el2 = NextCore();
|
||
|
return el2;
|
||
|
}
|
||
|
|
||
|
/** <summary>
|
||
|
* Do the work of getting the next element, making sure that it's
|
||
|
* a tree node or subtree. Deal with the optimization of single-
|
||
|
* element list versus list of size > 1. Throw an exception
|
||
|
* if the stream is empty or we're out of elements and size>1.
|
||
|
* protected so you can override in a subclass if necessary.
|
||
|
* </summary>
|
||
|
*/
|
||
|
protected virtual object NextCore() {
|
||
|
int n = Count;
|
||
|
if (n == 0) {
|
||
|
throw new RewriteEmptyStreamException(elementDescription);
|
||
|
}
|
||
|
if (cursor >= n) { // out of elements?
|
||
|
if (n == 1) { // if size is 1, it's ok; return and we'll dup
|
||
|
return ToTree(singleElement);
|
||
|
}
|
||
|
// out of elements and size was not 1, so we can't dup
|
||
|
throw new RewriteCardinalityException(elementDescription);
|
||
|
}
|
||
|
// we have elements
|
||
|
if (singleElement != null) {
|
||
|
cursor++; // move cursor even for single element list
|
||
|
return ToTree(singleElement);
|
||
|
}
|
||
|
// must have more than one in list, pull from elements
|
||
|
object o = ToTree(elements[cursor]);
|
||
|
cursor++;
|
||
|
return o;
|
||
|
}
|
||
|
|
||
|
/** <summary>
|
||
|
* When constructing trees, sometimes we need to dup a token or AST
|
||
|
* subtree. Dup'ing a token means just creating another AST node
|
||
|
* around it. For trees, you must call the adaptor.dupTree() unless
|
||
|
* the element is for a tree root; then it must be a node dup.
|
||
|
* </summary>
|
||
|
*/
|
||
|
protected abstract object Dup(object el);
|
||
|
|
||
|
/** <summary>
|
||
|
* Ensure stream emits trees; tokens must be converted to AST nodes.
|
||
|
* AST nodes can be passed through unmolested.
|
||
|
* </summary>
|
||
|
*/
|
||
|
protected virtual object ToTree(object el) {
|
||
|
return el;
|
||
|
}
|
||
|
|
||
|
public virtual bool HasNext {
|
||
|
get {
|
||
|
return (singleElement != null && cursor < 1) ||
|
||
|
(elements != null && cursor < elements.Count);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public virtual int Count {
|
||
|
get {
|
||
|
int n = 0;
|
||
|
if (singleElement != null) {
|
||
|
n = 1;
|
||
|
}
|
||
|
if (elements != null) {
|
||
|
return elements.Count;
|
||
|
}
|
||
|
return n;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public virtual string Description {
|
||
|
get {
|
||
|
return elementDescription;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|