CFXR_ExpressionParser.cs 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  1. //--------------------------------------------------------------------------------------------------------------------------------
  2. // Cartoon FX
  3. // (c) 2012-2020 Jean Moreno
  4. //--------------------------------------------------------------------------------------------------------------------------------
  5. using System.Collections.Generic;
  6. using System.IO;
  7. // Parse conditional expressions from CFXR_MaterialInspector to show/hide some parts of the UI easily
  8. namespace CartoonFX
  9. {
  10. public static class ExpressionParser
  11. {
  12. public delegate bool EvaluateFunction(string content);
  13. //--------------------------------------------------------------------------------------------------------------------------------
  14. // Main Function to use
  15. static public bool EvaluateExpression(string expression, EvaluateFunction evalFunction)
  16. {
  17. //Remove white spaces and double && ||
  18. string cleanExpr = "";
  19. for(int i = 0; i < expression.Length; i++)
  20. {
  21. switch(expression[i])
  22. {
  23. case ' ': break;
  24. case '&': cleanExpr += expression[i]; i++; break;
  25. case '|': cleanExpr += expression[i]; i++; break;
  26. default: cleanExpr += expression[i]; break;
  27. }
  28. }
  29. List<Token> tokens = new List<Token>();
  30. StringReader reader = new StringReader(cleanExpr);
  31. Token t = null;
  32. do
  33. {
  34. t = new Token(reader);
  35. tokens.Add(t);
  36. } while(t.type != Token.TokenType.EXPR_END);
  37. List<Token> polishNotation = Token.TransformToPolishNotation(tokens);
  38. var enumerator = polishNotation.GetEnumerator();
  39. enumerator.MoveNext();
  40. Expression root = MakeExpression(ref enumerator, evalFunction);
  41. return root.Evaluate();
  42. }
  43. //--------------------------------------------------------------------------------------------------------------------------------
  44. // Expression Token
  45. public class Token
  46. {
  47. static Dictionary<char, KeyValuePair<TokenType, string>> typesDict = new Dictionary<char, KeyValuePair<TokenType, string>>()
  48. {
  49. {'(', new KeyValuePair<TokenType, string>(TokenType.OPEN_PAREN, "(")},
  50. {')', new KeyValuePair<TokenType, string>(TokenType.CLOSE_PAREN, ")")},
  51. {'!', new KeyValuePair<TokenType, string>(TokenType.UNARY_OP, "NOT")},
  52. {'&', new KeyValuePair<TokenType, string>(TokenType.BINARY_OP, "AND")},
  53. {'|', new KeyValuePair<TokenType, string>(TokenType.BINARY_OP, "OR")}
  54. };
  55. public enum TokenType
  56. {
  57. OPEN_PAREN,
  58. CLOSE_PAREN,
  59. UNARY_OP,
  60. BINARY_OP,
  61. LITERAL,
  62. EXPR_END
  63. }
  64. public TokenType type;
  65. public string value;
  66. public Token(StringReader s)
  67. {
  68. int c = s.Read();
  69. if(c == -1)
  70. {
  71. type = TokenType.EXPR_END;
  72. value = "";
  73. return;
  74. }
  75. char ch = (char)c;
  76. //Special case: solve bug where !COND_FALSE_1 && COND_FALSE_2 would return True
  77. bool embeddedNot = (ch == '!' && s.Peek() != '(');
  78. if(typesDict.ContainsKey(ch) && !embeddedNot)
  79. {
  80. type = typesDict[ch].Key;
  81. value = typesDict[ch].Value;
  82. }
  83. else
  84. {
  85. string str = "";
  86. str += ch;
  87. while(s.Peek() != -1 && !typesDict.ContainsKey((char)s.Peek()))
  88. {
  89. str += (char)s.Read();
  90. }
  91. type = TokenType.LITERAL;
  92. value = str;
  93. }
  94. }
  95. static public List<Token> TransformToPolishNotation(List<Token> infixTokenList)
  96. {
  97. Queue<Token> outputQueue = new Queue<Token>();
  98. Stack<Token> stack = new Stack<Token>();
  99. int index = 0;
  100. while(infixTokenList.Count > index)
  101. {
  102. Token t = infixTokenList[index];
  103. switch(t.type)
  104. {
  105. case Token.TokenType.LITERAL:
  106. outputQueue.Enqueue(t);
  107. break;
  108. case Token.TokenType.BINARY_OP:
  109. case Token.TokenType.UNARY_OP:
  110. case Token.TokenType.OPEN_PAREN:
  111. stack.Push(t);
  112. break;
  113. case Token.TokenType.CLOSE_PAREN:
  114. while(stack.Peek().type != Token.TokenType.OPEN_PAREN)
  115. {
  116. outputQueue.Enqueue(stack.Pop());
  117. }
  118. stack.Pop();
  119. if(stack.Count > 0 && stack.Peek().type == Token.TokenType.UNARY_OP)
  120. {
  121. outputQueue.Enqueue(stack.Pop());
  122. }
  123. break;
  124. default:
  125. break;
  126. }
  127. index++;
  128. }
  129. while(stack.Count > 0)
  130. {
  131. outputQueue.Enqueue(stack.Pop());
  132. }
  133. var list = new List<Token>(outputQueue);
  134. list.Reverse();
  135. return list;
  136. }
  137. }
  138. //--------------------------------------------------------------------------------------------------------------------------------
  139. // Boolean Expression Classes
  140. public abstract class Expression
  141. {
  142. public abstract bool Evaluate();
  143. }
  144. public class ExpressionLeaf : Expression
  145. {
  146. private string content;
  147. private EvaluateFunction evalFunction;
  148. public ExpressionLeaf(EvaluateFunction _evalFunction, string _content)
  149. {
  150. this.evalFunction = _evalFunction;
  151. this.content = _content;
  152. }
  153. override public bool Evaluate()
  154. {
  155. //embedded not, see special case in Token declaration
  156. if(content.StartsWith("!"))
  157. {
  158. return !this.evalFunction(content.Substring(1));
  159. }
  160. return this.evalFunction(content);
  161. }
  162. }
  163. public class ExpressionAnd : Expression
  164. {
  165. private Expression left;
  166. private Expression right;
  167. public ExpressionAnd(Expression _left, Expression _right)
  168. {
  169. this.left = _left;
  170. this.right = _right;
  171. }
  172. override public bool Evaluate()
  173. {
  174. return left.Evaluate() && right.Evaluate();
  175. }
  176. }
  177. public class ExpressionOr : Expression
  178. {
  179. private Expression left;
  180. private Expression right;
  181. public ExpressionOr(Expression _left, Expression _right)
  182. {
  183. this.left = _left;
  184. this.right = _right;
  185. }
  186. override public bool Evaluate()
  187. {
  188. return left.Evaluate() || right.Evaluate();
  189. }
  190. }
  191. public class ExpressionNot : Expression
  192. {
  193. private Expression expr;
  194. public ExpressionNot(Expression _expr)
  195. {
  196. this.expr = _expr;
  197. }
  198. override public bool Evaluate()
  199. {
  200. return !expr.Evaluate();
  201. }
  202. }
  203. static public Expression MakeExpression(ref List<Token>.Enumerator polishNotationTokensEnumerator, EvaluateFunction _evalFunction)
  204. {
  205. if(polishNotationTokensEnumerator.Current.type == Token.TokenType.LITERAL)
  206. {
  207. Expression lit = new ExpressionLeaf(_evalFunction, polishNotationTokensEnumerator.Current.value);
  208. polishNotationTokensEnumerator.MoveNext();
  209. return lit;
  210. }
  211. else
  212. {
  213. if(polishNotationTokensEnumerator.Current.value == "NOT")
  214. {
  215. polishNotationTokensEnumerator.MoveNext();
  216. Expression operand = MakeExpression(ref polishNotationTokensEnumerator, _evalFunction);
  217. return new ExpressionNot(operand);
  218. }
  219. else if(polishNotationTokensEnumerator.Current.value == "AND")
  220. {
  221. polishNotationTokensEnumerator.MoveNext();
  222. Expression left = MakeExpression(ref polishNotationTokensEnumerator, _evalFunction);
  223. Expression right = MakeExpression(ref polishNotationTokensEnumerator, _evalFunction);
  224. return new ExpressionAnd(left, right);
  225. }
  226. else if(polishNotationTokensEnumerator.Current.value == "OR")
  227. {
  228. polishNotationTokensEnumerator.MoveNext();
  229. Expression left = MakeExpression(ref polishNotationTokensEnumerator, _evalFunction);
  230. Expression right = MakeExpression(ref polishNotationTokensEnumerator, _evalFunction);
  231. return new ExpressionOr(left, right);
  232. }
  233. }
  234. return null;
  235. }
  236. }
  237. }