Inheritance: IDisposable
Example #1
0
        internal static SpriteBlock ParseSpriteDeclaration(ParserStream stream)
        {
            var start = stream.Position;

            stream.AdvancePast("(");

            var ignored = new StringBuilder();
            var adTo    = stream.ScanUntil(ignored, '"', '\'');

            if (ignored.ToString().Any(a => !char.IsWhiteSpace(a)))
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected quotation mark");
                throw new StoppedParsingException();
            }

            if (adTo == null)
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected quotation mark");
                throw new StoppedParsingException();
            }

            var nameStart = stream.Position;
            var name      = new StringBuilder();

            name.Append(adTo.Value);
            stream.ScanUntil(name, adTo.Value);
            name.Append(adTo.Value);
            var nameStop = stream.Position;

            stream.AdvancePast(")");

            var rules = ParseSpriteRules(stream);

            return(new SpriteBlock((QuotedStringValue)Value.Parse(name.ToString(), nameStart, nameStop, Current.CurrentFilePath), rules, start, stream.Position, Current.CurrentFilePath));
        }
Example #2
0
        internal static Import ParseImportDirective(ParserStream stream)
        {
            var start = stream.Position;

            var buffer = new StringBuilder();

            stream.ScanUntilWithNesting(buffer, ';');

            var toParse = buffer.ToString().Trim();

            Value      val;
            MediaQuery media;
            string     mediaStr;

            if (Regex.IsMatch(toParse, @"url\s*?\(", RegexOptions.IgnoreCase))
            {
                var i = toParse.IndexOf(')');

                if (i == -1)
                {
                    Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected ')'");;
                    throw new StoppedParsingException();
                }

                val = Value.Parse(toParse.Substring(0, i + 1), start, start + i + 1, Current.CurrentFilePath);

                mediaStr = toParse.Substring(i + 1);
            }
            else
            {
                if (!(toParse.StartsWith("\"") || toParse.StartsWith("'")))
                {
                    Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected quote");
                    throw new StoppedParsingException();
                }

                var i = toParse.LastIndexOf(toParse[0]);

                if (i == -1)
                {
                    Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected '" + toParse[0] + "'");
                    throw new StoppedParsingException();
                }

                val      = Value.Parse(toParse.Substring(0, i + 1), start, start + i + 1, Current.CurrentFilePath);
                mediaStr = toParse.Substring(i + 1);
            }

            mediaStr = mediaStr.Trim();
            if (mediaStr.Length > 0)
            {
                media = MediaQueryParser.Parse(mediaStr, Position.Create(start, stream.Position, Current.CurrentFilePath));
            }
            else
            {
                media = new MediaType(Media.all, Position.Create(start, stream.Position, Current.CurrentFilePath));
            }

            return(new Import(val, media, start, stream.Position, Current.CurrentFilePath));
        }
Example #3
0
        private static MathValue ParseMathValue(char op, Value lhs, ParserStream stream, IPosition forPosition)
        {
            stream.Advance(); // skip operator

            if (lhs == null)
            {
                Current.RecordError(ErrorType.Parser, forPosition, "Expected value, found '" + op + "'");
                throw new StoppedParsingException();
            }

            Operator @operator;

            switch (op)
            {
            case '+': @operator = Operator.Plus; break;

            case '-': @operator = Operator.Minus; break;

            case '*': @operator = Operator.Mult; break;

            case '/': @operator = Operator.Div; break;

            case '%': @operator = Operator.Mod; break;

            default: throw new InvalidOperationException("Unexpected operator [" + op + "]");
            }

            var rhs = ParseImpl(stream, forPosition, allowSelectorIncludes: false);

            return(new MathValue(lhs, @operator, rhs));
        }
Example #4
0
        internal static CssCharset ParseCharsetDirective(ParserStream stream)
        {
            var start = stream.Position;

            var ignored = new StringBuilder();
            var quote   = stream.ScanUntil(ignored, '"', '\'');

            if (quote == null)
            {
                Current.RecordError(ErrorType.Parser, Model.Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected quotation mark");
                throw new StoppedParsingException();
            }

            var isoName = new StringBuilder();

            stream.ScanUntil(isoName, quote.Value);

            stream.AdvancePast(";");

            var isoNameStr = isoName.ToString();

            if (!CssCharset.KnownCharset(isoNameStr))
            {
                Current.RecordWarning(ErrorType.Parser, Model.Position.Create(start, stream.Position, Current.CurrentFilePath), "Unrecognized charset");
            }

            return(new CssCharset(new QuotedStringValue(isoNameStr), start, stream.Position));
        }
Example #5
0
        internal static MixinBlock ParseMixinDeclaration(string name, ParserStream stream)
        {
            var start   = stream.Position;
            var @params = new StringBuilder();

            stream.ScanUntil(@params, ')');

            name = name.Trim();

            if (name.Length == 0)
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected mixin name");
                throw new StoppedParsingException();
            }


            if (name.ToLower().In(ReservedWords))
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "'" + name + "' cannot be the name of a mixin.");
            }

            stream.AdvancePast("{");

            return
                (new MixinBlock(
                     name,
                     ParseMixinDeclarationParameter(@params.ToString(), start),
                     ParseCssRules(InvalidSelector.Singleton, stream),
                     start,
                     stream.Position,
                     Current.CurrentFilePath
                     ));
        }
Example #6
0
        // Basically, it's realy common for background-position to be something like "0px -20px", which shouldn't be interpretted as a math value
        //   so detect the case where two number with unit values are separated by whitespace and treat them as a compound value instead
        //   otherwise, fallback to normal parsing
        internal static Value ParseBackgroundPositionValue(ParserStream stream)
        {
            var start    = stream.Position;
            var valueStr = new StringBuilder();

            stream.ScanUntilWithNesting(valueStr, ';');

            var value = valueStr.ToString();

            var parts = value.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

            var pos = Position.Create(start, stream.Position, Current.CurrentFilePath);

            // something's up
            if (parts.Length != 2)
            {
                return(MoreValueParser.Parse(value, pos));
            }

            var p1 = parts[0];
            var p2 = parts[1];

            var v1 = MoreValueParser.Parse(p1, pos);
            var v2 = MoreValueParser.Parse(p2, pos);

            if (v1 is NumberWithUnitValue && v2 is NumberWithUnitValue)
            {
                return(new CompoundValue(v1, v2));
            }

            return(MoreValueParser.Parse(value, pos));
        }
Example #7
0
        internal static MediaBlock ParseMediaDirective(ParserStream stream)
        {
            var start = stream.Position;

            var media = new StringBuilder();

            stream.ScanUntil(media, '{');

            var mediaStr = media.ToString().Trim();

            if (mediaStr.IsNullOrEmpty())
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected media list");
                throw new StoppedParsingException();
            }

            var mediaQuery = MediaQueryParser.Parse(mediaStr, Position.Create(start, stream.Position, Current.CurrentFilePath));

            var contained = new List <Block>();

            char c;

            while ((c = stream.Peek()) != '}')
            {
                if (char.IsWhiteSpace(c))
                {
                    stream.AdvancePastWhiteSpace();
                    continue;
                }

                // More directive (probably)
                if (c == '@')
                {
                    contained.Add(ParseDirective(stream));
                    continue;
                }

                // Selector + block time!
                contained.Add(ParseSelectorAndBlock(stream));
            }

            var notAllowed = contained.Where(x => !(x is SelectorAndBlock || x is MoreVariable));

            foreach (var illegal in notAllowed)
            {
                Current.RecordError(ErrorType.Parser, illegal, "@media can only contain blocks and variable declarations");
            }

            if (notAllowed.Count() != 0)
            {
                throw new StoppedParsingException();
            }

            // Skip past }
            stream.Advance();

            return(new MediaBlock(mediaQuery, contained, start, stream.Position, Current.CurrentFilePath));
        }
Example #8
0
        internal static FontFaceBlock ParseFontFace(ParserStream stream, int start)
        {
            var ignored = new StringBuilder();

            stream.ScanUntil(ignored, '{');

            var rules = ParseCssRules(InvalidSelector.Singleton, stream);

            return(new FontFaceBlock(rules, start, stream.Position, Current.CurrentFilePath));
        }
Example #9
0
        internal static Value ParseMoreValue(ParserStream stream)
        {
            var start    = stream.Position;
            var valueStr = new StringBuilder();

            stream.ScanUntilWithNesting(valueStr, ';');

            var value = valueStr.ToString();

            return(Value.Parse(value, start, stream.Position, Current.CurrentFilePath));
        }
Example #10
0
        internal static GroupedValue ParseGroup(ParserStream stream, IPosition forPosition)
        {
            var toParse = new StringBuilder();

            stream.Advance(); // skip (
            stream.ScanUntilWithNesting(toParse, ')');

            var group = toParse.ToString().Trim();
            var ret   = Parse(group, forPosition);

            return(new GroupedValue(ret));
        }
