自由モノイドプログラミング言語の作成(MonIter バージョン1.2 仕様(14))
構文解析のクラス
C# のパーサコンビネータ Sprache (Sprache/README.md at develop · sprache/Sprache · GitHub)を使っています。Visual Studio のソリューションエクスプローラーでプロジェクト名(MonIter)を右クリックしてメニューから「NuGet パッケージの管理」を選択するとウィンドウが表示されます。「参照」タブで Sprache を検索して Sprache をインストールします。
using Sprache;
と書くと Sprache を使うことができます。
拡張メソッドのためのクラス(ParserExtension)
「拡張メソッド」については「自由モノイドのイテレーター(13) - エレファント・ビジュアライザー調査記録」でも書きましたが詳しいことは拡張メソッド - C# | Microsoft Learnを見てください。
ある静的クラスを作って(上記のページでは非ジェネリックの静的クラスとなっています)そのクラスの静的メソッドで最初のパラメーターに this をつけたものが拡張メソッド(最初のパラメーターのクラスのメソッドとして使えます)となります。
このシステムでは以下のクラスを作成します。
このクラスでは以下の Sprache のメソッドを使っています。
| Contained | ある要素で囲まれた構文のパース |
|---|---|
| Char | 文字のパース |
| Token | 前後に空白などを含んでも良い文字列のパース:その文字列を返す |
| DelimitedBy | ある要素で区切られたリストのパース |
from … select という「クエリ式」の形で書くことができます。from に書いたパーサーの結果を select 内で使用することができます。
/// <summary> /// パーサーの拡張メソッドのためのクラス /// </summary> internal static class ParserExtension { /// <summary> /// かっこで囲まれた式の構文解析 /// </summary> /// <typeparam name="T">構文解析で取得される型</typeparam> /// <param name="parser">式のパーサー</param> /// <returns>構文解析の結果の構文要素</returns> public static Parser<T> ParenEnclosed<T>(this Parser<T> parser) { return parser.Contained(Parse.Char('(').Token(), Parse.Char(')').Token()); } /// <summary> /// 区切り文字で区切られたリストの構文解析 /// </summary> /// <typeparam name="T">構文解析で取得される型</typeparam> /// <param name="parser">各要素のパーサー</param> /// <param name="sep">区切り文字</param> /// <returns>構文解析の結果の構文要素</returns> public static Parser<IEnumerable<T>> ListBy<T>(this Parser<T> parser, char sep) { return parser.DelimitedBy(Parse.Char(sep).Token()); } /// <summary> /// 単項演算の構文解析 /// </summary> /// <typeparam name="T">演算数の構文解析で取得される型</typeparam> /// <typeparam name="S">構文解析で取得される型</typeparam> /// <param name="parser">演算数のパーサー</param> /// <param name="op_parser">演算子のパーサー</param> /// <param name="syngen">構文要素を作る関数</param> /// <returns>構文解析の結果の構文要素</returns> public static Parser<S> Unary<T, S>(this Parser<T> parser, Parser<string> op_parser, Func<string, T, S> syngen) { return from op in op_parser from exp in parser select syngen(op, exp); } }
構文解析のクラス(MonParser)
このクラスで BNF のような形で構文を書けるようにするか、または「ドキュメントコメント」から構文の説明ができるようにしたいと考えているのですが、どちらもまだできていません。
Visual Studio で「ドキュメントコメント」の XML ファイルを出力するには、メニューの「プロジェクト - MonIter のプロパティ」でプロパティのウィンドウを開き、「ビルド」の「出力」を選択して「ドキュメントファイル」をチェックします。
このクラスでは以下の Sprache のメソッドを使っています。
| Ref | 後で定義されているパーサーを使うときに使う |
|---|---|
| Number | 数値のパース |
| Letter | アルファベットの文字のパース |
| LetterOrDigit | 英数字のパース |
| Or | どちらかのパースが成功したときその結果を返す |
| Identifier | 最初の文字とその後の文字列を指定した文字列のパース |
| String | 文字列のパース |
| Text | パースの結果を(配列ではなく)文字列として返す |
| ChainOperator | 連続する二項演算のパース |
/// <summary> /// 構文解析を行うクラス /// </summary> internal class MonParser { /// <summary> /// 式(無限自由モノイド)の構文解析 /// </summary> private static readonly Parser<Expression> ParseExpression = Parse.Ref(() => ParseConjunction); /// <summary> /// 式(数値)の構文解析 /// </summary> private static readonly Parser<DomainExpression> ParseDomainExpression = Parse.Ref(() => ParseDomainSum); /// <summary> /// 単項演算(数値)の構文解析 /// </summary> private static readonly Parser<DomainExpression> ParseDomainUnary = Parse.Ref(() => ParseDomainUnary_); /// <summary> /// 数値の構文解析 /// </summary> private static readonly Parser<Number> ParseNumber = from num in Parse.Number.Token() select Prog.Number(num); /// <summary> /// 名前(関数名)の構文解析 /// </summary> private static readonly Parser<Identifier> ParseIdentifier = from name in Parse.Identifier(Parse.Letter, Parse.LetterOrDigit.Or(Parse.Char('_'))).Token() select Prog.Identifier(name); /// <summary> /// パラメーター名の構文解析 /// </summary> private static readonly Parser<ParameterName> ParseParameterName = from name in Parse.Identifier(Parse.Letter, Parse.LetterOrDigit.Or(Parse.Char('_'))).Token() select Prog.ParameterName(name); /// <summary> /// 数値の式の構文解析 /// </summary> private static readonly Parser<DomainExpression> ParseDomainTerm = ((Parser<DomainExpression>)ParseIdentifier) .Or(ParseNumber) .Or(ParseDomainExpression.ParenEnclosed()); /// <summary> /// 単項演算子の構文解析 /// </summary> private static readonly Parser<string> ParseUnaryOperator = (Parse.String("-")).Token().Text(); /// <summary> /// 単項演算(数値)の構文解析 /// </summary> private static readonly Parser<DomainExpression> ParseDomainUnary_ = ParseDomainUnary.Unary(ParseUnaryOperator, Prog.Unary) .Or(ParseDomainTerm); /// <summary> /// 乗法系演算子の構文解析 /// </summary> private static readonly Parser<string> ParseProdOperator = (Parse.String("*").Or(Parse.String("/")).Or(Parse.String("%"))).Token().Text(); /// <summary> /// 乗法系演算(数値)の構文解析 /// </summary> private static readonly Parser<DomainExpression> ParseDomainProd = Parse.ChainOperator(ParseProdOperator, ParseDomainUnary, Prog.Binary); /// <summary> /// 加法系演算子の構文解析 /// </summary> private static readonly Parser<string> ParseSumOperator = (Parse.String("+").Or(Parse.String("-"))).Token().Text(); /// <summary> /// 加法系演算(数値)の構文解析 /// </summary> private static readonly Parser<DomainExpression> ParseDomainSum = Parse.ChainOperator(ParseSumOperator, ParseDomainProd, Prog.Binary); /// <summary> /// 数値の式のリストの構文解析 /// </summary> private static readonly Parser<IEnumerable<DomainExpression>> ParseDomainExpressionList = ParseDomainExpression.ListBy(','); /// <summary> /// 関数(数値のパラメーター)呼び出しの構文解析 /// </summary> private static readonly Parser<DomainFunction> ParseDomainFunction = from name in ParseIdentifier from list in ParseDomainExpressionList.ParenEnclosed() select Prog.DomainFunction(name, list); /// <summary> /// 式(無限自由モノイド)のリストの構文解析 /// </summary> private static readonly Parser<IEnumerable<Expression>> ParseExpressionList = ParseExpression.ListBy(','); /// <summary> /// 関数(自由モノイドのパラメーター)呼び出しの構文解析 /// </summary> private static readonly Parser<Function> ParseFunction = from pr in Parse.Char('$').Token() from name in ParseIdentifier from list in ParseExpressionList.ParenEnclosed() select Prog.Function(name, list); /// <summary> /// 数値の式からなる項の構文解析 /// </summary> private static readonly Parser<Term> ParseDomainUnit = from exp in ParseDomainExpression select Prog.DomainExpression(exp); /// <summary> /// 項(無限自由モノイドの元)の構文解析 /// </summary> private static readonly Parser<Term> ParseTerm = ((Parser<Term>)ParseFunction) .Or(ParseDomainFunction) .Or(ParseDomainUnit); /// <summary> /// 無限自由モノイドの元の構文解析 /// </summary> private static readonly Parser<Expression> ParseConjunction = from list in ParseTerm.ListBy('&') select new Expression(list); /// <summary> /// 無限自由モノイドの積の構文解析 /// </summary> private static readonly Parser<Statement> ParseExpressionStatement = from exp in ParseExpression select Prog.ExpressionStatement(exp); /// <summary> /// パラメーターのリストの構文解析 /// </summary> private static readonly Parser<IEnumerable<ParameterName>> ParseParameterList = ParseParameterName.ListBy(','); /// <summary> /// 関数定義の構文解析 /// </summary> private static readonly Parser<Definition> ParseDefinition = from pr in Parse.String("def").Token() from name in ParseIdentifier from list in ParseParameterList.ParenEnclosed() from eq in Parse.Char('=').Token() from exp in ParseExpression select Prog.Definition(name, list, exp); /// <summary> /// ステートメントの構文解析 /// </summary> private static readonly Parser<Statement> ParseStatement = ParseDefinition .Or(ParseExpressionStatement); /// <summary> /// プログラム全体の構文解析 /// </summary> private static readonly Parser<Prog> ParseProgram = from list in ParseStatement.ListBy(';') select new Prog(list); /// <summary> /// プログラム全体の構文解析 /// </summary> public static Prog ParseProg(string src) { return ParseProgram.Parse(src); } }





