Files
Flee/Parsing/RepeatElement.cs

242 lines
6.7 KiB
C#

using System;
using System.Collections;
using System.IO;
namespace Flee.Parsing
{
/**
* A regular expression element repeater. The element repeats the
* matches from a specified element, attempting to reach the
* maximum repetition count.
*/
internal class RepeatElement : Element
{
public enum RepeatType
{
GREEDY = 1,
RELUCTANT = 2,
POSSESSIVE = 3
}
private readonly Element _elem;
private readonly int _min;
private readonly int _max;
private readonly RepeatType _type;
private int _matchStart;
private BitArray _matches;
public RepeatElement(Element elem,
int min,
int max,
RepeatType type)
{
this._elem = elem;
this._min = min;
if (max <= 0)
{
this._max = Int32.MaxValue;
}
else
{
this._max = max;
}
this._type = type;
this._matchStart = -1;
this._matches = null;
}
public override object Clone()
{
return new RepeatElement((Element)_elem.Clone(),
_min,
_max,
_type);
}
public override int Match(Matcher m,
ReaderBuffer buffer,
int start,
int skip)
{
if (skip == 0)
{
_matchStart = -1;
_matches = null;
}
switch (_type)
{
case RepeatType.GREEDY:
return MatchGreedy(m, buffer, start, skip);
case RepeatType.RELUCTANT:
return MatchReluctant(m, buffer, start, skip);
case RepeatType.POSSESSIVE:
if (skip == 0)
{
return MatchPossessive(m, buffer, start, 0);
}
break;
}
return -1;
}
private int MatchGreedy(Matcher m,
ReaderBuffer buffer,
int start,
int skip)
{
// Check for simple case
if (skip == 0)
{
return MatchPossessive(m, buffer, start, 0);
}
// Find all matches
if (_matchStart != start)
{
_matchStart = start;
_matches = new BitArray(10);
FindMatches(m, buffer, start, 0, 0, 0);
}
// Find first non-skipped match
for (int i = _matches.Count - 1; i >= 0; i--)
{
if (_matches[i])
{
if (skip == 0)
{
return i;
}
skip--;
}
}
return -1;
}
private int MatchReluctant(Matcher m,
ReaderBuffer buffer,
int start,
int skip)
{
if (_matchStart != start)
{
_matchStart = start;
_matches = new BitArray(10);
FindMatches(m, buffer, start, 0, 0, 0);
}
// Find first non-skipped match
for (int i = 0; i < _matches.Count; i++)
{
if (_matches[i])
{
if (skip == 0)
{
return i;
}
skip--;
}
}
return -1;
}
private int MatchPossessive(Matcher m,
ReaderBuffer buffer,
int start,
int count)
{
int length = 0;
int subLength = 1;
// Match as many elements as possible
while (subLength > 0 && count < _max)
{
subLength = _elem.Match(m, buffer, start + length, 0);
if (subLength >= 0)
{
count++;
length += subLength;
}
}
// Return result
if (_min <= count && count <= _max)
{
return length;
}
else
{
return -1;
}
}
private void FindMatches(Matcher m,
ReaderBuffer buffer,
int start,
int length,
int count,
int attempt)
{
int subLength;
// Check match ending here
if (count > _max)
{
return;
}
if (_min <= count && attempt == 0)
{
if (_matches.Length <= length)
{
_matches.Length = length + 10;
}
_matches[length] = true;
}
// Check element match
subLength = _elem.Match(m, buffer, start, attempt);
if (subLength < 0)
{
return;
}
else if (subLength == 0)
{
if (_min == count + 1)
{
if (_matches.Length <= length)
{
_matches.Length = length + 10;
}
_matches[length] = true;
}
return;
}
// Find alternative and subsequent matches
FindMatches(m, buffer, start, length, count, attempt + 1);
FindMatches(m,
buffer,
start + subLength,
length + subLength,
count + 1,
0);
}
public override void PrintTo(TextWriter output, string indent)
{
output.Write(indent + "Repeat (" + _min + "," + _max + ")");
if (_type == RepeatType.RELUCTANT)
{
output.Write("?");
}
else if (_type == RepeatType.POSSESSIVE)
{
output.Write("+");
}
output.WriteLine();
_elem.PrintTo(output, indent + " ");
}
}
}