254 lines
9.0 KiB
C#
254 lines
9.0 KiB
C#
/*
|
|
* [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.</summary>
|
|
*/
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
}
|