Skip to content

Commit

Permalink
Version 1.2.1.3: Added support for string case conversion (tolower / …
Browse files Browse the repository at this point in the history
…toupper)
  • Loading branch information
timothylcooke committed Jul 5, 2018
1 parent b1d2136 commit 1c0f84e
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 12 deletions.
25 changes: 23 additions & 2 deletions MathConverter/Parser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ private AbstractSyntaxTree Primary()
var lex = (t as LexicalToken).Lex;
Func<object> formula0 = null;
Func<double, double> formula1 = null;
Func<object, object> formula1_obj = null;
Func<object, object, object> formula2 = null;
Func<IEnumerable<object>, object> formulaN = null;
switch (lex.ToLower())
Expand Down Expand Up @@ -353,6 +354,14 @@ private AbstractSyntaxTree Primary()
case "rad":
formula1 = x => x / 180 * Math.PI;
break;
case "tolower":
case "lcase":
formula1_obj = x => x == null ? null : $"{x}".ToLowerInvariant();
break;
case "toupper":
case "ucase":
formula1_obj = x => x == null ? null : $"{x}".ToUpperInvariant();
break;
case "round":
formula2 = (x, y) =>
{
Expand Down Expand Up @@ -465,7 +474,7 @@ private AbstractSyntaxTree Primary()

return new FormulaNode0(lex, formula0);
}
else if (formula1 != null)
else if (formula1 != null || formula1_obj != null)
{
// Create a formula1.
var ex = $"{lex} is a formula that takes one argument. You must specify the arguments like this: \"{lex}(3)\"";
Expand All @@ -487,7 +496,19 @@ private AbstractSyntaxTree Primary()
if (scanner.GetToken().TokenType != TokenType.RParen)
throw new ParsingException(scanner.Position, ex);

return new FormulaNode1(lex, formula1, arg);
if (formula1 != null)
{
formula1_obj = x =>
{
var val = MathConverter.ConvertToDouble(x);
if (val.HasValue)
return formula1(val.Value);
else
return null;
};
}

return new FormulaNode1(lex, formula1_obj, arg);
}
else if (formula2 != null)
{
Expand Down
4 changes: 2 additions & 2 deletions MathConverter/Properties/AssemblyInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.2.1.2")]
[assembly: AssemblyFileVersion("1.2.1.2")]
[assembly: AssemblyVersion("1.2.1.3")]
[assembly: AssemblyFileVersion("1.2.1.3")]

[assembly: XmlnsPrefix("http://hexinnovation.com/math", "math")]
[assembly: XmlnsDefinition("http://hexinnovation.com/math", "HexInnovation")]
Expand Down
10 changes: 3 additions & 7 deletions MathConverter/SyntaxTree.cs
Original file line number Diff line number Diff line change
Expand Up @@ -483,23 +483,19 @@ public override string ToString()
/// </summary>
class FormulaNode1 : AbstractSyntaxTree
{
public FormulaNode1(string FormulaName, Func<double, double> formula, AbstractSyntaxTree input)
public FormulaNode1(string FormulaName, Func<object, object> formula, AbstractSyntaxTree input)
{
this.FormulaName = FormulaName;
this.formula = formula;
this.input = input;
}
private string FormulaName;
private Func<double, double> formula;
private Func<object, object> formula;
private AbstractSyntaxTree input;

public override object Evaluate(object[] Parameters)
{
var value = MathConverter.ConvertToDouble(input.Evaluate(Parameters));
if (value == null)
return null;
else
return formula(value.Value);
return formula(input.Evaluate(Parameters));
}
public override string ToString()
{
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ The Converter `format("Language {0}: {1}",x+1,y)` will essentially `return strin

The real magic being done here is in the [`System.String.Format`](https://msdn.microsoft.com/en-us/library/b1csw23d(v=vs.110).aspx) method. If you unfamiliar with this method, you can see a lot of examples [here](http://msdn.microsoft.com/en-us/library/txafckwd).
`format` is another example of an N-value function. The only difference is that this particular function does not return a numeric value, but rather returns a string. Other non-numeric N-value functions are: `and`, `or`, `nor`. These functions take and return boolean values. The zero-value function function `now()` returns [`System.DateTime.Now`](https://msdn.microsoft.com/en-us/library/system.datetime.now(v=vs.110).aspx)
`format` is another example of an N-value function. The only difference is that this particular function does not return a numeric value, but rather returns a string. Other non-numeric N-value functions are: `and`, `or`, `nor`. These functions take and return boolean values. The zero-value function function `now()` returns [`System.DateTime.Now`](https://msdn.microsoft.com/en-us/library/system.datetime.now(v=vs.110).aspx). There are also one-value functions useful for case-conversion: `tolower` / `lcase`, and `toupper` / `ucase`. When passed `null`, these functions will return `null`. When passed an object, they will call `ToString()` on the object and then convert to lower/upper case.
Let's look again at the `ConverterParameter` in the previous example: `ConverterParameter="format(&quot;Language {0}: {1}&quot;,x+1,y)"`. Because this is xaml, the `&quot;` characters are converted to `"` characters. Thus, at runtime, the `ConverterParameter` is `format("Language {0}: {1}",x+1,y)`. When `MathConverter` parses the `ConverterParameter`, it sees that "Language {0}: {1}" is a string. In order to include special characters in the string, you can simply backslash-escape them, just like you're used to. So `\r`, `\n`, `\"`, and `\t` (among others) are valid special characters that can be added to strings. At the moment, arbitrary unicode characters (such as `\u0000`) are not supported, but these can be added if there is sufficient demand.

Expand Down
7 changes: 7 additions & 0 deletions UnitTests/UnitTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,13 @@ public void TestFunctions()
Assert.AreEqual(true, Converter.Convert(new object[] { new object[] { "hello", "world" } }, typeof(object), "contains(x, `hello`)", CultureInfo.GetCultureInfo("de")));
Assert.AreEqual(false, Converter.Convert(new object[] { new object[] { "hello", "world" } }, typeof(object), "contains(x, `Hello`)", CultureInfo.GetCultureInfo("de")));

Assert.AreEqual(true, Converter.Convert(new object[] { "ψñíçθдë têsт", "ΨÑÍÇΘДË TÊSТ" }, typeof(object), "toupper(x) == y", CultureInfo.GetCultureInfo("de")));
Assert.AreEqual(true, Converter.Convert(new object[] { "ψñíçθдë têsт", "ΨÑÍÇΘДË TÊSТ" }, typeof(object), "tolower(y) == x", CultureInfo.GetCultureInfo("de")));
Assert.AreEqual(true, Converter.Convert(new object[] { "ψñíçθдë têsт", "ΨÑÍÇΘДË TÊSТ" }, typeof(object), "toupper(y) == y", CultureInfo.GetCultureInfo("de")));
Assert.AreEqual(true, Converter.Convert(new object[] { "ψñíçθдë têsт", "ΨÑÍÇΘДË TÊSТ" }, typeof(object), "tolower(x) == x", CultureInfo.GetCultureInfo("de")));
Assert.AreEqual(true, Converter.Convert(new object[] { "ψñíçθдë têsт", "ΨÑÍÇΘДË TÊSТ" }, typeof(object), "toupper(y) != tolower(y)", CultureInfo.GetCultureInfo("de")));
Assert.AreEqual(true, Converter.Convert(new object[] { "ψñíçθдë têsт", "ΨÑÍÇΘДË TÊSТ" }, typeof(object), "toupper(x) != tolower(x)", CultureInfo.GetCultureInfo("de")));

foreach (var x in new bool[] { true, false })
{
Assert.AreEqual(x, Converter.Convert(new object[] { x }, typeof(object), "and(AND(true,true,true,true,true),x,true)", CultureInfo.GetCultureInfo("de")));
Expand Down

0 comments on commit 1c0f84e

Please sign in to comment.