Example #11
0
        private static QuotedStringValue ParseQuotedString(char quote, ParserStream stream, IPosition forPosition)
        {
            stream.Advance(); // skip the quote
            var buffer = new StringBuilder();

            var x = stream.ScanUntil(buffer, quote);

            if (x == null)
            {
                Current.RecordError(ErrorType.Parser, forPosition, "Expected '" + quote + "'");
                throw new StoppedParsingException();
            }

            return(new QuotedStringValue(buffer.ToString()));
        }
Example #12
0
        internal static KeyFrame ParseKeyFrame(ParserStream stream)
        {
            var start = stream.Position;

            var buffer = new StringBuilder();

            stream.ScanUntil(buffer, '{');

            var percents = new List <decimal>();

            var percentsStr = buffer.ToString();

            foreach (var p in percentsStr.Split(',').Select(s => s.Trim()))
            {
                if (p.Length == 0)
                {
                    Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected `from`, `to`, or a percentage");
                    throw new StoppedParsingException();
                }

                decimal percent;
                if (p.Equals("from", StringComparison.InvariantCultureIgnoreCase))
                {
                    percent = 0;
                }
                else
                {
                    if (p.Equals("to", StringComparison.InvariantCultureIgnoreCase))
                    {
                        percent = 100;
                    }
                    else
                    {
                        if (!p.EndsWith("%") || !decimal.TryParse(p.Substring(0, p.Length - 1), out percent))
                        {
                            Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected `from`, `to`, or a percentage.  Found `" + p + "`");
                            throw new StoppedParsingException();
                        }
                    }
                }

                percents.Add(percent);
            }

            var rules = ParseCssRules(InvalidSelector.Singleton, stream);

            return(new KeyFrame(percents, rules, start, stream.Position, Current.CurrentFilePath));
        }
Example #13
0
        internal static Value ParseFontLikeValue(ParserStream stream)
        {
            var start    = stream.Position;
            var valueStr = new StringBuilder();

            stream.ScanUntilWithNesting(valueStr, ';');

            var value = valueStr.ToString();

            // The shorthand isn't in use, so we can handle this
            if (value.IndexOf('/') == -1)
            {
                return(MoreValueParser.Parse(value, Position.Create(start, stream.Position, Current.CurrentFilePath)));
            }

            return(new StringValue(value));
        }
Example #14
0
        internal static SelectorAndBlock ParseSelectorAndBlock(ParserStream stream, string selectorStr = null)
        {
            var mark = stream.Position;

            int selectorStop, selectorStart;

            if (selectorStr.IsNullOrEmpty())
            {
                selectorStart = stream.Position;
                var selector = new StringBuilder();
                stream.ScanUntil(selector, '{');
                selectorStop = stream.Position;

                selectorStr = selector.ToString().Trim();

                if (selectorStr.IsNullOrEmpty())
                {
                    Current.RecordError(ErrorType.Parser, Position.Create(mark, stream.Position, Current.CurrentFilePath), "Expected selector");
                    throw new StoppedParsingException();
                }
            }
            else
            {
                selectorStop  = mark;
                selectorStart = selectorStop - selectorStr.Length;
            }

            var sel      = Selector.Parse(selectorStr, selectorStart, selectorStop, Current.CurrentFilePath);
            var cssRules = ParseCssRules(sel, stream);

            // Bind @reset() properties to their location w.r.t. selectors
            cssRules =
                cssRules.Select(
                    delegate(Property prop)
            {
                if (!(prop is ResetSelfProperty))
                {
                    return(prop);
                }

                return(((ResetSelfProperty)prop).BindToSelector(sel));
            }
                    ).ToList();

            return(new SelectorAndBlock(sel, cssRules, null, mark, stream.Position, Current.CurrentFilePath));
        }
Example #15
0
        internal static MoreVariable ParseMoreVariable(string name, ParserStream stream, int start)
        {
            name = name.Trim();

            var valueStart = stream.Position;
            var valueStr   = new StringBuilder();

            stream.ScanUntilWithNesting(valueStr, ';');

            var value = Value.Parse(valueStr.ToString().Trim(), valueStart, stream.Position, Current.CurrentFilePath, allowSelectorIncludes: true);

            if (name.ToLower().In(ReservedWords))
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "'" + name + "' cannot be a variable name.");
            }

            return(new MoreVariable(name, value, start, stream.Position, Current.CurrentFilePath));
        }
Example #16
0
        private static Value ParseImportant(ParserStream stream)
        {
            var buffer = new StringBuilder();

            while (stream.HasMore() && (char.IsLetterOrDigit(stream.Peek()) || stream.Peek().In('!', '-', '_')))
            {
                buffer.Append(stream.Read());
            }

            var ret = buffer.ToString();

            if (ret.Equals("!important", StringComparison.InvariantCultureIgnoreCase))
            {
                return(ImportantValue.Singleton);
            }

            return(new StringValue(ret));
        }
Example #17
0
        internal static SpriteRule ParseSpriteRule(ParserStream stream)
        {
            var start = stream.Position;

            if (stream.Peek() != '@')
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected '@'");
                throw new StoppedParsingException();
            }

            stream.Advance(); // Advance past @

            var name = new StringBuilder();

            while (stream.HasMore() && stream.Peek() != '=')
            {
                name.Append(stream.Read());
            }

            stream.AdvancePast("="); // Advance past =

            var ignored = new StringBuilder();
            var quote   = stream.ScanUntil(ignored, '\'', '"');

            if (quote == null)
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected quotation mark");
                throw new StoppedParsingException();
            }

            var valueStart = stream.Position;
            var valueStr   = new StringBuilder();

            valueStr.Append(quote.Value);
            stream.ScanUntil(valueStr, quote.Value);
            valueStr.Append(quote.Value);

            stream.AdvancePast(";");

            var value = (QuotedStringValue)Value.Parse(valueStr.ToString(), valueStart, stream.Position, Current.CurrentFilePath);

            return(new SpriteRule(name.ToString().Trim(), value, start, stream.Position, Current.CurrentFilePath));
        }
Example #18
0
        internal static List <Property> ParseCssRules(Selector selector, ParserStream stream)
        {
            var ret = new List <Property>();

            while (stream.HasMore() && stream.Peek() != '}')
            {
                var c = stream.Peek();

                if (char.IsWhiteSpace(c))
                {
                    stream.AdvancePastWhiteSpace();
                }
                else
                {
                    ret.Add(ParseRule(stream));
                }
            }

            stream.AdvancePast("}");
            return(ret);
        }
Example #19
0
        public static Value Parse(string value, IPosition forPosition, bool allowSelectorIncludes = false)
        {
            var ret = new List<Value>();

            using (var stream = new ParserStream(new StringReader(value)))
            {
                while (stream.HasMore())
                {
                    var buffer = new StringBuilder();
                    stream.ScanUntilWithNesting(buffer, ',', requireFound: false);
                    using (var subStream = new ParserStream(new StringReader(buffer.ToString())))
                    {
                        ret.Add(ParseImpl(subStream, forPosition, allowSelectorIncludes));
                    }
                }
            }

            if (ret.Count == 1) return ret[0];

            return new CommaDelimittedValue(ret);
        }
Example #20
0
        public List <Block> Parse(string filePath, TextReader reader)
        {
            try
            {
                using (var stream = new ParserStream(reader))
                {
                    Current.SwitchToFile(filePath);

                    var ret = new List <Block>();

                    while (stream.HasMore())
                    {
                        char c = stream.Peek();

                        //Ignore white space
                        if (char.IsWhiteSpace(c))
                        {
                            stream.AdvancePastWhiteSpace();
                            continue;
                        }

                        // More directive (probably)
                        if (c == '@')
                        {
                            ret.Add(ParseDirective(stream));
                            continue;
                        }

                        // Selector + block time!
                        ret.Add(ParseSelectorAndBlock(stream));
                    }

                    return(ret);
                }
            }
            catch (StoppedParsingException)
            {
                return(null);
            }
        }
Example #21
0
        public List<Block> Parse(string filePath, TextReader reader)
        {
            try
            {
                using (var stream = new ParserStream(reader))
                {
                    Current.SwitchToFile(filePath);

                    var ret = new List<Block>();

                    while (stream.HasMore())
                    {
                        char c = stream.Peek();

                        //Ignore white space
                        if (char.IsWhiteSpace(c))
                        {
                            stream.AdvancePastWhiteSpace();
                            continue;
                        }

                        // More directive (probably)
                        if (c == '@')
                        {
                            ret.Add(ParseDirective(stream));
                            continue;
                        }

                        // Selector + block time!
                        ret.Add(ParseSelectorAndBlock(stream));
                    }

                    return ret;
                }
            }
            catch (StoppedParsingException)
            {
                return null;
            }
        }
