/// <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); }
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)); }