242 lines
6.7 KiB
C#
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 + " ");
|
|
}
|
|
}
|
|
}
|