Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version
Go to top

Cat - A Statically Typed Programming Language Interpreter in C#

, 4 Nov 2006
This article contains the public domain implementation of an interpreter for a statically typed stack-based programming language in C# called Cat. The accompanying article is a high-level description of how the various modules work, a brief description of the language, and links to related work.
/// Public domain code by Christopher Diggins
/// http://www.cat-language.com

using System;
using System.Reflection;
using System.Reflection.Emit;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
using System.ComponentModel;
using System.Text.RegularExpressions;

namespace Cat
{
    /// <summary>
    /// Manages the main stack and auxiliary stack and exposes functions for manipulating them.
    /// </summary>
    static public class Executor 
    {
        #region fields
        public static CatStack main_stack = new CatStack();
        public static CatStack aux_stack = new CatStack();
        public static Scope scope = new Scope();
        public static Parser line_parser = new Parser();
        #endregion

        #region public functions
        public static bool IsIncomplete()
        {
            return line_parser.IsIncomplete();
        }
        public static void Push(Object o)
        {
            main_stack.Push(o);
        }
        public static void PushInt(int n)
        {
            main_stack.Push(n);
        }
        public static void PushString(string s)
        {
            main_stack.Push(s);
        }
        public static void PushRef(Function p)
        {
            main_stack.Push(p);
        }
        public static Object Pop()
        {
            return main_stack.Pop();
        }
        public static T TypedPop<T>()
        {
            if (main_stack.Count == 0)
                throw new Exception("Trying to pop an empty stack");
            Object o = main_stack.Pop();
            if (!(o is T))
                throw new Exception("Expected type " + typeof(T).Name + " but instead found " + o.GetType().Name);
            return (T)o;
        }
        public static int PopInt()
        {
            return TypedPop<int>();
        }
        public static bool PopBoolean()
        {
            return TypedPop<bool>();
        }
        public static Function PopProgram()
        {
            return TypedPop<Function>();
        }
        public static ArrayList PopList()
        {
            return TypedPop<ArrayList>();
        }
        public static String PopString()
        {
            return TypedPop<String>();
        }
        public static Object Peek()
        {
            return main_stack.Peek();
        }
        public static T TypedPeek<T>()
        {
            if (main_stack.Count == 0)
                throw new Exception("Trying to peek into an empty stack ");
            Object o = main_stack.Peek();
            if (!(o is T))
                throw new Exception("Expected type " + typeof(T).Name + " but instead found " + o.GetType().Name);
            return (T)o;
        }
        public static Function PeekProgram()
        {
            return TypedPeek<Function>();
        }
        public static ArrayList PeekList()
        {
            return TypedPeek<ArrayList>();
        }
        public static String PeekString()
        {
            return TypedPeek<String>();
        }
        public static bool IsEmpty()
        {
            return (main_stack.Count == 0) && (aux_stack.Count == 0);
        }
        public static bool StackTopIsTrue()
        {
            return TypedPeek<Boolean>();
        }
        #endregion

        #region environment serialization
        public static void LoadSession(string s)
        {
            try
            {
                string line;
                Parser p = new Parser();

                // Read the file and display it line by line.
                System.IO.StreamReader file = new System.IO.StreamReader(s);
                bool bResult = true;
                try
                {
                    while ((line = file.ReadLine()) != null)
                    {
                        p.Load(line + '\n');
                    }
                    p.Parse(" ");
                }
                catch (Exception e)
                {
                    file.Close();
                    throw e;
                }
                file.Close();
                if (!bResult)
                {
                    throw new Exception("The file was ill-formed");
                }

                // now create statements 
                foreach (AstNode node in p.GetChildren())
                {
                    Scope.Global().ProcessNode(node);
                }

                // TODO: optimize, etc. 
            }
            catch (Exception e)
            {
                Console.WriteLine("Failed to load \"" + s + "\"");
                Console.WriteLine("Error: {0}", e.Message);
            }
        }
        #endregion

        #region exection functions
        public static void Execute(Scope pScope)
        {
            try
            {
                foreach (Function p in pScope.GetInstructions())
                {
                    p.Exec();
                }
            }
            catch (Exception e)
            {
                pScope.GetInstructions().Clear();
                throw e;
            }
            pScope.GetInstructions().Clear();
        }
        public static void Execute(string s)
        {
            line_parser.Parse(s);
            AstNode node;
            while ((node = line_parser.ExtractNode()) != null)
            {
                Scope.Global().ProcessNode(node);
                Execute(Scope.Global());
            }
        }
        #endregion