Example #22
0
        internal static ColorValue ParseHashColor(ParserStream stream, IPosition forPosition)
        {
            stream.Advance(); // skip #
            var buffer = new StringBuilder();

            while (buffer.Length < 6 && stream.HasMore() && char.ToLower(stream.Peek()).In('a', 'b', 'c', 'd', 'e', 'f', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0'))
            {
                buffer.Append(stream.Read());
            }

            if (buffer.Length != 3 && buffer.Length != 6)
            {
                Current.RecordError(ErrorType.Parser, forPosition, "Expected 3 or 6 hexidecimal characters");
                throw new StoppedParsingException();
            }

            if (buffer.Length == 3)
            {
                return(HexTripleColorValue.Parse(buffer.ToString()));
            }

            return(HexSextupleColorValue.Parse(buffer.ToString()));
        }
Example #23
0
        internal static Using ParseUsingDirective(ParserStream stream)
        {
            var start = stream.Position;

            var ignored = new StringBuilder();
            var quote   = stream.ScanUntil(ignored, '"', '\'');

            if (quote == null)
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected quotation mark");
                throw new StoppedParsingException();
            }

            var file = new StringBuilder();

            stream.ScanUntil(file, quote.Value);

            int mediaStart = stream.Position;
            var mediaBuff  = new StringBuilder();

            stream.ScanUntil(mediaBuff, ';');
            int mediaEnd = stream.Position;

            MediaQuery media;
            var        mediaStr = mediaBuff.ToString().Trim();

            if (mediaStr.Length > 0)
            {
                media = MediaQueryParser.Parse(mediaStr, Position.Create(mediaStart, mediaEnd, Current.CurrentFilePath));
            }
            else
            {
                media = new MediaType(Media.all, Position.NoSite);
            }

            return(new Using(file.ToString(), media, start, stream.Position, Current.CurrentFilePath));
        }
Example #24
0
        public static Value Parse(string value, IPosition forPosition, bool allowSelectorIncludes = false)
        {
            var ret = new List <Value>();

            using (var stream = new ParserStream(new StringReader(value)))
            {
                while (stream.HasMore())
                {
                    var buffer = new StringBuilder();
                    stream.ScanUntilWithNesting(buffer, ',', requireFound: false);
                    using (var subStream = new ParserStream(new StringReader(buffer.ToString())))
                    {
                        ret.Add(ParseImpl(subStream, forPosition, allowSelectorIncludes));
                    }
                }
            }

            if (ret.Count == 1)
            {
                return(ret[0]);
            }

            return(new CommaDelimittedValue(ret));
        }
Example #25
0
        internal static List <SpriteRule> ParseSpriteRules(ParserStream stream)
        {
            var ret = new List <SpriteRule>();

            stream.AdvancePast("{");

            while (stream.HasMore() && stream.Peek() != '}')
            {
                var c = stream.Peek();

                if (char.IsWhiteSpace(c))
                {
                    stream.AdvancePastWhiteSpace();
                }
                else
                {
                    ret.Add(ParseSpriteRule(stream));
                }
            }

            stream.AdvancePast("}");

            return(ret);
        }
Example #26
0
        internal static InnerMediaProperty ParseInnerMediaDirective(ParserStream stream)
        {
            var start = stream.Position;

            var media = new StringBuilder();

            stream.ScanUntil(media, '{');

            var mediaStr = media.ToString().Trim();

            if (mediaStr.IsNullOrEmpty())
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected media list");
                throw new StoppedParsingException();
            }

            var mediaQuery = MediaQueryParser.Parse(mediaStr, Position.Create(start, stream.Position, Current.CurrentFilePath));

            var props = ParseCssRules(InvalidSelector.Singleton, stream);

            var blockEquiv = new SelectorAndBlock(InvalidSelector.Singleton, props, null, start, stream.Position, Current.CurrentFilePath);

            return(new InnerMediaProperty(mediaQuery, blockEquiv, start, stream.Position, Current.CurrentFilePath));
        }
Example #27
0
        internal static Block ParseDirective(ParserStream stream)
        {
            const string import       = "import";
            const string @using       = "using";
            const string sprite       = "sprite";
            const string charset      = "charset";
            const string media        = "media";
            const string keyframes    = "keyframes";
            const string mozKeyframes = "-moz-keyframes";
            const string webKeyframes = "-webkit-keyframes";
            const string fontFace     = "font-face";
            const string reset        = "reset";

            stream.Advance(); // Advance past @

            var bufferStart = stream.Position;
            var buffer      = new StringBuilder();
            var next        = stream.WhichNextInsensitive(buffer, @using, sprite, import, charset, media, keyframes, mozKeyframes, webKeyframes, fontFace, reset);

            if (next == @using)
            {
                return(ParseUsingDirective(stream));
            }

            if (next == sprite)
            {
                return(ParseSpriteDeclaration(stream));
            }

            if (next == import)
            {
                return(ParseImportDirective(stream));
            }

            if (next == charset)
            {
                return(ParseCharsetDirective(stream));
            }

            if (next == media)
            {
                return(ParseMediaDirective(stream));
            }

            if (next.In(keyframes, mozKeyframes, webKeyframes))
            {
                string prefix = "";
                if (next == mozKeyframes)
                {
                    prefix = "-moz-";
                }
                if (next == webKeyframes)
                {
                    prefix = "-webkit-";
                }

                return(ParseKeyFramesDirective(prefix, stream, bufferStart));
            }

            if (next == fontFace)
            {
                return(ParseFontFace(stream, bufferStart));
            }

            if (next == reset)
            {
                return(ParseResetDirective(stream, bufferStart));
            }

            stream.PushBack(buffer.ToString());

            var leader = new StringBuilder();

            var eqOrPara = stream.ScanUntil(leader, '=', '(');

            if (eqOrPara == '=')
            {
                return(ParseMoreVariable(leader.ToString(), stream, bufferStart));
            }

            return(ParseMixinDeclaration(leader.ToString(), stream));
        }
Example #28
0
        private static MathValue ParseMathValue(char op, Value lhs, ParserStream stream, IPosition forPosition)
        {
            stream.Advance(); // skip operator

            if (lhs == null)
            {
                Current.RecordError(ErrorType.Parser, forPosition, "Expected value, found '" + op + "'");
                throw new StoppedParsingException();
            }

            Operator @operator;
            switch (op)
            {
                case '+': @operator = Operator.Plus; break;
                case '-': @operator = Operator.Minus; break;
                case '*': @operator = Operator.Mult; break;
                case '/': @operator = Operator.Div; break;
                case '%': @operator = Operator.Mod; break;
                default: throw new InvalidOperationException("Unexpected operator [" + op + "]");
            }

            var rhs = ParseImpl(stream, forPosition, allowSelectorIncludes: false);

            return new MathValue(lhs, @operator, rhs);
        }
Example #29
0
        internal static KeyFramesBlock ParseKeyFramesDirective(string prefix, ParserStream stream, int start)
        {
            var buffer = new StringBuilder();

            stream.ScanUntil(buffer, '{');

            var name = buffer.ToString().Trim();

            if (name.Length == 0)
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected a name for the keyframe animation");

                throw new StoppedParsingException();
            }

            if ((name[0] != '-' && char.IsDigit(name[0])) ||
                (name[0] == '-' && name.Length > 1 && char.IsDigit(name[1])) ||
                name.Any(a => a != '-' && !char.IsLetterOrDigit(a)))
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Animation name `" + name + "` is not a valid identifier");
                throw new StoppedParsingException();
            }

            var frames = new List <KeyFrame>();
            var rules  = new List <VariableProperty>();

            while (stream.HasMore())
            {
                var c = stream.Peek();

                if (char.IsWhiteSpace(c))
                {
                    stream.AdvancePastWhiteSpace();
                    continue;
                }

                if (c == '}')
                {
                    break;
                }

                if (c == '@')
                {
                    var rule = ParseMixinOrVariableRule(stream);
                    if (!(rule is VariableProperty))
                    {
                        Current.RecordError(ErrorType.Parser, rule, "Expected variable declaration");
                        throw new StoppedParsingException();
                    }
                    rules.Add((VariableProperty)rule);

                    continue;
                }

                frames.Add(ParseKeyFrame(stream));
            }

            if (stream.Peek() != '}')
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected '}'");
                throw new StoppedParsingException();
            }

            stream.Advance(); // Skip }

            return(new KeyFramesBlock(prefix, name, frames, rules, start, stream.Position, Current.CurrentFilePath));
        }
