自由モノイドプログラミング言語の作成(MonIter バージョン1.2 仕様(22))
C# 形式のコードから構文オブジェクトに変換して実行
「Roslyn」の「Syntax API」で C# の構文解析を行います。「
自由モノイドのイテレーター(14) - エレファント・ビジュアライザー調査記録」に少しだけ説明を書いています。
「自由モノイドのイテレーター(30) - エレファント・ビジュアライザー調査記録」と同様に「Microsoft.CodeAnalysis.CSharp」をインストールして
using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax;
と書くと使うことができます。
C# スタイルのコードを MonIter の構文オブジェクトに変換するためのクラス
/// <summary> /// C# スタイルのコードを MonIter の構文オブジェクトに変換するためのクラス /// </summary> internal class CSStyleConvert { /// <summary> /// /// </summary> private static string[] monoid_functions = ["zipsum", "tail"]; /// <summary> /// C# の構文木をステートメントブロックに変換する /// </summary> /// <param name="root">C# の構文木の根</param> /// <returns>プログラム全体の構文オブジェクト</returns> public static Prog ToStatementBlock(CompilationUnitSyntax root) { List<Statement> statements = new List<Statement>(); foreach (var child in root.Members) { if (child is GlobalStatementSyntax globalStatement) { statements.Add(ToStatement(globalStatement)); } } return new Prog(statements); } /// <summary> /// C# の構文木をステートメントに変換する /// </summary> /// <param name="node">C# のステートメントの構文木</param> /// <returns>ステートメントの構文オブジェクト</returns> private static Statement ToStatement(GlobalStatementSyntax node) { var child = node.ChildNodes().ElementAt(0); if (child is LocalFunctionStatementSyntax localFunctionStatement) { return ToFunctionDefinition(localFunctionStatement); } else if (child is ExpressionStatementSyntax expressionStatement) { return ToExpressionStatement(expressionStatement); } return null; } /// <summary> /// C# の構文木を関数定義に変換する /// </summary> /// <param name="node">C# の関数定義の構文木</param> /// <returns>関数定義の構文オブジェクト</returns> private static Statement ToFunctionDefinition(LocalFunctionStatementSyntax node) { Identifier name = Prog.Identifier(node.Identifier.Text); // 下位の1つめは戻り値の型(使わない) // 下位の2つめはパラメーターリスト List<ParameterName> parameter_list = ToParameterList(node.ChildNodes().ElementAt(1).ChildNodes()); // 下位の3つめは ArrowExpressionClause ArrowExpressionClauseSyntax arrow = node.ChildNodes().ElementAt(2) as ArrowExpressionClauseSyntax; // その下位の1つめは本体の式 Expression exp = ToExpression(arrow.ChildNodes().ElementAt(0) as ExpressionSyntax); return Prog.Definition(name, parameter_list, exp); } /// <summary> /// C# の構文木をパラメーターリストに変換する /// </summary> /// <param name="nodelist">C# のパラメーターリストの構文木</param> /// <returns>パラメーターリストの構文オブジェクト</returns> private static List<ParameterName> ToParameterList(IEnumerable<SyntaxNode> nodelist) { List<ParameterName> list = new List<ParameterName>(); foreach (var node in nodelist) { if (node is ParameterSyntax parameter_node) { list.Add(Prog.ParameterName(parameter_node.Identifier.Text)); } } return list; } /// <summary> /// C# の構文木を式のステートメントに変換する /// </summary> /// <param name="node">C# の式のステートメントの構文木</param> /// <returns>式のステートメントの構文オブジェクト</returns> private static Statement ToExpressionStatement(ExpressionStatementSyntax node) { return Prog.ExpressionStatement(ToExpression(node.ChildNodes().ElementAt(0) as ExpressionSyntax)); } /// <summary> /// C# の構文木を式(自由モノイドの元)に変換する /// </summary> /// <param name="node">C# の式の構文木</param> /// <returns>式(自由モノイドの元)の構文オブジェクト</returns> private static Expression ToExpression(ExpressionSyntax node) { switch (node.Kind()) { case SyntaxKind.BitwiseAndExpression: var args = ToExpressionList(node.ChildNodes()); return args[0].Concat(args[1]); case SyntaxKind.InvocationExpression: var funcname = (node.ChildNodes().ElementAt(0)! as IdentifierNameSyntax).Identifier.Text; if (monoid_functions.Contains(funcname)) { var funcargs = ToArgumentList(node.ChildNodes().ElementAt(1).ChildNodes()); return Expression.Unit(Prog.Function(Prog.Identifier(funcname), funcargs)); } else { var funcargs = ToDomainArgumentList(node.ChildNodes().ElementAt(1).ChildNodes()); return Expression.Unit(Prog.DomainFunction(Prog.Identifier(funcname), funcargs)); } default: return Expression.Unit(Prog.DomainExpression(ToDomainExpression(node))); } return null; } /// <summary> /// C# の構文木を引数(自由モノイドの元)リストに変換する /// </summary> /// <param name="nodelist">C# の引数リストの構文木</param> /// <returns>式(自由モノイドの元)のリストの構文オブジェクト</returns> private static List<Expression> ToArgumentList(IEnumerable<SyntaxNode> nodelist) { List<Expression> list = new List<Expression>(); foreach (var node in nodelist) { if (node is ArgumentSyntax arg_node) { ExpressionSyntax exp_node = arg_node.Expression; list.Add(ToExpression(exp_node)); } } return list; } /// <summary> /// C# の構文木を引数(数値)リストに変換する /// </summary> /// <param name="nodelist">C# の引数リストの構文木</param> /// <returns>式(数値)のリストの構文オブジェクト</returns> private static List<DomainExpression> ToDomainArgumentList(IEnumerable<SyntaxNode> nodelist) { List<DomainExpression> list = new List<DomainExpression>(); foreach (var node in nodelist) { if (node is ArgumentSyntax arg_node) { ExpressionSyntax exp_node = arg_node.Expression; list.Add(ToDomainExpression(exp_node)); } } return list; } /// <summary> /// C# の構文木を式(自由モノイドの元)のリストに変換する /// </summary> /// <param name="nodelist">C# の式のリストの構文木</param> /// <returns>式(自由モノイドの元)のリストの構文オブジェクト</returns> private static List<Expression> ToExpressionList(IEnumerable<SyntaxNode> nodelist) { List<Expression> list = new List<Expression>(); foreach (var node in nodelist) { if (node is ExpressionSyntax exp_node) { list.Add(ToExpression(exp_node)); } } return list; } /// <summary> /// C# の構文木を式(数値)に変換する /// </summary> /// <param name="node">C# の式の構文木</param> /// <returns>式(数値)の構文オブジェクト</returns> private static DomainExpression ToDomainExpression(ExpressionSyntax node) { var args = ToDomainExpressionList(node.ChildNodes()); switch (node.Kind()) { case SyntaxKind.AddExpression: return Prog.Binary("+", args[0], args[1]); case SyntaxKind.SubtractExpression: return Prog.Binary("-", args[0], args[1]); case SyntaxKind.MultiplyExpression: return Prog.Binary("*", args[0], args[1]); case SyntaxKind.UnaryMinusExpression: return Prog.Unary("-", args[0]); case SyntaxKind.ParenthesizedExpression: return args[0]; case SyntaxKind.IdentifierName: return Prog.Identifier((node as IdentifierNameSyntax).Identifier.Text); case SyntaxKind.NumericLiteralExpression: return Prog.Number((node as LiteralExpressionSyntax).Token.Text); } return null; } /// <summary> /// C# の構文木を式(数値)のリストに変換する /// </summary> /// <param name="nodelist">C# の式のリストの構文木</param> /// <returns>式(数値)のリストの構文オブジェクト</returns> private static List<DomainExpression> ToDomainExpressionList(IEnumerable<SyntaxNode> nodelist) { List<DomainExpression> list = new List<DomainExpression>(); foreach (var node in nodelist) { if (node is ExpressionSyntax exp_node) { list.Add(ToDomainExpression(exp_node)); } } return list; } }
引数で渡すバージョン
C# スタイルのコード
mon fib(int x, int y) => x & fib(y, x + y); fib(0, 1)
を構文解析して C# の構文木を取得します。その根 root を
Prog prog = CSStyleConvert.ToStatementBlock(root);
で MonIter の構文を表すクラス Prog のオブジェクトに変換します。その後実行します。以下のようになります。
/// <summary> /// フィボナッチ数列 C# スタイルのコードから構文オブジェクトに変換して実行(引数あり) /// </summary> /// <returns>フィボナッチ数列の文字列</returns> private string InfiniteFibpnacciPrmsCSStyle() { string src = "mon fib(int x, int y) => x & fib(y, x + y); fib(0, 1)"; var tree = CSharpSyntaxTree.ParseText(src); var root = tree.GetCompilationUnitRoot(); Prog prog = CSStyleConvert.ToStatementBlock(root); return string.Join(", ", prog.Eval().Take(GetCount())); }
Zip を使うバージョン
C# スタイルのコード
mon fib(int x, int y) => x & y & zipsum(fib(x, y), tail(fib(x, y)); fib(0, 1)
を構文解析して C# の構文木を取得します。その根 root を
Prog prog = CSStyleConvert.ToStatementBlock(root);
で MonIter の構文を表すクラス Prog のオブジェクトに変換します。その後実行します。以下のようになります。
/// <summary> /// フィボナッチ数列 C# スタイルのコードから構文オブジェクトに変換して実行(Zip) /// </summary> /// <returns>フィボナッチ数列の文字列</returns> private string InfiniteFibpnacciZipCSStyle() { string src = "mon fib(int x, int y) => x & y & zipsum(fib(x, y), tail(fib(x, y)); fib(0, 1)"; var tree = CSharpSyntaxTree.ParseText(src); var root = tree.GetCompilationUnitRoot(); Prog prog = CSStyleConvert.ToStatementBlock(root); return string.Join(", ", prog.Eval().Take(GetCount())); }





