diff --git a/mustache-sharp.test/FormatCompilerTester.cs b/mustache-sharp.test/FormatCompilerTester.cs index ad563e5..b57d1fc 100644 --- a/mustache-sharp.test/FormatCompilerTester.cs +++ b/mustache-sharp.test/FormatCompilerTester.cs @@ -1387,5 +1387,69 @@ Odd } #endregion + + #region Strings + + /// + /// We will use a string variable to determine whether or not to print out a line. + /// + [TestMethod] + public void TestCompile_StringArgument_PassedToTag() + { + FormatCompiler compiler = new FormatCompiler(); + const string format = @"{{#if 'hello'}}Hello{{/if}}"; + Generator generator = compiler.Compile(format); + string actual = generator.Render(null); + string expected = "Hello"; + Assert.AreEqual(expected, actual, "The string was not passed to the formatter."); + } + + /// + /// We will use a string variable to determine whether or not to print out a line. + /// + [TestMethod] + public void TestCompile_EmptyStringArgument_PassedToTag() + { + FormatCompiler compiler = new FormatCompiler(); + const string format = @"{{#if ''}}Hello{{/if}}"; + Generator generator = compiler.Compile(format); + string actual = generator.Render(null); + string expected = ""; + Assert.AreEqual(expected, actual, "The string was not passed to the formatter."); + } + + #endregion + + #region Numbers + + /// + /// We will use a number variable to determine whether or not to print out a line. + /// + [TestMethod] + public void TestCompile_NumberArgument_PassedToTag() + { + FormatCompiler compiler = new FormatCompiler(); + const string format = @"{{#if 4}}Hello{{/if}}"; + Generator generator = compiler.Compile(format); + string actual = generator.Render(null); + string expected = "Hello"; + Assert.AreEqual(expected, actual, "The number was not passed to the formatter."); + } + + /// + /// We will use a string variable to determine whether or not to print out a line. + /// + [TestMethod] + public void TestCompile_ZeroNumberArgument_PassedToTag() + { + FormatCompiler compiler = new FormatCompiler(); + const string format = @"{{#if 00.0000}}Hello{{/if}}"; + Generator generator = compiler.Compile(format); + string actual = generator.Render(null); + string expected = ""; + Assert.AreEqual(expected, actual, "The number was not passed to the formatter."); + } + + #endregion } } diff --git a/mustache-sharp/ArgumentCollection.cs b/mustache-sharp/ArgumentCollection.cs index 97d41f8..07c23af 100644 --- a/mustache-sharp/ArgumentCollection.cs +++ b/mustache-sharp/ArgumentCollection.cs @@ -9,25 +9,25 @@ namespace Mustache /// internal sealed class ArgumentCollection { - private readonly Dictionary _argumentLookup; + private readonly Dictionary _argumentLookup; /// /// Initializes a new instance of an ArgumentCollection. /// public ArgumentCollection() { - _argumentLookup = new Dictionary(); + _argumentLookup = new Dictionary(); } /// /// Associates the given parameter to the key placeholder. /// /// The parameter to associate the key with. - /// The key placeholder used as the argument. + /// The argument. /// If the key is null, the default value of the parameter will be used. - public void AddArgument(TagParameter parameter, string key) + public void AddArgument(TagParameter parameter, IArgument argument) { - _argumentLookup.Add(parameter, key); + _argumentLookup.Add(parameter, argument); } /// @@ -36,10 +36,10 @@ namespace Mustache /// The name of the parameter. public string GetKey(TagParameter parameter) { - string key; - if (_argumentLookup.TryGetValue(parameter, out key)) + IArgument argument; + if (_argumentLookup.TryGetValue(parameter, out argument) && argument != null) { - return key; + return argument.GetKey(); } else { @@ -56,20 +56,16 @@ namespace Mustache public Dictionary GetArguments(Scope keyScope, Scope contextScope) { Dictionary arguments = new Dictionary(); - foreach (KeyValuePair pair in _argumentLookup) + foreach (KeyValuePair pair in _argumentLookup) { object value; if (pair.Value == null) { value = pair.Key.DefaultValue; } - else if (pair.Value.StartsWith("@")) - { - value = contextScope.Find(pair.Value.Substring(1)); - } else { - value = keyScope.Find(pair.Value); + value = pair.Value.GetValue(keyScope, contextScope); } arguments.Add(pair.Key.Name, value); } @@ -78,7 +74,7 @@ namespace Mustache public Dictionary GetArgumentKeyNames() { - return _argumentLookup.ToDictionary(p => p.Key.Name, p => (object)p.Value); + return _argumentLookup.ToDictionary(p => p.Key.Name, p => (object)GetKey(p.Key)); } } } diff --git a/mustache-sharp/FormatCompiler.cs b/mustache-sharp/FormatCompiler.cs index f9dfea1..530eb92 100644 --- a/mustache-sharp/FormatCompiler.cs +++ b/mustache-sharp/FormatCompiler.cs @@ -152,7 +152,7 @@ namespace Mustache private static string getKeyRegex() { - return @"((?@?" + RegexHelper.CompoundKey + @")(,(?(\+|-)?[\d]+))?(:(?.*?))?)"; + return @"((?" + RegexHelper.CompoundKey + @")(,(?(\+|-)?[\d]+))?(:(?.*?))?)"; } private static string getTagRegex(TagDefinition definition) @@ -164,8 +164,8 @@ namespace Mustache foreach (TagParameter parameter in definition.Parameters) { regexBuilder.Append(@"(\s+?"); - regexBuilder.Append(@"(?(@?"); - regexBuilder.Append(RegexHelper.CompoundKey); + regexBuilder.Append(@"(?("); + regexBuilder.Append(RegexHelper.Argument); regexBuilder.Append(@")))"); if (!parameter.IsRequired) { @@ -341,28 +341,46 @@ namespace Mustache foreach (var pair in arguments) { string placeholder = pair.Value; + IArgument argument = null; if (placeholder != null) { if (placeholder.StartsWith("@")) { + string variableName = placeholder.Substring(1); VariableFoundEventArgs args = new VariableFoundEventArgs(placeholder.Substring(1), String.Empty, String.Empty, context.ToArray()); if (VariableFound != null) { VariableFound(this, args); - placeholder = "@" + args.Name; + variableName = args.Name; + } + argument = new VariableArgument(variableName); + } + else if (RegexHelper.IsString(placeholder)) + { + string value = placeholder.Trim('\''); + argument = new StringArgument(value); + } + else if (RegexHelper.IsNumber(placeholder)) + { + decimal number; + if (Decimal.TryParse(placeholder, out number)) + { + argument = new NumberArgument(number); } } else { + string placeholderName = placeholder; PlaceholderFoundEventArgs args = new PlaceholderFoundEventArgs(placeholder, String.Empty, String.Empty, context.ToArray()); if (PlaceholderFound != null) { PlaceholderFound(this, args); - placeholder = args.Key; + placeholderName = args.Key; } + argument = new PlaceholderArgument(placeholderName); } } - collection.AddArgument(pair.Key, placeholder); + collection.AddArgument(pair.Key, argument); } return collection; } diff --git a/mustache-sharp/IArgument.cs b/mustache-sharp/IArgument.cs new file mode 100644 index 0000000..4877cc2 --- /dev/null +++ b/mustache-sharp/IArgument.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Mustache +{ + public interface IArgument + { + string GetKey(); + + object GetValue(Scope keyScope, Scope contextScope); + } +} diff --git a/mustache-sharp/NumberArgument.cs b/mustache-sharp/NumberArgument.cs new file mode 100644 index 0000000..ec40ae4 --- /dev/null +++ b/mustache-sharp/NumberArgument.cs @@ -0,0 +1,24 @@ +using System; + +namespace Mustache +{ + public class NumberArgument : IArgument + { + private readonly decimal value; + + public NumberArgument(decimal value) + { + this.value = value; + } + + public string GetKey() + { + return null; + } + + public object GetValue(Scope keyScope, Scope contextScope) + { + return value; + } + } +} diff --git a/mustache-sharp/PlaceholderArgument.cs b/mustache-sharp/PlaceholderArgument.cs new file mode 100644 index 0000000..39047f3 --- /dev/null +++ b/mustache-sharp/PlaceholderArgument.cs @@ -0,0 +1,24 @@ +using System; + +namespace Mustache +{ + public class PlaceholderArgument : IArgument + { + private readonly string name; + + public PlaceholderArgument(string name) + { + this.name = name; + } + + public string GetKey() + { + return name; + } + + public object GetValue(Scope keyScope, Scope contextScope) + { + return keyScope.Find(name); + } + } +} diff --git a/mustache-sharp/RegexHelper.cs b/mustache-sharp/RegexHelper.cs index c6738cb..c3bbc1a 100644 --- a/mustache-sharp/RegexHelper.cs +++ b/mustache-sharp/RegexHelper.cs @@ -6,10 +6,13 @@ namespace Mustache /// /// Provides utility methods that require regular expressions. /// - public static class RegexHelper + internal static class RegexHelper { - internal const string Key = @"[_\w][_\w\d]*"; - internal const string CompoundKey = Key + @"(\." + Key + ")*"; + public const string Key = @"[_\w][_\w\d]*"; + public const string String = @"'.*?'"; + public const string Number = @"[-+]?\d*\.?\d+"; + public const string CompoundKey = "@?" + Key + @"(?:\." + Key + ")*"; + public const string Argument = @"(?:(?" + CompoundKey + @")|(?" + String + @")|(?" + Number + @"))"; /// /// Determines whether the given name is a legal identifier. @@ -25,5 +28,25 @@ namespace Mustache Regex regex = new Regex("^" + Key + "$"); return regex.IsMatch(name); } + + public static bool IsString(string value) + { + if (value == null) + { + return false; + } + Regex regex = new Regex("^" + String + "$"); + return regex.IsMatch(value); + } + + public static bool IsNumber(string value) + { + if (value == null) + { + return false; + } + Regex regex = new Regex("^" + Number + "$"); + return regex.IsMatch(value); + } } } diff --git a/mustache-sharp/StringArgument.cs b/mustache-sharp/StringArgument.cs new file mode 100644 index 0000000..36aeec6 --- /dev/null +++ b/mustache-sharp/StringArgument.cs @@ -0,0 +1,24 @@ +using System; + +namespace Mustache +{ + public class StringArgument : IArgument + { + private readonly string value; + + public StringArgument(string value) + { + this.value = value; + } + + public string GetKey() + { + return null; + } + + public object GetValue(Scope keyScope, Scope contextScope) + { + return value; + } + } +} diff --git a/mustache-sharp/VariableArgument.cs b/mustache-sharp/VariableArgument.cs new file mode 100644 index 0000000..c59e128 --- /dev/null +++ b/mustache-sharp/VariableArgument.cs @@ -0,0 +1,24 @@ +using System; + +namespace Mustache +{ + public class VariableArgument : IArgument + { + private readonly string name; + + public VariableArgument(string name) + { + this.name = name; + } + + public string GetKey() + { + return null; + } + + public object GetValue(Scope keyScope, Scope contextScope) + { + return contextScope.Find(name); + } + } +} diff --git a/mustache-sharp/mustache-sharp.csproj b/mustache-sharp/mustache-sharp.csproj index 7e35b1b..22eecdc 100644 --- a/mustache-sharp/mustache-sharp.csproj +++ b/mustache-sharp/mustache-sharp.csproj @@ -40,7 +40,12 @@ + + + + +