public Variable(string markup) { _markup = markup; Name = null; Filters = new List <Filter>(); Match match = MyRegex.Match(markup, string.Format(R.Q(@"\s*({0})(.*)"), Liquid.QuotedAssignFragment)); if (match.Success) { Name = match.Groups[1].Value; Match filterMatch = MyRegex.Match(match.Groups[2].Value, string.Format(R.Q(@"{0}\s*(.*)"), Liquid.FilterSeparator)); if (filterMatch.Success) { foreach (string f in R.Scan(filterMatch.Value, FilterParser)) { Match filterNameMatch = MyRegex.Match(f, R.Q(@"\s*(\w+)")); if (filterNameMatch.Success) { string filterName = filterNameMatch.Groups[1].Value; List <string> filterArgs = R.Scan(f, string.Format(R.Q(@"(?:{0}|{1})\s*({2})"), Liquid.FilterArgumentSeparator, Liquid.ArgumentSeparator, Liquid.QuotedFragment)); Filters.Add(new Filter(filterName, filterArgs.ToArray())); } } } } }
/// <summary> /// Uses the <tt>Liquid::TemplateParser</tt> regexp to tokenize the passed source /// </summary> /// <param name="source"></param> /// <returns></returns> internal static List <string> Tokenize(string source) { if (string.IsNullOrEmpty(source)) { return(new List <string>()); } // Trim leading whitespace. source = MyRegex.Replace(source, string.Format(@"([ \t]+)?({0}|{1})-", Liquid.VariableStart, Liquid.TagStart), "$2"); // Trim trailing whitespace. source = MyRegex.Replace(source, string.Format(@"-({0}|{1})(\n|\r\n|[ \t]+)?", Liquid.VariableEnd, Liquid.TagEnd), "$1"); List <string> tokens = Regex.Split(source, Liquid.TemplateParser).ToList(); // Trim any whitespace elements from the end of the array. for (int i = tokens.Count - 1; i > 0; --i) { if (tokens[i] == string.Empty) { tokens.RemoveAt(i); } } // Removes the rogue empty element at the beginning of the array if (tokens[0] != null && tokens[0] == string.Empty) { tokens.Shift(); } return(tokens); }
/// <summary> /// Remove all newlines from the string /// </summary> /// <param name="input"></param> /// <returns></returns> public static string StripNewlines(string input) { return(input.IsNullOrWhiteSpace() ? input : MyRegex.Replace(input, @"(\r?\n)", String.Empty)); //: Regex.Replace(input, Environment.NewLine, string.Empty); }
/// <summary> /// Replace occurrences of a string with another /// </summary> /// <param name="input"></param> /// <param name="string"></param> /// <param name="replacement"></param> /// <returns></returns> public static string Replace(string input, string @string, string replacement = "") { if (string.IsNullOrEmpty(input) || string.IsNullOrEmpty(@string)) { return(input); } return(string.IsNullOrEmpty(input) ? input : MyRegex.Replace(input, @string, replacement)); }
/// <summary> /// Add <br /> tags in front of all newlines in input string /// </summary> /// <param name="input"></param> /// <returns></returns> public static string NewlineToBr(string input) { return(input.IsNullOrWhiteSpace() ? input : MyRegex.Replace(input, @"(\r?\n)", "<br />$1")); }
public static string StripHtml(string input) { return(input.IsNullOrWhiteSpace() ? input : MyRegex.Replace(input, @"<.*?>", string.Empty)); }
/// <summary> /// Resolves namespaced queries gracefully. /// /// Example /// /// @context['hash'] = {"name" => 'tobi'} /// assert_equal 'tobi', @context['hash.name'] /// assert_equal 'tobi', @context['hash["name"]'] /// </summary> /// <param name="markup"></param> /// <returns></returns> private object Variable(string markup) { List <string> parts = R.Scan(markup, Liquid.VariableParser); Regex squareBracketed = MyRegex.GetCachedRegex(R.Q(@"^\[(.*)\]$")); string firstPart = parts.Shift(); Match firstPartSquareBracketedMatch = squareBracketed.Match(firstPart); if (firstPartSquareBracketedMatch.Success) { firstPart = Resolve(firstPartSquareBracketedMatch.Groups[1].Value).ToString(); } object @object; if ((@object = FindVariable(firstPart)) != null) { foreach (string forEachPart in parts) { Match partSquareBracketedMatch = squareBracketed.Match(forEachPart); bool partResolved = partSquareBracketedMatch.Success; object part = forEachPart; if (partResolved) { part = Resolve(partSquareBracketedMatch.Groups[1].Value); } // If object is a KeyValuePair, we treat it a bit differently - we might be rendering // an included template. if (@object is KeyValuePair <string, object> && ((KeyValuePair <string, object>)@object).Key == (string)part) { object res = ((KeyValuePair <string, object>)@object).Value; @object = Liquidize(res); } // If object is a hash- or array-like object we look for the // presence of the key and if its available we return it else if (IsHashOrArrayLikeObject(@object, part)) { // If its a proc we will replace the entry with the proc object res = LookupAndEvaluate(@object, part); @object = Liquidize(res); } // Some special cases. If the part wasn't in square brackets and // no key with the same name was found we interpret following calls // as commands and call them on the current object else if (!partResolved && (@object is IEnumerable) && ((part as string) == "size" || (part as string) == "first" || (part as string) == "last")) { var castCollection = ((IEnumerable)@object).Cast <object>(); if ((part as string) == "size") { @object = castCollection.Count(); } else if ((part as string) == "first") { @object = castCollection.FirstOrDefault(); } else if ((part as string) == "last") { @object = castCollection.LastOrDefault(); } } // No key was present with the desired value and it wasn't one of the directly supported // keywords either. The only thing we got left is to return nil else { return(null); } // If we are dealing with a drop here we have to if (@object is IContextAware) { ((IContextAware)@object).Context = this; } } } return(@object); }
/// <summary> /// Look up variable, either resolve directly after considering the name. We can directly handle /// Strings, digits, floats and booleans (true,false). If no match is made we lookup the variable in the current scope and /// later move up to the parent blocks to see if we can resolve the variable somewhere up the tree. /// Some special keywords return symbols. Those symbols are to be called on the rhs object in expressions /// /// Example: /// /// products == empty #=> products.empty? /// </summary> /// <param name="key"></param> /// <returns></returns> private object Resolve(string key) { switch (key) { case null: case "nil": case "null": case "": return(null); case "true": return(true); case "false": return(false); case "blank": return(new Symbol(o => o is IEnumerable && !((IEnumerable)o).Cast <object>().Any())); case "empty": return(new Symbol(o => o is IEnumerable && !((IEnumerable)o).Cast <object>().Any())); } // Single quoted strings. Match match = MyRegex.Match(key, R.Q(@"^'(.*)'$")); if (match.Success) { return(match.Groups[1].Value); } // Double quoted strings. match = MyRegex.Match(key, R.Q(@"^""(.*)""$")); if (match.Success) { return(match.Groups[1].Value); } // Integer. match = MyRegex.Match(key, R.Q(@"^([+-]?\d+)$")); if (match.Success) { return(Convert.ToInt32(match.Groups[1].Value)); } // Ranges. match = MyRegex.Match(key, R.Q(@"^\((\S+)\.\.(\S+)\)$")); if (match.Success) { return(Range.Inclusive(Convert.ToInt32(Resolve(match.Groups[1].Value)), Convert.ToInt32(Resolve(match.Groups[2].Value)))); } // Floats. match = MyRegex.Match(key, R.Q(@"^([+-]?\d[\d\.|\,]+)$")); if (match.Success) { // For cultures with "," as the decimal separator, allow // both "," and "." to be used as the separator. // First try to parse using current culture. float result; if (float.TryParse(match.Groups[1].Value, out result)) { return(result); } // If that fails, try to parse using invariant culture. return(float.Parse(match.Groups[1].Value, CultureInfo.InvariantCulture)); } return(Variable(key)); }