这是我的第二个答案。我的第一个答案是快速拍摄。在这里,我尝试通过一件一件地做事情来创建一个解析器。
为了转换表达式,您需要对其进行解析。这意味着您必须分析其语法。在分析其语法时,您也可以生成输出。
1 首先要做的是定义所有有效表达式的语法。
在这里,我使用 EBNF 来做到这一点。EBNF 很简单。
{
并附}
上重复(可能为零)。
[
并附]
上一个可选部分。
|
分离备选方案。
有关 EBNF的更多详细信息,请参阅维基百科上的Extended Backus-Naur Form (EBNF) 。(此处使用的 EBNF 变体删除了连接运算符“,”)。
我们在 EBNF 中的语法
表达式 = { 术语}。
术语 = [ 数字 ] 因子。
因子 = 文本 | "(" 表达式 ")" | 学期。
例子
5(2(a)sz) => aaszaaszaaszaaszaasz
5(2a sz) => aaszaaszaaszaaszaasz
2 3(a 2b)c => abbabbabbabbabbabbc
2 词法分析
在分析语法之前,我们必须将整个表达式拆分为单个词汇标记(数字、运算符等)。我们使用anenum
来表示token类型
private enum TokenType
{
None,
LPar,
RPar,
Number,
Text
}
以下字段用于保存令牌信息和布尔值_error
,它告诉解析期间是否发生错误。
private IEnumerator<Match> _matches;
TokenType _tokenType;
string _text;
int _number;
bool _error;
该方法ConvertExpression
开始转换。它将表达式拆分为表示为的单个标记Regex.Matches
。这些被方法使用GetToken
,然后将其转换Regex.Matches
为更有用的信息。此信息存储在上述字段中。
public string ConvertExpression(string expression)
{
_matches = Regex.Matches(expression, @"\d+|\(|\)|[a-zA-Z]+")
.Cast<Match>()
.GetEnumerator();
_error = false;
return GetToken() ? Expression() : "";
}
private bool GetToken()
{
_number = 0;
_tokenType = TokenType.None;
_text = null;
if (_error || !_matches.MoveNext())
return false;
_text = _matches.Current.Value;
switch (_text[0]) {
case '(':
_tokenType = TokenType.LPar;
break;
case ')':
_tokenType = TokenType.RPar;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
_tokenType = TokenType.Number;
_number = Int32.Parse(_text);
break;
default:
_tokenType = TokenType.Text;
break;
}
return true;
}
3 句法和语义分析
现在我们拥有了执行实际解析和表达式转换所需的一切。下面的每个方法都会分析一个 EBNF 语法生成并将转换结果作为字符串返回。将 EBNF 转换为 C# 代码非常简单。语法中的重复将转换为 C# 循环语句。选项转换为if
语句,备选方案转换为switch
语句。
// Expression = { Term }.
private string Expression()
{
string s = "";
do {
s += Term();
} while (_tokenType != TokenType.RPar && _tokenType != TokenType.None);
return s;
}
// Term = [ Number ] Factor.
private string Term()
{
int n;
if (_tokenType == TokenType.Number) {
n = _number;
if (!GetToken()) {
_error = true;
return " Error: Factor expected.";
}
string factor = Factor();
if (_error) {
return factor;
}
var sb = new StringBuilder(n * factor.Length);
for (int i = 0; i < n; i++) {
sb.Append(factor);
}
return sb.ToString();
}
return Factor();
}
// Factor = Text | "(" Expression ")" | Term.
private string Factor()
{
switch (_tokenType) {
case TokenType.None:
_error = true;
return " Error: Unexpected end of Expression.";
case TokenType.LPar:
if (GetToken()) {
string s = Expression();
if (_tokenType == TokenType.RPar) {
GetToken();
return s;
} else {
_error = true;
return s + " Error ')' expected.";
}
} else {
_error = true;
return " Error: Unexpected end of Expression.";
}
case TokenType.RPar:
_error = true;
GetToken();
return " Error: Unexpected ')'.";
case TokenType.Text:
string t = _text;
GetToken();
return t;
default:
return Term();
}
}