        #region utility functions
        public static string StackToString(CatStack stk)
        {
            if (stk.Count == 0) return "_empty_";
            string s = "";
            int nMax = 5;
            if (stk.Count > nMax)
                s = "...";
            if (stk.Count < nMax)
                nMax = stk.Count;
            for (int i = nMax - 1; i >= 0; -- i)
            {
                Object o = stk[i];
                if (o is String)
                {
                    s += "\"" + (o as String) + "\"";
                }
                else
                {
                    s += o.ToString();
                }
                s += " ";
            }
            return s;
        }
        public static void OutputStack()
        {
            Console.WriteLine("main stack: " + StackToString(main_stack));
            Console.WriteLine("aux. stack: " + StackToString(aux_stack));
        }
        #endregion

        #region print functions
        public static void Print(object o)
        {
            if (o is ArrayList)
            {
                PrintArrayList(o as ArrayList);
            }
            else
            {
                Console.Write(o.ToString());
            }
        }
        public static void WriteLine()
        {
            if (main_stack.Count == 0)
            {
                Console.WriteLine("_empty_");
            }
            else
            {
                Print(main_stack.Pop());
                Console.WriteLine();
            }
        }
        public static void Write()
        {
            if (main_stack.Count == 0)
            {
                Console.Write("_empty_");
            }
            else
            {
                Print(main_stack.Pop());
            }
        }
        public static void ShowStack()
        {
            for (int i = 0; i < main_stack.Count; ++i)
            {
                Print(main_stack[i]);
            }
            Console.WriteLine();
        }
        public static void ClearStack()
        {
            main_stack.Clear();
            aux_stack.Clear();
        }
        public static void PrintArrayList(ArrayList a)
        {
            Console.Write("(");
            for (int i = 0; i < a.Count; ++i)
            {
                if (i > 0)
                {
                    Console.Write(",");
                }
                Print(a[i]);
            }
            Console.Write(")");
        }
        public static void PrintScope(Scope p)
        {
            Console.WriteLine("Scope: " + p.GetName());
            foreach (Function prog in p.GetDefinitions())
            {
                Console.WriteLine(prog.ToString());
            }
            foreach (Scope child in p.GetChildren())
            {
                PrintScope(child);
            }

        }
        public static void Bindings()
        {
            PrintScope(scope);
        }
        #endregion

        #region arithmetic operations
        public static void Max() { int b = PopInt(); int a = PopInt(); Push(a > b ? a : b); }
        public static void Min() { int b = PopInt(); int a = PopInt(); Push(a < b ? a : b); }
        public static void Inc() { Push(PopInt() + 1); }
        public static void Dec() { Push(PopInt() - 1); }
        public static void Add() { int x = PopInt(); int y = PopInt(); Push(y + x); }
        public static void Sub() { int x = PopInt(); int y = PopInt(); Push(y - x); }
        public static void Mul() { int x = PopInt(); int y = PopInt(); Push(y * x); }
        public static void Div() { int x = PopInt(); int y = PopInt(); Push(y / x); }
        public static void Mod() { int x = PopInt(); int y = PopInt(); Push(y % x); }
        public static void DivMod() { int x = PopInt(); int y = PopInt(); Push(y / x); Push(y % x); }
        public static void Neg() { Push(-PopInt()); }
        public static void Compl() { Push(~PopInt()); }
        #endregion

        #region stack manipulation operations
        public static void Swap()
        {
            Object x = Pop();
            Object y = Pop();
            Push(x);
            Push(y);
        }
        public static void Dup()
        {
            Object x = Peek();
            if (x is ArrayList) x = (x as ArrayList).Clone();
            Push(x);
        }
        public static void Zap() 
        { 
            Pop(); 
        }
        #endregion

        #region comparison operations
        public static void Gt() { int x = PopInt(); int y = PopInt(); Push(y > x); }
        public static void GtEq() { int x = PopInt(); int y = PopInt(); Push(y >= x); }
        public static void Lt() { int x = PopInt(); int y = PopInt(); Push(y < x); }
        public static void LtEq() { int x = PopInt(); int y = PopInt(); Push(y <= x); }
        public static void Eq() 
        {
            Object x = Pop();
            Object y = Pop();
            Push(CatBase.AreObjectsEqual(x, y));
        }

