Skip to content

Commit

Permalink
Fix #244: introduce fresh context for each predefined formula
Browse files Browse the repository at this point in the history
  • Loading branch information
ForNeVeR committed Nov 7, 2020
1 parent c51be28 commit 6aad0b5
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 114 deletions.
18 changes: 18 additions & 0 deletions src/WpfMath/Parsers/PredefinedFormulae/PredefinedFormulaContext.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Collections.Generic;

namespace WpfMath.Parsers.PredefinedFormulae
{
/// <summary>
/// This is a context for parsing a predefined formula. <c>PredefinedTexFormulas.xml</c> allows the users to
/// introduce their own named formulae via <example>&lt;CreateFormula name="f"&gt;</example>, and then call methods
/// on them.
/// <para/>
/// This context is the storage of these named formula values.
/// </summary>
internal class PredefinedFormulaContext
{
private readonly Dictionary<string, TexFormula> _formulae = new Dictionary<string, TexFormula>();
public void AddFormula(string name, TexFormula formula) => _formulae.Add(name, formula);
public TexFormula this[string name] => _formulae[name];
}
}
155 changes: 41 additions & 114 deletions src/WpfMath/TexPredefinedFormulaParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Reflection;
using System.Windows.Media;
using System.Xml.Linq;
using WpfMath.Parsers.PredefinedFormulae;

namespace WpfMath
{
Expand All @@ -25,7 +26,6 @@ static TexPredefinedFormulaParser()
argValueParsers = new Dictionary<string, ArgumentValueParser>();
actionParsers = new Dictionary<string, ActionParser>();
formulaParser = new TexFormulaParser();
var sharedCacheFormulas = new Dictionary<string, TexFormula>();

typeMappings.Add("Formula", typeof(TexFormula));
typeMappings.Add("string", typeof(string));
Expand All @@ -37,19 +37,19 @@ static TexPredefinedFormulaParser()
typeMappings.Add("Unit", typeof(TexUnit));
typeMappings.Add("AtomType", typeof(TexAtomType));

actionParsers.Add("CreateFormula", new CreateTeXFormulaParser(sharedCacheFormulas));
actionParsers.Add("MethodInvocation", new MethodInvocationParser(sharedCacheFormulas));
actionParsers.Add("Return", new ReturnParser(sharedCacheFormulas));

argValueParsers.Add("Formula", new TeXFormulaValueParser(sharedCacheFormulas));
argValueParsers.Add("string", new StringValueParser(sharedCacheFormulas));
argValueParsers.Add("double", new DoubleValueParser(sharedCacheFormulas));
argValueParsers.Add("int", new IntValueParser(sharedCacheFormulas));
argValueParsers.Add("bool", new BooleanValueParser(sharedCacheFormulas));
argValueParsers.Add("char", new CharValueParser(sharedCacheFormulas));
argValueParsers.Add("Color", new ColorConstantValueParser(sharedCacheFormulas));
argValueParsers.Add("Unit", new EnumParser(typeof(TexUnit), sharedCacheFormulas));
argValueParsers.Add("AtomType", new EnumParser(typeof(TexAtomType), sharedCacheFormulas));
actionParsers.Add("CreateFormula", new CreateTeXFormulaParser());
actionParsers.Add("MethodInvocation", new MethodInvocationParser());
actionParsers.Add("Return", new ReturnParser());

argValueParsers.Add("Formula", new TeXFormulaValueParser());
argValueParsers.Add("string", new StringValueParser());
argValueParsers.Add("double", new DoubleValueParser());
argValueParsers.Add("int", new IntValueParser());
argValueParsers.Add("bool", new BooleanValueParser());
argValueParsers.Add("char", new CharValueParser());
argValueParsers.Add("Color", new ColorConstantValueParser());
argValueParsers.Add("Unit", new EnumParser(typeof(TexUnit)));
argValueParsers.Add("AtomType", new EnumParser(typeof(TexAtomType)));
}

private static Type[] GetArgumentTypes(IEnumerable<XElement> args)
Expand All @@ -66,7 +66,7 @@ private static Type[] GetArgumentTypes(IEnumerable<XElement> args)
return result.ToArray();
}

