これが私の2番目の答えです。私の最初の答えは簡単なショットでした。ここでは、物事を1つずつ実行してパーサーを作成しようとしました。
式を変換するには、式を解析する必要があります。これは、その構文を分析する必要があることを意味します。その構文を分析しながら、出力を生成することもできます。
1最初に行うことは、すべての有効な式の構文を定義することです。
ここでは、EBNFを使用してそれを行います。EBNFは単純です。
{
}
繰り返しを囲みます(おそらくゼロ)。オプションパーツ
[
を同封します。選択肢を分離します。 ]
|
EBNFの詳細については、ウィキペディアの拡張バッカスナウア記法(EBNF)を参照してください。(ここで使用されるEBNFバリアントは、連結演算子 "、"を削除します)。
EBNFでの構文
式={用語}。
項=[数値]係数。
ファクター=テキスト| "("式 ")" | 期間。
例
5(2(a)sz)=> aaszaaszaaszaaszaasz
5(2a sz)=> aaszaaszaaszaaszaasz
2 3(a 2b)c => abbabbabbabbabbabbc
2字句解析
構文を分析する前に、式全体を単一の字句トークン(数値、演算子など)に分割する必要があります。enum
トークンタイプを示すためにを使用します
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構文および意味分析
これで、実際の解析と式の変換を実行するために必要なものがすべて揃いました。以下の各メソッドは、1つの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();
}
}