        #endregion

        #region control flow operations
        public static void If()
        {
            Function pFalse = PopProgram();
            Function pTrue = PopProgram();
            if (StackTopIsTrue())
            {
                Pop();
                pTrue.Exec();
            }
            else
            {
                Pop();
                pFalse.Exec();
            }
        }
        public static void While()
        {
            Function pLoop = PopProgram();
            Function pCond = PopProgram();
            pCond.Exec();
            while (StackTopIsTrue())
            {
                Pop();
                pLoop.Exec();
                pCond.Exec();
            }
            Pop();
        }
        #endregion

        #region list operations
        public static void QuoteToList()
        {
            Function a = PopProgram();
            CatStack old_stack = main_stack;
            main_stack = new CatStack();
            a.Exec();
            ArrayList b = new ArrayList(main_stack);
            main_stack = old_stack;
            main_stack.Push(b);
        }
        public static void Prepend()
        {
            Object x = Pop();
            ArrayList list = PeekList();
            list.Insert(0, x);
        }
        public static void Append()
        {
            Object x = Pop();
            ArrayList list = PeekList();
            list.Add(x);
        }
        public static void Cat()
        {
            ArrayList list = PopList();
            (PeekList()).AddRange(list);
        }
        public static void Uncons()
        {
            ArrayList list = PeekList();
            Push(list[0]);
            list.RemoveAt(0);
        }
        public static void Rev()
        {
            PeekList().Reverse();
        }
        public static void Nth()
        {
            int n = PopInt();
            ArrayList list = PeekList();
            Push(list[n]);
        }
        public static void Set()
        {
            Object o = Pop();
            int n = PopInt();
            PeekList()[n] = o;
        }
        public static void Last()
        {
            ArrayList list = PeekList();
            Push(list[list.Count - 1]);
        }
        public static void Empty()
        {
            Push(PeekList().Count > 0 ? 0 : 1);
        }
        public static void Small()
        {
            Push(PeekList().Count > 1 ? 0 : 1);
        }
        public static void First()
        {
            Push(PeekList()[0]);
        }
        public static void Head()
        {
            Push(PopList()[0]);
        }
        public static void Tail()
        {
            PeekList().RemoveAt(0);
        }
        public static void Count()
        {
            Push(PeekList().Count);
        }
        public static void Drop()
        {
            int n = PopInt();
            ArrayList list = PeekList();
            if (n > list.Count) n = list.Count;
            list.RemoveRange(0, n);
        }
        public static void Take()
        {
            int n = PopInt();
            ArrayList list = PeekList();
            n = (list.Count - n);
            int m = (list.Count - n);
            list.RemoveRange(m, n);
        }
        public static void Cut()
        {
            int n = PopInt();
            ArrayList list = PeekList();
            ArrayList a = new ArrayList(list.GetRange(n, list.Count - n));
            list.RemoveRange(n, list.Count - n);
            Push(a);
        }
        public static void Split()
        {
            Function pred = PopProgram();
            ArrayList o = PopList();
            ArrayList a = new ArrayList();
            ArrayList b = new ArrayList();
            for (int i = 0; i < o.Count; ++i)
            {
                Push(o[i]);
                pred.Exec();
                if (StackTopIsTrue())
                {
                    a.Add(o[i]);
                }
                else
                {
                    b.Add(o[i]);
                }
            }
            Push(a);
            Push(b);
        }
        public static void Merge()
        {
            Function pred = PopProgram();
            ArrayList o = new ArrayList();
            ArrayList b = PopList();
            ArrayList a = PopList();
            int i = 0;
            int j = 0;
            while ((i < a.Count) && (j < b.Count))
            {
                Push(a[i]);
                Push(b[j]);
                pred.Exec();
                if (StackTopIsTrue())
                {
                    o.Add(a[i++]);
                }
                else
                {
                    o.Add(b[j++]);
                }
            }
            // Add the remaining items from whatever list was longer
            // only one of the two lines will be executed 
            while (i < a.Count) { o.Add(a[i++]); }
            while (j < b.Count) { o.Add(b[j++]); }
            Push(o);
        }
        public static void Range()
        {
            int n = PopInt();
            int i = PopInt();
            ArrayList list = PopList();
            Push(list.GetRange(i, n));
        }
        public static void Map()
        {
            Function a = PopProgram();
            ArrayList b = PeekList();
            for (int i = 0; i < b.Count; ++i)
            {
                Push(b[i]);
                a.Exec();
                b[i] = Pop();
            }
        }
        public static void ForEach()
        {
            Function a = PopProgram();
            ArrayList b = PeekList();
            for (int i = 0; i < b.Count; ++i)
            {
                Push(b[i]);
                a.Exec();
            }
        }
        public static void Filter()
        {
            Function pred = PopProgram();
            ArrayList a = PeekList();
            for (int i = a.Count; i > 0; --i)
            {
                Push(a[i - 1]);
                pred.Exec();
                bool b = PopBoolean();
                if (!b)
                {
                    a.RemoveAt(i - 1);
                }
            }
        }
        public static void Fold()
        {
            Function a = PopProgram();
            Swap();
            ArrayList b = PopList();
            for (int i = 0; i < b.Count; ++i)
            {
                Push(b[i]);
                a.Exec();
            }
        }
        #endregion