Example #30
0
        private static Value ParseString(ParserStream stream, IPosition forPosition)
        {
            var c = stream.Peek();

            if (c == '!')
            {
                return ParseImportant(stream);
            }

            var buffer = new StringBuilder();
            while (stream.HasMore() && !char.IsWhiteSpace(stream.Peek()))
            {
                buffer.Append(stream.Read());

                if (buffer.Length == 3 && (stream.HasMore() && stream.Peek() == '('))
                {
                    var toDate = buffer.ToString();
                    if (toDate.Equals("rgb", StringComparison.InvariantCultureIgnoreCase) || toDate.Equals("hsl", StringComparison.InvariantCultureIgnoreCase))
                    {
                        var group = ParseGroup(stream, forPosition);
                        var @params = group.Value as CommaDelimittedValue;
                        if (@params == null || @params.Values.Count() != 3)
                        {
                            Current.RecordError(ErrorType.Parser, forPosition, "Expected 3 parameters to '" + toDate + "'");
                            throw new StoppedParsingException();
                        }

                        if(toDate == "rgb")
                        {
                            return
                                new RGBColorValue(
                                    @params.Values.ElementAt(0),
                                    @params.Values.ElementAt(1),
                                    @params.Values.ElementAt(2)
                                );
                        }

                        if (toDate == "hsl")
                        {
                            return
                                new HSLColorValue
                                (
                                    @params.Values.ElementAt(0),
                                    @params.Values.ElementAt(1),
                                    @params.Values.ElementAt(2)
                                );
                        }
                    }

                    if (toDate.Equals("url", StringComparison.InvariantCultureIgnoreCase))
                    {
                        var value = ParseGroup(stream, forPosition).Value;

                        if(value is StringValue || value is QuotedStringValue)
                        {
                            return new UrlValue(value);
                        }

                        Current.RecordError(ErrorType.Parser, forPosition, "Expected string or quoted string");
                        throw new StoppedParsingException();
                    }
                }

                if (buffer.Length == 4 && (stream.HasMore() && stream.Peek() == '('))
                {
                    var toDate = buffer.ToString();
                    if (toDate.Equals("rgba", StringComparison.InvariantCultureIgnoreCase))
                    {
                        var @params = ParseGroup(stream, forPosition).Value as CommaDelimittedValue;
                        if (@params == null || @params.Values.Count() != 4)
                        {
                            Current.RecordError(ErrorType.Parser, forPosition, "Expected 4 parameters to '" + toDate + "'");
                            throw new StoppedParsingException();
                        }

                        return
                            new RGBAColorValue(
                                @params.Values.ElementAt(0),
                                @params.Values.ElementAt(1),
                                @params.Values.ElementAt(2),
                                @params.Values.ElementAt(3)
                            );
                    }
                }

                if (buffer.Length == 5 && (stream.HasMore() && stream.Peek() == '('))
                {
                    var toDate = buffer.ToString();
                    if (toDate.Equals("local", StringComparison.InvariantCultureIgnoreCase))
                    {
                        var val = ParseGroup(stream, forPosition).Value;
                        var comma = val as CommaDelimittedValue;
                        if (comma != null)
                        {
                            Current.RecordError(ErrorType.Parser, forPosition, "Expected 1 parameter to local() value, found "+comma.Values.Count());
                            throw new StoppedParsingException();
                        }

                        return new LocalValue(val);
                    }
                }

                if (buffer.Length == 6 && (stream.HasMore() && stream.Peek() == '('))
                {
                    var toDate = buffer.ToString();
                    if (toDate.Equals("format", StringComparison.InvariantCultureIgnoreCase))
                    {
                        var val = ParseGroup(stream, forPosition).Value;
                        var comma = val as CommaDelimittedValue;
                        if (comma != null)
                        {
                            Current.RecordError(ErrorType.Parser, forPosition, "Expected 1 parameter to format() value, found " + comma.Values.Count());
                            throw new StoppedParsingException();
                        }

                        return new FormatValue(val);
                    }
                }
            }

            NamedColor color;
            if (Enum.TryParse<NamedColor>(buffer.ToString(), ignoreCase: true, result: out color))
            {
                return new NamedColorValue(color);
            }

            var str = buffer.ToString();

            if (str.Contains("!"))
            {
                var i = str.IndexOf('!');
                var left = str.Substring(0, i);
                var right = str.Substring(i);

                var ret = new List<Value>();
                var lhs = Parse(left, forPosition);
                var rhs = Parse(right, forPosition);

                if (lhs is CompoundValue)
                {
                    ret.AddRange(((CompoundValue)lhs).Values);
                }
                else
                {
                    ret.Add(lhs);
                }

                if (rhs is CompoundValue)
                {
                    ret.AddRange(((CompoundValue)rhs).Values);
                }
                else
                {
                    ret.Add(rhs);
                }

                return new CompoundValue(ret);
            }

            return new StringValue(buffer.ToString());
        }
Example #31
0
        internal static SpriteBlock ParseSpriteDeclaration(ParserStream stream)
        {
            var start = stream.Position;

            stream.AdvancePast("(");

            var ignored = new StringBuilder();
            var adTo = stream.ScanUntil(ignored, '"', '\'');

            if (ignored.ToString().Any(a => !char.IsWhiteSpace(a)))
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected quotation mark");
                throw new StoppedParsingException();
            }

            if (adTo == null)
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected quotation mark");
                throw new StoppedParsingException();
            }

            var nameStart = stream.Position;
            var name = new StringBuilder();
            name.Append(adTo.Value);
            stream.ScanUntil(name, adTo.Value);
            name.Append(adTo.Value);
            var nameStop = stream.Position;

            stream.AdvancePast(")");

            var rules = ParseSpriteRules(stream);

            return new SpriteBlock((QuotedStringValue)Value.Parse(name.ToString(), nameStart, nameStop, Current.CurrentFilePath), rules, start, stream.Position, Current.CurrentFilePath);
        }
Example #32
0
        internal static List<SpriteRule> ParseSpriteRules(ParserStream stream)
        {
            var ret = new List<SpriteRule>();

            stream.AdvancePast("{");

            while (stream.HasMore() && stream.Peek() != '}')
            {
                var c = stream.Peek();

                if (char.IsWhiteSpace(c))
                {
                    stream.AdvancePastWhiteSpace();
                }
                else
                {
                    ret.Add(ParseSpriteRule(stream));
                }
            }

            stream.AdvancePast("}");

            return ret;
        }
Example #33
0
        internal static MoreVariable ParseMoreVariable(string name, ParserStream stream, int start)
        {
            name = name.Trim();

            var valueStart = stream.Position;
            var valueStr = new StringBuilder();
            stream.ScanUntilWithNesting(valueStr, ';');

            var value = Value.Parse(valueStr.ToString().Trim(), valueStart, stream.Position, Current.CurrentFilePath, allowSelectorIncludes: true);

            if (name.ToLower().In(ReservedWords))
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "'" + name + "' cannot be a variable name.");
            }

            return new MoreVariable(name, value, start, stream.Position, Current.CurrentFilePath);
        }
Example #34
0
        internal static Value ParseMoreValue(ParserStream stream)
        {
            var start = stream.Position;
            var valueStr = new StringBuilder();
            stream.ScanUntilWithNesting(valueStr, ';');

            var value = valueStr.ToString();

            return Value.Parse(value, start, stream.Position, Current.CurrentFilePath);
        }
Example #35
0
        internal static FontFaceBlock ParseFontFace(ParserStream stream, int start)
        {
            var ignored = new StringBuilder();

            stream.ScanUntil(ignored, '{');

            var rules = ParseCssRules(InvalidSelector.Singleton, stream);

            return new FontFaceBlock(rules, start, stream.Position, Current.CurrentFilePath);
        }
Example #36
0
        internal static Value ParseFontLikeValue(ParserStream stream)
        {
            var start = stream.Position;
            var valueStr = new StringBuilder();
            stream.ScanUntilWithNesting(valueStr, ';');

            var value = valueStr.ToString();

            // The shorthand isn't in use, so we can handle this
            if(value.IndexOf('/') == -1)
            {
                return MoreValueParser.Parse(value, Position.Create(start, stream.Position, Current.CurrentFilePath));
            }

            return new StringValue(value);
        }
Example #37
0
        internal static MediaBlock ParseMediaDirective(ParserStream stream)
        {
            var start = stream.Position;

            var media = new StringBuilder();
            stream.ScanUntil(media, '{');

            var mediaStr = media.ToString().Trim();
            if (mediaStr.IsNullOrEmpty())
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected media list");
                throw new StoppedParsingException();
            }

            var mediaQuery = MediaQueryParser.Parse(mediaStr, Position.Create(start, stream.Position, Current.CurrentFilePath));

            var contained = new List<Block>();

            char c;
            while ((c = stream.Peek()) != '}')
            {
                if (char.IsWhiteSpace(c))
                {
                    stream.AdvancePastWhiteSpace();
                    continue;
                }

                // More directive (probably)
                if (c == '@')
                {
                    contained.Add(ParseDirective(stream));
                    continue;
                }

                // Selector + block time!
                contained.Add(ParseSelectorAndBlock(stream));
            }

            var notAllowed = contained.Where(x => !(x is SelectorAndBlock || x is MoreVariable));

            foreach (var illegal in notAllowed)
            {
                Current.RecordError(ErrorType.Parser, illegal, "@media can only contain blocks and variable declarations");
            }

            if (notAllowed.Count() != 0)
            {
                throw new StoppedParsingException();
            }

            // Skip past }
            stream.Advance();

            return new MediaBlock(mediaQuery, contained, start, stream.Position, Current.CurrentFilePath);
        }
