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
#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>
internal sealed class ArgumentCollection
{
private readonly Dictionary<TagParameter, string> _argumentLookup;
private readonly Dictionary<TagParameter, IArgument> _argumentLookup;
/// <summary>
/// Initializes a new instance of an ArgumentCollection.
/// </summary>
public ArgumentCollection()
{
_argumentLookup = new Dictionary<TagParameter, string>();
_argumentLookup = new Dictionary<TagParameter, IArgument>();
}
/// <summary>
/// Associates the given parameter to the key placeholder.
/// </summary>
/// <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>
public void AddArgument(TagParameter parameter, string key)
public void AddArgument(TagParameter parameter, IArgument argument)
{
_argumentLookup.Add(parameter, key);
_argumentLookup.Add(parameter, argument);
}
/// <summary>
@ -36,10 +36,10 @@ namespace Mustache
/// <param name="parameterName">The name of the parameter.</param>
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<string, object> GetArguments(Scope keyScope, Scope contextScope)
{
Dictionary<string, object> arguments = new Dictionary<string,object>();
foreach (KeyValuePair<TagParameter, string> pair in _argumentLookup)
foreach (KeyValuePair<TagParameter, IArgument> 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<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()
{
return @"((?<key>@?" + RegexHelper.CompoundKey + @")(,(?<alignment>(\+|-)?[\d]+))?(:(?<format>.*?))?)";
return @"((?<key>" + RegexHelper.CompoundKey + @")(,(?<alignment>(\+|-)?[\d]+))?(:(?<format>.*?))?)";
}
private static string getTagRegex(TagDefinition definition)
@ -164,8 +164,8 @@ namespace Mustache
foreach (TagParameter parameter in definition.Parameters)
{
regexBuilder.Append(@"(\s+?");
regexBuilder.Append(@"(?<argument>(@?");
regexBuilder.Append(RegexHelper.CompoundKey);
regexBuilder.Append(@"(?<argument>(");
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;
}

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>
/// Provides utility methods that require regular expressions.
/// </summary>
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 = @"(?:(?<arg_key>" + CompoundKey + @")|(?<arg_string>" + String + @")|(?<arg_number>" + Number + @"))";
/// <summary>
/// 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);
}
}
}

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="Context.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="VariableArgument.cs" />
<Compile Include="VariableFoundEventArgs.cs" />
<Compile Include="SetTagDefinition.cs" />
<Compile Include="NewlineTagDefinition.cs" />