public ScanUntilWithNesting ( StringBuilder buffer, char needle, bool requireFound = true ) : void | ||
buffer | StringBuilder | |
needle | char | |
requireFound | bool | |
return | void |
// Basically, it's realy common for background-position to be something like "0px -20px", which shouldn't be interpretted as a math value // so detect the case where two number with unit values are separated by whitespace and treat them as a compound value instead // otherwise, fallback to normal parsing internal static Value ParseBackgroundPositionValue(ParserStream stream) { var start = stream.Position; var valueStr = new StringBuilder(); stream.ScanUntilWithNesting(valueStr, ';'); var value = valueStr.ToString(); var parts = value.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); var pos = Position.Create(start, stream.Position, Current.CurrentFilePath); // something's up if (parts.Length != 2) { return(MoreValueParser.Parse(value, pos)); } var p1 = parts[0]; var p2 = parts[1]; var v1 = MoreValueParser.Parse(p1, pos); var v2 = MoreValueParser.Parse(p2, pos); if (v1 is NumberWithUnitValue && v2 is NumberWithUnitValue) { return(new CompoundValue(v1, v2)); } return(MoreValueParser.Parse(value, pos)); }
internal static Import ParseImportDirective(ParserStream stream) { var start = stream.Position; var buffer = new StringBuilder(); stream.ScanUntilWithNesting(buffer, ';'); var toParse = buffer.ToString().Trim(); Value val; MediaQuery media; string mediaStr; if (Regex.IsMatch(toParse, @"url\s*?\(", RegexOptions.IgnoreCase)) { var i = toParse.IndexOf(')'); if (i == -1) { Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected ')'");; throw new StoppedParsingException(); } val = Value.Parse(toParse.Substring(0, i + 1), start, start + i + 1, Current.CurrentFilePath); mediaStr = toParse.Substring(i + 1); } else { if (!(toParse.StartsWith("\"") || toParse.StartsWith("'"))) { Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected quote"); throw new StoppedParsingException(); } var i = toParse.LastIndexOf(toParse[0]); if (i == -1) { Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected '" + toParse[0] + "'"); throw new StoppedParsingException(); } val = Value.Parse(toParse.Substring(0, i + 1), start, start + i + 1, Current.CurrentFilePath); mediaStr = toParse.Substring(i + 1); } mediaStr = mediaStr.Trim(); if (mediaStr.Length > 0) { media = MediaQueryParser.Parse(mediaStr, Position.Create(start, stream.Position, Current.CurrentFilePath)); } else { media = new MediaType(Media.all, Position.Create(start, stream.Position, Current.CurrentFilePath)); } return(new Import(val, media, start, stream.Position, Current.CurrentFilePath)); }
internal static Value ParseMoreValue(ParserStream stream) { var start = stream.Position; var valueStr = new StringBuilder(); stream.ScanUntilWithNesting(valueStr, ';'); var value = valueStr.ToString(); return(Value.Parse(value, start, stream.Position, Current.CurrentFilePath)); }
internal static GroupedValue ParseGroup(ParserStream stream, IPosition forPosition) { var toParse = new StringBuilder(); stream.Advance(); // skip ( stream.ScanUntilWithNesting(toParse, ')'); var group = toParse.ToString().Trim(); var ret = Parse(group, forPosition); return(new GroupedValue(ret)); }
internal static Value ParseFontLikeValue(ParserStream stream) { var start = stream.Position; var valueStr = new StringBuilder(); stream.ScanUntilWithNesting(valueStr, ';'); var value = valueStr.ToString(); // The shorthand isn't in use, so we can handle this if (value.IndexOf('/') == -1) { return(MoreValueParser.Parse(value, Position.Create(start, stream.Position, Current.CurrentFilePath))); } return(new StringValue(value)); }
internal static MoreVariable ParseMoreVariable(string name, ParserStream stream, int start) { name = name.Trim(); var valueStart = stream.Position; var valueStr = new StringBuilder(); stream.ScanUntilWithNesting(valueStr, ';'); var value = Value.Parse(valueStr.ToString().Trim(), valueStart, stream.Position, Current.CurrentFilePath, allowSelectorIncludes: true); if (name.ToLower().In(ReservedWords)) { Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "'" + name + "' cannot be a variable name."); } return(new MoreVariable(name, value, start, stream.Position, Current.CurrentFilePath)); }
public static Value Parse(string value, IPosition forPosition, bool allowSelectorIncludes = false) { var ret = new List<Value>(); using (var stream = new ParserStream(new StringReader(value))) { while (stream.HasMore()) { var buffer = new StringBuilder(); stream.ScanUntilWithNesting(buffer, ',', requireFound: false); using (var subStream = new ParserStream(new StringReader(buffer.ToString()))) { ret.Add(ParseImpl(subStream, forPosition, allowSelectorIncludes)); } } } if (ret.Count == 1) return ret[0]; return new CommaDelimittedValue(ret); }
public static Value Parse(string value, IPosition forPosition, bool allowSelectorIncludes = false) { var ret = new List <Value>(); using (var stream = new ParserStream(new StringReader(value))) { while (stream.HasMore()) { var buffer = new StringBuilder(); stream.ScanUntilWithNesting(buffer, ',', requireFound: false); using (var subStream = new ParserStream(new StringReader(buffer.ToString()))) { ret.Add(ParseImpl(subStream, forPosition, allowSelectorIncludes)); } } } if (ret.Count == 1) { return(ret[0]); } return(new CommaDelimittedValue(ret)); }
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 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)); }
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); }
internal static GroupedValue ParseGroup(ParserStream stream, IPosition forPosition) { var toParse = new StringBuilder(); stream.Advance(); // skip ( stream.ScanUntilWithNesting(toParse, ')'); var group = toParse.ToString().Trim(); var ret = Parse(group, forPosition); return new GroupedValue(ret); }
// Basically, it's realy common for background-position to be something like "0px -20px", which shouldn't be interpretted as a math value // so detect the case where two number with unit values are separated by whitespace and treat them as a compound value instead // otherwise, fallback to normal parsing internal static Value ParseBackgroundPositionValue(ParserStream stream) { var start = stream.Position; var valueStr = new StringBuilder(); stream.ScanUntilWithNesting(valueStr, ';'); var value = valueStr.ToString(); var parts = value.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries); var pos = Position.Create(start, stream.Position, Current.CurrentFilePath); // something's up if (parts.Length != 2) { return MoreValueParser.Parse(value, pos); } var p1 = parts[0]; var p2 = parts[1]; var v1 = MoreValueParser.Parse(p1, pos); var v2 = MoreValueParser.Parse(p2, pos); if (v1 is NumberWithUnitValue && v2 is NumberWithUnitValue) { return new CompoundValue(v1, v2); } return MoreValueParser.Parse(value, pos); }
internal static MoreVariable ParseMoreVariable(string name, ParserStream stream, int start) { name = name.Trim(); var valueStart = stream.Position; var valueStr = new StringBuilder(); stream.ScanUntilWithNesting(valueStr, ';'); var value = Value.Parse(valueStr.ToString().Trim(), valueStart, stream.Position, Current.CurrentFilePath, allowSelectorIncludes: true); if (name.ToLower().In(ReservedWords)) { Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "'" + name + "' cannot be a variable name."); } return new MoreVariable(name, value, start, stream.Position, Current.CurrentFilePath); }
internal static Value ParseMoreValue(ParserStream stream) { var start = stream.Position; var valueStr = new StringBuilder(); stream.ScanUntilWithNesting(valueStr, ';'); var value = valueStr.ToString(); return Value.Parse(value, start, stream.Position, Current.CurrentFilePath); }
internal static Value ParseFontLikeValue(ParserStream stream) { var start = stream.Position; var valueStr = new StringBuilder(); stream.ScanUntilWithNesting(valueStr, ';'); var value = valueStr.ToString(); // The shorthand isn't in use, so we can handle this if(value.IndexOf('/') == -1) { return MoreValueParser.Parse(value, Position.Create(start, stream.Position, Current.CurrentFilePath)); } return new StringValue(value); }
internal static Import ParseImportDirective(ParserStream stream) { var start = stream.Position; var buffer = new StringBuilder(); stream.ScanUntilWithNesting(buffer, ';'); var toParse = buffer.ToString().Trim(); Value val; MediaQuery media; string mediaStr; if (Regex.IsMatch(toParse, @"url\s*?\(", RegexOptions.IgnoreCase)) { var i = toParse.IndexOf(')'); if (i == -1) { Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected ')'"); ; throw new StoppedParsingException(); } val = Value.Parse(toParse.Substring(0, i + 1), start, start + i + 1, Current.CurrentFilePath); mediaStr = toParse.Substring(i + 1); } else { if (!(toParse.StartsWith("\"") || toParse.StartsWith("'"))) { Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected quote"); throw new StoppedParsingException(); } var i = toParse.LastIndexOf(toParse[0]); if (i == -1) { Current.RecordError(ErrorType.Parser, Position.Create(start, stream.Position, Current.CurrentFilePath), "Expected '" + toParse[0] + "'"); throw new StoppedParsingException(); } val = Value.Parse(toParse.Substring(0, i + 1), start, start + i + 1, Current.CurrentFilePath); mediaStr = toParse.Substring(i + 1); } mediaStr = mediaStr.Trim(); if (mediaStr.Length > 0) { media = MediaQueryParser.Parse(mediaStr, Position.Create(start, stream.Position, Current.CurrentFilePath)); } else { media = new MediaType(Media.all, Position.Create(start, stream.Position, Current.CurrentFilePath)); } return new Import(val, media, start, stream.Position, Current.CurrentFilePath); }