Example #38
0
        internal static KeyFramesBlock ParseKeyFramesDirective(string prefix, ParserStream stream, int start)
        {
            var buffer = new StringBuilder();

            stream.ScanUntil(buffer, '{');

            var name = buffer.ToString().Trim();

            if (name.Length == 0)
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected a name for the keyframe animation");

                throw new StoppedParsingException();
            }

            if((name[0] != '-' && char.IsDigit(name[0])) ||
               (name[0] == '-' && name.Length > 1 && char.IsDigit(name[1])) ||
               name.Any(a => a != '-' && !char.IsLetterOrDigit(a)))
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Animation name `"+name+"` is not a valid identifier");
                throw new StoppedParsingException();
            }

            var frames = new List<KeyFrame>();
            var rules = new List<VariableProperty>();

            while (stream.HasMore())
            {
                var c = stream.Peek();

                if (char.IsWhiteSpace(c))
                {
                    stream.AdvancePastWhiteSpace();
                    continue;
                }

                if (c == '}')
                {
                    break;
                }

                if (c == '@')
                {
                    var rule = ParseMixinOrVariableRule(stream);
                    if (!(rule is VariableProperty))
                    {
                        Current.RecordError(ErrorType.Parser, rule, "Expected variable declaration");
                        throw new StoppedParsingException();
                    }
                    rules.Add((VariableProperty)rule);

                    continue;
                }

                frames.Add(ParseKeyFrame(stream));
            }

            if (stream.Peek() != '}')
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected '}'");
                throw new StoppedParsingException();
            }

            stream.Advance(); // Skip }

            return new KeyFramesBlock(prefix, name, frames, rules, start, stream.Position, Current.CurrentFilePath);
        }
Example #39
0
        internal static KeyFrame ParseKeyFrame(ParserStream stream)
        {
            var start = stream.Position;

            var buffer = new StringBuilder();
            stream.ScanUntil(buffer, '{');

            var percents = new List<decimal>();

            var percentsStr = buffer.ToString();
            foreach (var p in percentsStr.Split(',').Select(s => s.Trim()))
            {
                if (p.Length == 0)
                {
                    Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected `from`, `to`, or a percentage");
                    throw new StoppedParsingException();
                }

                decimal percent;
                if (p.Equals("from", StringComparison.InvariantCultureIgnoreCase))
                {
                    percent = 0;
                }
                else
                {
                    if (p.Equals("to", StringComparison.InvariantCultureIgnoreCase))
                    {
                        percent = 100;
                    }
                    else
                    {
                        if (!p.EndsWith("%") || !decimal.TryParse(p.Substring(0, p.Length -1 ), out percent))
                        {
                            Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected `from`, `to`, or a percentage.  Found `" + p + "`");
                            throw new StoppedParsingException();
                        }
                    }
                }

                percents.Add(percent);
            }

            var rules = ParseCssRules(InvalidSelector.Singleton, stream);

            return new KeyFrame(percents, rules, start, stream.Position, Current.CurrentFilePath);
        }
Example #40
0
        internal static Property ParseMixinOrVariableRule(ParserStream stream)
        {
            var start = stream.Position;

            var name = new StringBuilder();

            stream.Advance(); // Skip @

            bool trimmingWhiteSpace = false;

            while (stream.HasMore() && !stream.Peek().In('(', '='))
            {
                // Check for nested media block syntax
                if (name.ToString().Equals("media", StringComparison.InvariantCultureIgnoreCase))
                {
                    return(ParseInnerMediaDirective(stream));
                }

                var c = stream.Read();

                if (char.IsWhiteSpace(c))
                {
                    trimmingWhiteSpace = true;
                    continue;
                }

                if (trimmingWhiteSpace || (!char.IsLetterOrDigit(c) && !c.In('-', '_')))
                {
                    Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Unexpected character '" + c + "'");
                    throw new StoppedParsingException();
                }
                name.Append(c);
            }

            if (!stream.Peek().In('(', '='))
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected '(' or '='");
                throw new StoppedParsingException();
            }

            if (stream.Peek() == '=')
            {
                stream.Advance();

                var localValue = ParseMoreValue(stream);
                var varName    = name.ToString().Trim();

                if (varName.ToLower().In(ReservedWords))
                {
                    Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "'" + varName + "' cannot be a variable name.");
                }

                return(new VariableProperty(varName, localValue, start, stream.Position, Current.CurrentFilePath));
            }

            stream.Advance();

            var startParams = stream.Position;

            var paramStart = stream.Position;
            var @params    = new StringBuilder();

            stream.ScanUntilWithNesting(@params, ')');
            var paramStop = stream.Position;

            var options      = new StringBuilder();
            var optionsStart = stream.Position;

            stream.ScanUntil(options, ';');

            var nameStr    = name.ToString().Trim();
            var paramsStr  = @params.ToString().Trim();
            var optionsStr = options.ToString().Trim();

            var optional  = optionsStr.Contains('?');
            var overrides = optionsStr.Contains('!');

            var unexpected = optionsStr.Where(c => !char.IsWhiteSpace(c) && c != '?' && c != '!');

            if (unexpected.Count() != 0)
            {
                if (unexpected.Count() == 0)
                {
                    Current.RecordError(ErrorType.Parser, Position.Create(start, optionsStart + options.Length, Current.CurrentFilePath), "Unexpected character '" + unexpected.ElementAt(0) + "'");
                }
                else
                {
                    Current.RecordError(
                        ErrorType.Parser,
                        Position.Create(
                            start,
                            optionsStart + options.Length,
                            Current.CurrentFilePath
                            ),
                        "Unexpected characters " +
                        string.Join(", ", unexpected.Select(c => "'" + c + "'"))
                        );
                }

                throw new StoppedParsingException();
            }

            if (name.Length == 0)
            {
                if (optional)
                {
                    Current.RecordWarning(ErrorType.Parser, Position.Create(start, optionsStart + options.Length, Current.CurrentFilePath), "Include directives are always optional, no trailing '?' is needed.");
                }
                return(new IncludeSelectorProperty(Selector.Parse(paramsStr, paramStart, paramStop, Current.CurrentFilePath), overrides, start, stream.Position, Current.CurrentFilePath));
            }

            if (name.ToString().Trim().Equals("reset", StringComparison.InvariantCultureIgnoreCase))
            {
                if (paramsStr.Trim().Length != 0)
                {
                    return(new ResetProperty(Selector.Parse(paramsStr, paramStart, paramStop, Current.CurrentFilePath), start, stream.Position, Current.CurrentFilePath));
                }

                return(new ResetSelfProperty(InvalidSelector.Singleton, start, stream.Position, Current.CurrentFilePath));
            }

            return(new MixinApplicationProperty(nameStr, ParseApplicationParameters(paramsStr, startParams), optional: optional, overrides: overrides, start: start, stop: stream.Position, filePath: Current.CurrentFilePath));
        }
Example #41
0
        internal static InnerMediaProperty ParseInnerMediaDirective(ParserStream stream)
        {
            var start = stream.Position;

            var media = new StringBuilder();
            stream.ScanUntil(media, '{');

            var mediaStr = media.ToString().Trim();
            if (mediaStr.IsNullOrEmpty())
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected media list");
                throw new StoppedParsingException();
            }

            var mediaQuery = MediaQueryParser.Parse(mediaStr, Position.Create(start, stream.Position, Current.CurrentFilePath));

            var props = ParseCssRules(InvalidSelector.Singleton, stream);

            var blockEquiv = new SelectorAndBlock(InvalidSelector.Singleton, props, null, start, stream.Position, Current.CurrentFilePath);

            return new InnerMediaProperty(mediaQuery, blockEquiv, start, stream.Position, Current.CurrentFilePath);
        }
Example #42
0
        internal static ResetBlock ParseResetDirective(ParserStream stream, int start)
        {
            var ignored = new StringBuilder();
            stream.ScanUntil(ignored, '{');

            var contained = new List<Block>();

            char c;
            while ((c = stream.Peek()) != '}')
            {
                if (char.IsWhiteSpace(c))
                {
                    stream.AdvancePastWhiteSpace();
                    continue;
                }

                // More directive (probably)
                if (c == '@')
                {
                    contained.Add(ParseDirective(stream));
                    continue;
                }

                // Selector + block time!
                contained.Add(ParseSelectorAndBlock(stream));
            }

            var notAllowed = contained.Where(x => !(x is SelectorAndBlock || x is MoreVariable));

            foreach (var illegal in notAllowed)
            {
                Current.RecordError(ErrorType.Parser, illegal, "@reset can only contain blocks and variable declarations");
            }

            if (notAllowed.Count() != 0)
            {
                throw new StoppedParsingException();
            }

            // The whole @reset{} block disappears pretty quickly, but the actual
            //   blocks need to know what they were "near" for variable resolution.
            var variables = contained.OfType<MoreVariable>();
            var bound = new List<Block>();
            foreach (var x in contained)
            {
                var asBlock = x as SelectorAndBlock;
                if (asBlock != null)
                {
                    bound.Add(asBlock.InReset(variables));
                }
                else
                {
                    bound.Add(x);
                }
            }

            // Skip past }
            stream.Advance();

            return new ResetBlock(bound, start, stream.Position, Current.CurrentFilePath);
        }
