الگوی Interpreter

الگوی Interpreter( مترجم-مفسر) در دسته الگوهای رفتاری قرار دارد. با توجه به یک زبان، یک نمایش برای دستور زبان (grammar) خود را همراه با یک مترجم(interpreter) که از نمایش برای تفسیر جملات در زبان استفاده می شود، تعریف کنید.به عبارت دیگر تعریف قوانین یک زبان به صورت شی گرا برای بیان جملات زبان و فراهم کردن مفسری که بتواند جملات زبان را تفسیر کند.UML class diagram شركت كنندگان کلاس ها و اشیاء شرکت کننده در این الگو عبارتند از:اصطلاح انتزاعی AbstractExpression (اصطلاح در مثال دنیای واقعی Expression):یک واسط(interface) برای اجرای یک عملیات را اعلام می کند.به عبارت دیگر کلاسی است که به صورت انتزاعی برای سر ترم زبان تعریف می شود( مانند RegularExpression در مثال عبارت های باقاعده). این کلاس دارای متد مجردی به اسم Interpret() است که به همه زیر کلاس هایش (ترم های میانی و پایانی) به ارث میرسد. پایانه اصطلاح TerminalExpression ( هزار ، صد ريال ده ، یک ThousandExpression, HundredExpression, TenExpression, OneExpression ) :یک عملیات Interpret مرتبط با نمادهای پایانه در دستور زبان پیاده سازی می کند.یک مثال-نمونه برای هر نماد پایانه در جمله مورد نیاز است.به عبارت دیگر کلاسی است که به ازای هر ترم پایانی تعریف می شود( مانند LiteralExpression در مثال عبارت های باقاعده). اصطلاح غیر متمرکز NonterminalExpression (در مثال دنیای واقعی استفاده نشده not used ):یک کلاس چنین برای هر قانون R :: = R1R2 ... Rn در دستور زبان مورد نیاز است.متغیرهای نمونه ای از نوع AbstractExpression را برای هر یک از نمادهای R1 تا Rn نگه می دارد. عملیات تفسیری را برای نمادهای غیرمتمرکز در گرامر پیاده سازی می کند. مفسر معمولا خود را به صورت بازگشتی در متغیرهایی که R1 را از طریق Rn نشان می دهند، صدا میزند. به عبارت دیگر کلاسی است که برای ترم های میانی تعریف می شود( مانندAlternationExpression،RepetitionExpressionوSequenceExpression در مثال عبارت های باقاعده).سپس در این کلاسها به ازای هر یک از ترم های سمت راست قانون آنها، یک فیلد از جنس کلاس آن ترم تعریف می شود.زمینه Context (زمینه Context):شامل اطلاعاتی است که به مترجم جهانی( global to the interpreter ) است.به عبارت دیگر حاوی اطلاعاتی است که برای مفسر-مترجم به طور سراسری قابل دسترس است . کلاینت Client (اپ مترجم InterpreterApp): یک درخت ترکیبی انتزاعی را ارائه می دهد (یا داده می شود) که یک جمله خاص را در زبان گرامر تعریف می کند.نحو(syntax) درخت انتزاعی از موارد instances NonterminalExpression و TerminalExpression مونتاژ می شود. عملیات Interpret را فراخوانی می کند.عبارت دیگر کسی است که با توجه به کلاس‌های تعریف شده و قوانین زبان، بر اساس جمله ای از زبان، درخت گرامر ایجاد میکند که گره های آن نمونه ای از کلاس های تعریف شده برای ترم ها هستند. سپس متد Interpret() را برای گره ریشه ای(Root) درختی که ایجاد کرده صدا زده و یک شی Context را به آن پاس می‌دهد و بدین ترتیب دستور تفسیر جمله را صادر می کند. ساختار کلی کد نشان می دهد که الگوهای مترجم، که با استفاده از یک زبان گرامر تعریف شده، مترجمی را فراهم می کند که فرآیندهای تجزیه شده را پردازش می کند. using System; using System.Collections; namespace DoFactory.GangOfFour.Interpreter.Structural { /// <summary> /// MainApp startup class for Structural /// Interpreter Design Pattern. /// </summary> class MainApp { /// <summary> /// Entry point into console application. /// </summary> static void Main () { Context context = new Context (); // Usually a tree ArrayList list = new ArrayList (); // Populate 'abstract syntax tree' list.Add (new Terminal)); list.Add (new Nonterminal)); list.Add (new Terminal)); list.Add (new Terminal)); // Interpret foreach (AbstractExpression exp in list) { exp.Interpret (context); } // Wait for user Console.ReadKey (); } } /// <summary> /// The 'Context' class /// </summary> class Context { } /// <summary> /// The 'AbstractExpression' abstract class /// </summary> abstract class AbstractExpression { public abstract void Interpret (Context context); } /// <summary> /// The 'TerminalExpression' class /// </summary> class TerminalExpression : AbstractExpression { public override void Interpret (Context context) { Console.WriteLine ("Called Terminal.Interpret()"); } } /// <summary> /// The 'NonterminalExpression' class /// </summary> class NonterminalExpression : AbstractExpression { public override void Interpret (Context context) { Console.WriteLine ("Called Nonterminal.Interpret()"); } } } OutputCalled Terminal.Interpret ()Called Nonterminal.Interpret ()Called Terminal.Interpret ()Called Terminal.Interpret ()یا/* expression ::= exp exp ::= parenthesis | binary | number parenthesis ::= '(' exp ')' binary ::= exp Operator exp Operator ::= '+' | '-' | '*' | '/' digit = '0' | '1' | ... '9' number ::= digit | digit number */ abstract class Expression { public abstract void Interpret(Context context); public static ParenthesisExpression Parenthesis(Expression expression) { return new Parenthesisexpression); } public static BinaryExpression Binary(Expression left, OperatorExpression op, Expression right) { return new Binaryleft, op, right); } public static NumberExpression Number(double value) { return new Numbervalue); } public static IdentifierExpression Identifier(string name) { return new Identifiername); } public static OperatorExpression Operator(char op) { return new Operatorop); } } class ParenthesisExpression: Expression { private Expression expression; public ParenthesisExpression expression) { this.expression = expression; } public override void Interpret(Context context) { expression.Interpret(context); } } class BinaryExpression: Expression { public Expression Left { get; private set; } public OperatorExpression op { get; private set; } public Expression Right { get; private set; } public BinaryExpression left, OperatorExpression op, Expression right) { this.Left = left; this.op = op; this.Right = right; } public override void Interpret(Context context) { var tmp1 = context.Clone(); var tmp2 = context.Clone(); Left.Interpret(tmp1); Right.Interpret(tmp2); switch (op.Value) { case '+': context.Value = tmp1.Value + tmp2.Value; break; case '-': context.Value = tmp1.Value - tmp2.Value; break; case '*': context.Value = tmp1.Value * tmp2.Value; break; case '/': context.Value = tmp1.Value / tmp2.Value; break; default: throw new ApplicationException("Invalid operator"); } } } class OperatorExpression: Expression { public char Value { get; private set; } public Operatorchar value) { if (value == '+' | value == '-' | value == '*' | value == '/') Value = value; else throw new ApplicationException("Invalid operator"); } public override void Interpret(Context context) { } } class IdentifierExpression: Expression { public double Value { get; set; } public string Name { get; private set; } public Identifierstring name) { if (!Regex.IsMatch(name, @"[a-zA-Z]\w*")) throw new ApplicationException("Invalid identifier"); Name = name; } public override void Interpret(Context context) { context.Value = context.Variables[this.Name]; this.Value = context.Value; } } class NumberExpression: Expression { public double Value { get; set; } public Numberdouble value) { Value = value; } public override void Interpret(Context context) { context.Value = this.Value; } } class Context { public double Value { get; set; } public Dictionary<string, double> Variables { get; private set; } public Context() { Variables = new Dictionary<string, double>(); } public Context Clone() { return (Context)this.MemberwiseClone(); } public void MergeVariables(IEnumerable<string> variables, bool clearExisting = false) { foreach (var variable in variables) { if (!Variables.ContainsKey(variable)) { Variables.Add(variable, 0); } else if (clearExisting) { Variables[variable] = 0; } } } } class Test { static void Main() { // create abstract syntax tree for // the expression: a + b * 4 - 1 var exp1 = Expression.Binary(Expression.Identifier("b"), Expression.Operator('*'), Expression.Number(4)); var exp2 = Expression.Binary(Expression.Identifier("a"), Expression.Operator('+'), exp1); var exp3 = Expression.Binary(exp2, Expression.Operator('-'), Expression.Number(1)); var context = new Context(); context.Variables.Add("a", 2); context.Variables.Add("b", 3); exp3.Interpret(context); Console.WriteLine(context.Value); // writes 13 Console.ReadKey(); } }مثال دنیای واقعی الگوی مترجم نشان داده می شود که برای تبدیل یک عدد رومی به یک عدد اعشاری استفاده می شود. using System; using System.Collections.Generic; namespace DoFactory.GangOfFour.Interpreter.RealWorld { /// <summary> /// MainApp startup class for Real-World /// Interpreter Design Pattern. /// </summary> class MainApp { /// <summary> /// Entry point into console application. /// </summary> static void Main () { string roman = "MCMXXVIII"; Context context = new Context (roman); // Build the 'parse tree' List<Expression> tree = new List<Expression> (); tree.Add (new Thousand)); tree.Add (new Hundred)); tree.Add (new Ten)); tree.Add (new One)); // Interpret foreach (Expression exp in tree) { exp.Interpret (context); } Console.WriteLine ("{0} = {1}", roman, context.Output); // Wait for user Console.ReadKey (); } } /// <summary> /// The 'Context' class /// </summary> class Context { private string _input; private int _output; // Constructor public Context (string input) { this._input = input; } // Gets or sets input public string Input { get { return _input; } set { _input = value; } } // Gets or sets output public int Output { get { return _output; } set { _output = value; } } } /// <summary> /// The 'AbstractExpression' class /// </summary> abstract class Expression { public void Interpret (Context context) { if (context.Input.Length == 0) return; if (context.Input.StartsWith (Nine ())) { context.Output += (9 * Multiplier ()); context.Input = context.Input.Substring (2); } else if (context.Input.StartsWith (Four ())) { context.Output += (4 * Multiplier ()); context.Input = context.Input.Substring (2); } else if (context.Input.StartsWith (Five ())) { context.Output += (5 * Multiplier ()); context.Input = context.Input.Substring (1); } while (context.Input.StartsWith (One ())) { context.Output += (1 * Multiplier ()); context.Input = context.Input.Substring (1); } } public abstract string One (); public abstract string Four (); public abstract string Five (); public abstract string Nine (); public abstract int Multiplier (); } /// <summary> /// A 'TerminalExpression' class /// <remarks> /// Thousand checks for the Roman Numeral M /// </remarks> /// </summary> class ThousandExpression : Expression { public override string One () { return "M"; } public override string Four () { return " "; } public override string Five () { return " "; } public override string Nine () { return " "; } public override int Multiplier () { return 1000; } } /// <summary> /// A 'TerminalExpression' class /// <remarks> /// Hundred checks C, CD, D or CM /// </remarks> /// </summary> class HundredExpression : Expression { public override string One () { return "C"; } public override string Four () { return "CD"; } public override string Five () { return "D"; } public override string Nine () { return "CM"; } public override int Multiplier () { return 100; } } /// <summary> /// A 'TerminalExpression' class /// <remarks> /// Ten checks for X, XL, L and XC /// </remarks> /// </summary> class TenExpression : Expression { public override string One () { return "X"; } public override string Four () { return "XL"; } public override string Five () { return "L"; } public override string Nine () { return "XC"; } public override int Multiplier () { return 10; } } /// <summary> /// A 'TerminalExpression' class /// <remarks> /// One checks for I, II, III, IV, V, VI, VI, VII, VIII, IX /// </remarks> /// </summary> class OneExpression : Expression { public override string One () { return "I"; } public override string Four () { return "IV"; } public override string Five () { return "V"; } public override string Nine () { return "IX"; } public override int Multiplier () { return 1; } } } OutputMCMXXVIII = 1928منبع