private static object?[] GetArgumentValues(IEnumerable<XElement> args)
private static object?[] GetArgumentValues(IEnumerable<XElement> args, PredefinedFormulaContext context)
{
var result = new List<object?>();
foreach (var curArg in args)
Expand All @@ -75,7 +75,7 @@ private static Type[] GetArgumentTypes(IEnumerable<XElement> args)
var value = curArg.AttributeValue("value");

var parser = ((ArgumentValueParser)argValueParsers[typeName]);
result.Add(parser.Parse(value, typeName));
result.Add(parser.Parse(value, typeName, context));
}

return result.ToArray();
Expand Down Expand Up @@ -108,13 +108,14 @@ public void Parse(IDictionary<string, Func<SourceSpan, TexFormula?>> predefinedT

public TexFormula? ParseFormula(SourceSpan source, XElement formulaElement)
{
var context = new PredefinedFormulaContext();
foreach (var element in formulaElement.Elements())
{
var parser = actionParsers[element.Name.ToString()];
if (parser == null)
continue;

parser.Parse(source, element);
parser.Parse(source, element, context);
if (parser is ReturnParser)
return ((ReturnParser)parser).Result;
}
Expand All @@ -123,48 +124,34 @@ public void Parse(IDictionary<string, Func<SourceSpan, TexFormula?>> predefinedT

public class MethodInvocationParser : ActionParser
{
public MethodInvocationParser(IDictionary<string, TexFormula> sharedCacheFormulas)
: base(sharedCacheFormulas)
{
}

public override void Parse(SourceSpan source, XElement element)
public override void Parse(SourceSpan source, XElement element, PredefinedFormulaContext context)
{
var methodName = element.AttributeValue("name");
var objectName = element.AttributeValue("formula");
var args = element.Elements("Argument");

var formula = this.SharedCacheFormulas[objectName];
var formula = context[objectName];
Debug.Assert(formula != null);

var argTypes = GetArgumentTypes(args);
var argValues = GetArgumentValues(args);
var argValues = GetArgumentValues(args, context);

var helper = new TexFormulaHelper(formula, source);
var methodInvocation = typeof(TexFormulaHelper).GetMethod(methodName, argTypes)!;

// Since PredefinedTexFormulas can be used again when MethodInvocation is executed,
// we need to protect ourselves from re-adding the key-value to the SharedCacheFormulas
this.SharedCacheFormulas.Remove(objectName);
methodInvocation.Invoke(helper, argValues);
this.SharedCacheFormulas[objectName] = formula;
}
}

public class CreateTeXFormulaParser : ActionParser
{
public CreateTeXFormulaParser(IDictionary<string, TexFormula> sharedCacheFormulas)
: base(sharedCacheFormulas)
{
}

public override void Parse(SourceSpan source, XElement element)
public override void Parse(SourceSpan source, XElement element, PredefinedFormulaContext context)
{
var name = element.AttributeValue("name");
var args = element.Elements("Argument");

var argTypes = GetArgumentTypes(args);
var argValues = GetArgumentValues(args);
var argValues = GetArgumentValues(args, context);

Debug.Assert(argValues.Length == 1 || argValues.Length == 0);
TexFormula formula;
Expand All @@ -178,54 +165,38 @@ public override void Parse(SourceSpan source, XElement element)
formula = new TexFormula { Source = source };
}

this.SharedCacheFormulas.Add(name, formula);
context.AddFormula(name, formula);
}
}

public class ReturnParser : ActionParser
{
public ReturnParser(IDictionary<string, TexFormula> sharedCacheFormulas)
: base(sharedCacheFormulas)
{
}

public TexFormula? Result
{
get;
private set;
}

public override void Parse(SourceSpan source, XElement element)
public override void Parse(SourceSpan source, XElement element, PredefinedFormulaContext context)
{
var name = element.AttributeValue("name");
var result = this.SharedCacheFormulas[name];
var result = context[name];
Debug.Assert(result != null);
this.Result = result;
this.SharedCacheFormulas.Clear();
}
}

public class DoubleValueParser : ArgumentValueParser
{
public DoubleValueParser(IDictionary<string, TexFormula> sharedCacheFormulas)
: base(sharedCacheFormulas)
{
}

public override object Parse(string value, string type)
public override object Parse(string value, string type, PredefinedFormulaContext context)
{
return double.Parse(value, CultureInfo.InvariantCulture);
}
}

public class CharValueParser : ArgumentValueParser
{
public CharValueParser(IDictionary<string, TexFormula> sharedCacheFormulas)
: base(sharedCacheFormulas)
{
}

public override object Parse(string value, string type)
public override object Parse(string value, string type, PredefinedFormulaContext context)
{
Debug.Assert(value.Length == 1);
return value[0];
Expand All @@ -234,69 +205,44 @@ public override object Parse(string value, string type)

public class BooleanValueParser : ArgumentValueParser
{
public BooleanValueParser(IDictionary<string, TexFormula> sharedCacheFormulas)
: base(sharedCacheFormulas)
{
}

public override object Parse(string value, string type)
public override object Parse(string value, string type, PredefinedFormulaContext context)
{
return bool.Parse(value);
}
}

public class IntValueParser : ArgumentValueParser
{
public IntValueParser(IDictionary<string, TexFormula> sharedCacheFormulas)
: base(sharedCacheFormulas)
{
}

public override object Parse(string value, string type)
public override object Parse(string value, string type, PredefinedFormulaContext context)
{
return int.Parse(value, CultureInfo.InvariantCulture);
}
}

public class StringValueParser : ArgumentValueParser
{
public StringValueParser(IDictionary<string, TexFormula> sharedCacheFormulas)
: base(sharedCacheFormulas)
{
}

public override object Parse(string value, string type)
public override object Parse(string value, string type, PredefinedFormulaContext context)
{
return value;
}
}

public class TeXFormulaValueParser : ArgumentValueParser
{
public TeXFormulaValueParser(IDictionary<string, TexFormula> sharedCacheFormulas)
: base(sharedCacheFormulas)
{
}

public override object? Parse(string value, string type)
public override object? Parse(string value, string type, PredefinedFormulaContext context)
{
if (value == null)
return null;

var formula = this.SharedCacheFormulas[value];
var formula = context[value];
Debug.Assert(formula != null);
return (TexFormula)formula;
return formula;
}
}

public class ColorConstantValueParser : ArgumentValueParser
{
public ColorConstantValueParser(IDictionary<string, TexFormula> sharedCacheFormulas)
: base(sharedCacheFormulas)
{
}

public override object? Parse(string value, string type)
public override object? Parse(string value, string type, PredefinedFormulaContext context)
{
return typeof(Color).GetField(value)!.GetValue(null);
}
Expand All @@ -306,44 +252,25 @@ public class EnumParser : ArgumentValueParser
{
private Type enumType;

public EnumParser(Type enumType, IDictionary<string, TexFormula> sharedCacheFormulas)
: base(sharedCacheFormulas)
public EnumParser(Type enumType)
{
this.enumType = enumType;
}

public override object Parse(string value, string type)
public override object Parse(string value, string type, PredefinedFormulaContext context)
{
return Enum.Parse(this.enumType, value);
}
}

public abstract class ActionParser : ParserBase
public abstract class ActionParser
{
protected ActionParser(IDictionary<string, TexFormula> sharedCacheFormulas)
: base(sharedCacheFormulas)
{}

public abstract void Parse(SourceSpan source, XElement element);
public abstract void Parse(SourceSpan source, XElement element, PredefinedFormulaContext context);
}

public abstract class ArgumentValueParser : ParserBase
public abstract class ArgumentValueParser
{
protected ArgumentValueParser(IDictionary<string, TexFormula> sharedCacheFormulas)
: base(sharedCacheFormulas)
{}

public abstract object? Parse(string value, string type);
}

public abstract class ParserBase
{
public ParserBase(IDictionary<string, TexFormula> sharedCacheFormulas)
{
this.SharedCacheFormulas = sharedCacheFormulas;
}

public IDictionary<string, TexFormula> SharedCacheFormulas { get; }
public abstract object? Parse(string value, string type, PredefinedFormulaContext context);
}
}
}

0 comments on commit 6aad0b5

Please sign in to comment.