Example #43
0
        internal static SpriteRule ParseSpriteRule(ParserStream stream)
        {
            var start = stream.Position;

            if (stream.Peek() != '@')
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected '@'");
                throw new StoppedParsingException();
            }

            stream.Advance(); // Advance past @

            var name = new StringBuilder();

            while (stream.HasMore() && stream.Peek() != '=')
            {
                name.Append(stream.Read());
            }

            stream.AdvancePast("="); // Advance past =

            var ignored = new StringBuilder();
            var quote = stream.ScanUntil(ignored, '\'', '"');

            if (quote == null)
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected quotation mark");
                throw new StoppedParsingException();
            }

            var valueStart = stream.Position;
            var valueStr = new StringBuilder();
            valueStr.Append(quote.Value);
            stream.ScanUntil(valueStr, quote.Value);
            valueStr.Append(quote.Value);

            stream.AdvancePast(";");

            var value = (QuotedStringValue)Value.Parse(valueStr.ToString(), valueStart, stream.Position, Current.CurrentFilePath);

            return new SpriteRule(name.ToString().Trim(), value, start, stream.Position, Current.CurrentFilePath);
        }
Example #44
0
        internal static Property ParseRule(ParserStream stream)
        {
            var start = stream.Position;

            if (stream.Peek() == '@')
            {
                return ParseMixinOrVariableRule(stream);
            }

            var ruleName = new StringBuilder();
            var found = stream.ScanUntil(ruleName, ';', '{', '}');

            // Final semi-colon in a block is optional, so inject a ';' if we encounter a '}' in this case
            if (found == '}')
            {
                found = ';';
                // trailing semi-colon may be optional
                if (ruleName[ruleName.Length - 1] == ';')
                {
                    ruleName = ruleName.Remove(ruleName.Length - 1, 1);
                }
                stream.PushBack(new[] { '}' });
            }

            if (found == '{')
            {
                var nestedBlock = ParseSelectorAndBlock(stream, ruleName.ToString().Trim());

                return new NestedBlockProperty(nestedBlock, start, stream.Position);
            }

            if (found == null)
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected ';', '{', or '}'");
                throw new StoppedParsingException();
            }

            var colon = ruleName.ToString().IndexOf(':');

            if (colon == -1)
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected ':'");
                throw new StoppedParsingException();
            }

            var name = ruleName.ToString().Substring(0, colon).Trim();
            var valStr = ruleName.ToString().Substring(colon + 1).Trim() + ";";

            if (valStr == ";")
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected value");
                throw new StoppedParsingException();
            }

            stream.PushBack(valStr);

            Value value;

            if (name.Equals("font", StringComparison.InvariantCultureIgnoreCase))
            {
                // font has a goofy shorthand, needs special treatment
                value = ParseFontLikeValue(stream);
            }
            else
            {
                if (name.Equals("background-position"))
                {
                    // likewise, background-position can often look like a math statement
                    value = ParseBackgroundPositionValue(stream);
                }
                else
                {
                    if (name.Equals("border-radius"))
                    {
                        value = ParseFontLikeValue(stream);
                    }
                    else
                    {
                        value = ParseMoreValue(stream);
                    }
                }
            }

            return new NameValueProperty(name, value, start, stream.Position, Current.CurrentFilePath);
        }
Example #45
0
        internal static SelectorAndBlock ParseSelectorAndBlock(ParserStream stream, string selectorStr = null)
        {
            var mark = stream.Position;

            int selectorStop, selectorStart;

            if (selectorStr.IsNullOrEmpty())
            {
                selectorStart = stream.Position;
                var selector = new StringBuilder();
                stream.ScanUntil(selector, '{');
                selectorStop = stream.Position;

                selectorStr = selector.ToString().Trim();

                if (selectorStr.IsNullOrEmpty())
                {
                    Current.RecordError(ErrorType.Parser, Position.Create(mark, stream.Position, Current.CurrentFilePath), "Expected selector");
                    throw new StoppedParsingException();
                }
            }
            else
            {
                selectorStop = mark;
                selectorStart= selectorStop -  selectorStr.Length;
            }

            var sel = Selector.Parse(selectorStr, selectorStart, selectorStop, Current.CurrentFilePath);
            var cssRules = ParseCssRules(sel, stream);

            // Bind @reset() properties to their location w.r.t. selectors
            cssRules =
                cssRules.Select(
                    delegate(Property prop)
                    {
                        if (!(prop is ResetSelfProperty)) return prop;

                        return ((ResetSelfProperty)prop).BindToSelector(sel);
                    }
                ).ToList();

            return new SelectorAndBlock(sel, cssRules, null, mark, stream.Position, Current.CurrentFilePath);
        }
Example #46
0
        internal static Property ParseRule(ParserStream stream)
        {
            var start = stream.Position;

            if (stream.Peek() == '@')
            {
                return(ParseMixinOrVariableRule(stream));
            }

            var ruleName = new StringBuilder();
            var found    = stream.ScanUntil(ruleName, ';', '{', '}');

            // Final semi-colon in a block is optional, so inject a ';' if we encounter a '}' in this case
            if (found == '}')
            {
                found = ';';
                // trailing semi-colon may be optional
                if (ruleName[ruleName.Length - 1] == ';')
                {
                    ruleName = ruleName.Remove(ruleName.Length - 1, 1);
                }
                stream.PushBack(new[] { '}' });
            }

            if (found == '{')
            {
                var nestedBlock = ParseSelectorAndBlock(stream, ruleName.ToString().Trim());

                return(new NestedBlockProperty(nestedBlock, start, stream.Position));
            }

            if (found == null)
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected ';', '{', or '}'");
                throw new StoppedParsingException();
            }

            var colon = ruleName.ToString().IndexOf(':');

            if (colon == -1)
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected ':'");
                throw new StoppedParsingException();
            }

            var name   = ruleName.ToString().Substring(0, colon).Trim();
            var valStr = ruleName.ToString().Substring(colon + 1).Trim() + ";";

            if (valStr == ";")
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected value");
                throw new StoppedParsingException();
            }

            stream.PushBack(valStr);

            Value value;

            if (name.Equals("font", StringComparison.InvariantCultureIgnoreCase))
            {
                // font has a goofy shorthand, needs special treatment
                value = ParseFontLikeValue(stream);
            }
            else
            {
                if (name.Equals("background-position"))
                {
                    // likewise, background-position can often look like a math statement
                    value = ParseBackgroundPositionValue(stream);
                }
                else
                {
                    if (name.Equals("border-radius"))
                    {
                        value = ParseFontLikeValue(stream);
                    }
                    else
                    {
                        value = ParseMoreValue(stream);
                    }
                }
            }

            return(new NameValueProperty(name, value, start, stream.Position, Current.CurrentFilePath));
        }
Example #47
0
        internal static Value ParseFuncValue(ParserStream stream, IPosition forPosition, bool allowSelectorIncludes)
        {
            stream.Advance(); // skip @
            var buffer = new StringBuilder();

            var c = stream.Peek();

            while (char.IsWhiteSpace(c))
            {
                stream.Advance();
                c = stream.Peek();
            }

            if (!char.IsLetter(c) && c != '(')
            {
                Current.RecordError(ErrorType.Parser, forPosition, "Expected letter or '(', found '" + c + "'");
                throw new StoppedParsingException();
            }

            if (c != '(')
            {
                buffer.Append(stream.Read());

                while (stream.HasMore() && char.IsLetterOrDigit(stream.Peek()))
                {
                    buffer.Append(stream.Read());
                }
            }

            var funcName = buffer.ToString();
            buffer.Clear();

            while (stream.HasMore() && char.IsWhiteSpace(stream.Peek()))
            {
                buffer.Append(stream.Read());
            }

            if (stream.HasMore() && stream.Peek() == '(')
            {
                stream.Read(); // Skip (

                buffer.Clear();
                stream.ScanUntilWithNesting(buffer, ')');

                if (funcName.Length == 0)
                {
                    if (allowSelectorIncludes)
                    {
                        var sel = Selector.Parse(buffer.ToString(), forPosition.Start, forPosition.Stop, forPosition.FilePath);
                        return new IncludeSelectorValue(sel);
                    }

                    return new StringValue("@(" + buffer + ")");
                }

                var value = MoreValueParser.Parse(buffer.ToString().Trim(), forPosition, allowSelectorIncludes: true);
                if (value is NotFoundValue) throw new StoppedParsingException();

                var @params = new List<Value>();
                if (value is CommaDelimittedValue)
                {
                    @params.AddRange(((CommaDelimittedValue)value).Values);
                }
                else
                {
                    @params.Add(value);
                }

                return new FuncAppliationValue(funcName, @params);
            }

            return new FuncValue(funcName);
        }
