Skip to content

Commit

Permalink
Enables the expression language to have only single chars operations
Browse files Browse the repository at this point in the history
  • Loading branch information
npehrsson committed Mar 27, 2020
1 parent f3756a8 commit fe7e60e
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 37 deletions.
50 changes: 20 additions & 30 deletions Jace.Tests/Jace.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
Expand All @@ -17,6 +18,8 @@
<IsCodedUITest>False</IsCodedUITest>
<TestProjectType>UnitTest</TestProjectType>
<TargetFrameworkProfile />
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
Expand All @@ -38,23 +41,17 @@
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.2.0.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<HintPath>..\packages\MSTest.TestFramework.2.0.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core">
<RequiredTargetFramework>3.5</RequiredTargetFramework>
</Reference>
</ItemGroup>
<Choose>
<When Condition="('$(VisualStudioVersion)' == '10.0' or '$(VisualStudioVersion)' == '') and '$(TargetFrameworkVersion)' == 'v3.5'">
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework, Version=10.1.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL" />
</ItemGroup>
</When>
<Otherwise>
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.UnitTestFramework" />
</ItemGroup>
</Otherwise>
</Choose>
<ItemGroup>
<Compile Include="AssertExtensions.cs" />
<Compile Include="AstBuilderTests.cs" />
Expand All @@ -75,26 +72,19 @@
<Name>Jace</Name>
</ProjectReference>
</ItemGroup>
<Choose>
<When Condition="'$(VisualStudioVersion)' == '10.0' And '$(IsCodedUITest)' == 'True'">
<ItemGroup>
<Reference Include="Microsoft.VisualStudio.QualityTools.CodedUITestFramework, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Common, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITest.Extension, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.VisualStudio.TestTools.UITesting, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
</ItemGroup>
</When>
</Choose>
<ItemGroup>
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.props'))" />
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.targets'))" />
</Target>
<Import Project="..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.2.0.0\build\net45\MSTest.TestAdapter.targets')" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
Expand Down
23 changes: 23 additions & 0 deletions Jace.Tests/TokenReaderTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,29 @@ public void TestTokenReader1()
Assert.AreEqual(2, tokens[2].Length);
}

[TestMethod]
[DataRow("42==31", '=', false)]
[DataRow("42=31", '=', true)]
[DataRow("42!=31", '≠', false)]
[DataRow("42!31", '≠', true)]
[DataRow("42||31", '|', false)]
[DataRow("42|31", '|', true)]
[DataRow("42&&31", '&', false)]
[DataRow("42&31", '&', true)]
public void TestTokenReader_Equals(string expression, object token, bool enableSingleCharacterOperations)
{
TokenReader reader = new TokenReader(new JaceOptions() { EnableSingleCharacterOperations = enableSingleCharacterOperations });
List<Token> tokens = reader.Read(expression);

Assert.AreEqual(3, tokens.Count);

Assert.AreEqual(42, tokens[0].Value);

Assert.AreEqual(token, tokens[1].Value);

Assert.AreEqual(31, tokens[2].Value);
}

