public static bool ProcessTime(ref MinMax <float> timeRange, ref bool isFade, float initialTime, float finalTime, ref List <ParticleEmitterToken> .Enumerator tokenIter)
        {
            // determine if this time specification is a fade directive
            if (tokenIter.Current.Type == TokenType.KeywordFade)
            {
                // it is... the next token must be "so"
                ParticleEmitterTokenizer.NextToken(ref tokenIter);

                if (tokenIter.Current.Type != TokenType.KeywordSo)
                {
                    throw new Exception("Expecting \"so\" after \"fade\".");
                }

                // flip the fade flag on
                isFade = true;

                // move to next token (so that we land on "at" for the code below)
                ParticleEmitterTokenizer.NextToken(ref tokenIter);
            }
            else
            {
                isFade = false; // just to be safe
            }

            switch (tokenIter.Current.Type)
            {
            case TokenType.KeywordAt:
            {
                // easy, just grab the time
                ParticleEmitterTokenizer.NextToken(ref tokenIter);

                timeRange = ProcessNumber(ref tokenIter);
            }

            break;

            case TokenType.KeywordInitial:
            {
                // use initial time that was passed in.
                if (isFade)
                {
                    throw new Exception("Cannot use \"fade so\" modifier on \"initial\" times.");
                }

                timeRange.Min = initialTime;
                timeRange.Max = initialTime;

                ParticleEmitterTokenizer.NextToken(ref tokenIter);
            }

            break;

            case TokenType.KeywordFinal:
            {
                // use final time that was passed in.
                timeRange.Min = finalTime;
                timeRange.Max = finalTime;

                ParticleEmitterTokenizer.NextToken(ref tokenIter);
            }

            break;

            default:
                throw new Exception("Expected time specifier: \"[fade so] at\", \"initial\", or \"[fade so] final\"");
            }

            return(true);
        }
        public static MinMax <float> ProcessNumber(ref List <ParticleEmitterToken> .Enumerator tokenIter)
        {
            MinMax <float> number = new MinMax <float>();

            // the first token is either the random keyword, or it's an actual number.
            switch (tokenIter.Current.Type)
            {
            case TokenType.KeywordRandom:
                // parse random number into minmax
                ParticleEmitterTokenizer.NextToken(ref tokenIter);

                if (tokenIter.Current.Type != TokenType.OpenParen)
                {
                    throw new Exception("Expecting opening paren after Random keyword.");
                }

                ParticleEmitterTokenizer.NextToken(ref tokenIter);

                if (tokenIter.Current.Type != TokenType.RealNumber)
                {
                    throw new Exception("Expecting first number within Random(...).");
                }

                number.Min = float.Parse(tokenIter.Current.StringValue, CultureInfo.InvariantCulture.NumberFormat);

                ParticleEmitterTokenizer.NextToken(ref tokenIter);

                if (tokenIter.Current.Type != TokenType.Comma)
                {
                    throw new Exception("Expecting comma within Random(...).");
                }

                ParticleEmitterTokenizer.NextToken(ref tokenIter);

                if (tokenIter.Current.Type != TokenType.RealNumber)
                {
                    throw new Exception("Expecting second number within Random(...).");
                }

                number.Max = float.Parse(tokenIter.Current.StringValue, CultureInfo.InvariantCulture.NumberFormat);

                ParticleEmitterTokenizer.NextToken(ref tokenIter);

                if (tokenIter.Current.Type != TokenType.CloseParen)
                {
                    throw new Exception("Missing close paren on Random(...).");
                }

                ParticleEmitterTokenizer.NextToken(ref tokenIter);

                break;

            case TokenType.RealNumber:
                // min and max both equal realnumber
                if (tokenIter.Current.Type != TokenType.RealNumber)
                {
                    throw new Exception("Expecting number.");
                }

                number.Max = number.Min = float.Parse(tokenIter.Current.StringValue, CultureInfo.InvariantCulture.NumberFormat);

                ParticleEmitterTokenizer.NextToken(ref tokenIter);

                break;

            default:
                throw new Exception("Expecting either Random(...) or a number value.");
            }

            return(number);
        }
        public static MinMax <Color> ProcessColor(ref List <ParticleEmitterToken> .Enumerator tokenIter)
        {
            MinMax <Color> c = new MinMax <Color>();

            // this token needs to be a RGBA keyword.
            if (tokenIter.Current.Type != TokenType.KeywordColor)
            {
                throw new Exception("Expecting RGBA(...)!");
            }

            ParticleEmitterTokenizer.NextToken(ref tokenIter);

            if (tokenIter.Current.Type != TokenType.OpenParen)
            {
                throw new Exception("Expecting ( after RGBA!");
            }

            MinMax <float> r = new MinMax <float>();
            MinMax <float> g = new MinMax <float>();
            MinMax <float> b = new MinMax <float>();
            MinMax <float> a = new MinMax <float>();

            ParticleEmitterTokenizer.NextToken(ref tokenIter);

            r = ProcessNumber(ref tokenIter);

            if (tokenIter.Current.Type != TokenType.Comma)
            {
                throw new Exception("Color components must be seperated by a comma.");
            }

            ParticleEmitterTokenizer.NextToken(ref tokenIter);

            g = ProcessNumber(ref tokenIter);
            if (tokenIter.Current.Type != TokenType.Comma)
            {
                throw new Exception("Color components must be seperated by a comma.");
            }

            ParticleEmitterTokenizer.NextToken(ref tokenIter);

            b = ProcessNumber(ref tokenIter);
            if (tokenIter.Current.Type != TokenType.Comma)
            {
                throw new Exception("Color components must be seperated by a comma.");
            }

            ParticleEmitterTokenizer.NextToken(ref tokenIter);

            a = ProcessNumber(ref tokenIter);
            if (tokenIter.Current.Type != TokenType.CloseParen)
            {
                throw new Exception("Expecting ) to close vector.");
            }

            ParticleEmitterTokenizer.NextToken(ref tokenIter);

            c.Min = new Color(r.Min, g.Min, b.Min, a.Min);
            c.Max = new Color(r.Max, g.Max, b.Max, a.Max);

            return(c);
        }