2 using System.Collections.Generic;
5 using System.Text.RegularExpressions;
19 IList<Token> Tokenize(
string input);
75 private static string QUOTE =
@"(?<quote>(""|(?<=(^|\s))'))(?<string>(.*?))(?<!\e)\k<quote>";
76 private static string NUMBERSNG =
@"(?<number>([+-]?([0-9]+(\.[0-9]+)?|\.[0-9]+)))";
77 private static string NUMBER =
@"(?<number>([0-9]+(\.[0-9]+)?|\.[0-9]+))";
78 private static string STRINGA =
@"(?<string>((\w";
79 private static string STRINGB =
@")+";
80 private static string STRINGC =
@"))";
81 private static string SYMBOLA =
@"(?<symbol>([\\]?[^a-zA-Z0-9\s";
82 private static string SYMBOLB =
@"]))";
84 private static Tuple<string, TokenType>[] GROUPS =
new[]
86 Tuple.Create(
"number", TokenType.Number),
88 Tuple.Create(
"string", TokenType.String)
94 private HashSet<string> kwhash;
96 private Func<char, bool> forbidden =
97 (
char x) => {
return char.IsControl(x) ||
char.IsWhiteSpace(x); };
101 private void CreateRegex()
103 StringBuilder sb =
new StringBuilder();
106 sb.Append(QUOTE).Append(
'|');
110 var nstr = Options.HasFlag(TokenizerOptions.IgnoreSigns) ? NUMBER : NUMBERSNG;
113 sb.Append(
@"(?:(^|\s))").Append(nstr).Append(
@"(?:($|\s))");
120 if (Alpha.Length != 0) sb.Append(
"|[\\e]?[").Append(Alpha).Append(
"]");
123 var kws = Keywords.Where(x => x.Any(c => !char.IsLetterOrDigit(c)))
124 .OrderByDescending(x => x.Length);
126 foreach (
string keyword
in kws)
127 sb.Append(
'|').Append(Regex.Escape(keyword));
129 sb.Append(STRINGC).Append(
'|').Append(SYMBOLA).Append(Alpha).Append(SYMBOLB);
131 RegexOptions regopt = RegexOptions.None;
132 if (Options.HasFlag(
TokenizerOptions.IgnoreCase)) regopt |= RegexOptions.IgnoreCase;
133 if (Options.HasFlag(
TokenizerOptions.CompiledMatch)) regopt |= RegexOptions.Compiled;
135 regex =
new Regex(sb.ToString(), regopt);
160 if (alpha == null)
throw new ArgumentNullException(
"alpha");
161 if (keywords == null)
throw new ArgumentNullException(
"keywords");
163 kwhash =
new HashSet<string>(options.HasFlag(TokenizerOptions.IgnoreCase)
164 ? StringComparer.OrdinalIgnoreCase
165 : StringComparer.Ordinal
168 Alpha = alpha.ToArray();
169 Keywords = keywords.ToArray();
172 if (Alpha.Any(x => forbidden(x)))
173 throw new ArgumentException(
string.Format(
"Alpha contains invalid symbols"));
175 for (
int ix = 0; ix < Keywords.Length; ++ix)
176 kwhash.Add(Keywords[ix]);
184 public string[] Keywords {
get;
private set; }
187 public char[] Alpha {
get;
private set; }
194 #region [ ITokenizer ]
198 List<Token> results =
new List<Token>();
199 Match match = regex.Match(input);
203 bool quoted = match.Groups[
"quote"].Captures.Count != 0;
205 for (
int ix = 0; ix < GROUPS.Length; ++ix)
207 Group group = match.Groups[GROUPS[ix].Item1];
209 if (group.Captures.Count == 0)
continue;
211 Capture capture = group.Captures[0];
214 if (
string.IsNullOrEmpty(capture.Value))
217 if ((toktype ==
TokenType.String) && !quoted && kwhash.Contains(capture.Value))
218 toktype = TokenType.Keyword;
220 results.Add(
new Token(capture.Value, toktype, match.Index, quoted));
223 match = match.NextMatch();
summary>Ignores case when matching patterns.
Tokenizer()
Creates a default tokenizer
Token, a part of an input text recognized by tokenizer
Edge created by input scanning
summary>With this option, numbers must be separated by spaces.
A default implementation of ITokenizer interface using Microsoft Regular Expressions. Splits tokens in the following fashion:
Tokenizer(IEnumerable< char > alpha)
Creates a tokenizer with custom alpha characters
summary>Will not detect numbers, but consider them strings instead.
IList< Token > Tokenize(string input)
Splits source string into tokens.
summary>Detects only positive numbers.
TokenType
Defines token types. The actual conditions depend on the Mercury.Scanning.ITokenizer implementation...
TokenizerOptions
Options for the tokenizer
summary>Treats quote characters as symbols.
Tokenizer(IEnumerable< char > alpha, IEnumerable< string > keywords, TokenizerOptions options)
Creates a tokenizer with custom alphanumeric characters, keywords and options.