[TestMethod]
public void TestTokenReader2()
{
Expand Down
6 changes: 6 additions & 0 deletions Jace.Tests/packages.config
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.VisualStudio.TestPlatform" version="14.0.0.0" targetFramework="net461" />
<package id="MSTest.TestAdapter" version="2.0.0" targetFramework="net461" />
<package id="MSTest.TestFramework" version="2.0.0" targetFramework="net461" />
</packages>
7 changes: 6 additions & 1 deletion Jace/CalculationEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ namespace Jace
/// </summary>
public class CalculationEngine
{
private readonly JaceOptions options;
private readonly IExecutor executor;
private readonly Optimizer optimizer;
private readonly CultureInfo cultureInfo;
private readonly MemoryCache<string, Func<IDictionary<string, double>, double>> executionFormulaCache;
private readonly bool cacheEnabled;
private readonly bool optimizerEnabled;
private readonly bool caseSensitive;
private readonly bool enableSingleCharacterOperations;

/// <summary>
/// Creates a new instance of the <see cref="CalculationEngine"/> class with
Expand Down Expand Up @@ -106,13 +108,15 @@ public CalculationEngine(CultureInfo cultureInfo, ExecutionMode executionMode, b
/// <param name="options">The <see cref="JaceOptions"/> to configure the behaviour of the engine.</param>
public CalculationEngine(JaceOptions options)
{
this.options = options;
this.executionFormulaCache = new MemoryCache<string, Func<IDictionary<string, double>, double>>(options.CacheMaximumSize, options.CacheReductionSize);
this.FunctionRegistry = new FunctionRegistry(false);
this.ConstantRegistry = new ConstantRegistry(false);
this.cultureInfo = options.CultureInfo;
this.cacheEnabled = options.CacheEnabled;
this.optimizerEnabled = options.OptimizerEnabled;
this.caseSensitive = options.CaseSensitive;
this.enableSingleCharacterOperations = options.EnableSingleCharacterOperations;

if (options.ExecutionMode == ExecutionMode.Interpreted)
executor = new Interpreter(caseSensitive);
Expand Down Expand Up @@ -420,7 +424,8 @@ private void RegisterDefaultConstants()
/// <returns>The abstract syntax tree of the formula.</returns>
private Operation BuildAbstractSyntaxTree(string formulaText, ConstantRegistry compiledConstants)
{
TokenReader tokenReader = new TokenReader(cultureInfo);
TokenReader tokenReader = new TokenReader(cultureInfo, options);

List<Token> tokens = tokenReader.Read(formulaText);

AstBuilder astBuilder = new AstBuilder(FunctionRegistry, caseSensitive, compiledConstants);
Expand Down
8 changes: 4 additions & 4 deletions Jace/JaceOptions.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
using Jace.Execution;
using System;
using System.Collections.Generic;
using System;
using Jace.Execution;
using System.Globalization;
using System.Text;

namespace Jace
{
Expand All @@ -22,6 +20,7 @@ public JaceOptions()
DefaultConstants = true;
CacheMaximumSize = DefaultCacheMaximumSize;
CacheReductionSize = DefaultCacheReductionSize;
EnableSingleCharacterOperations = false;
}

/// <summary>
Expand Down Expand Up @@ -83,5 +82,6 @@ public bool AdjustVariableCase {
/// Enable or disable the default constants.
/// </summary>
public bool DefaultConstants { get; set; }
public bool EnableSingleCharacterOperations { get; set; }
}
}
38 changes: 36 additions & 2 deletions Jace/Tokenizer/TokenReader.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;

namespace Jace.Tokenizer
Expand All @@ -14,19 +13,34 @@ public class TokenReader
private readonly CultureInfo cultureInfo;
private readonly char decimalSeparator;
private readonly char argumentSeparator;
private readonly bool enableSingleCharacterOperations;

public TokenReader(JaceOptions options)
: this()
{
if (options == null) throw new ArgumentNullException(nameof(options));
enableSingleCharacterOperations = options.EnableSingleCharacterOperations;
}

public TokenReader()
: this(CultureInfo.CurrentCulture)
{
}

public TokenReader(CultureInfo cultureInfo, JaceOptions options)
: this(cultureInfo)
{
if (options == null) throw new ArgumentNullException(nameof(options));
enableSingleCharacterOperations = options.EnableSingleCharacterOperations;
}

public TokenReader(CultureInfo cultureInfo)
{
this.cultureInfo = cultureInfo;
this.decimalSeparator = cultureInfo.NumberFormat.NumberDecimalSeparator[0];
this.argumentSeparator = cultureInfo.TextInfo.ListSeparator[0];
}

/// <summary>
/// Read in the provided formula and convert it into a list of takens that can be processed by the
/// Abstract Syntax Tree Builder.
Expand Down Expand Up @@ -182,6 +196,11 @@ public List<Token> Read(string formula)
{
tokens.Add(new Token() { TokenType = TokenType.Operation, Value = '≠', StartPosition = i++, Length = 2 });
isFormulaSubPart = false;
}
else if (enableSingleCharacterOperations)
{
tokens.Add(new Token() { TokenType = TokenType.Operation, Value = '≠', StartPosition = i, Length = 1 });
isFormulaSubPart = false;
}
else
throw new ParseException(string.Format("Invalid token \"{0}\" detected at position {1}.", characters[i], i));
Expand All @@ -192,6 +211,11 @@ public List<Token> Read(string formula)
tokens.Add(new Token() { TokenType = TokenType.Operation, Value = '&', StartPosition = i++, Length = 2 });
isFormulaSubPart = false;
}
else if (enableSingleCharacterOperations)
{
tokens.Add(new Token() { TokenType = TokenType.Operation, Value = '&', StartPosition = i, Length = 1 });
isFormulaSubPart = false;
}
else
throw new ParseException(string.Format("Invalid token \"{0}\" detected at position {1}.", characters[i], i));
break;
Expand All @@ -201,6 +225,11 @@ public List<Token> Read(string formula)
tokens.Add(new Token() { TokenType = TokenType.Operation, Value = '|', StartPosition = i++, Length = 2 });
isFormulaSubPart = false;
}
else if (enableSingleCharacterOperations)
{
tokens.Add(new Token() { TokenType = TokenType.Operation, Value = '|', StartPosition = i, Length = 1 });
isFormulaSubPart = false;
}
else
throw new ParseException(string.Format("Invalid token \"{0}\" detected at position {1}.", characters[i], i));
break;
Expand All @@ -210,6 +239,11 @@ public List<Token> Read(string formula)
tokens.Add(new Token() { TokenType = TokenType.Operation, Value = '=', StartPosition = i++, Length = 2 });
isFormulaSubPart = false;
}
else if (enableSingleCharacterOperations)
{
tokens.Add(new Token() { TokenType = TokenType.Operation, Value = '=', StartPosition = i, Length = 1 });
isFormulaSubPart = false;
}
else
throw new ParseException(string.Format("Invalid token \"{0}\" detected at position {1}.", characters[i], i));
break;
Expand Down

0 comments on commit fe7e60e

Please sign in to comment.