我正在制作一个简单的编译器,并且正在进行字符串解析。目前,我的代码是:
while (stringToParse.Contains(" + ") || stringToParse.Contains("+ ") || stringToParse.Contains(" +")) {
stringToParse = stringToParse.Replace(" +", "+").Replace("+ ", "+").Replace(" + ", "+");
}
string[] splitString = stringToParse.Split("+");
但是类似:
"\"hello \" + \"world \" + \" + \" + \"hello\""
会返回:
["\"hello "\", "\"world \"", "\"", "\"", ]
(不带反斜杠)
但是类似:
""hello " + "world " + " + " + "hello""
会返回:
[""hello "", ""world "", """, """, ]
那么如何指定" + "
是字符串还是分隔符?也许有一种方法可以检测到以下情况?
...(any number of non " or + characters)...+...(any number of " or + characters)
我的预期输出是:
[""hello "", ""world "", ""+""]
答案 0 :(得分:4)
为此,不使用任何专用库,我建议构建一个状态机。
您将遍历字符串的字符,并根据遇到的字符更新计算机的状态。 可以进行优化,但让我们从传统的角度出发。
var characters = input.ToCharArray();
var results = new List<string>();
var current = string.Empty;
// 0 = not inside quotes, we expect +
// 1 = not inside quotes, we expect "
// 2 = inside quotes
var state = 1;
foreach (var character in characters)
{
switch (state)
{
case 0:
// We are not inside quotes, we expect +
if (character == '+')
{
state = 1;
continue;
}
if (char.IsWhiteSpace(character))
{
continue;
}
// error?
break;
case 1:
// We are not inside quotes, we expect "
if (character == '\"')
{
state = 2;
continue;
}
if (char.IsWhiteSpace(character))
{
continue;
}
// error?
break;
case 2:
// We are inside quotes, we expect "
if (character == '\"')
{
state = 0;
results.Add(current);
current = string.Empty;
continue;
}
current += character;
break;
default:
// error?
break;
}
}
if (state != 0)
{
// error
}
// You can use results.ToArray();
可能的优化:
您还可以看到如何为自己的转义序列或任何其他内容添加支持。
我想指出,这不是组织此代码的唯一方法。如果您是我,我将创建一个具有两个方法的伪迭代器类:
这种方法的主要优点是,我不再需要一个一个字符一个字符地步进,因此,我不需要state
变量。相反,我可以允许代码结构类似于我的语法。
等等,我已经编写了此类:StringProcessor。它是Theraot.Core nuget的一部分,用于将字符串解析为BigInteger。
var processor = new Theraot.Core.StringProcessor(input);
var results = new List<string>();
while (!processor.EndOfString)
{
// SkipWhile skips all the characters that match
processor.SkipWhile(char.IsWhiteSpace);
// Read returns true (and advances after) if what is next matches the paramter
if (processor.Read('"'))
{
// ReadUntil advances after and returns everything found before the parameter
// Note: it does not advance after the parameter.
results.Add(processor.ReadUntil('"'));
processor.Read('"');
}
processor.SkipWhile(char.IsWhiteSpace);
if (!processor.Read('+'))
{
// error?
}
}
请注意,上面使用的诸如StringProcessor
之类的类可以减少很多毛病,这使其对于简单语言而言是可行的。
当然,对于更复杂的东西,您可能需要寻找令牌生成器。
举一个例子,认为这是我们拥有的“语法”:
Document: Many
{
Whitespace
String:
{
QuoteSymbol
NonQuoteSymbol
QuoteSymbol
}
Whitespace
PlusSymbol
}
不,这不是任何常用的元语言。但是,以这种方式编写的代码很容易看到我们上面的代码与语言的相似之处。
写如下代码不好吗?
var QuoteSymbol = Pattern.Literal("QuoteSymbol", '"');
var NonQuoteSymbol = Pattern.Custom("NonQuoteSymbol", s => s.ReadUntil('"'));
var String = Pattern.Conjunction("String", QuoteSymbol, NonQuoteSymbol, QuoteSymbol);
var WhiteSpace = Pattern.Custom("WhiteSpace", s => s.ReadWhile(char.IsWhiteSpace));
var PlusSymbol = Pattern.Literal("PlusSymbol", '+');
var Document = Pattern.Repetition(
Pattern.Conjunction(WhiteSpace, String, WhiteSpace, PlusSymbol)
);
var results = from TerminalSymbol symbol
in Document.Parse(input)
where symbol.Pattern == String
select symbol.ToString();
编写这样的代码将使修改语言变得更加容易。 好吧,我们仍在编写代码,但是您可以想象解析一个文件,该文件具有您要解析的语言的语法...花哨!
如您所料,需要额外的工作来构建必要的代码才能使其正常工作。或者,您知道获得some code that already works(链接的代码围绕StringProcessor
构建)。
前面介绍的代码不适合用于prettyprinter,并且无法从语法错误中恢复。 可以将其修改为执行此类操作。也不与任何级别的代码编辑器集成。
如果您需要完整的解决方案。我有两个建议:
如果您想在顶部创建编程语言,就会使用这些东西。
当然,我应该将您链接到"Compilers: Principles, Techniques, and Tools",通常被称为“龙书”。