Click here to Skip to main content
15,893,588 members
Articles / Programming Languages / C#

Generic DFA State Machine for .NET

Rate me:
Please Sign up or sign in to vote.
4.75/5 (42 votes)
21 May 2003BSD6 min read 224.7K   3.6K   75  
Seemless NFA to DFA transfers with GraphViz graphing integration
namespace leppie.FA
{
   /******************************************************************************************
    * Open Source Software License. Copyright 2003. Llewellyn Pritchard. All rights reserved.*
    * ****************************************************************************************
    * This notice may not be removed.                                                        *
    * Software is refered to all code following this notice up to the end of the file.       *
    * The software is provided as is.                                                        *
    * The software may not be sublicensed without permission from the license provider.      *
    * The software is free to use and distribute without permission in the original or       *
    * modified form for all non-commercial purposes.                                         *
    * Explicit permission is required from the license provider for all commercial purposes. *
    * In such a case, a commercial license might be required.                                *
    * Any vagueness about the license granted, must be verified by contacting the            *
    * license provider (llewellyn@pritchard.org or leppie@ataris.co.za).                     *
    ******************************************************************************************/
   
   using System;
   using System.Collections;
   
   public class AtomicState
   {
      protected AtomicState() : this(new object()){}

      private AtomicState(object key)
      { 
         this.key = key;
         this.tag = null; 
         child = null; 
         next = null;
      }

      protected object key;
      //can be used for data, but this is really just a toggle
      //cant see where data would be used..., maybe later, maybe a surprise at the end???
      internal object tag;
      internal AtomicState child;
      internal AtomicState next;

      private AtomicState GetLastNext()
      {
         AtomicState last = this;
         while (last.next != null)
            last = last.next;
         return last;
      }

      private AtomicState MatchKey(object key)
      {
         AtomicState currentnode = this;
         while (currentnode != null)
         {
            if (currentnode.key.Equals(key))
               return currentnode;
            currentnode = currentnode.next;
         }
         return null;
      }

      public AtomicState GetAtomicStateAt(Array stack)
      {
         if (stack == null || stack.Length == 0) return null;

         AtomicState parent = this;
         int counter = 0;
         do
         {
            AtomicState i = parent.MatchKey(stack.GetValue(counter++));
            if (null == i)
               return null;
            if (null == i.child && counter < stack.Length)
               return null;
            if (counter == stack.Length)
               return i;
            parent = i.child;
         }
         while (true);
      }

      private bool AddStackToAtomicState(Array stack, AtomicState parent, 
         AtomicState prev, int counter, object tag)
      {
         do
         {  
            if (counter == stack.Length)
            {
               if (tag == null || parent.tag != null)
               {
                  parent.tag = null;
                  return true;
               }
               if (parent.tag != null) 
                  return false;
               parent.tag = tag;
            }
            else 
            {
               AtomicState pnode = new AtomicState(stack.GetValue(counter));
 
               if (null != parent)
                  parent.child = pnode;
               if (null != prev)
               {
                  prev.GetLastNext().next = pnode;
                  prev = null;
               }
               parent = pnode;
            }
         }
         while (counter++ < stack.Length);
         return true;
      }

      protected Array Match(Array stack)
      {
         ArrayList arr = new ArrayList();
         //again I hate M$ here, if we dont do this, the setvalue will fail below
         object[] ostack = new object[stack.Length];
         stack.CopyTo(ostack, 0);
         MatchWild(ostack, ostack, arr);
         if (arr.Count > 0)
            return arr.ToArray(arr[0].GetType());
         else
            return null;
      }

