BITFALL/Assets/Plugins/PluginMaster/DesignTools/Editor/CommonPaint/Scripts/PatternMachine.cs

189 lines
8.3 KiB
C#

/*
Copyright (c) 2020 Omar Duarte
Unauthorized copying of this file, via any medium is strictly prohibited.
Writen by Omar Duarte, 2020.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
using System.Linq;
namespace PluginMaster
{
public class PatternMachine
{
#region STATES AND TOKENS
public enum PatternState
{
START,
INDEX,
OPENING_PARENTHESIS,
CLOSING_PARENTHESIS,
COMMA,
ASTERISK,
MULTIPLIER,
ELLIPSIS,
END
}
public class Token
{
public readonly PatternState state = PatternState.START;
protected Token(PatternState state) => this.state = state;
public static Token START = new Token(PatternState.START);
public static Token OPENING_PARENTHESIS = new Token(PatternState.OPENING_PARENTHESIS);
public static Token CLOSING_PARENTHESIS = new Token(PatternState.CLOSING_PARENTHESIS);
public static Token COMMA = new Token(PatternState.COMMA);
public static Token ASTERISK = new Token(PatternState.ASTERISK);
public static Token ELLIPSIS = new Token(PatternState.ELLIPSIS);
public static Token END = new Token(PatternState.END);
}
public class IntToken : Token
{
public readonly int value = -1;
public IntToken(int value, PatternState state) : base(state) => this.value = value;
}
public class MultiplierToken : IntToken
{
private int _count = 0;
public int count => _count;
public MultiplierToken(int value) : base(value, PatternState.MULTIPLIER) { }
public int IncreaseCount() => ++_count;
public void Reset() => _count = 0;
}
#endregion
#region VALIDATE
public enum ValidationResult
{
VALID,
EMPTY,
INDEX_OUT_OF_RANGE,
MISPLACED_PERIOD,
MISPLACED_ASTERISK,
MISPLACED_COMMA,
UNPAIRED_PARENTHESIS,
EMPTY_PARENTHESIS,
INVALID_MULTIPLIER,
INVALID_CHARACTER
}
public static ValidationResult Validate(string frecuencyPattern, int lastIndex, out Token[] tokens)
{
tokens = null;
frecuencyPattern = frecuencyPattern.Replace(" ", "");
if (frecuencyPattern == string.Empty) return ValidationResult.EMPTY;
var validCharactersRemoved = System.Text.RegularExpressions.Regex.Replace(frecuencyPattern, @"[\d,.*()]", "");
if (validCharactersRemoved != string.Empty) return ValidationResult.INVALID_CHARACTER;
var validBracketsRemoved = System.Text.RegularExpressions.Regex.Replace(frecuencyPattern,
@"\((?>\((?<c>)|[^()]+|\)(?<-c>))*(?(c)(?!))\)", "");
if (System.Text.RegularExpressions.Regex.Match(validBracketsRemoved,
@"\(|\)").Success) return ValidationResult.UNPAIRED_PARENTHESIS;
if (System.Text.RegularExpressions.Regex.Match(frecuencyPattern,
@"\(\)").Success) return ValidationResult.EMPTY_PARENTHESIS;
var validMultiplicationsRemoved = System.Text.RegularExpressions.Regex.Replace(frecuencyPattern,
@"(\d+|\))\*\d+", "");
if(System.Text.RegularExpressions.Regex.Match(validMultiplicationsRemoved,
@"\*").Success) return ValidationResult.MISPLACED_ASTERISK;
var validCommasRemoved = System.Text.RegularExpressions.Regex.Replace(frecuencyPattern,
@"(\)|\d+)(,(\(|\d+))+", "");
if (System.Text.RegularExpressions.Regex.Match(validCommasRemoved, @",").Success)
return ValidationResult.MISPLACED_COMMA;
var validDotsRemoved = System.Text.RegularExpressions.Regex.Replace(frecuencyPattern, @"(\d|\))\.\.\.(?!.)", "");
if (System.Text.RegularExpressions.Regex.Match(validDotsRemoved, @"\.").Success)
return ValidationResult.MISPLACED_PERIOD;
var matches = System.Text.RegularExpressions.Regex.Matches(frecuencyPattern, @"\d+|[(),*]|\.\.\.");
var tokenList = new System.Collections.Generic.List<Token>();
tokenList.Add(Token.START);
foreach(System.Text.RegularExpressions.Match match in matches)
{
if (match.Value == "(") tokenList.Add(Token.OPENING_PARENTHESIS);
else if (match.Value == ")") tokenList.Add(Token.CLOSING_PARENTHESIS);
else if (match.Value == ",") tokenList.Add(Token.COMMA);
else if (match.Value == "*") tokenList.Add(Token.ASTERISK);
else if (match.Value == "...")
{
if(tokenList.Last() is MultiplierToken) return ValidationResult.MISPLACED_PERIOD;
tokenList.Add(Token.ELLIPSIS);
}
else
{
var value = int.Parse(match.Value);
var state = tokenList.Count > 0 && tokenList.Last() == Token.ASTERISK
? PatternState.MULTIPLIER : PatternState.INDEX;
if (state == PatternState.INDEX && value > lastIndex) return ValidationResult.INDEX_OUT_OF_RANGE;
else if (state == PatternState.MULTIPLIER && value < 2) return ValidationResult.INVALID_MULTIPLIER;
tokenList.Add(state == PatternState.INDEX ? new IntToken(value, state) : new MultiplierToken(value));
}
}
tokenList.Add(Token.END);
tokens = tokenList.ToArray();
return ValidationResult.VALID;
}
#endregion
#region MACHINE
private Token[] _tokens = null;
private int _tokenIndex = 0;
private System.Collections.Generic.Stack<int> _parenthesisStack = new System.Collections.Generic.Stack<int>();
private int _lastParenthesis = -1;
public PatternMachine(Token[] tokens) => _tokens = tokens;
public void SetTokens(Token[] tokens)
{
if (Enumerable.SequenceEqual(tokens, _tokens)) return;
_tokens = tokens;
}
public void Reset()
{
_tokenIndex = 0;
foreach(var token in _tokens) if(token is MultiplierToken) (token as MultiplierToken).Reset();
}
public int nextIndex
{
get
{
if (_tokenIndex == -1) return -1;
var currentState = _tokens[_tokenIndex].state;
if (currentState == PatternState.END) return -1;
++_tokenIndex;
var nextToken = _tokens[_tokenIndex];
switch(nextToken.state)
{
case PatternState.INDEX:
return (nextToken as IntToken).value;
case PatternState.OPENING_PARENTHESIS:
_parenthesisStack.Push(_tokenIndex);
break;
case PatternState.CLOSING_PARENTHESIS:
_lastParenthesis = _parenthesisStack.Pop();
break;
case PatternState.MULTIPLIER:
var mult = nextToken as MultiplierToken;
if (mult.IncreaseCount() < mult.value)
_tokenIndex = currentState == PatternState.CLOSING_PARENTHESIS
? _lastParenthesis : _tokenIndex - 3;
break;
case PatternState.ELLIPSIS:
_tokenIndex = currentState == PatternState.CLOSING_PARENTHESIS
? _lastParenthesis - 1 : _tokenIndex - 2;
break;
case PatternState.END:
return -1;
default:
break;
}
return nextIndex;
}
}
#endregion
}
}