示例#1
0
        static CssValue Consume_MediaFeature_Value(DataConsumer <CssToken> Stream)
        {
            if (Stream is null)
            {
                throw new CssParserException(CssErrors.STREAM_IS_NULL);
            }
            /* Consume: <number> | <dimension> | <ident> | <ratio> */
            Consume_All_Whitespace(Stream);

            switch (Stream.Next.Type)
            {
            case ECssTokenType.Number:
            {
                var numTok = Stream.Consume() as NumberToken;
                /* This could be a ratio - so check if it is */
                /* A ratio is a <number> <?whitespace> / <?whitespace> <number> */
                if (ParserCommon.Starts_Ratio_Value(Stream.AsSpan()))
                {
                    Consume_All_Whitespace(Stream);
                    DelimToken dtok = Stream.Consume() as DelimToken;
                    Consume_All_Whitespace(Stream);
                    NumberToken numTok2 = Stream.Consume() as NumberToken;

                    double ratioValue = ((double)numTok.Number / (double)numTok2.Number);
                    return(new CssValue(ECssValueTypes.RATIO, ratioValue));
                }

                /* Nope, its just a number */
                return(new CssValue(ECssValueTypes.NUMBER, numTok.Number));
            }

            case ECssTokenType.Dimension:
            case ECssTokenType.Ident:
            {
                return(Consume_CssValue(Stream));
            }
            }
            // throw new CssSyntaxErrorException($"Expected Number/Dimension/Keyword token but got: \"{Enum.GetName(typeof(ECssTokenType), Stream.Next.Type)}\"");
            throw new CssSyntaxErrorException(CssErrors.UNEXPECTED_TOKEN, Stream);
        }
示例#2
0
        /// <summary>
        /// Consumes a new <see cref="MediaCondition"/> or <see cref="MediaFeature"/>
        /// </summary>
        /// <param name="Stream"></param>
        /// <returns></returns>
        static IMediaCondition Consume_Media_Condition(DataConsumer <CssToken> Stream)
        {/* Docs: https://www.w3.org/TR/mediaqueries-4/#media-condition */
            if (Stream is null)
            {
                throw new CssParserException(CssErrors.STREAM_IS_NULL);
            }

            Consume_All_Whitespace(Stream);
            if (ParserCommon.Starts_Media_Feature(Stream.AsSpan()))
            {
                /* Consume opening parentheses */
                Stream.Consume();
                var feature = Consume_Media_Feature(Stream);

                /* Consume closing parentheses */
                if (Stream.Next.Type == ECssTokenType.Parenth_Close)
                {
                    Stream.Consume();
                }

                return(feature);
            }
            else if (ParserCommon.Starts_Media_Condition(Stream.AsSpan()))
            {
                EMediaCombinator Combinator = EMediaCombinator.None;
                var conditionList           = new LinkedList <IMediaCondition>();


                if (Stream.Next.Type == ECssTokenType.Parenth_Close)
                {
                    /* Empty media condition block */
                    Stream.Consume();
                    return(new MediaCondition(EMediaCombinator.None, Array.Empty <MediaFeature>()));
                }
                else if (Stream.Next.Type == ECssTokenType.Parenth_Open)
                {
                    Stream.Consume();
                }

                Consume_All_Whitespace(Stream);
                /* Repeatedly consume sub-media-conditions until we hit a closing parentheses */
                do
                {
                    Consume_All_Whitespace(Stream);

                    if (Stream.Next.Type == ECssTokenType.Parenth_Close)
                    {
                        /* End of this media condition block */
                        break;
                    }

                    /* Anything other than the first condition *MUST* specify a combinator */
                    if (conditionList.Count > 0)
                    {
                        if (!ParserCommon.Is_Combinator(Stream.Next))
                        {
                            throw new CssSyntaxErrorException(CssErrors.EXPECTING_COMBINATOR, Stream);
                        }
                    }

                    /* Otherwise we just COULD have a combinator */
                    if (ParserCommon.Is_Combinator(Stream.Next))
                    {
                        /* Consume combinator */
                        IdentToken combinatorToken = Stream.Consume() as IdentToken;
                        if (!Lookup.TryEnum(combinatorToken.Value, out EMediaCombinator combLookup))
                        {
                            throw new CssSyntaxErrorException(String.Format(CultureInfo.InvariantCulture, CssErrors.INVALID_COMBINATOR, combinatorToken.Value), Stream);
                        }
                        else if (Combinator == EMediaCombinator.None)
                        {/* This is the first combinator specified */
                            Combinator = combLookup;
                        }
                        else if (Combinator != EMediaCombinator.None && combLookup != Combinator)
                        {/* Ensure this new combinator matches the combinator for this method group */
                            throw new CssSyntaxErrorException(CssErrors.INVALID_MULTIPLE_COMBINATORS_ON_MEDIARULE, Stream);
                        }
                    }

                    Consume_All_Whitespace(Stream);
                    if (Stream.Next.Type != ECssTokenType.Parenth_Open)
                    {
                        throw new CssSyntaxErrorException(CssErrors.EXPECTING_OPENING_PARENTHESES, Stream);
                    }

                    /* Oh look a yummy little sub-condition for us to gobble up! */
                    var feature = Consume_Media_Condition(Stream);
                    conditionList.AddLast(feature);
                }while (Stream.Next.Type != ECssTokenType.EOF);

                /* Consume closing parentheses */
                if (Stream.Next.Type == ECssTokenType.Parenth_Close)
                {
                    Stream.Consume();
                }

                return(new MediaCondition(Combinator, conditionList));
            }

            throw new CssSyntaxErrorException(CssErrors.EXPECTING_MEDIA_CONDITION_START, Stream);
        }