        #region boolean operations
        public static void True() { Push(true); }
        public static void False() { Push(false); }
        public static void And() { Boolean x = TypedPop<Boolean>(); Boolean y = TypedPop<Boolean>(); Push(y && x); }
        public static void NAnd() { Boolean x = TypedPop<Boolean>(); Boolean y = TypedPop<Boolean>(); Push(!(y && x)); }
        public static void Or() { Boolean x = TypedPop<Boolean>(); Boolean y = TypedPop<Boolean>(); Push(y || x); }
        public static void NOr() { Boolean x = TypedPop<Boolean>(); Boolean y = TypedPop<Boolean>(); Push(!(y || x)); }
        public static void XOr() { Boolean x = TypedPop<Boolean>(); Boolean y = TypedPop<Boolean>(); Push(x ^ y); }
        public static void Not() { Push(!TypedPop<Boolean>()); }
        #endregion

        #region various operations
        public static void NullOp() { 
        }
        public static void Id() 
        {
            // May seem strange, but assures that an empty stack results in a failure. 
            Push(Pop());
        }
        public static void Exec()
        {
            PopProgram().Exec();
        }
        static Random genRandom = new Random();
        public static void Rnd()
        {
            Push(genRandom.Next());
        }
        #endregion

        #region list making operations
        public static void NewList()
        {
            Push(new ArrayList());
        }
        public static void Pair()
        {
            ArrayList a = new ArrayList();
            object o = Pop();
            a.Add(Pop());
            a.Add(o);
            Push(a);
        }
        public static void Single()
        {
            ArrayList a = new ArrayList();
            a.Add(Pop());
            Push(a);
        }
        public static void Gen()
        {
            ArrayList a = new ArrayList();
            Function loop = PopProgram();
            Function cond = PopProgram();
            cond.Exec();
            Dup();
            while (StackTopIsTrue())
            {
                a.Add(Peek());
                loop.Exec();
                Dup();
                cond.Exec();
            }
            Pop();
            Push(a);
        }
        public static void N()
        {
            int n = PopInt();
            ArrayList a = new ArrayList(n);
            for (int i = 0; i < n; ++i)
            {
                a.Add(i);
            }
            Push(a);
        }
        public static void Init()
        {
            Function gen = PopProgram();
            int n = PopInt();
            ArrayList list = new ArrayList(n);
            for (int i = 0; i < n; ++i)
            {
                Push(i);
                gen.Exec();
                list.Add(Pop());
            }
            Push(list);
        }
        #endregion

        #region parameterized operations
        public static void Load(int n)
        {
            main_stack.Push(aux_stack[n]);
        }
        public static void Store(int n)
        {
            aux_stack[n] = main_stack.Peek();
        }
        #endregion

