エレファント・ビジュアライザー調査記録

ビジュアルプログラミングで数式の変形を表すことを考えていくブロクです。

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

自由モノイドプログラミング言語の作成(MonIter C#版 バージョン1.2)

「Roslyn for Scripting」で C# のコードを実行できるようになりました。これについては簡単な説明が書かれた文献が見つかりませんでした。【レポート】C#でスクリプト実行できる Roslyn for Scripting の始め方 | エヌエスアイ フリークC#スクリプト(Roslyn)でLINQを使おうとして困った話 #C# - Qiitaを参考にしました。

構文解析の部分は以下のようになります。数値の加減乗除と符号を変える(正の数を負の数にする)演算とかっこを使えるようにしました。

「拡張メソッド」については拡張メソッド - C# | Microsoft Learnを参照してください。

拡張メソッドのクラス

    internal static class ParserExtension
    {
        public static Parser<T> ParenEnclosed<T>(this Parser<T> parser)
        {
            return parser.Contained(Parse.Char('(').Token(), Parse.Char(')').Token());
        }
        public static Parser<IEnumerable<T>> ListBy<T>(this Parser<T> parser, char sep)
        {
            return parser.DelimitedBy(Parse.Char(sep).Token());
        }
        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);
        }
    }

構文解析のクラス

    internal class MonParser
    {
        private static readonly Parser<Expression> ParseExpression =
            Parse.Ref(() => ParseConjunction);

        private static readonly Parser<DomainExpression> ParseDomainExpression =
            Parse.Ref(() => ParseDomainSum);

        private static readonly Parser<DomainExpression> ParseDomainUnary =
            Parse.Ref(() => ParseDomainUnary_);

        private static readonly Parser<Number> ParseNumber =
            from num in Parse.Decimal.Token()
            select DomainExpression.Number(num);

        private static readonly Parser<Identifier> ParseIdentifier =
            from name in Parse.Identifier(Parse.Letter, Parse.LetterOrDigit.Or(Parse.Char('_'))).Token()
            select DomainExpression.Identifier(name);

        private static readonly Parser<ParameterName> ParseParameterName =
            from name in Parse.Identifier(Parse.Letter, Parse.LetterOrDigit.Or(Parse.Char('_'))).Token()
            select Expression.ParameterName(name);

        private static readonly Parser<DomainExpression> ParseDomainTerm =
            ((Parser<DomainExpression>)ParseIdentifier)
            .Or(ParseNumber)
            .Or(ParseDomainExpression.ParenEnclosed());

        private static readonly Parser<string> ParseUnaryOperator =
            (Parse.String("-")).Token().Text();

        private static readonly Parser<DomainExpression> ParseDomainUnary_ =
            ParseDomainUnary.Unary(ParseUnaryOperator, DomainExpression.Unary)
            .Or(ParseDomainTerm);

        private static readonly Parser<string> ParseProdOperator =
            (Parse.String("*").Or(Parse.String("/")).Or(Parse.String("%"))).Token().Text();

        private static readonly Parser<DomainExpression> ParseDomainProd =
            Parse.ChainOperator(ParseProdOperator, ParseDomainUnary, DomainExpression.Binary);

        private static readonly Parser<string> ParseSumOperator =
            (Parse.String("+").Or(Parse.String("-"))).Token().Text();

        private static readonly Parser<DomainExpression> ParseDomainSum =
            Parse.ChainOperator(ParseSumOperator, ParseDomainProd, DomainExpression.Binary);

        private static readonly Parser<IEnumerable<DomainExpression>> ParseDomainExpressionList =
            ParseDomainExpression.ListBy(',');

        private static readonly Parser<DomainFunction> ParseDomainFunction =
            from name in ParseIdentifier
            from list in ParseDomainExpressionList.ParenEnclosed()
            select Expression.DomainFunction(name, list);

        private static readonly Parser<IEnumerable<Expression>> ParseExpressionList =
            ParseExpression.ListBy(',');

        private static readonly Parser<Function> ParseFunction =
            from pr in Parse.Char('$').Token()
            from name in ParseIdentifier
            from list in ParseExpressionList.ParenEnclosed()
            select Expression.Function(name, list);

        private static readonly Parser<Term> ParseDomainUnit =
            from exp in ParseDomainExpression
            select Expression.DomainExpression(exp);

        private static readonly Parser<Term> ParseTerm =
            ((Parser<Term>)ParseFunction)
            .Or(ParseDomainFunction)
            .Or(ParseDomainUnit);

        private static readonly Parser<Expression> ParseConjunction =
            from list in ParseTerm.ListBy('&')
            select new Expression(list);

        private static readonly Parser<Statement> ParseExpressionStatement =
            from exp in ParseExpression
            select Statement.ExpressionStatement(exp);

        private static readonly Parser<IEnumerable<ParameterName>> ParseParameterList =
            ParseParameterName.ListBy(',');

        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 Statement.Definition(name, list, exp);

        private static readonly Parser<Statement> ParseStatement =
            ParseDefinition
            .Or(ParseExpressionStatement);

        private static readonly Parser<Prog> ParseProgram =
            from list in ParseStatement.ListBy(';')
            select new Prog(list);

        public static Prog ParseProg(string src) { return ParseProgram.Parse(src); }
    }