示例#3
0
        static IMediaCondition Consume_Media_Feature(DataConsumer <CssToken> Stream)
        {/* Docs: https://drafts.csswg.org/mediaqueries-4/#mq-syntax */
            if (Stream is null)
            {
                throw new CssParserException(CssErrors.STREAM_IS_NULL);
            }


            /* Consume feature name */
            if (ParserCommon.Starts_Boolean_Feature(Stream.AsSpan()))
            {
                /* Consume feature name */
                IdentToken nameTok = Stream.Consume() as IdentToken;

                /* Resolve the name */
                if (!Lookup.TryEnum(nameTok.Value, out EMediaFeatureName Name))
                {
                    throw new CssParserException(String.Format(CultureInfo.InvariantCulture, CssErrors.INVALID_MEDIA_TYPE, nameTok.Value), Stream);
                }

                return(new MediaFeature(Name));
            }
            else if (ParserCommon.Starts_Discreet_Feature(Stream.AsSpan()))
            {
                /* Consume feature name */
                IdentToken nameTok = Stream.Consume() as IdentToken;

                /* Resolve the name */
                if (!Lookup.TryEnum(nameTok.Value, out EMediaFeatureName Name))
                {
                    throw new CssParserException(String.Format(CultureInfo.InvariantCulture, CssErrors.INVALID_MEDIA_TYPE, nameTok.Value), Stream);
                }

                /* Consume the value to match */
                Consume_All_Whitespace(Stream);
                var value = Consume_MediaFeature_Value(Stream);

                return(new MediaFeature(new CssValue[] { CssValue.From(Name), value }, new EMediaOperator[] { EMediaOperator.EqualTo }));
            }
            else if (ParserCommon.Starts_Range_Feature(Stream.AsSpan()))
            {
                /* This is a range feature of some sort, it could be a short one or a long one */
                /* Repeatedly consume CssValues, operator, and a single ident */
                LinkedList <CssValue>       Values = new LinkedList <CssValue>();
                LinkedList <EMediaOperator> Ops    = new LinkedList <EMediaOperator>();
                bool firstToken        = true;
                bool lastWasComparator = false;

                while (Stream.Next != CssToken.EOF)
                {
                    Consume_All_Whitespace(Stream);

                    if (Stream.Next.Type == ECssTokenType.Parenth_Close)
                    {
                        break;
                    }
                    else if (Stream.Next.Type == ECssTokenType.Ident)
                    {
                        if (!firstToken && !lastWasComparator)
                        {
                            throw new CssSyntaxErrorException(CssErrors.EXPECTING_COMPARATOR, Stream);
                        }

                        var nameTok = (IdentToken)Stream.Consume();
                        /* Resolve the name */
                        if (!Lookup.TryEnum(nameTok.Value, out EMediaFeatureName Name))
                        {
                            throw new CssParserException(String.Format(CultureInfo.InvariantCulture, CssErrors.INVALID_MEDIA_TYPE, nameTok.Value), Stream);
                        }

                        var value = CssValue.From(Name);
                        Values.AddLast(value);
                        lastWasComparator = false;
                    }
                    else if (ParserCommon.Starts_MF_Ident_Or_Value(Stream.AsSpan()))
                    {
                        if (!firstToken && !lastWasComparator)
                        {
                            throw new CssSyntaxErrorException(CssErrors.EXPECTING_COMPARATOR, Stream);
                        }

                        CssValue value = Consume_MediaFeature_Value(Stream);
                        Values.AddLast(value);
                        lastWasComparator = false;
                    }
                    else if (ParserCommon.Is_Comparator(Stream.Next))
                    {
                        if (lastWasComparator || firstToken)
                        {
                            throw new CssSyntaxErrorException(CssErrors.UNEXPECTED_TOKEN, Stream);
                        }

                        var comparatorTok = (ValuedTokenBase)Stream.Consume();
                        if (!Lookup.TryEnum(comparatorTok.Value, out EMediaOperator outComparator))
                        {
                            throw new CssParserException(CssErrors.EXPECTING_COMPARATOR, Stream);
                        }

                        Ops.AddLast(outComparator);
                        lastWasComparator = true;
                    }

                    firstToken = false;
                }

                return(new MediaFeature(Values.ToArray(), Ops.ToArray()));
            }

            return(null);
        }