        #region type operations
        public static void TypeOf()
        {
            if (Peek() is Int32)
            {
                Push(CatType.IntType);
            }
            else if (Peek() is ArrayList)
            {
                Push(CatType.ListType);
            }
            else if (Peek() is String)
            {
                Push(CatType.StringType);
            }
            else if (Peek() is CatType)
            {
                Push(CatType.TypeType);
            }
            else if (Peek() is Boolean)
            {
                Push(CatType.BoolType);
            }
            else if (Peek() is Char)
            {
                Push(CatType.CharType);
            }
            else if (Peek() is Function)
            {
                Function p = Peek() as Function;
                Push(p.mpType);
            }
            else
            {
                throw new Exception("Unknown type");
            }
        }
        public static void TypeEq()
        {
            CatType x = TypedPop<CatType>();
            CatType y = TypedPeek<CatType>();
            Push(x);
            Push(x.Equals(y));
        }
        public static void PushIntType()
        {
            Push(CatType.IntType);
        }
        public static void PushCharType()
        {
            Push(CatType.CharType);
        }
        public static void PushStringType()
        {
            Push(CatType.StringType);
        }
        public static void PushListType()
        {
            Push(CatType.ListType);
        }
        public static void PushBoolType()
        {
            Push(CatType.BoolType);
        }
        public static void PushVarType()
        {
            Push(CatType.VarType);
        }
        #endregion

        #region function operations
        public static void Man()
        {
            Function p = TypedPop<Function>();
            Console.WriteLine("name:        " + p.GetFullName());
            Console.WriteLine("description: " + p.GetDesc());
            Console.WriteLine("type:        " + p.GetFxnType().ToString());
            
            string s = "";
            if (p is Definition)
            {
                Definition d = p as Definition;
                foreach (Function child in d.mpChildren)
                    s += child.GetFullName() + " ";
            }

            Console.WriteLine("decompose:  " + s);
        }
        public static void Decompose()
        {
            Function p = TypedPop<Function>();
            ArrayList a = new ArrayList();
            if (p is Definition)
            {
                Definition d = p as Definition;
                a.AddRange(d.mpChildren);
            }
            else
            {
                a.Add(p);
            }
            Push(a);
        }
        public static void Compose()
        {
            ArrayList a = TypedPop<ArrayList>();
            Function p = new ComposedFunction(a, Scope.Global());
            Push(p);
        }
        public static void NameOf()
        {
            Function p = TypedPeek<Function>();
            Push(p.GetFullName());
        }
        public static void DescOf()
        {
            Function p = TypedPeek<Function>();
            Push(p.GetDesc());
        }
        #endregion

        #region auxiliary stack manipulators
        public static void Load()
        {
            aux_stack.Push(main_stack.Pop());
        }
        public static void Store()
        {
            main_stack.Push(aux_stack.Pop());
        }
        #endregion

        #region environment manipulation
        public static void ClearDefs()
        {
            Scope.Global().Clear();
            Primitives.RegisterAtomicPrograms();
            Primitives.RegisterStdPrograms();
        }
        class ProgramSorter : IComparer
        {
            public int Compare(object x, object y)
            {
                return ((x as Function).GetFullName().CompareTo((y as Function).GetFullName()));
            }
        }
        public static void Defs()
        {
            ArrayList a = new ArrayList();
            foreach (Function p in Scope.Global().GetDefinitions())
            {
                // We don't want anonymous or unnamed functions included 
                if (p.IsNamed())
                {
                    a.Add(p);
                }
            }
            // TEMP: I haven't decided yet how I feel about sorting.
            // a.Sort(new ProgramSorter());
            Push(a);
        }
        public static void IsStackEmpty()
        {
            Push(IsEmpty() ? 1 : 0);
        }
        #endregion

        #region shell operations
        public static void ShellExecute()
        {
            /*TODO: finish.
             * 
            ProcessStartInfo psi = new ProcessStartInfo();
            psi.FileName = Path.GetDirectoryName(Application.ExecutablePath) + "\\Some.exe";
            psi.Arguments = Path.GetDirectoryName(Application.ExecutablePath) + "\\Some.dat";
            psi.WindowStyle = ProcessWindowStyle.Hidden;
            System.Diagnostics.Process.Start(psi);
             */
        }
        #endregion

        #region Casting functions
        public static void CastToVar()
        {
            // Don't do anything ... in this executor everything is stored on the stack 
            // as an object. 
        }
        
