非専門的シンギュラリティー研究所

無限に動き続けるシステムを表す方法を AI なども使って考えていきます。

自由モノイドのイテレーター(38)

自由モノイドプログラミング言語の作成(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()));
        }