Example #48
0
        internal static Block ParseDirective(ParserStream stream)
        {
            const string import = "import";
            const string @using = "using";
            const string sprite = "sprite";
            const string charset = "charset";
            const string media = "media";
            const string keyframes = "keyframes";
            const string mozKeyframes = "-moz-keyframes";
            const string webKeyframes = "-webkit-keyframes";
            const string fontFace = "font-face";
            const string reset = "reset";

            stream.Advance(); // Advance past @

            var bufferStart = stream.Position;
            var buffer = new StringBuilder();
            var next = stream.WhichNextInsensitive(buffer, @using, sprite, import, charset, media, keyframes, mozKeyframes, webKeyframes, fontFace, reset);

            if (next == @using)
            {
                return ParseUsingDirective(stream);
            }

            if (next == sprite)
            {
                return ParseSpriteDeclaration(stream);
            }

            if (next == import)
            {
                return ParseImportDirective(stream);
            }

            if (next == charset)
            {
                return ParseCharsetDirective(stream);
            }

            if (next == media)
            {
                return ParseMediaDirective(stream);
            }

            if (next.In(keyframes, mozKeyframes, webKeyframes))
            {
                string prefix = "";
                if (next == mozKeyframes) prefix = "-moz-";
                if (next == webKeyframes) prefix = "-webkit-";

                return ParseKeyFramesDirective(prefix, stream, bufferStart);
            }

            if (next == fontFace)
            {
                return ParseFontFace(stream, bufferStart);
            }

            if (next == reset)
            {
                return ParseResetDirective(stream, bufferStart);
            }

            stream.PushBack(buffer.ToString());

            var leader = new StringBuilder();

            var eqOrPara = stream.ScanUntil(leader, '=', '(');

            if (eqOrPara == '=')
            {
                return ParseMoreVariable(leader.ToString(), stream, bufferStart);
            }

            return ParseMixinDeclaration(leader.ToString(), stream);
        }
Example #49
0
        private static QuotedStringValue ParseQuotedString(char quote, ParserStream stream, IPosition forPosition)
        {
            stream.Advance(); // skip the quote
            var buffer = new StringBuilder();

            var x = stream.ScanUntil(buffer, quote);

            if (x == null)
            {
                Current.RecordError(ErrorType.Parser, forPosition, "Expected '" + quote + "'");
                throw new StoppedParsingException();
            }

            return new QuotedStringValue(buffer.ToString());
        }
Example #50
0
        internal static List<Property> ParseCssRules(Selector selector, ParserStream stream)
        {
            var ret = new List<Property>();

            while (stream.HasMore() && stream.Peek() != '}')
            {
                var c = stream.Peek();

                if (char.IsWhiteSpace(c))
                {
                    stream.AdvancePastWhiteSpace();
                }
                else
                {
                    ret.Add(ParseRule(stream));
                }
            }

            stream.AdvancePast("}");
            return ret;
        }
Example #51
0
        private static Value ParseNumber(ParserStream stream, IPosition forPosition)
        {
            var pushbackBuffer = new StringBuilder();

            var buffer = new StringBuilder();

            bool negate = false;

            if (stream.Peek() == '-')
            {
                negate = true;
                stream.Advance();
                pushbackBuffer.Append('-');
            }

            bool decimalPassed = false;

            while (stream.HasMore() && (char.IsDigit(stream.Peek()) || (stream.Peek() == '.' && !decimalPassed)))
            {
                var c = stream.Read();
                buffer.Append(c);
                pushbackBuffer.Append(c);

                if (c == '.') decimalPassed = true;
            }

            Unit? unit = null;
            decimal digit;
            if (!decimal.TryParse(buffer.ToString(), out digit))
            {
                // Looked like a number, but wasn't!
                stream.PushBack(pushbackBuffer.ToString());
                return ParseString(stream, forPosition);
            }

            if (negate) digit *= -1m;

            buffer.Clear();
            while (stream.HasMore() && char.IsWhiteSpace(stream.Peek()))
            {
                buffer.Append(stream.Read());
            }

            var nextFour = new StringBuilder();
            for (int i = 0; i < 4; i++)
            {
                if (stream.HasMore())
                {
                    nextFour.Append(stream.Read());
                }
            }

            var possibleUnit = nextFour.ToString();
            List<char> pushBack;

            unit = ParsePossibleUnit(possibleUnit, out pushBack);

            if (unit == null)
            {
                stream.PushBack(nextFour.ToString());
                stream.PushBack(buffer.ToString());
            }
            else
            {
                stream.PushBack(pushBack);
                return new NumberWithUnitValue(digit, unit.Value);
            }

            return new NumberValue(digit);
        }
Example #52
0
        internal static CssCharset ParseCharsetDirective(ParserStream stream)
        {
            var start = stream.Position;

            var ignored = new StringBuilder();
            var quote = stream.ScanUntil(ignored, '"', '\'');

            if (quote == null)
            {
                Current.RecordError(ErrorType.Parser, Model.Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected quotation mark");
                throw new StoppedParsingException();
            }

            var isoName = new StringBuilder();
            stream.ScanUntil(isoName, quote.Value);

            stream.AdvancePast(";");

            var isoNameStr = isoName.ToString();

            if (!CssCharset.KnownCharset(isoNameStr))
            {
                Current.RecordWarning(ErrorType.Parser, Model.Position.Create(start, stream.Position, Current.CurrentFilePath), "Unrecognized charset");
            }

            return new CssCharset(new QuotedStringValue(isoNameStr), start, stream.Position);
        }
Example #53
0
        internal static ResetBlock ParseResetDirective(ParserStream stream, int start)
        {
            var ignored = new StringBuilder();

            stream.ScanUntil(ignored, '{');

            var contained = new List <Block>();

            char c;

            while ((c = stream.Peek()) != '}')
            {
                if (char.IsWhiteSpace(c))
                {
                    stream.AdvancePastWhiteSpace();
                    continue;
                }

                // More directive (probably)
                if (c == '@')
                {
                    contained.Add(ParseDirective(stream));
                    continue;
                }

                // Selector + block time!
                contained.Add(ParseSelectorAndBlock(stream));
            }

            var notAllowed = contained.Where(x => !(x is SelectorAndBlock || x is MoreVariable));

            foreach (var illegal in notAllowed)
            {
                Current.RecordError(ErrorType.Parser, illegal, "@reset can only contain blocks and variable declarations");
            }

            if (notAllowed.Count() != 0)
            {
                throw new StoppedParsingException();
            }

            // The whole @reset{} block disappears pretty quickly, but the actual
            //   blocks need to know what they were "near" for variable resolution.
            var variables = contained.OfType <MoreVariable>();
            var bound     = new List <Block>();

            foreach (var x in contained)
            {
                var asBlock = x as SelectorAndBlock;
                if (asBlock != null)
                {
                    bound.Add(asBlock.InReset(variables));
                }
                else
                {
                    bound.Add(x);
                }
            }

            // Skip past }
            stream.Advance();

            return(new ResetBlock(bound, start, stream.Position, Current.CurrentFilePath));
        }
Example #54
0
        // Basically, it's realy common for background-position to be something like "0px -20px", which shouldn't be interpretted as a math value
        //   so detect the case where two number with unit values are separated by whitespace and treat them as a compound value instead
        //   otherwise, fallback to normal parsing
        internal static Value ParseBackgroundPositionValue(ParserStream stream)
        {
            var start = stream.Position;
            var valueStr = new StringBuilder();
            stream.ScanUntilWithNesting(valueStr, ';');

            var value = valueStr.ToString();

            var parts = value.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

            var pos = Position.Create(start, stream.Position, Current.CurrentFilePath);

            // something's up
            if (parts.Length != 2)
            {
                return MoreValueParser.Parse(value, pos);
            }

            var p1 = parts[0];
            var p2 = parts[1];

            var v1 = MoreValueParser.Parse(p1, pos);
            var v2 = MoreValueParser.Parse(p2, pos);

            if (v1 is NumberWithUnitValue && v2 is NumberWithUnitValue)
            {
                return new CompoundValue(v1, v2);
            }

            return MoreValueParser.Parse(value, pos);
        }
Example #55
0
        internal static Value ParseImpl(ParserStream stream, IPosition forPosition, bool allowSelectorIncludes)
        {
            Value ret = null;

            while (stream.HasMore())
            {
                var c = stream.Peek();

                if (char.IsWhiteSpace(c))
                {
                    stream.AdvancePastWhiteSpace();
                    continue;
                }

                if (ret != null)
                {
                    if (c.In('+', '-', '*', '/', '%'))
                    {
                        ret = ParseMathValue(c, ret, stream, forPosition);
                        continue;
                    }

                    if (c == '?')
                    {
                        stream.Advance(); // skip ?
                        if (stream.HasMore() && stream.Peek() == '?')
                        {
                            if (ret == null)
                            {
                                Current.RecordError(ErrorType.Parser, forPosition, "Expected value, found '??'");
                                throw new StoppedParsingException();
                            }

                            stream.Advance(); // skip second ?
                            var rhs = ParseImpl(stream, forPosition, allowSelectorIncludes);
                            ret = new MathValue(ret, Operator.Take_Exists, rhs);
                            continue;
                        }

                        ret = new LeftExistsValue(ret);
                        continue;
                    }
                }

                if (char.IsDigit(c) || c == '.' || c == '-')
                {
                    ret = Combine(ret, ParseNumber(stream, forPosition));
                    continue;
                }

                if (c == '(')
                {
                    ret = Combine(ret, ParseGroup(stream, forPosition));
                    continue;
                }

                if (c.In('\'', '"'))
                {
                    ret = Combine(ret, ParseQuotedString(c, stream, forPosition));
                    continue;
                }

                if (c == '#')
                {
                    ret = Combine(ret, ParseHashColor(stream, forPosition));
                    continue;
                }

                if (c == '@')
                {
                    ret = Combine(ret, ParseFuncValue(stream, forPosition, allowSelectorIncludes));
                    continue;
                }

                ret = Combine(ret, ParseString(stream, forPosition));
            }

            return(ret);
        }
Example #56
0
        internal static Using ParseUsingDirective(ParserStream stream)
        {
            var start = stream.Position;

            var ignored = new StringBuilder();
            var quote = stream.ScanUntil(ignored, '"', '\'');

            if (quote == null)
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected quotation mark");
                throw new StoppedParsingException();
            }

            var file = new StringBuilder();
            stream.ScanUntil(file, quote.Value);

            int mediaStart = stream.Position;
            var mediaBuff = new StringBuilder();
            stream.ScanUntil(mediaBuff, ';');
            int mediaEnd = stream.Position;

            MediaQuery media;
            var mediaStr = mediaBuff.ToString().Trim();
            if (mediaStr.Length > 0)
            {
                media = MediaQueryParser.Parse(mediaStr, Position.Create(mediaStart, mediaEnd, Current.CurrentFilePath));
            }
            else
            {
                media = new MediaType(Media.all, Position.NoSite);
            }

            return new Using(file.ToString(), media, start, stream.Position, Current.CurrentFilePath);
        }