        public static void CastToInt()
        {
            Object o = Pop();
            if (o is Int32)
            {
                // Do nothing.
            }
            else if (o is Boolean)
            {
                bool b = (Boolean)o;
                Push(b ? 1 : 0);
            }
            else if (o is String)
            {               
                int n = Parser.StringToInt(o as string);
                Push(n);
            }
            else if (o is ArrayList)
            {
                throw new Exception("can not convert list to int");
            }
            else 
            {
                throw new Exception("value of unexpected type " + o.ToString());
            }
        }

        public static void CastToString()
        {
            Object o = Pop();
            if (o is Int32)
            {
                int n = (int)o ;
                Push(n.ToString());
            }
            else if (o is Boolean)
            {
                bool b = (bool)o;
                Push(b ? "1" : "0");
            }
            else if (o is String)
            {
                // do nothing
            }
            else if (o is ArrayList)
            {
                ArrayList a = o as ArrayList;
                string s = "(";
                for (int i = 0; i < 5 && i < a.Count; ++i)
                {
                    if (i != 0)
                    {
                        s += ", ";
                    }
                    s += a[i].ToString();
                }
                if (a.Count > 5)
                {
                    s += " ...";
                }
                s += ")";
                Push(s);
            }
            else if (o is CatType)
            {
                Push((o as CatType).ToString());
            }
            else
            {
                // Seems rather contradictory doesn't it? 
                throw new Exception("can not convert " + o.ToString() + " to string");
            }
        }

        public static void CastToBool()
        {
            Object o = Pop();
            if (o is Int32)
            {
                int n = (int)o;
                Push(n == 0 ? false : true);
            }
            else if (o is Boolean)
            {
                // do nothing
            }
            else if (o is String)
            {
                string s = o as String;
                int n = Parser.StringToInt(s);
                Push(n == 0 ? false : true);
            }
            else if (o is ArrayList)
            {
                throw new Exception("can not convert a list to a boolean");
            }
            else
            {
                throw new Exception("value of unexpected type " + o.ToString());
            }
        }

        public static void CastToList()
        {
            Object o = Pop();
            if (o is ArrayList)
            {
                // do nothing
            }
            else 
            {
                ArrayList a = new ArrayList();
                a.Add(o);
                Push(a);
            }
        }
        #endregion

        #region string and regex function
        public static void StrCat()
        {
            string s = PopString();
            Push(PopString() + s);
        }
        public static void Tab()
        {
            Push("\t");
        }
        public static void NewLine()
        {
            Push("\n");
        }
        public static void NewString()
        {
            Push("");
        }
        public static void StrLen()
        {
            Push(TypedPeek<String>().Length);
        }
        public static void SubStr()
        {
            String s = TypedPop<String>();
            int nCount = PopInt();
            int nIndex = PopInt();
            Push(s.Substring(nIndex, nCount));
        }
        #endregion 

        #region regular exepression functions
        public static void RegExSplit()
        {
            string sPat = TypedPop<String>();
            string sInput = TypedPop<String>();
            string[] ret = Regex.Split(sInput, sPat);
            ArrayList a = new ArrayList(ret);
            Push(a);
        }
        public static void RegExReplace()
        {
            string sRep = TypedPop<String>();
            string sPat = TypedPop<String>();
            string sInput = TypedPop<String>();
            Push(Regex.Replace(sInput, sPat, sRep));
        }
        public static void RegExMatch()
        {
            string sPat = TypedPop<String>();
            string sInput = TypedPop<String>();
            MatchCollection mc = Regex.Matches(sInput, sPat);
            ArrayList a = new ArrayList();
            foreach (Match m in mc)
                a.Add(m.Value);
            Push(a);
        }
        #endregion

        #region file functions
        public static void StrFromFile()
        {
            string sFile = PopString();
            StreamReader r = new StreamReader(sFile);
            Push(r.ReadToEnd());
            r.Close();
        }
        public static void StrToFile()
        {
            string sFile = PopString();
            string sText = PopString();
            StreamWriter r = new StreamWriter(sFile);
            r.Write(sText);
            r.Flush();
            r.Close();
        }
        #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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Christopher Diggins
Software Developer Autodesk
Canada Canada
This article was written by Christopher Diggins, a computer science nerd who currently works at Autodesk as an SDK specialist.
Follow on   Twitter   Google+   LinkedIn

| Advertise | Privacy | Mobile
Web02 | 2.8.140922.1 | Last Updated 4 Nov 2006
Article Copyright 2006 by Christopher Diggins
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid