private static Value ParseImportant(ParserStream stream) { var buffer = new StringBuilder(); while (stream.HasMore() && (char.IsLetterOrDigit(stream.Peek()) || stream.Peek().In('!', '-', '_'))) { buffer.Append(stream.Read()); } var ret = buffer.ToString(); if (ret.Equals("!important", StringComparison.InvariantCultureIgnoreCase)) { return(ImportantValue.Singleton); } return(new StringValue(ret)); }
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)); }
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())); }
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)); }
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); }
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); }
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); }
private static Value ParseString(ParserStream stream, IPosition forPosition) { var c = stream.Peek(); if (c == '!') { return ParseImportant(stream); } var buffer = new StringBuilder(); while (stream.HasMore() && !char.IsWhiteSpace(stream.Peek())) { buffer.Append(stream.Read()); if (buffer.Length == 3 && (stream.HasMore() && stream.Peek() == '(')) { var toDate = buffer.ToString(); if (toDate.Equals("rgb", StringComparison.InvariantCultureIgnoreCase) || toDate.Equals("hsl", StringComparison.InvariantCultureIgnoreCase)) { var group = ParseGroup(stream, forPosition); var @params = group.Value as CommaDelimittedValue; if (@params == null || @params.Values.Count() != 3) { Current.RecordError(ErrorType.Parser, forPosition, "Expected 3 parameters to '" + toDate + "'"); throw new StoppedParsingException(); } if(toDate == "rgb") { return new RGBColorValue( @params.Values.ElementAt(0), @params.Values.ElementAt(1), @params.Values.ElementAt(2) ); } if (toDate == "hsl") { return new HSLColorValue ( @params.Values.ElementAt(0), @params.Values.ElementAt(1), @params.Values.ElementAt(2) ); } } if (toDate.Equals("url", StringComparison.InvariantCultureIgnoreCase)) { var value = ParseGroup(stream, forPosition).Value; if(value is StringValue || value is QuotedStringValue) { return new UrlValue(value); } Current.RecordError(ErrorType.Parser, forPosition, "Expected string or quoted string"); throw new StoppedParsingException(); } } if (buffer.Length == 4 && (stream.HasMore() && stream.Peek() == '(')) { var toDate = buffer.ToString(); if (toDate.Equals("rgba", StringComparison.InvariantCultureIgnoreCase)) { var @params = ParseGroup(stream, forPosition).Value as CommaDelimittedValue; if (@params == null || @params.Values.Count() != 4) { Current.RecordError(ErrorType.Parser, forPosition, "Expected 4 parameters to '" + toDate + "'"); throw new StoppedParsingException(); } return new RGBAColorValue( @params.Values.ElementAt(0), @params.Values.ElementAt(1), @params.Values.ElementAt(2), @params.Values.ElementAt(3) ); } } if (buffer.Length == 5 && (stream.HasMore() && stream.Peek() == '(')) { var toDate = buffer.ToString(); if (toDate.Equals("local", StringComparison.InvariantCultureIgnoreCase)) { var val = ParseGroup(stream, forPosition).Value; var comma = val as CommaDelimittedValue; if (comma != null) { Current.RecordError(ErrorType.Parser, forPosition, "Expected 1 parameter to local() value, found "+comma.Values.Count()); throw new StoppedParsingException(); } return new LocalValue(val); } } if (buffer.Length == 6 && (stream.HasMore() && stream.Peek() == '(')) { var toDate = buffer.ToString(); if (toDate.Equals("format", StringComparison.InvariantCultureIgnoreCase)) { var val = ParseGroup(stream, forPosition).Value; var comma = val as CommaDelimittedValue; if (comma != null) { Current.RecordError(ErrorType.Parser, forPosition, "Expected 1 parameter to format() value, found " + comma.Values.Count()); throw new StoppedParsingException(); } return new FormatValue(val); } } } NamedColor color; if (Enum.TryParse<NamedColor>(buffer.ToString(), ignoreCase: true, result: out color)) { return new NamedColorValue(color); } var str = buffer.ToString(); if (str.Contains("!")) { var i = str.IndexOf('!'); var left = str.Substring(0, i); var right = str.Substring(i); var ret = new List<Value>(); var lhs = Parse(left, forPosition); var rhs = Parse(right, forPosition); if (lhs is CompoundValue) { ret.AddRange(((CompoundValue)lhs).Values); } else { ret.Add(lhs); } if (rhs is CompoundValue) { ret.AddRange(((CompoundValue)rhs).Values); } else { ret.Add(rhs); } return new CompoundValue(ret); } return new StringValue(buffer.ToString()); }
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); }
private static Value ParseImportant(ParserStream stream) { var buffer = new StringBuilder(); while (stream.HasMore() && (char.IsLetterOrDigit(stream.Peek()) || stream.Peek().In('!', '-', '_'))) { buffer.Append(stream.Read()); } var ret = buffer.ToString(); if (ret.Equals("!important", StringComparison.InvariantCultureIgnoreCase)) return ImportantValue.Singleton; return new StringValue(ret); }
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()); }
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)); }
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)); }
private static Value ParseString(ParserStream stream, IPosition forPosition) { var c = stream.Peek(); if (c == '!') { return(ParseImportant(stream)); } var buffer = new StringBuilder(); while (stream.HasMore() && !char.IsWhiteSpace(stream.Peek())) { buffer.Append(stream.Read()); if (buffer.Length == 3 && (stream.HasMore() && stream.Peek() == '(')) { var toDate = buffer.ToString(); if (toDate.Equals("rgb", StringComparison.InvariantCultureIgnoreCase) || toDate.Equals("hsl", StringComparison.InvariantCultureIgnoreCase)) { var group = ParseGroup(stream, forPosition); var @params = group.Value as CommaDelimittedValue; if (@params == null || @params.Values.Count() != 3) { Current.RecordError(ErrorType.Parser, forPosition, "Expected 3 parameters to '" + toDate + "'"); throw new StoppedParsingException(); } if (toDate == "rgb") { return (new RGBColorValue( @params.Values.ElementAt(0), @params.Values.ElementAt(1), @params.Values.ElementAt(2) )); } if (toDate == "hsl") { return (new HSLColorValue ( @params.Values.ElementAt(0), @params.Values.ElementAt(1), @params.Values.ElementAt(2) )); } } if (toDate.Equals("url", StringComparison.InvariantCultureIgnoreCase)) { var value = ParseGroup(stream, forPosition).Value; if (value is StringValue || value is QuotedStringValue) { return(new UrlValue(value)); } Current.RecordError(ErrorType.Parser, forPosition, "Expected string or quoted string"); throw new StoppedParsingException(); } } if (buffer.Length == 4 && (stream.HasMore() && stream.Peek() == '(')) { var toDate = buffer.ToString(); if (toDate.Equals("rgba", StringComparison.InvariantCultureIgnoreCase)) { var @params = ParseGroup(stream, forPosition).Value as CommaDelimittedValue; if (@params == null || @params.Values.Count() != 4) { Current.RecordError(ErrorType.Parser, forPosition, "Expected 4 parameters to '" + toDate + "'"); throw new StoppedParsingException(); } return (new RGBAColorValue( @params.Values.ElementAt(0), @params.Values.ElementAt(1), @params.Values.ElementAt(2), @params.Values.ElementAt(3) )); } if (toDate.Equals("attr", StringComparison.InvariantCultureIgnoreCase)) { var @params = ParseGroup(stream, forPosition).Value; Value attrAndType, attr, type, fallback; var comma = @params as CommaDelimittedValue; if (comma != null) { if (comma.Values.Count() > 2) { Current.RecordError(ErrorType.Parser, forPosition, "attr expects 1 or 2 parameters, found " + comma); throw new StoppedParsingException(); } attrAndType = comma.Values.ElementAt(0); fallback = comma.Values.Count() == 2 ? comma.Values.ElementAt(1) : null; } else { attrAndType = @params; fallback = null; } var compound = attrAndType as CompoundValue; if (compound != null) { if (compound.Values.Count() > 2) { Current.RecordError(ErrorType.Parser, forPosition, "attr expects an attribute name and optionally a type, found " + compound); throw new StoppedParsingException(); } attr = compound.Values.ElementAt(0); type = compound.Values.Count() == 2 ? compound.Values.ElementAt(1) : null; } else { attr = attrAndType; type = null; } return(new AttributeValue(attr, type, fallback)); } if (toDate.Equals("calc", StringComparison.InvariantCultureIgnoreCase)) { var @params = ParseGroup(stream, forPosition).Value; if (@params is CommaDelimittedValue) { Current.RecordError(ErrorType.Parser, forPosition, "calc expects an expression, found " + @params); } string strVersion; using (var writer = new StringWriter()) { @params.Write(writer); strVersion = writer.ToString(); } // Things passed to calc are opting out of More magic, they don't get anything but in string replacements return(new CalcValue(new StringValue(strVersion))); } } if (buffer.Length == 5 && (stream.HasMore() && stream.Peek() == '(')) { var toDate = buffer.ToString(); if (toDate.Equals("local", StringComparison.InvariantCultureIgnoreCase)) { var val = ParseGroup(stream, forPosition).Value; var comma = val as CommaDelimittedValue; if (comma != null) { Current.RecordError(ErrorType.Parser, forPosition, "Expected 1 parameter to local() value, found " + comma.Values.Count()); throw new StoppedParsingException(); } return(new LocalValue(val)); } if (toDate.Equals("cycle", StringComparison.InvariantCultureIgnoreCase)) { var val = ParseGroup(stream, forPosition).Value; var param = new List <Value>(); var comma = val as CommaDelimittedValue; if (comma != null) { param.AddRange(comma.Values); } else { param.Add(val); } return(new CycleValue(param)); } if (toDate.Equals("steps", StringComparison.InvariantCultureIgnoreCase)) { var val = ParseGroup(stream, forPosition).Value; var comma = val as CommaDelimittedValue; if (comma != null && comma.Values.Count() > 2) { Current.RecordError(ErrorType.Parser, forPosition, "Expected at most 2 parameters to steps() value, found " + comma.Values.Count()); throw new StoppedParsingException(); } var steps = comma != null?comma.Values.ElementAt(0) : val; var dir = comma != null?comma.Values.ElementAt(1) : null; return(new StepsValue(steps, dir)); } } if (buffer.Length == 6 && (stream.HasMore() && stream.Peek() == '(')) { var toDate = buffer.ToString(); if (toDate.Equals("format", StringComparison.InvariantCultureIgnoreCase)) { var val = ParseGroup(stream, forPosition).Value; var comma = val as CommaDelimittedValue; if (comma != null) { Current.RecordError(ErrorType.Parser, forPosition, "Expected 1 parameter to format() value, found " + comma.Values.Count()); throw new StoppedParsingException(); } return(new FormatValue(val)); } } if (buffer.Length == 7 && (stream.HasMore() && stream.Peek() == '(')) { var toDate = buffer.ToString(); if (toDate.Equals("counter", StringComparison.InvariantCultureIgnoreCase)) { var val = ParseGroup(stream, forPosition).Value; var comma = val as CommaDelimittedValue; if (comma != null && comma.Values.Count() > 2) { Current.RecordError(ErrorType.Parser, forPosition, "Expected at most 2 parameters to counter() value, found " + comma.Values.Count()); throw new StoppedParsingException(); } var counter = comma != null?comma.Values.ElementAt(0) : val; var style = comma != null?comma.Values.ElementAt(1) : null; return(new CounterValue(counter, style)); } } if (buffer.Length == 8 && (stream.HasMore() && stream.Peek() == '(')) { var toDate = buffer.ToString(); if (toDate.Equals("counters", StringComparison.InvariantCultureIgnoreCase)) { var val = ParseGroup(stream, forPosition).Value; var comma = val as CommaDelimittedValue; if (comma != null && comma.Values.Count() > 2) { Current.RecordError(ErrorType.Parser, forPosition, "Expected at most 2 parameters to counters() value, found " + comma.Values.Count()); throw new StoppedParsingException(); } var counter = comma != null?comma.Values.ElementAt(0) : val; var style = comma != null?comma.Values.ElementAt(1) : null; return(new CountersValue(counter, style)); } } if (buffer.Length == 12 && (stream.HasMore() && stream.Peek() == '(')) { var toDate = buffer.ToString(); if (toDate.Equals("cubic-bezier", StringComparison.InvariantCultureIgnoreCase)) { var val = ParseGroup(stream, forPosition).Value; var comma = val as CommaDelimittedValue; if (comma == null || comma.Values.Count() != 4) { Current.RecordError(ErrorType.Parser, forPosition, "Excepted 4 parameters to cubic-bezier()"); throw new StoppedParsingException(); } return (new CubicBezierValue( comma.Values.ElementAt(0), comma.Values.ElementAt(1), comma.Values.ElementAt(2), comma.Values.ElementAt(3) )); } } if (buffer.Length == 15 && (stream.HasMore() && stream.Peek() == '(')) { var toDate = buffer.ToString(); if (toDate.Equals("linear-gradient", StringComparison.InvariantCultureIgnoreCase)) { var val = ParseGroup(stream, forPosition).Value; var comma = val as CommaDelimittedValue; if (comma == null || comma.Values.Count() < 2) { Current.RecordError(ErrorType.Parser, forPosition, "linear-gradient expects at least two parameters"); throw new StoppedParsingException(); } return(new LinearGradientValue(comma.Values)); } if (toDate.Equals("radial-gradient", StringComparison.InvariantCultureIgnoreCase)) { var val = ParseGroup(stream, forPosition).Value; var comma = val as CommaDelimittedValue; if (comma == null || comma.Values.Count() < 2) { Current.RecordError(ErrorType.Parser, forPosition, "radial-gradient expects at least two parameters"); throw new StoppedParsingException(); } return(new RadialGradientValue(comma.Values)); } } } NamedColor color; if (Enum.TryParse <NamedColor>(buffer.ToString(), ignoreCase: true, result: out color)) { return(new NamedColorValue(color)); } var str = buffer.ToString(); if (str.Contains("!")) { var i = str.IndexOf('!'); var left = str.Substring(0, i); var right = str.Substring(i); var ret = new List <Value>(); var lhs = Parse(left, forPosition); var rhs = Parse(right, forPosition); if (lhs is CompoundValue) { ret.AddRange(((CompoundValue)lhs).Values); } else { ret.Add(lhs); } if (rhs is CompoundValue) { ret.AddRange(((CompoundValue)rhs).Values); } else { ret.Add(rhs); } return(new CompoundValue(ret)); } return(new StringValue(buffer.ToString())); }
private static Value ParseString(ParserStream stream, IPosition forPosition) { var c = stream.Peek(); if (c == '!') { return ParseImportant(stream); } var buffer = new StringBuilder(); while (stream.HasMore() && !char.IsWhiteSpace(stream.Peek())) { buffer.Append(stream.Read()); if (buffer.Length == 3 && (stream.HasMore() && stream.Peek() == '(')) { var toDate = buffer.ToString(); if (toDate.Equals("rgb", StringComparison.InvariantCultureIgnoreCase) || toDate.Equals("hsl", StringComparison.InvariantCultureIgnoreCase)) { var group = ParseGroup(stream, forPosition); var @params = group.Value as CommaDelimittedValue; if (@params == null || @params.Values.Count() != 3) { Current.RecordError(ErrorType.Parser, forPosition, "Expected 3 parameters to '" + toDate + "'"); throw new StoppedParsingException(); } if(toDate == "rgb") { return new RGBColorValue( @params.Values.ElementAt(0), @params.Values.ElementAt(1), @params.Values.ElementAt(2) ); } if (toDate == "hsl") { return new HSLColorValue ( @params.Values.ElementAt(0), @params.Values.ElementAt(1), @params.Values.ElementAt(2) ); } } if (toDate.Equals("url", StringComparison.InvariantCultureIgnoreCase)) { var value = ParseGroup(stream, forPosition).Value; if(value is StringValue || value is QuotedStringValue) { return new UrlValue(value); } Current.RecordError(ErrorType.Parser, forPosition, "Expected string or quoted string"); throw new StoppedParsingException(); } } if (buffer.Length == 4 && (stream.HasMore() && stream.Peek() == '(')) { var toDate = buffer.ToString(); if (toDate.Equals("rgba", StringComparison.InvariantCultureIgnoreCase)) { var @params = ParseGroup(stream, forPosition).Value as CommaDelimittedValue; if (@params == null || @params.Values.Count() != 4) { Current.RecordError(ErrorType.Parser, forPosition, "Expected 4 parameters to '" + toDate + "'"); throw new StoppedParsingException(); } return new RGBAColorValue( @params.Values.ElementAt(0), @params.Values.ElementAt(1), @params.Values.ElementAt(2), @params.Values.ElementAt(3) ); } if (toDate.Equals("attr", StringComparison.InvariantCultureIgnoreCase)) { var @params = ParseGroup(stream, forPosition).Value; Value attrAndType, attr, type, fallback; var comma = @params as CommaDelimittedValue; if (comma != null) { if (comma.Values.Count() > 2) { Current.RecordError(ErrorType.Parser, forPosition, "attr expects 1 or 2 parameters, found " + comma); throw new StoppedParsingException(); } attrAndType = comma.Values.ElementAt(0); fallback = comma.Values.Count() == 2 ? comma.Values.ElementAt(1) : null; } else { attrAndType = @params; fallback = null; } var compound = attrAndType as CompoundValue; if (compound != null) { if (compound.Values.Count() > 2) { Current.RecordError(ErrorType.Parser, forPosition, "attr expects an attribute name and optionally a type, found " + compound); throw new StoppedParsingException(); } attr = compound.Values.ElementAt(0); type = compound.Values.Count() == 2 ? compound.Values.ElementAt(1) : null; } else { attr = attrAndType; type = null; } return new AttributeValue(attr, type, fallback); } if (toDate.Equals("calc", StringComparison.InvariantCultureIgnoreCase)) { var @params = ParseGroup(stream, forPosition).Value; if (@params is CommaDelimittedValue) { Current.RecordError(ErrorType.Parser, forPosition, "calc expects an expression, found " + @params); } string strVersion; using(var writer = new StringWriter()) { @params.Write(writer); strVersion = writer.ToString(); } // Things passed to calc are opting out of More magic, they don't get anything but in string replacements return new CalcValue(new StringValue(strVersion)); } } if (buffer.Length == 5 && (stream.HasMore() && stream.Peek() == '(')) { var toDate = buffer.ToString(); if (toDate.Equals("local", StringComparison.InvariantCultureIgnoreCase)) { var val = ParseGroup(stream, forPosition).Value; var comma = val as CommaDelimittedValue; if (comma != null) { Current.RecordError(ErrorType.Parser, forPosition, "Expected 1 parameter to local() value, found " + comma.Values.Count()); throw new StoppedParsingException(); } return new LocalValue(val); } if (toDate.Equals("cycle", StringComparison.InvariantCultureIgnoreCase)) { var val = ParseGroup(stream, forPosition).Value; var param = new List<Value>(); var comma = val as CommaDelimittedValue; if (comma != null) { param.AddRange(comma.Values); } else { param.Add(val); } return new CycleValue(param); } if (toDate.Equals("steps", StringComparison.InvariantCultureIgnoreCase)) { var val = ParseGroup(stream, forPosition).Value; var comma = val as CommaDelimittedValue; if (comma != null && comma.Values.Count() > 2) { Current.RecordError(ErrorType.Parser, forPosition, "Expected at most 2 parameters to steps() value, found " + comma.Values.Count()); throw new StoppedParsingException(); } var steps = comma != null ? comma.Values.ElementAt(0) : val; var dir = comma != null ? comma.Values.ElementAt(1) : null; return new StepsValue(steps, dir); } } if (buffer.Length == 6 && (stream.HasMore() && stream.Peek() == '(')) { var toDate = buffer.ToString(); if (toDate.Equals("format", StringComparison.InvariantCultureIgnoreCase)) { var val = ParseGroup(stream, forPosition).Value; var comma = val as CommaDelimittedValue; if (comma != null) { Current.RecordError(ErrorType.Parser, forPosition, "Expected 1 parameter to format() value, found " + comma.Values.Count()); throw new StoppedParsingException(); } return new FormatValue(val); } } if (buffer.Length == 7 && (stream.HasMore() && stream.Peek() == '(')) { var toDate = buffer.ToString(); if (toDate.Equals("counter", StringComparison.InvariantCultureIgnoreCase)) { var val = ParseGroup(stream, forPosition).Value; var comma = val as CommaDelimittedValue; if (comma != null && comma.Values.Count() > 2) { Current.RecordError(ErrorType.Parser, forPosition, "Expected at most 2 parameters to counter() value, found " + comma.Values.Count()); throw new StoppedParsingException(); } var counter = comma != null ? comma.Values.ElementAt(0) : val; var style = comma != null ? comma.Values.ElementAt(1) : null; return new CounterValue(counter, style); } } if (buffer.Length == 8 && (stream.HasMore() && stream.Peek() == '(')) { var toDate = buffer.ToString(); if (toDate.Equals("counters", StringComparison.InvariantCultureIgnoreCase)) { var val = ParseGroup(stream, forPosition).Value; var comma = val as CommaDelimittedValue; if (comma != null && comma.Values.Count() > 2) { Current.RecordError(ErrorType.Parser, forPosition, "Expected at most 2 parameters to counters() value, found " + comma.Values.Count()); throw new StoppedParsingException(); } var counter = comma != null ? comma.Values.ElementAt(0) : val; var style = comma != null ? comma.Values.ElementAt(1) : null; return new CountersValue(counter, style); } } if (buffer.Length == 12 && (stream.HasMore() && stream.Peek() == '(')) { var toDate = buffer.ToString(); if (toDate.Equals("cubic-bezier", StringComparison.InvariantCultureIgnoreCase)) { var val = ParseGroup(stream, forPosition).Value; var comma = val as CommaDelimittedValue; if (comma == null || comma.Values.Count() != 4) { Current.RecordError(ErrorType.Parser, forPosition, "Excepted 4 parameters to cubic-bezier()"); throw new StoppedParsingException(); } return new CubicBezierValue( comma.Values.ElementAt(0), comma.Values.ElementAt(1), comma.Values.ElementAt(2), comma.Values.ElementAt(3) ); } } if (buffer.Length == 15 && (stream.HasMore() && stream.Peek() == '(')) { var toDate = buffer.ToString(); if (toDate.Equals("linear-gradient", StringComparison.InvariantCultureIgnoreCase)) { var val = ParseGroup(stream, forPosition).Value; var comma = val as CommaDelimittedValue; if (comma == null || comma.Values.Count() < 2) { Current.RecordError(ErrorType.Parser, forPosition, "linear-gradient expects at least two parameters"); throw new StoppedParsingException(); } return new LinearGradientValue(comma.Values); } if (toDate.Equals("radial-gradient", StringComparison.InvariantCultureIgnoreCase)) { var val = ParseGroup(stream, forPosition).Value; var comma = val as CommaDelimittedValue; if (comma == null || comma.Values.Count() < 2) { Current.RecordError(ErrorType.Parser, forPosition, "radial-gradient expects at least two parameters"); throw new StoppedParsingException(); } return new RadialGradientValue(comma.Values); } } } NamedColor color; if (Enum.TryParse<NamedColor>(buffer.ToString(), ignoreCase: true, result: out color)) { return new NamedColorValue(color); } var str = buffer.ToString(); if (str.Contains("!")) { var i = str.IndexOf('!'); var left = str.Substring(0, i); var right = str.Substring(i); var ret = new List<Value>(); var lhs = Parse(left, forPosition); var rhs = Parse(right, forPosition); if (lhs is CompoundValue) { ret.AddRange(((CompoundValue)lhs).Values); } else { ret.Add(lhs); } if (rhs is CompoundValue) { ret.AddRange(((CompoundValue)rhs).Values); } else { ret.Add(rhs); } return new CompoundValue(ret); } return new StringValue(buffer.ToString()); }