by Moridin8
16. April 2008 20:24
The Interpreter Pattern
namespace ooppatterns
{
using System;
using System.Collections.Generic;
internal class Program
{
private static void Main( string[] args )
{
string expression = "8 ++ 1 4 + N 200 * +";
PostFixParser PFP = new PostFixParser( expression );
Console.WriteLine( "'{0}' equals {1}", expression, PFP.Evaluate() );
Console.ReadLine();
}
}
interface IExpression
{
void Parse( Stack<double> s );
}
class PostFixParser
{
private List<IExpression> tokenList = new List<IExpression>();
public PostFixParser( string s )
{
foreach ( string token in s.Split( ' ' ) )
switch ( token )
{
case "AND":
case "&&":
this.tokenList.Add( new LAnd() );
break;
case "!":
this.tokenList.Add( new LNot() );
break;
case "N":
this.tokenList.Add( new Neg() );
break;
case "OR":
case "||":
this.tokenList.Add( new LOr() );
break;
case "&":
this.tokenList.Add( new BAnd() );
break;
case "~":
this.tokenList.Add( new BNot() );
break;
case "|":
this.tokenList.Add( new BOr() );
break;
case "<>":
case "!=":
this.tokenList.Add( new NotEquals() );
break;
case "==":
this.tokenList.Add( new Equals() );
break;
case ">":
this.tokenList.Add( new GreaterThan() );
break;
case "<":
this.tokenList.Add( new LessThan() );
break;
case ">=":
this.tokenList.Add( new GreaterOrEqualThan() );
break;
case "<=":
this.tokenList.Add( new LessOrEqualThan() );
break;
case "+":
this.tokenList.Add( new Plus() );
break;
case "++":
this.tokenList.Add( new Inc() );
break;
case "-":
this.tokenList.Add( new Minus() );
break;
case "--":
this.tokenList.Add( new Dec() );
break;
case "*":
this.tokenList.Add( new Multiply() );
break;
case "/":
this.tokenList.Add( new Divide() );
break;
case "%":
this.tokenList.Add( new Modulus() );
break;
case "^":
this.tokenList.Add( new Power() );
break;
default:
this.tokenList.Add( new Number( double.Parse( token ) ) );
break;
}
}
public double Evaluate()
{
Stack<double> evalStack = new Stack<double>();
foreach ( IExpression e in this.tokenList )
e.Parse( evalStack );
return evalStack.Pop();
}
}
// Mathmatical operators
class Number : IExpression
{
private double number;
public Number( double number )
{
this.number = number;
}
public void Parse( Stack<double> s )
{
s.Push( number );
}
}
class Plus : IExpression
{
public void Parse( Stack<double> s )
{
s.Push( s.Pop() + s.Pop() );
}
}
class Inc : IExpression
{
public void Parse( Stack<double> s )
{
double tmp = s.Pop();
s.Push( ++tmp );
}
}
class Dec : IExpression
{
public void Parse( Stack<double> s )
{
double tmp = s.Pop();
s.Push( --tmp );
}
}
class Minus : IExpression
{
public void Parse( Stack<double> s )
{
double tmp = s.Pop();
s.Push( s.Pop() - tmp );
}
}
class Multiply : IExpression
{
public void Parse( Stack<double> s )
{
double tmp = s.Pop();
s.Push( s.Pop() * tmp );
}
}
class Divide : IExpression
{
public void Parse( Stack<double> s )
{
double tmp = s.Pop();
s.Push( s.Pop() / tmp );
}
}
class Modulus : IExpression
{
public void Parse( Stack<double> s )
{
double tmp = s.Pop();
s.Push( s.Pop() % tmp );
}
}
class Power : IExpression
{
public void Parse( Stack<double> s )
{
double tmp = s.Pop();
s.Push( Convert.ToInt32( Math.Pow( s.Pop(), tmp ) ) );
}
}
// Equality operators
class Equals : IExpression
{
public void Parse( Stack<double> s )
{
double tmp = s.Pop();
s.Push( s.Pop() == tmp ? -1 : 0 );
}
}
class NotEquals : IExpression
{
public void Parse( Stack<double> s )
{
double tmp = s.Pop();
s.Push( s.Pop() == tmp ? 0 : -1 );
}
}
class GreaterThan : IExpression
{
public void Parse( Stack<double> s )
{
double tmp = s.Pop();
s.Push( s.Pop() > tmp ? -1 : 0 );
}
}
class LessThan : IExpression
{
public void Parse( Stack<double> s )
{
double tmp = s.Pop();
s.Push( s.Pop() < tmp ? 0 : -1 );
}
}
class GreaterOrEqualThan : IExpression
{
public void Parse( Stack<double> s )
{
double tmp = s.Pop();
s.Push( s.Pop() >= tmp ? -1 : 0 );
}
}
class LessOrEqualThan : IExpression
{
public void Parse( Stack<double> s )
{
double tmp = s.Pop();
s.Push( s.Pop() <= tmp ? 0 : -1 );
}
}
// Logical operators
class LAnd : IExpression
{
public void Parse( Stack<double> s )
{
double LHS = s.Pop();
double RHS = s.Pop();
if ( LHS < -1 || RHS < -1 || LHS > 0 || RHS > 0 )
throw new InvalidOperationException();
if ( LHS == RHS )
s.Push( -1 );
else
s.Push( 0 );
}
}
class LOr : IExpression
{
public void Parse( Stack<double> s )
{
double LHS = s.Pop();
double RHS = s.Pop();
if ( LHS < -1 || RHS < -1 || LHS > 0 || RHS > 0 )
throw new InvalidOperationException();
if ( LHS == -1 || RHS == -1 )
s.Push( -1 );
else
s.Push( 0 );
}
}
class Neg : IExpression
{
public void Parse( Stack<double> s )
{
double LHS = s.Pop();
s.Push( -LHS );
}
}
class LNot : IExpression
{
public void Parse( Stack<double> s )
{
double LHS = s.Pop();
if ( LHS < -1 || LHS > 0 )
throw new InvalidOperationException();
if ( LHS == -1 )
s.Push( 0 );
else
s.Push( -1 );
}
}
// Bitwise operators
class BAnd : IExpression
{
public void Parse( Stack<double> s )
{
double LHS = s.Pop();
double RHS = s.Pop();
s.Push( Convert.ToInt32(LHS) & Convert.ToInt32(RHS) );
}
}
class BOr : IExpression
{
public void Parse( Stack<double> s )
{
double LHS = s.Pop();
double RHS = s.Pop();
s.Push( Convert.ToInt32(LHS) | Convert.ToInt32(RHS) );
}
}
class BNot : IExpression
{
public void Parse( Stack<double> s )
{
double LHS = s.Pop();
s.Push( ~Convert.ToInt32(LHS) );
}
}
}
by Moridin8
12. April 2008 20:20
[For a bod in ##csharp]
** In an attempt to try and make things a little more 'realistic' I have added a real example of the use of this pattern below the basic example given here.
namespace ooppatterns
{
using System;
internal class Program
{
private static void Main( string[] args )
{
// Build responsibility chain
MailRoom MR = new MailRoom();
MR.Next = new SmallOrderDepartment();
MR.Next.Next = new LargeOrderDepartment();
MR.Next.Next.Next = new WarehouseOrderDepartment();
// Process incomming mail...
MR.ProcessRequest( new Mail( OrderType.CompanyOrder ) );
Console.WriteLine( new string( '-', 60 ) );
MR.ProcessRequest( new Mail( OrderType.WarehouseOrder ) );
Console.WriteLine( new string( '-', 60 ) );
MR.ProcessRequest( new Mail( OrderType.None ) );
Console.WriteLine( new string( '-', 60 ) );
MR.ProcessRequest( new Mail( OrderType.HomeOrder ) );
Console.ReadLine();
}
}
internal enum OrderType
{
None,
HomeOrder,
ProfessionalOrder,
CompanyOrder,
WarehouseOrder
}
class Mail
{
public OrderType Order;
public Mail( OrderType order )
{
this.Order = order;
}
}
abstract class Handler
{
private Handler _next;
public Handler Next
{
get { return this._next; }
set { this._next = value; }
}
public abstract void ProcessRequest( Mail letter );
}
// Handlers with specific Responsibility criteria...
class MailRoom : Handler
{
public override void ProcessRequest( Mail letter )
{
if ( letter.Order == OrderType.None )
Console.WriteLine( "Mis-mailed... MailRoom handles" );
else if ( base.Next != null )
{
Console.WriteLine
( "MailRoom can not handle this. Passing along..." );
base.Next.ProcessRequest( letter );
}
else
Console.WriteLine( "Could not delegate order!" );
}
}
class SmallOrderDepartment : Handler
{
public override void ProcessRequest( Mail letter )
{
if ( letter.Order == OrderType.HomeOrder
|| letter.Order == OrderType.ProfessionalOrder )
Console.WriteLine( "SmallOrderDepartment handled order" );
else if ( base.Next != null )
{
Console.WriteLine( "SmallOrderDepartment can not handle this."
+ @" Passing along..." );
base.Next.ProcessRequest( letter );
}
else
Console.WriteLine( "Could not delegate order!" );
}
}
class LargeOrderDepartment : Handler
{
public override void ProcessRequest( Mail letter )
{
if ( letter.Order == OrderType.CompanyOrder )
Console.WriteLine( "LargeOrderDepartment handled order" );
else if ( base.Next != null )
{
Console.WriteLine( "LargeOrderDepartment can not handle this."
+ @" Passing along..." );
base.Next.ProcessRequest( letter );
}
else
Console.WriteLine( "Could not delegate order!" );
}
}
class WarehouseOrderDepartment : Handler
{
public override void ProcessRequest( Mail letter )
{
if ( letter.Order == OrderType.WarehouseOrder )
Console.WriteLine( "WarehouseOrderDepartment handled order" );
else if ( base.Next != null )
{
Console.WriteLine( "LargeOrderDepartment can not handle this."
+ @" Passing along..." );
base.Next.ProcessRequest( letter );
}
else
Console.WriteLine( "Could not delegate order!" );
}
}
}
InFix Notation Parser
I resurrected my InFix parser routine from my CMS code snippet library, removed a couple of things and presented for the site. This is a prime example of the Chain pattern in use. Please be aware of a few things though.
- The operator precedence method is just a dumb switch routine. I removed the precedence parser because it wouldn't work after I removed the things I did for the example. It is *not* fully tested. It seems to work though.
- Due to the way that the partner PostFix routine works, negations are parsed to the character 'N'.
- The PostFix routine that partner's this example is the example I am using for the Interpreter Pattern. Please feel free to meld the two.
Enjoy...
namespace ooppatterns
{
using System;
using System.Collections.Generic;
using System.Text;
internal class Program
{
private static void Main( string[] args )
{
string infix = "8++ + -(1+4) * -200";
// Instantiate a parser package
Package cont = new Package( infix );
// Create InFixParser and chain responsibilities
InFixParser IFP = new InFixParser();
IFP.Next = new Number();
IFP.Next.Next = new Parenthesis();
IFP.Next.Next.Next = new Operators();
IFP.Next.Next.Next.Next = new UnknownCharacters();
// Parse
IFP.Parse( cont );
// Now take results from the PostFix queue and
// convert to a string for viewing.
StringBuilder SB = new StringBuilder();
while ( cont.PostFix.Count > 0 )
SB.Append( cont.PostFix.Dequeue() + " " );
Console.WriteLine( SB.ToString() );
Console.ReadLine();
}
// Infix parser package. Holds all information pertinant
// to the conversion of InFix to PostFix
class Package
{
private Queue<string> _postFix = new Queue<string>();
private Stack<string> _operator = new Stack<string>();
private Stack<char> _unary = new Stack<char>();
private int _parenthesis = 0;
private bool _lastParsedAnOp = false;
private string _infix;
private int _i;
public Package( string infix )
{
this._infix = infix;
}
public Queue<string> PostFix
{
get { return this._postFix; }
}
public Stack<string> Operator
{
get { return this._operator; }
}
public Stack<char> Unary
{
get { return this._unary; }
}
public int Parenthesis
{
get { return this._parenthesis; }
set { this._parenthesis = value; }
}
public bool LastParsedAnOp
{
get { return this._lastParsedAnOp; }
set { this._lastParsedAnOp = value; }
}
public void SkipNextChar()
{
this._i++;
}
public char ReadNextChar()
{
return this._infix[this._i++];
}
public char PeekNextChar()
{
if ( this.IsEOF )
return '\0';
return this._infix[this._i];
}
public bool IsEOF
{
get { return !( this._i < this._infix.Length ); }
}
}
abstract class Handler
{
private Handler _next;
public Handler Next
{
get { return this._next; }
set { this._next = value; }
}
public abstract void Parse( Package con );
}
class InFixParser : Handler
{
// this handler simply pushes tasks along the chain
// and finishes the work left by the other links.
public override void Parse( Package con )
{
while ( !con.IsEOF )
base.Next.Parse( con );
// clear out operator stack...
while ( con.Operator.Count > 0 )
con.PostFix.Enqueue( con.Operator.Pop().ToString() );
}
}
class Number : Handler
{
// Handles numeric values
public override void Parse( Package con )
{
char ch = con.PeekNextChar();
if ( ( ch >= '0' && ch <= '9' ) || ch == '.' )
{
List<char> tb = new List<char>();
// get the whole number
for ( ; ( ch >= '0' && ch <= '9' ) || ch == '.' || ch == '-';
ch = con.PeekNextChar()
)
tb.Add( con.ReadNextChar() );
// Handle negation
if ( con.Unary.Count != 0 && con.Parenthesis == 0 )
{
con.Unary.Pop();
con.PostFix.Enqueue( "N" + new String( tb.ToArray() ) );
}
else
con.PostFix.Enqueue( new String( tb.ToArray() ) );
con.LastParsedAnOp = false;
}
else if ( base.Next != null )
base.Next.Parse( con );
}
}
class Parenthesis : Handler
{
// Handles any brackets (Parenthesis)
public override void Parse( Package con )
{
char ch = con.PeekNextChar();
if ( ch == '(' )
{
con.Operator.Push( ch.ToString() );
con.Parenthesis++;
con.SkipNextChar();
con.LastParsedAnOp = false;
}
else if ( ch == ')' )
{
while ( con.Operator.Count > 0 && ( con.Operator.Peek() ) != "(" )
con.PostFix.Enqueue( con.Operator.Pop().ToString() );
con.Operator.Pop();
con.Parenthesis--;
// place any unary operators in queue.
if ( con.Unary.Count > 0 )
con.PostFix.Enqueue( con.Unary.Pop().ToString() );
con.SkipNextChar();
con.LastParsedAnOp = false;
}
else if ( base.Next != null )
base.Next.Parse( con );
}
}
class Operators : Handler
{
// handle operators
public override void Parse( Package con )
{
char ch = con.PeekNextChar();
if ( ( ch >= '%' && ch <= '/' || ch == '!' || ch == '~' )
&&
ch != '.' && ch != '(' && ch != ')'
)
if ( con.LastParsedAnOp || con.PostFix.Count == 0 )
{
// if this just previously handled an operator,
// then this one must be unary
if ( ch == '-' )
con.Unary.Push( 'N' );
else if ( Precedence( ch.ToString() ) == 90 )
con.Unary.Push( ch );
else
throw new InvalidOperationException();
con.SkipNextChar();
con.LastParsedAnOp = false;
}
else
{
// Process operator...
List<char> tb = new List<char>();
for ( ; tb.Count < 2 && ( ch >= '%' && ch <= '/' )
&& ch != '.' && ch != '(' && ch != ')';
ch = con.PeekNextChar()
)
tb.Add( con.ReadNextChar() );
string token = new String( tb.ToArray() );
string top = "";
int opPrecA = Precedence( token );
// handle post increment/decrement operators
if ( opPrecA == 100 )
{
con.PostFix.Enqueue( token );
return;
}
// Discover precedence of operator and sort stack accordingly.
//
// NB: the precedence stack sorter is not fully tested!
//
if ( con.Operator.Count > 0 )
top = con.Operator.Peek();
int opPrecB = Precedence( top );
if ( opPrecA > opPrecB )
con.Operator.Push( token );
else
{
while ( opPrecB >= opPrecA )
{
string tmp = con.Operator.Pop();
top = con.Operator.Peek();
con.Operator.Push( tmp );
opPrecB = Precedence( top );
}
con.Operator.Push( top );
}
con.LastParsedAnOp = true;
}
else if ( base.Next != null )
base.Next.Parse( con );
}
// Get operator precedence
// NB: the precedence stack sorter is not fully tested!
private static int Precedence( string ch )
{
switch ( ch )
{
case "++":
case "--":
return 100;
case "!":
case "~":
return 90;
case "*":
case "/":
case "%":
case "^":
case "&":
case "|":
return 80;
case "+":
case "-":
return 70;
case "<<":
case ">>":
return 60;
case "<":
case ">":
case "<=":
case ">=":
return 40;
case "==":
case "!=":
case "<>":
return 30;
case "&&":
return 20;
case "||":
return 10;
default:
return 0;
}
}
}
class UnknownCharacters : Handler
{
// handle other characters (ignore)
public override void Parse( Package con )
{
con.SkipNextChar();
}
}
}
}
by Moridin8
18. March 2008 20:14
You could consider this singleton to be the web application friendly version of the ThreadSingleton (Well... almost). It lives inside the HttpContext Session dictionary as a standard session serialized object instead of staying in state against a specific thread and ss usual for session data it is (de)serialized against which ever session serialisation source you are using
In more complicated incarnations this form of class can allow all sorts of extensions to your web session model. Using the various facilities available in .NET for instance you can engineer and associate all forms of models directly against the session data in a much more OOP friendly manner.
This is my prefered way of working with session stated data. I sometimes wonder why so many other people don't themselves...
[Serializable]
public sealed class SessionSingleton
{
private const string SESSION_KEY
= "MySession_3BC433AB-3925-4bee-9EDD-7F874D241AD0";
/// <summary>
/// Some basic instance data.
/// </summary>
private string _SomeInstanceData;
/// <summary>
/// Gets or sets some instance data.
/// </summary>
/// <value>Some instance data.</value>
public string SomeInstanceData
{
get { return this._SomeInstanceData; }
set { this._SomeInstanceData = value; }
}
/// <summary>
/// Initializes a new instance of the <see cref="SessionSingleton"/> class.
/// This is privately invoked
/// </summary>
private SessionSingleton()
{
this._SomeInstanceData = "Hi!";
}
/// <summary>
/// Gets the singleton.
/// </summary>
/// <value>The singleton.</value>
public static SessionSingleton Session
{
get
{
// double check locking...
if( HttpContext.Current.Session[SESSION_KEY] == null )
lock( HttpContext.Current.Session.SyncRoot )
if( HttpContext.Current.Session[SESSION_KEY] == null )
HttpContext.Current.Session[SESSION_KEY] =
new SessionSingleton();
return HttpContext.Current.Session[SESSION_KEY] as SessionSingleton;
}
}
}