ScanUntil() public method

Advances the stream until one of needles is found, placing everything *before* needle into buffer. Advances past the needle as well. If none of the needles are found, an error is encountered. needles appear immediately after \ (the escape char) will not be considered matching.
public ScanUntil ( StringBuilder buffer ) : char?
buffer StringBuilder
return char?
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 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 #3
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 #4
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 #5
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 #6
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 #7
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 #8
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 #9
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 #10
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 #11
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 #12
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 #13
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 #14
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 #15
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 #16
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 #17
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 #18
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 #19
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 #20
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 #21
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 #22
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 #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
        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 #25
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 #26
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 #27
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 #28
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 #29
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 #30
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 #31
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 #32
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 #33
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);
        }