Printed from www.rmfusion.com A Developer website designed for Developers

Interpreter Pattern Code Review

Code Download

Define the Element Base Class and Associated Child Clases

Define the Element class which contains all the core functions to be used in this pattern. All other classes will derive from this class.

class Element { public Int32 Weight { get; set; } public Byte Level { get; set; } public Element Next { get; set; } public Element Child { get; set; }
public static Int32 SumLab { get; set; } public static Int32 SumTest { get; set; }
public static Int32[] m_values; public static Int32 m_counter; private static Byte m_level = 1;
public virtual String Display() { return (String.Concat(this.Weight, "%")); } public void OutputResults(Context context, Int32[] values) { context.Output = 0; m_values = values; m_counter = 0;
this.ParseResults(context); } public void Print() { switch (this.Level) { case 0: Console.WriteLine("{0}", this); break; case 1: Console.WriteLine("\t{0} {1}", this, this.Weight); break; case 2: Console.WriteLine("\t\t{0} {1}", this, this.Weight); break; } if (this.Child != null) { this.Child.Next.Print(); } if (this.Next != null) { this.Next.Print(); } } public void Summarize() { if (this is Lab) { SumLab += this.Weight; } else if (this is Test) { SumTest += this.Weight; } else if (((this is MidTerm) || (this is Exam)) && (this.Child == null)) { SumTest += this.Weight; } if (this.Child != null) { this.Child.Next.Summarize(); } if (this.Next != null) { this.Next.Summarize(); } } public void Parse(Context context) { String validInput = "LTME"; Char element = Char.MinValue;
if (context.Input.Length > 0) { if (validInput.IndexOf(context.Input[0]) >= 0) { element = context.Input[0]; } } if (element != Char.MinValue) { switch (element) { case 'L': this.Next = new Lab(); break;
case 'T': this.Next = new Test(); >break;
case 'M': this.Next = new MidTerm(); break;
case 'E': this.Next = new Exam(); break; } this.Next.Weight = this.ParseNumber(context); this.Next.Level = m_level;
if (context.Input.Length > 0) { element = context.Input[0]; } if (element == '(') { context.Input = context.Input.Substring(1); this.Next.Child = new Element(); m_level++; this.Next.Child.Parse(context); m_level--;
Element e = this.Next.Child; while (e != null) { e.Weight = e.Weight * this.Next.Weight / 100; e = e.Next; } if (context.Input.Length >= 2) { context.Input = context.Input.Substring(2); } else { context.Input = context.Input.Substring(1); } } else { this.Next.Child = null; } this.Next.Parse(context); } } private Int32 ParseNumber(Context context) { Int32 posX = context.Input.IndexOf('|'); Int32 value = Int32.Parse(context.Input.Substring(1, posX - 1)); context.Input = context.Input.Substring(posX + 1); return (value); } private void ParseResults(Context context) { if ((this is Lab) || (this is Test)) { context.Output += m_values[m_counter] * this.Weight; m_counter++; } else if (((this is MidTerm) || (this is Exam)) && (this.Child == null)) { context.Output += m_values[m_counter] * this.Weight; m_counter++; } if (this.Child != null) { this.Child.ParseResults(context); } if (this.Next != null) { this.Next.ParseResults(context); } } }

The input data to this class is provided by the Context class, and is declared in a predefined grammatical syntax. The Parse() and ParseNumber() methods work on the input data to generate an object structure of objects derived from the Element class (object). These include Lab, Test, MidTerm and Exam objects.

Once the input data has been organised into the object structure, we can execute "interpreter" methods against the data to generate any outputs required. The ParseResults() method calculates ("interprets") student course results based on predefined weighting for each element of the course.

The Course class (object) represents the first element in the object structure. This class inherits from the Element class, and therefore inherits all the methods defined in the Element class.

class Course : Element { public String Name { get; set; }
public Course(Context context) { this.Name = context.Input.Substring(0, 6); context.Input = context.Input.Substring(7); }
public override String Display() { return (this.Name); } public override string ToString() { return "Course"; } }

The Lab class (object) represents another element in the object structure. This class inherits from the Element class, and therefore inherits all the methods defined in the Element class.

class Lab : Element { public override string ToString() { return "Lab"; } }

The Test class (object) represents another element in the object structure. This class inherits from the Element class, and therefore inherits all the methods defined in the Element class.

class Test : Element { public override string ToString() { return "Test"; } }

The MidTerm class (object) represents another element in the object structure. This class inherits from the Element class, and therefore inherits all the methods defined in the Element class.

class MidTerm : Element { public override string ToString() { return "Midterm"; } }

The Exam class (object) represents another element in the object structure. This class inherits from the Element class, and therefore inherits all the methods defined in the Element class.

class Exam : Element { public override string ToString() { return "Exam"; } }

Define the Pattern Context Object

The Context class (object) is simple class that stores that input data to, and output data from the Element class (object).

class Context { public String Input { get; set; } public Double Output { get; set; }
public Context(String c) { this.Input = c; this.Output = 0; } }

Use the Interpreter Pattern

The Program class creates an instance of the Context and Course classes.

static class IntArrayExtension { public static string Display(this int[] a) { string s = "["; foreach (int i in a) s += i + ", "; return s.Substring(0, s.Length - 2) + "]"; } } class Program { static void Main(string[] args) { String rules = "COS333|L2|L2|L2|L2|L2|M25|(L40|T60|)|L10|E55|(L28|T73|)";
Int32[][] values = new[] { new [] {80,0,100,100,85,51,52,50,57,56}, new [] {87,95,100,100,77,70,99,100,75,94}, new [] {0,55,100,65,55,75,73,74,71,72} }; Console.WriteLine("{0}", rules);
Context context = new Context(rules);
Element course = new Course(context); course.Parse(context);
Console.WriteLine("\nCourse Structure:"); course.Print(); course.Summarize();
Console.WriteLine("\nSumming the weights\nLabs {0}% and Tests {1}%", Element.SumLab, Element.SumTest);
Console.WriteLine("\nInterpreter"); foreach (Int32[] student in values) { Console.Write(student.Display()); course.OutputResults(context, student); Console.WriteLine(" = {0}", context.Output / 100); } Console.Read(); } }

The Context class is updated with predefined grammatical input and passed to the Course class for parsing. Once the Context data has been parsed, the data is "interpreted" and results output.