      private void MatchWild(Array stack, Array stacktemp, ArrayList results)
      {
         AtomicState pn = null;
         int i = 0;
         for (; i < stacktemp.Length; i++)
         {
            if (stacktemp.GetValue(i) == null) 
               break;
         }

         object[] substack = new object[stacktemp.Length - i];

         for (int j = 0; j < substack.Length; j++)
            substack.SetValue(stacktemp.GetValue(i + j), j);

         if (substack.Length > 0)
         {
            pn = GetAtomicStateAt(Trim(stacktemp));

            if (pn != null)
               pn = pn.child;
            else if (i == 0)
               pn = this;

            while (pn != null)
            {
               substack.SetValue(pn.key, 0);
               //this one
               stack.SetValue(pn.key, i);
               pn.MatchWild(stack, substack, results);    
               pn = pn.next;
            }
            substack.SetValue(null, 0);
         }
         else
         {
            pn = GetAtomicStateAt(stacktemp);
            if (pn != null)
               if (pn.tag != null)
                  results.Add(stack.Clone());
         }
      } 

      protected bool Add(Array stack)
      {
         return Add(stack, this);
      }

      protected bool Remove(Array stack)
      {
         return Add(stack, null);
      }

      private bool Add(Array stack, object tag)
      {
         if (stack.Length == 0) return false;
         
         AtomicState parent = this;

         int counter = 0;
         do
         {
            AtomicState i = parent.MatchKey(stack.GetValue(counter++));
            if (null == i)
               return AddStackToAtomicState(stack, null, parent, --counter, tag);
            if (null == i.child && counter < stack.Length)
               return AddStackToAtomicState(stack, i, null, counter, tag);
            if (counter == stack.Length)
               return AddStackToAtomicState(stack, i, null, counter, tag);
            parent = i.child;
         }
         while (true);
      }

      protected bool Accepts(Array stack)
      {
         AtomicState i = GetAtomicStateAt(stack);
         if (null != i)
            if (i.tag != null)
               return true;
         return false;
      }

      public int AcceptStateCount
      {
         get 
         {
            int size = 0;
            if (next != null)
               size += next.AcceptStateCount;
            if (child != null)
               size += child.AcceptStateCount;
            if (tag != null)
               size++;
            return size;
         }
      }

      public int TotalStateCount
      {
         get 
         {
            int size = 0;
            if (next != null)
               size += next.TotalStateCount;
            if (child != null)
               size += child.TotalStateCount;
            size++;
            return size;
         }
      }
 
      private Array Trim(Array buffer)
      {
         int i = 0;
         for (; i < buffer.Length; i++)
            if (buffer.GetValue(i) == null)
               break;

         Array newbuf = Array.CreateInstance(key.GetType(), i);

         for (i = 0; i < newbuf.Length; i++)
            newbuf.SetValue(buffer.GetValue(i), i);
         
         return newbuf;
      }

      private bool MatchItem(Array buffer, ref int index, int pos, int size, Array results)
      {
         buffer.SetValue(key, pos);

         if (tag != null && index != 0)
         {
            buffer.SetValue(null, pos + 1);
            results.SetValue(Trim(buffer) ,size - index--);
         }

         if (index >= 0 & child != null)
            if (child.MatchItem(buffer, ref index, pos + 1, size, results))
               return true;

         if (index >= 0 & next != null)
            if (next.MatchItem(buffer, ref index, pos, size, results))
               return true;
        
         return false;
      } 

      public Array AcceptStates(Array stack)
      {
         AtomicState pnode = GetAtomicStateAt(stack);

         if (stack == null || stack.Length == 0)
            pnode = this;
         else
         {
            if (pnode == null || pnode.child == null)
               return null;
            else
               pnode = pnode.child;
         }

         int index = pnode.AcceptStateCount;
         int size = index;

         Array buffer = Array.CreateInstance(typeof(object), pnode.Depth(2)); //pad it, and child (boxing)
         Array results = Array.CreateInstance(typeof(Array), size);

         pnode.MatchItem(buffer, ref index, 0, size, results);

         //dont have to check, already knows there is at least one
         Array newres = Array.CreateInstance(results.GetValue(0).GetType(), size);
         results.CopyTo(newres, 0);
         return newres;
      }

      internal int Depth(int depth)
      {
         int c = 0, n = 0;
         if (child != null)
            c = child.Depth(depth + 1);
         if (next != null)
            n = next.Depth(depth);

         if (c > depth)
            depth = c;
         if (n > depth)
            depth = n;

         return depth;
      }

      public override string ToString()
      {
         return key.ToString();
      }
   }

   #region Implementations

