public static void Parse_Integer(DataConsumer <char> Stream, out int outValue) { if (Try_Parse_Integer(Stream, out long outParsed)) { outValue = (Int32)outParsed; return; } else { throw new DomSyntaxError($"Unable to parse \"{Stream.AsSpan().ToString()}\" as integer"); } }
/* 64-bit */ public static void Parse_Integer(ReadOnlyMemory <char> input, out long outValue) { DataConsumer <char> Stream = new DataConsumer <char>(input, EOF); if (Try_Parse_Integer(Stream, out int outParsed)) { outValue = outParsed; return; } else { throw new DomSyntaxError($"Unable to parse \"{Stream.AsSpan().ToString()}\" as integer"); } }
public static void Parse_FloatingPoint(DataConsumer <char> Stream, out double outValue) { if (Stream is null) { throw new ArgumentNullException(nameof(Stream)); } Contract.EndContractBlock(); if (Try_Parse_FloatingPoint(Stream, out double outParsed)) { outValue = outParsed; return; } else { throw new DomSyntaxError($"Unable to parse \"{Stream.AsSpan().ToString()}\" as floating-point number"); } }
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); }
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); }
/// <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); }