Все классы расширяют Object неявно, так что это просто избыточное кодирование, не имеющее никакого влияния.
Исходная версия, которую я отправил здесь как ответ, имела проблему, в которой она только работала, в то время как был больше чем один "Regex", который соответствовал текущему выражению. Таким образом, как только только один Regex соответствовал, он возвратит маркер - тогда как большинство людей хочет, чтобы Regex был "жадным". Это особенно имело место для вещей, таких как "заключенные в кавычки строки".
Единственное решение, которое находится сверху Regex, состоит в том, чтобы считать вход линию за линией (что означает, что у Вас не может быть маркеров, которые охватывают несколько строк). Я могу жить с этим - это - в конце концов, лексический анализатор бедного человека! Кроме того, обычно полезно вытащить информацию о номере строки из Лексического анализатора в любом случае.
Так, вот новая версия, которая решает эти проблемы. Кредит также переходит к этому
public interface IMatcher
{
/// <summary>
/// Return the number of characters that this "regex" or equivalent
/// matches.
/// </summary>
/// <param name="text">The text to be matched</param>
/// <returns>The number of characters that matched</returns>
int Match(string text);
}
sealed class RegexMatcher : IMatcher
{
private readonly Regex regex;
public RegexMatcher(string regex) => this.regex = new Regex(string.Format("^{0}", regex));
public int Match(string text)
{
var m = regex.Match(text);
return m.Success ? m.Length : 0;
}
public override string ToString() => regex.ToString();
}
public sealed class TokenDefinition
{
public readonly IMatcher Matcher;
public readonly object Token;
public TokenDefinition(string regex, object token)
{
this.Matcher = new RegexMatcher(regex);
this.Token = token;
}
}
public sealed class Lexer : IDisposable
{
private readonly TextReader reader;
private readonly TokenDefinition[] tokenDefinitions;
private string lineRemaining;
public Lexer(TextReader reader, TokenDefinition[] tokenDefinitions)
{
this.reader = reader;
this.tokenDefinitions = tokenDefinitions;
nextLine();
}
private void nextLine()
{
do
{
lineRemaining = reader.ReadLine();
++LineNumber;
Position = 0;
} while (lineRemaining != null && lineRemaining.Length == 0);
}
public bool Next()
{
if (lineRemaining == null)
return false;
foreach (var def in tokenDefinitions)
{
var matched = def.Matcher.Match(lineRemaining);
if (matched > 0)
{
Position += matched;
Token = def.Token;
TokenContents = lineRemaining.Substring(0, matched);
lineRemaining = lineRemaining.Substring(matched);
if (lineRemaining.Length == 0)
nextLine();
return true;
}
}
throw new Exception(string.Format("Unable to match against any tokens at line {0} position {1} \"{2}\"",
LineNumber, Position, lineRemaining));
}
public string TokenContents { get; private set; }
public object Token { get; private set; }
public int LineNumber { get; private set; }
public int Position { get; private set; }
public void Dispose() => reader.Dispose();
}
Пример программы:
string sample = @"( one (two 456 -43.2 "" \"" quoted"" ))";
var defs = new TokenDefinition[]
{
// Thanks to [steven levithan][2] for this great quoted string
// regex
new TokenDefinition(@"([""'])(?:\\\1|.)*?\1", "QUOTED-STRING"),
// Thanks to http://www.regular-expressions.info/floatingpoint.html
new TokenDefinition(@"[-+]?\d*\.\d+([eE][-+]?\d+)?", "FLOAT"),
new TokenDefinition(@"[-+]?\d+", "INT"),
new TokenDefinition(@"#t", "TRUE"),
new TokenDefinition(@"#f", "FALSE"),
new TokenDefinition(@"[*<>\?\-+/A-Za-z->!]+", "SYMBOL"),
new TokenDefinition(@"\.", "DOT"),
new TokenDefinition(@"\(", "LEFT"),
new TokenDefinition(@"\)", "RIGHT"),
new TokenDefinition(@"\s", "SPACE")
};
TextReader r = new StringReader(sample);
Lexer l = new Lexer(r, defs);
while (l.Next())
Console.WriteLine("Token: {0} Contents: {1}", l.Token, l.TokenContents);
Вывод:
Token: LEFT Contents: (
Token: SPACE Contents:
Token: SYMBOL Contents: one
Token: SPACE Contents:
Token: LEFT Contents: (
Token: SYMBOL Contents: two
Token: SPACE Contents:
Token: INT Contents: 456
Token: SPACE Contents:
Token: FLOAT Contents: -43.2
Token: SPACE Contents:
Token: QUOTED-STRING Contents: " \" quoted"
Token: SPACE Contents:
Token: RIGHT Contents: )
Token: RIGHT Contents: )
У Malcolm Crowe есть большая реализация LEX/YACC для C# здесь . Работы путем создания регулярных выражений для LEX...
Это может быть излишеством, но взглянуть на Иронию на CodePlex.
Ирония является комплектом разработчика для реализации языков на платформе.NET. Это использует гибкость и питание c# языка и Платформы.NET 3.5 для реализации абсолютно новой и оптимизированной технологии конструкции компилятора. В отличие от большинства существующих yacc/lex-style решений Ирония не использует сканера или генерации кода синтаксического анализатора от спецификаций грамматики, записанных в специализированном метаязыке. Насмешливо грамматика выходного языка кодируется непосредственно в операторе использования c#, перегружающемся для выражения конструкций грамматики. Сканер иронии и модули синтаксического анализатора используют грамматику, закодированную как c# класс для управления процессом парсинга. Посмотрите образец грамматики выражения для примера определения грамматики в c# классе и использование его в рабочем синтаксическом анализаторе.
Возможно использовать Flex и Бизона для C#.
исследователь А в Университете Ирландии разработал частичную реализацию, которая может быть найдена в следующей ссылке: Flex/бизон для C#
можно было определенно считать, что 'бедные укомплектовывают лексический анализатор', поскольку у него, кажется еще, все еще есть некоторые проблемы с его реализацией, такие как никакой препроцессор, проблемы с 'свисанием' случай, и т.д.
Если у Вас нет очень нетрадиционной грамматики, я был бы сильно , рекомендуют не лексическому анализатору/синтаксическому анализатору самокрутки.
я обычно нахожу, что лексическому анализатору/синтаксическим анализаторам для C# действительно недостает. Однако F# идет с fslex и fsyacc, который можно изучить, как использовать в этом учебном руководстве . Я записал несколько лексических анализаторов/синтаксических анализаторов в F# и использовал их в C# и его очень легком, чтобы сделать.
я предполагаю не действительно лексический анализатор/синтаксический анализатор бедного человека, видя, что необходимо выучить совершенно новый язык для начала работы, но это - запуск.
Изменение моего исходного ответа.
Смотрят на SharpTemplate, который имеет синтаксические анализаторы для различных типов синтаксиса, например,
#foreach ($product in $Products)
<tr><td>$product.Name</td>
#if ($product.Stock > 0)
<td>In stock</td>
#else
<td>Backordered</td>
#end
</tr>
#end
Он использует regexes для каждого типа маркера:
public class Velocity : SharpTemplateConfig
{
public Velocity()
{
AddToken(TemplateTokenType.ForEach, @"#(foreach|{foreach})\s+\(\s*(?<iterator>[a-z_][a-z0-9_]*)\s+in\s+(?<expr>.*?)\s*\)", true);
AddToken(TemplateTokenType.EndBlock, @"#(end|{end})", true);
AddToken(TemplateTokenType.If, @"#(if|{if})\s+\((?<expr>.*?)\s*\)", true);
AddToken(TemplateTokenType.ElseIf, @"#(elseif|{elseif})\s+\((?<expr>.*?)\s*\)", true);
AddToken(TemplateTokenType.Else, @"#(else|{else})", true);
AddToken(TemplateTokenType.Expression, @"\${(?<expr>.*?)}", false);
AddToken(TemplateTokenType.Expression, @"\$(?<expr>[a-zA-Z_][a-zA-Z0-9_\.@]*?)(?![a-zA-Z0-9_\.@])", false);
}
}
, Который используется как это
foreach (Match match in regex.Matches(inputString))
{
...
switch (tokenMatch.TokenType)
{
case TemplateTokenType.Expression:
{
currentNode.Add(new ExpressionNode(tokenMatch));
}
break;
case TemplateTokenType.ForEach:
{
nodeStack.Push(currentNode);
currentNode = currentNode.Add(new ForEachNode(tokenMatch));
}
break;
....
}
....
}
, Это продвигает и появляется от Стека для хранения состояния.
Если Вы смотрите на ExpressionConverter в моем библиотека WPF Converters , это имеет основной lexing и парсинг выражений C#. Никакой включенный regex, из памяти.