   public class CharState : AtomicState 
   {
      public CharState() : base(){}

      public bool Add(char[] stack) { return base.Add(stack);}

      public bool Remove(char[] stack) { return base.Remove(stack);}

      public char[][] AcceptStates(char[] stack) {return (char[][]) base.AcceptStates(stack);}

      public bool Accepts(char[] stack) { return base.Accepts(stack);}

      public char[][] Match(char[] stack) 
      {
         //setup the "null" value
         object[] ostack = new object[stack.Length];
         for (int i = 0; i < stack.Length; i++)
         {
            if ((char)stack.GetValue(i) == '\0')
               ostack[i] = null;
            else
               ostack[i] = stack.GetValue(i);
         }
         Array res = base.Match(ostack);
         //god I hate M$, why cant I cast (even if I did specify the type)????
         //someone should shoot someone!!!!!!!!!!!!!!!!
         if (res != null)
         {
            char[][] output = new char[res.Length][];
            for (int i = 0; i < output.Length; i++)
            {
               Array inarr = (Array)res.GetValue(i);
               char[] cinarr = new char[inarr.Length];
               inarr.CopyTo(cinarr, 0);
               output[i] = cinarr;
            }
            return output;
         }
         else return null;
      }
   }

   public class StringState : AtomicState 
   {
      public StringState() : base(){}

      public bool Add(string[] stack) { return base.Add(stack);}

      public bool Remove(string[] stack) { return base.Remove(stack);}

      public string[][] AcceptStates(string[] stack) { return (string[][]) base.AcceptStates(stack);}

      public bool Accepts(string[] stack) { return base.Accepts(stack);}

      public string[][] Match(string[] stack) 
      { 
         Array res = base.Match(stack);
         //god I hate M$, why cant I cast (even if I did specify the type)????
         string[][] output = new string[res.Length][];
         res.CopyTo(output, 0);
         return output;
      }
   }

   public class Int32State : AtomicState 
   {
      public Int32State() : base(){}

      public bool Add(int[] stack) { return base.Add(stack);}

      public bool Remove(int[] stack) { return base.Remove(stack);}

      public int[][] AcceptStates(int[] stack) { return (int[][]) base.AcceptStates(stack);}

      public bool Accepts(int[] stack) { return base.Accepts(stack);}
   }

   public class FloatState : AtomicState 
   {
      public FloatState() : base(){}

      public bool Add(float[] stack) { return base.Add(stack);}

      public bool Remove(float[] stack) { return base.Remove(stack);}

      public float[][] AcceptStates(float[] stack) { return (float[][]) base.AcceptStates(stack);}

      public bool Accepts(float[] stack) { return base.Accepts(stack);}
   }

   public class BooleanState : AtomicState 
   {
      public BooleanState() : base(){}

      public bool Add(bool[] stack) { return base.Add(stack);}

      public bool Remove(bool[] stack) { return base.Remove(stack);}

      public bool[][] AcceptStates(bool[] stack) { return (bool[][]) base.AcceptStates(stack);}

      public bool Accepts(bool[] stack) { return base.Accepts(stack);}
   }

   public class ObjectState : AtomicState 
   {
      public ObjectState() : base(){}

      public bool Add(object[] stack) { return base.Add(stack);}

      public bool Remove(object[] stack) { return base.Remove(stack);}

      public object[][] AcceptStates(bool[] stack) { return (object[][]) base.AcceptStates(stack);}

      public bool Accepts(object[] stack) { return base.Accepts(stack);}
   }

   public class TypeState : AtomicState 
   {
      public TypeState() : base(){}

      public bool Add(Type[] stack) { return base.Add(stack);}

      public bool Remove(Type[] stack) { return base.Remove(stack);}

      public Type[][] AcceptStates(bool[] stack) { return (Type[][]) base.AcceptStates(stack);}

      public bool Accepts(Type[] stack) { return base.Accepts(stack);}

      public override string ToString()
      {
         if (key is Type)
            return ((Type)key).Name;
         else return base.ToString();
      }
   }


   #endregion
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The BSD License


Written By
Software Developer
South Africa South Africa
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions