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