Allow string and number arguments

This commit is contained in:
Travis Parks 2014-05-21 17:02:31 -04:00
parent 60f8294eb4
commit 19a0710d49
10 changed files with 240 additions and 24 deletions

View File

@ -1387,5 +1387,69 @@ Odd
} }
#endregion #endregion
#region Strings
/// <summary>
/// We will use a string variable to determine whether or not to print out a line.
/// </summary>
[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.");
}
/// <summary>
/// We will use a string variable to determine whether or not to print out a line.
/// </summary>
[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
/// <summary>
/// We will use a number variable to determine whether or not to print out a line.
/// </summary>
[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.");
}
/// <summary>
/// We will use a string variable to determine whether or not to print out a line.
/// </summary>
[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
} }
} }

View File

@ -9,25 +9,25 @@ namespace Mustache
/// </summary> /// </summary>
internal sealed class ArgumentCollection internal sealed class ArgumentCollection
{ {
private readonly Dictionary<TagParameter, string> _argumentLookup; private readonly Dictionary<TagParameter, IArgument> _argumentLookup;
/// <summary> /// <summary>
/// Initializes a new instance of an ArgumentCollection. /// Initializes a new instance of an ArgumentCollection.
/// </summary> /// </summary>
public ArgumentCollection() public ArgumentCollection()
{ {
_argumentLookup = new Dictionary<TagParameter, string>(); _argumentLookup = new Dictionary<TagParameter, IArgument>();
} }
/// <summary> /// <summary>
/// Associates the given parameter to the key placeholder. /// Associates the given parameter to the key placeholder.
/// </summary> /// </summary>
/// <param name="parameter">The parameter to associate the key with.</param> /// <param name="parameter">The parameter to associate the key with.</param>
/// <param name="key">The key placeholder used as the argument.</param> /// <param name="key">The argument.</param>
/// <remarks>If the key is null, the default value of the parameter will be used.</remarks> /// <remarks>If the key is null, the default value of the parameter will be used.</remarks>
public void AddArgument(TagParameter parameter, string key) public void AddArgument(TagParameter parameter, IArgument argument)
{ {
_argumentLookup.Add(parameter, key); _argumentLookup.Add(parameter, argument);
} }
/// <summary> /// <summary>
@ -36,10 +36,10 @@ namespace Mustache
/// <param name="parameterName">The name of the parameter.</param> /// <param name="parameterName">The name of the parameter.</param>
public string GetKey(TagParameter parameter) public string GetKey(TagParameter parameter)
{ {
string key; IArgument argument;
if (_argumentLookup.TryGetValue(parameter, out key)) if (_argumentLookup.TryGetValue(parameter, out argument) && argument != null)
{ {
return key; return argument.GetKey();
} }
else else
{ {
@ -56,20 +56,16 @@ namespace Mustache
public Dictionary<string, object> GetArguments(Scope keyScope, Scope contextScope) public Dictionary<string, object> GetArguments(Scope keyScope, Scope contextScope)
{ {
Dictionary<string, object> arguments = new Dictionary<string,object>(); Dictionary<string, object> arguments = new Dictionary<string,object>();
foreach (KeyValuePair<TagParameter, string> pair in _argumentLookup) foreach (KeyValuePair<TagParameter, IArgument> pair in _argumentLookup)
{ {
object value; object value;
if (pair.Value == null) if (pair.Value == null)
{ {
value = pair.Key.DefaultValue; value = pair.Key.DefaultValue;
} }
else if (pair.Value.StartsWith("@"))
{
value = contextScope.Find(pair.Value.Substring(1));
}
else else
{ {
value = keyScope.Find(pair.Value); value = pair.Value.GetValue(keyScope, contextScope);
} }
arguments.Add(pair.Key.Name, value); arguments.Add(pair.Key.Name, value);
} }
@ -78,7 +74,7 @@ namespace Mustache
public Dictionary<string, object> GetArgumentKeyNames() public Dictionary<string, object> GetArgumentKeyNames()
{ {
return _argumentLookup.ToDictionary(p => p.Key.Name, p => (object)p.Value); return _argumentLookup.ToDictionary(p => p.Key.Name, p => (object)GetKey(p.Key));
} }
} }
} }

View File

@ -152,7 +152,7 @@ namespace Mustache
private static string getKeyRegex() private static string getKeyRegex()
{ {
return @"((?<key>@?" + RegexHelper.CompoundKey + @")(,(?<alignment>(\+|-)?[\d]+))?(:(?<format>.*?))?)"; return @"((?<key>" + RegexHelper.CompoundKey + @")(,(?<alignment>(\+|-)?[\d]+))?(:(?<format>.*?))?)";
} }
private static string getTagRegex(TagDefinition definition) private static string getTagRegex(TagDefinition definition)
@ -164,8 +164,8 @@ namespace Mustache
foreach (TagParameter parameter in definition.Parameters) foreach (TagParameter parameter in definition.Parameters)
{ {
regexBuilder.Append(@"(\s+?"); regexBuilder.Append(@"(\s+?");
regexBuilder.Append(@"(?<argument>(@?"); regexBuilder.Append(@"(?<argument>(");
regexBuilder.Append(RegexHelper.CompoundKey); regexBuilder.Append(RegexHelper.Argument);
regexBuilder.Append(@")))"); regexBuilder.Append(@")))");
if (!parameter.IsRequired) if (!parameter.IsRequired)
{ {
@ -341,28 +341,46 @@ namespace Mustache
foreach (var pair in arguments) foreach (var pair in arguments)
{ {
string placeholder = pair.Value; string placeholder = pair.Value;
IArgument argument = null;
if (placeholder != null) if (placeholder != null)
{ {
if (placeholder.StartsWith("@")) if (placeholder.StartsWith("@"))
{ {
string variableName = placeholder.Substring(1);
VariableFoundEventArgs args = new VariableFoundEventArgs(placeholder.Substring(1), String.Empty, String.Empty, context.ToArray()); VariableFoundEventArgs args = new VariableFoundEventArgs(placeholder.Substring(1), String.Empty, String.Empty, context.ToArray());
if (VariableFound != null) if (VariableFound != null)
{ {
VariableFound(this, args); 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 else
{ {
string placeholderName = placeholder;
PlaceholderFoundEventArgs args = new PlaceholderFoundEventArgs(placeholder, String.Empty, String.Empty, context.ToArray()); PlaceholderFoundEventArgs args = new PlaceholderFoundEventArgs(placeholder, String.Empty, String.Empty, context.ToArray());
if (PlaceholderFound != null) if (PlaceholderFound != null)
{ {
PlaceholderFound(this, args); PlaceholderFound(this, args);
placeholder = args.Key; placeholderName = args.Key;
}
argument = new PlaceholderArgument(placeholderName);
} }
} }
} collection.AddArgument(pair.Key, argument);
collection.AddArgument(pair.Key, placeholder);
} }
return collection; return collection;
} }

View File

@ -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);
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -6,10 +6,13 @@ namespace Mustache
/// <summary> /// <summary>
/// Provides utility methods that require regular expressions. /// Provides utility methods that require regular expressions.
/// </summary> /// </summary>
public static class RegexHelper internal static class RegexHelper
{ {
internal const string Key = @"[_\w][_\w\d]*"; public const string Key = @"[_\w][_\w\d]*";
internal const string CompoundKey = Key + @"(\." + Key + ")*"; public const string String = @"'.*?'";
public const string Number = @"[-+]?\d*\.?\d+";
public const string CompoundKey = "@?" + Key + @"(?:\." + Key + ")*";
public const string Argument = @"(?:(?<arg_key>" + CompoundKey + @")|(?<arg_string>" + String + @")|(?<arg_number>" + Number + @"))";
/// <summary> /// <summary>
/// Determines whether the given name is a legal identifier. /// Determines whether the given name is a legal identifier.
@ -25,5 +28,25 @@ namespace Mustache
Regex regex = new Regex("^" + Key + "$"); Regex regex = new Regex("^" + Key + "$");
return regex.IsMatch(name); 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);
}
} }
} }

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}

View File

@ -40,7 +40,12 @@
<Compile Include="ContentTagDefinition.cs" /> <Compile Include="ContentTagDefinition.cs" />
<Compile Include="Context.cs" /> <Compile Include="Context.cs" />
<Compile Include="ContextParameter.cs" /> <Compile Include="ContextParameter.cs" />
<Compile Include="IArgument.cs" />
<Compile Include="NumberArgument.cs" />
<Compile Include="PlaceholderArgument.cs" />
<Compile Include="StringArgument.cs" />
<Compile Include="UpcastDictionary.cs" /> <Compile Include="UpcastDictionary.cs" />
<Compile Include="VariableArgument.cs" />
<Compile Include="VariableFoundEventArgs.cs" /> <Compile Include="VariableFoundEventArgs.cs" />
<Compile Include="SetTagDefinition.cs" /> <Compile Include="SetTagDefinition.cs" />
<Compile Include="NewlineTagDefinition.cs" /> <Compile Include="NewlineTagDefinition.cs" />