diff --git a/Core/ProfitTrailer/StrategyHelper.cs b/Core/ProfitTrailer/StrategyHelper.cs index bd91e87..bd564c3 100644 --- a/Core/ProfitTrailer/StrategyHelper.cs +++ b/Core/ProfitTrailer/StrategyHelper.cs @@ -12,9 +12,212 @@ using Core.Main; using Core.Helper; using Core.Main.DataObjects.PTMagicData; using Newtonsoft.Json; +using System.Text.RegularExpressions; namespace Core.ProfitTrailer { + public class OperandToken : Token + { + } + public class OrToken : OperandToken + { + } + + public class AndToken : OperandToken + { + } + + public class BooleanValueToken : Token + { + } + + public class FalseToken : BooleanValueToken + { + } + + public class TrueToken : BooleanValueToken + { + } + + public class ParenthesisToken : Token + { + } + + public class ClosedParenthesisToken : ParenthesisToken + { + } + + public class OpenParenthesisToken : ParenthesisToken + { + } + + public class NegationToken : Token + { + } + + public abstract class Token + { + } + + public class Tokenizer + { + private readonly StringReader _reader; + private string _text; + + public Tokenizer(string text) + { + _text = text; + _reader = new StringReader(text); + } + + public IEnumerable Tokenize() + { + var tokens = new List(); + while (_reader.Peek() != -1) + { + while (Char.IsWhiteSpace((char) _reader.Peek())) + { + _reader.Read(); + } + + if (_reader.Peek() == -1) + break; + + var c = (char) _reader.Peek(); + switch (c) + { + case '!': + tokens.Add(new NegationToken()); + _reader.Read(); + break; + case '(': + tokens.Add(new OpenParenthesisToken()); + _reader.Read(); + break; + case ')': + tokens.Add(new ClosedParenthesisToken()); + _reader.Read(); + break; + default: + if (Char.IsLetter(c)) + { + var token = ParseKeyword(); + tokens.Add(token); + } + else + { + var remainingText = _reader.ReadToEnd() ?? string.Empty; + throw new Exception(string.Format("Unknown grammar found at position {0} : '{1}'", _text.Length - remainingText.Length, remainingText)); + } + break; + } + } + return tokens; + } + + private Token ParseKeyword() + { + var text = new StringBuilder(); + while (Char.IsLetter((char) _reader.Peek())) + { + text.Append((char) _reader.Read()); + } + + var potentialKeyword = text.ToString().ToLower(); + + switch (potentialKeyword) + { + case "true": + return new TrueToken(); + case "false": + return new FalseToken(); + case "and": + return new AndToken(); + case "or": + return new OrToken(); + default: + throw new Exception("Expected keyword (True, False, and, or) but found "+ potentialKeyword); + } + } + } + public class Parser + { + private readonly IEnumerator _tokens; + + public Parser(IEnumerable tokens) + { + _tokens = tokens.GetEnumerator(); + _tokens.MoveNext(); + } + + public bool Parse() + { + while (_tokens.Current != null) + { + var isNegated = _tokens.Current is NegationToken; + if (isNegated) + _tokens.MoveNext(); + + var boolean = ParseBoolean(); + if (isNegated) + boolean = !boolean; + + while (_tokens.Current is OperandToken) + { + var operand = _tokens.Current; + if (!_tokens.MoveNext()) + { + throw new Exception("Missing expression after operand"); + } + var nextBoolean = ParseBoolean(); + + if (operand is AndToken) + boolean = boolean && nextBoolean; + else + boolean = boolean || nextBoolean; + + } + + return boolean; + } + + throw new Exception("Empty expression"); + } + + private bool ParseBoolean() + { + if (_tokens.Current is BooleanValueToken) + { + var current = _tokens.Current; + _tokens.MoveNext(); + + if (current is TrueToken) + return true; + + return false; + } + if (_tokens.Current is OpenParenthesisToken) + { + _tokens.MoveNext(); + + var expInPars = Parse(); + + if (!(_tokens.Current is ClosedParenthesisToken)) + throw new Exception("Expecting Closing Parenthesis"); + + _tokens.MoveNext(); + + return expInPars; + } + if (_tokens.Current is ClosedParenthesisToken) + throw new Exception("Unexpected Closed Parenthesis"); + + // since its not a BooleanConstant or Expression in parenthesis, it must be a expression again + var val = Parse(); + return val; + } + } + public static class StrategyHelper { public static string GetStrategyShortcut(string strategyName, bool onlyValidStrategies) @@ -36,7 +239,6 @@ namespace Core.ProfitTrailer } // buy/sell strategies beginning with PT 2.3.3 contain the stragegy designation letter followed by a colon and space. - // remove the letter and colon, change to shortcut, then reapply the letter and colon if (strategyName.Contains(":")) { @@ -317,10 +519,22 @@ namespace Core.ProfitTrailer if (!isValidStrategy ) { - // Temporary until a fix for formula true/false + // Parse Formulas if (strategy.Name.Contains("FORMULA")) { - strategyText += "FORM "; + string expression = strategy.Name.Remove(0, 10); + expression = expression.Replace("","true").Replace("","false").Replace("","").Replace("&&","and").Replace("||","or"); + expression = Regex.Replace(expression, @"[ABCDEFGHIJKLMNOPQRSTUVWXYZ]", String.Empty); + var tokens = new Tokenizer(expression).Tokenize(); + var parser = new Parser(tokens); + if (parser.Parse()) { + strategyText += "(FORM) "; + } + else + { + strategyText += "(FORM) "; + } + } else {