Ejemplo n.º 1
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);
        }
Ejemplo n.º 2
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));
        }