Advance() public method

Advance the stream by one character.
public Advance ( ) : void
return void
示例#1
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));
        }
示例#2
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));
        }
示例#3
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));
        }
示例#4
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()));
        }
示例#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));
        }
示例#6
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()));
        }
示例#7
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);
        }
示例#8
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);
        }
示例#9
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));
        }
示例#10
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);
        }
示例#11
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());
        }
示例#12
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);
        }
示例#13
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);
        }
示例#14
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));
        }
示例#15
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));
        }
示例#16
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);
        }
示例#17
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);
        }
示例#18
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;
        }
示例#19
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);
        }
示例#20
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);
        }
示例#21
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));
        }
示例#22
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);
        }
示例#23
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));
        }
示例#24
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());
        }
示例#25
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));
        }
示例#26
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);
        }