示例#4
0
        static MediaQuery Consume_MediaQuery(DataConsumer <CssToken> Stream = null)
        {/* Docs: https://drafts.csswg.org/mediaqueries-4/#mq-syntax */
            if (Stream is null)
            {
                throw new CssParserException(CssErrors.STREAM_IS_NULL);
            }

            if (Stream.Next.Type != ECssTokenType.Ident)
            {
                throw new CssSyntaxErrorException(CssErrors.EXPECTING_IDENT, Stream);
            }
            EMediaQueryModifier          modifier      = 0x0;
            EMediaType                   mediaType     = 0x0;
            LinkedList <IMediaCondition> conditionList = new LinkedList <IMediaCondition>();

            /* First check for media modifier */
            if (Stream.Next.Type != ECssTokenType.Ident)
            {
                throw new CssSyntaxErrorException(CssErrors.EXPECTING_IDENT, Stream);
            }
            if (Lookup.TryEnum((Stream.Next as IdentToken).Value, out EMediaQueryModifier mod))
            {
                Stream.Consume();// consume this token
                modifier = mod;
            }

            /* Skip 'and' keyword if present */
            if (ParserCommon.Is_Combinator(Stream.Next))
            {
                Stream.Consume();
            }

            /* Set the media type */
            if (Stream.Next.Type != ECssTokenType.Ident)
            {
                throw new CssSyntaxErrorException(CssErrors.EXPECTING_IDENT, Stream);
            }

            if (!Lookup.TryEnum((Stream.Next as IdentToken).Value, out EMediaType type))
            {
                throw new CssParserException(String.Format(CultureInfo.InvariantCulture, CssErrors.INVALID_MEDIA_TYPE, (Stream.Next as IdentToken).Value), Stream);
            }
            else
            {
                Stream.Consume();
            }

            /* Skip thew first combinator keyword if present */
            Consume_All_Whitespace(Stream);
            if (ParserCommon.Is_Combinator(Stream.Next))
            {
                Stream.Consume();
            }


            /* Now consume media conditions until we cant anymore */
            do
            {
                Consume_All_Whitespace(Stream);
                if (Stream.Next.Type != ECssTokenType.Parenth_Open)
                {/* This isn't invalid, it just signals that we have no more features to consume */
                    break;
                }

                var condition = Consume_Media_Condition(Stream);
                conditionList.AddLast(condition);
            }while (Stream.Next.Type != ECssTokenType.EOF);

            return(new MediaQuery(modifier, mediaType, conditionList));
        }