Example #57
0
        internal static MixinBlock ParseMixinDeclaration(string name, ParserStream stream)
        {
            var start = stream.Position;
            var @params = new StringBuilder();
            stream.ScanUntil(@params, ')');

            name = name.Trim();

            if (name.Length == 0)
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected mixin name");
                throw new StoppedParsingException();
            }

            if (name.ToLower().In(ReservedWords))
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "'" + name + "' cannot be the name of a mixin.");
            }

            stream.AdvancePast("{");

            return
                new MixinBlock(
                    name,
                    ParseMixinDeclarationParameter(@params.ToString(), start),
                    ParseCssRules(InvalidSelector.Singleton, stream),
                    start,
                    stream.Position,
                    Current.CurrentFilePath
                );
        }
Example #58
0
        internal static Import ParseImportDirective(ParserStream stream)
        {
            var start = stream.Position;

            var buffer = new StringBuilder();
            stream.ScanUntilWithNesting(buffer, ';');

            var toParse = buffer.ToString().Trim();

            Value val;
            MediaQuery media;
            string mediaStr;

            if (Regex.IsMatch(toParse, @"url\s*?\(", RegexOptions.IgnoreCase))
            {
                var i = toParse.IndexOf(')');

                if (i == -1)
                {
                    Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected ')'"); ;
                    throw new StoppedParsingException();
                }

                val = Value.Parse(toParse.Substring(0, i + 1), start, start + i + 1, Current.CurrentFilePath);

                mediaStr = toParse.Substring(i + 1);
            }
            else
            {
                if (!(toParse.StartsWith("\"") || toParse.StartsWith("'")))
                {
                    Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected quote");
                    throw new StoppedParsingException();
                }

                var i = toParse.LastIndexOf(toParse[0]);

                if (i == -1)
                {
                    Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected '" + toParse[0] + "'");
                    throw new StoppedParsingException();
                }

                val = Value.Parse(toParse.Substring(0, i + 1), start, start + i + 1, Current.CurrentFilePath);
                mediaStr = toParse.Substring(i + 1);
            }

            mediaStr = mediaStr.Trim();
            if (mediaStr.Length > 0)
            {
                media = MediaQueryParser.Parse(mediaStr, Position.Create(start, stream.Position, Current.CurrentFilePath));
            }
            else
            {
                media = new MediaType(Media.all, Position.Create(start, stream.Position, Current.CurrentFilePath));
            }

            return new Import(val, media, start, stream.Position, Current.CurrentFilePath);
        }
Example #59
0
        public void StreamPushBack()
        {
            using (var stream = new ParserStream(new StringReader("")))
            {
                var buffer = new StringBuilder();

                stream.PushBack("hello world!");
                stream.ScanUntil(buffer, '!');
                Assert.AreEqual("hello world", buffer.ToString());
                Assert.IsFalse(stream.HasMore());

                buffer.Clear();

                stream.PushBack("world!");
                stream.PushBack("hello ");
                stream.ScanUntil(buffer, '!');
                Assert.AreEqual("hello world", buffer.ToString());
                Assert.IsFalse(stream.HasMore());
            }
        }
Example #60
0
        internal static Property ParseMixinOrVariableRule(ParserStream stream)
        {
            var start = stream.Position;

            var name = new StringBuilder();
            stream.Advance(); // Skip @

            bool trimmingWhiteSpace = false;

            while (stream.HasMore() && !stream.Peek().In('(', '='))
            {
                // Check for nested media block syntax
                if (name.ToString().Equals("media", StringComparison.InvariantCultureIgnoreCase))
                {
                    return ParseInnerMediaDirective(stream);
                }

                var c = stream.Read();

                if (char.IsWhiteSpace(c))
                {
                    trimmingWhiteSpace = true;
                    continue;
                }

                if (trimmingWhiteSpace || (!char.IsLetterOrDigit(c) && !c.In('-', '_')))
                {
                    Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Unexpected character '" + c + "'");
                    throw new StoppedParsingException();
                }
                name.Append(c);
            }

            if (!stream.Peek().In('(', '='))
            {
                Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected '(' or '='");
                throw new StoppedParsingException();
            }

            if (stream.Peek() == '=')
            {
                stream.Advance();

                var localValue = ParseMoreValue(stream);
                var varName = name.ToString().Trim();

                if (varName.ToLower().In(ReservedWords))
                {
                    Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "'" + varName + "' cannot be a variable name.");
                }

                return new VariableProperty(varName, localValue, start, stream.Position, Current.CurrentFilePath);
            }

            stream.Advance();

            var startParams = stream.Position;

            var paramStart = stream.Position;
            var @params = new StringBuilder();
            stream.ScanUntilWithNesting(@params, ')');
            var paramStop = stream.Position;

            var options = new StringBuilder();
            var optionsStart = stream.Position;
            stream.ScanUntil(options, ';');

            var nameStr = name.ToString().Trim();
            var paramsStr = @params.ToString().Trim();
            var optionsStr = options.ToString().Trim();

            var optional = optionsStr.Contains('?');
            var overrides = optionsStr.Contains('!');

            var unexpected = optionsStr.Where(c => !char.IsWhiteSpace(c) && c != '?' && c != '!');

            if (unexpected.Count() != 0)
            {
                if (unexpected.Count() == 0)
                {
                    Current.RecordError(ErrorType.Parser, Position.Create(start, optionsStart + options.Length, Current.CurrentFilePath), "Unexpected character '" + unexpected.ElementAt(0) + "'");
                }
                else
                {
                    Current.RecordError(
                        ErrorType.Parser,
                        Position.Create(
                            start,
                            optionsStart + options.Length,
                            Current.CurrentFilePath
                        ),
                        "Unexpected characters "+
                            string.Join(", ", unexpected.Select(c => "'"+c+"'"))
                    );
                }

                throw new StoppedParsingException();
            }

            if (name.Length == 0)
            {
                if (optional)
                {
                    Current.RecordWarning(ErrorType.Parser, Position.Create(start, optionsStart + options.Length, Current.CurrentFilePath), "Include directives are always optional, no trailing '?' is needed.");
                }
                return new IncludeSelectorProperty(Selector.Parse(paramsStr, paramStart, paramStop, Current.CurrentFilePath), overrides, start, stream.Position, Current.CurrentFilePath);
            }

            if (name.ToString().Trim().Equals("reset", StringComparison.InvariantCultureIgnoreCase))
            {
                if (paramsStr.Trim().Length != 0)
                {
                    return new ResetProperty(Selector.Parse(paramsStr, paramStart, paramStop, Current.CurrentFilePath), start, stream.Position, Current.CurrentFilePath);
                }

                return new ResetSelfProperty(InvalidSelector.Singleton, start, stream.Position, Current.CurrentFilePath);
            }

            return new MixinApplicationProperty(nameStr, ParseApplicationParameters(paramsStr, startParams), optional: optional, overrides: overrides, start: start, stop: stream.Position, filePath: Current.CurrentFilePath);
        }