expressiontree.py (13079B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\evegraphics\controllers\expressiontree.py 2 from __future__ import print_function 3 import re 4 5 class ExpressionNode(object): 6 7 def __init__(self, inputs = 0): 8 self._inputs = [] 9 if inputs > 0: 10 self._inputs = [None] * inputs 11 self.startPosition = -1 12 13 def Store(self): 14 pass 15 16 def BuildExpression(self): 17 pass 18 19 def GetTitle(self): 20 pass 21 22 def GetInputs(self): 23 return self._inputs 24 25 def GetPrecedence(self): 26 return 100 27 28 def SetInput(self, index, inputValue): 29 self._inputs[index] = inputValue 30 31 def SetInputs(self, inputs): 32 for i, x in enumerate(inputs): 33 self._inputs[i] = x 34 35 def GetTextTemplate(self): 36 return ('', '') 37 38 def Dump(self, indent = 0): 39 print('%s%s' % (' ' * indent, self.GetTitle())) 40 for each in self._inputs: 41 each.Dump(indent + 2) 42 43 44 class Variable(ExpressionNode): 45 46 def __init__(self, name): 47 super(Variable, self).__init__() 48 self.name = name 49 50 def Store(self): 51 return ['Variable', self.name] 52 53 def GetTitle(self): 54 return self.name 55 56 def BuildExpression(self): 57 return self.name 58 59 def GetTextTemplate(self): 60 return (self.name, '') 61 62 63 class NumericConstant(ExpressionNode): 64 65 def __init__(self, number): 66 super(NumericConstant, self).__init__() 67 self.number = number 68 69 def Store(self): 70 return ['NumericConstant', self.number] 71 72 def GetTitle(self): 73 return str(self.number) 74 75 def BuildExpression(self): 76 return str(self.number) 77 78 def GetTextTemplate(self): 79 return (str(self.number), '') 80 81 82 class UnaryOperator(ExpressionNode): 83 84 def __init__(self, operator, precedence = 10): 85 super(UnaryOperator, self).__init__(1) 86 self.operator = operator 87 self.precedence = precedence 88 89 def Store(self): 90 return ['UnaryOperator', self.operator] 91 92 def GetTitle(self): 93 return self.operator 94 95 def BuildExpression(self): 96 e = self.GetInputs()[0].BuildExpression() 97 if self.GetInputs()[0].GetPrecedence() < self.GetPrecedence(): 98 e = '(%s)' % e 99 return '%s%s' % (self.operator, e) 100 101 def GetPrecedence(self): 102 return self.precedence 103 104 def GetTextTemplate(self): 105 return (self.operator, '') 106 107 108 def GetOperatorPrecedence(operator): 109 for i, each in enumerate(OP_PRIORITIES): 110 if operator in each: 111 return i 112 113 return 0 114 115 116 class Operator(ExpressionNode): 117 118 def __init__(self, operator): 119 super(Operator, self).__init__(2) 120 self.operator = operator 121 122 def Store(self): 123 return ['Operator', self.operator] 124 125 def GetTitle(self): 126 return self.operator 127 128 def BuildExpression(self): 129 e0 = self.GetInputs()[0].BuildExpression() 130 if self.GetInputs()[0].GetPrecedence() < self.GetPrecedence(): 131 e0 = '(%s)' % e0 132 e1 = self.GetInputs()[1].BuildExpression() 133 if self.GetInputs()[1].GetPrecedence() < self.GetPrecedence(): 134 e1 = '(%s)' % e1 135 return '%s %s %s' % (e0, self.operator, e1) 136 137 def GetPrecedence(self): 138 return GetOperatorPrecedence(self.operator) 139 140 def GetTextTemplate(self): 141 return (self.operator, '') 142 143 144 class FunctionCall(ExpressionNode): 145 146 def __init__(self, name, paramsCount): 147 super(FunctionCall, self).__init__(paramsCount) 148 self.name = name 149 150 def Store(self): 151 return ['FunctionCall', self.name, len(self._inputs)] 152 153 def GetTitle(self): 154 return self.name 155 156 def BuildExpression(self): 157 args = [ x.BuildExpression() for x in self.GetInputs() ] 158 return '%s(%s)' % (self.name, ', '.join(args)) 159 160 def GetTextTemplate(self): 161 return ('%s(' % self.name, ')') 162 163 164 class StringFunctionCall(ExpressionNode): 165 166 def __init__(self, name, param): 167 super(StringFunctionCall, self).__init__(0) 168 self.name = name 169 self.param = param 170 self.paramStartPosition = -1 171 172 def Store(self): 173 return ['StringFunctionCall', self.name, self.param] 174 175 def GetTitle(self): 176 return '%s\n%s' % (self.name, self.param[1:-1]) 177 178 def BuildExpression(self): 179 return '%s(%s)' % (self.name, self.param) 180 181 def GetTextTemplate(self): 182 return ('%s("' % self.name, '")') 183 184 185 class ResultExpression(ExpressionNode): 186 187 def __init__(self): 188 super(ResultExpression, self).__init__(1) 189 190 def Store(self): 191 return ['ResultExpression'] 192 193 def GetTitle(self): 194 return 'Result' 195 196 def BuildExpression(self): 197 return self.GetInputs()[0].BuildExpression() 198 199 200 class Token(object): 201 202 def __init__(self, token, string, startPosition): 203 self.token = token 204 self.string = string 205 self.startPosition = startPosition 206 207 def __eq__(self, other): 208 return isinstance(other, Token) and self.token == other.token and self.string == other.string 209 210 def __ne__(self, other): 211 return not isinstance(other, Token) or self.token != other.token or self.string != other.string 212 213 214 class Stream(object): 215 216 def __init__(self, string): 217 self._string = string 218 self._position = 0 219 self._length = len(string) 220 221 def Eof(self): 222 return self._position >= self._length 223 224 def Peek(self, length): 225 return self._string[self._position:self._position + length] 226 227 def Consume(self, length): 228 result = self.Peek(length) 229 self._position += len(result) 230 return result 231 232 def GetPosition(self): 233 return self._position 234 235 def PeekAll(self): 236 return self._string[self._position:] 237 238 239 _TOKENS = re.compile('\\A\\s*((&&|\\|\\||>=|<=|!=|==|[+\\-*/^%><=!()])|([a-zA-Z_][a-zA-Z_0-9]*)|(\\"[^\\"]*\\")|(-?(([0-9]*\\.[0-9]*)|[0-9]+)([eE][+\\-]?[0-9]+)?))') 240 241 def _GetToken(stream): 242 pos = stream.GetPosition() 243 if stream.Eof(): 244 return Token('', '', pos) 245 elif stream.Peek(2) in ('&&', '||', '<=', '>=', '!=', '=='): 246 return Token('OP', stream.Consume(2), pos) 247 elif stream.Peek(1) in ('+', '-', '*', '/', '^', '%', '>', '<', '=', '!', '(', ')', ','): 248 return Token('OP', stream.Consume(1), pos) 249 m = _TOKENS.match(stream.PeekAll()) 250 if not m: 251 raise ValueError(repr(stream.PeekAll())) 252 stream.Consume(len(m.group(0))) 253 if m.group(2): 254 return Token('OP', m.group(2), pos) 255 elif m.group(3): 256 return Token('ID', m.group(3), pos) 257 elif m.group(4): 258 return Token('STR', m.group(4), pos) 259 else: 260 return Token('NUM', m.group(5), pos) 261 262 263 OP_PRIORITIES = [('=',), 264 (), 265 ('||',), 266 ('&&',), 267 ('<=', '>=', '!=', '==', '>', '<'), 268 ('+', '-'), 269 ('*', '/', '%'), 270 ('^',)] 271 _OPEN_PAREN = Token('OP', '(', 0) 272 _CLOSE_PAREN = Token('OP', ')', 0) 273 _COMA = Token('OP', ',', 0) 274 275 def _Term(token, stream): 276 if token.token == 'ID': 277 name = token.string 278 start = token.startPosition 279 token = _GetToken(stream) 280 if token == _OPEN_PAREN: 281 r = None 282 args = [] 283 while True: 284 token = _GetToken(stream) 285 if token.token == 'STR': 286 r = StringFunctionCall(name, token.string) 287 r.startPosition = start 288 r.paramStartPosition = token.startPosition 289 token = _GetToken(stream) 290 if token != _CLOSE_PAREN: 291 raise ValueError() 292 break 293 elif token == _CLOSE_PAREN: 294 break 295 arg, token = _Expr(token, stream, 0) 296 args.append(arg) 297 if token == _CLOSE_PAREN: 298 break 299 elif token != _COMA: 300 raise ValueError() 301 302 if not r: 303 r = FunctionCall(name, len(args)) 304 r.startPosition = start 305 r.SetInputs(args) 306 token = _GetToken(stream) 307 else: 308 r = Variable(name) 309 r.startPosition = start 310 return (r, token) 311 if token.token == 'NUM': 312 e = NumericConstant(float(token.string)) 313 e.startPosition = token.startPosition 314 return (e, _GetToken(stream)) 315 if token.string in ('+', '-'): 316 e = UnaryOperator(token.string, 10) 317 e.startPosition = token.startPosition 318 r, token = _Term(_GetToken(stream), stream) 319 e.SetInput(0, r) 320 return (e, token) 321 if token == _OPEN_PAREN: 322 result, token = _Expr(_GetToken(stream), stream, 0) 323 if token != _CLOSE_PAREN: 324 raise ValueError() 325 return (result, _GetToken(stream)) 326 raise ValueError() 327 328 329 def _Expr(token, stream, priority): 330 left, token = _Term(token, stream) if priority + 1 >= len(OP_PRIORITIES) else _Expr(token, stream, priority + 1) 331 while token.string in OP_PRIORITIES[priority]: 332 op = Operator(token.string) 333 op.startPosition = token.startPosition 334 op.SetInput(0, left) 335 token = _GetToken(stream) 336 right, token = _Term(token, stream) if priority + 1 >= len(OP_PRIORITIES) else _Expr(token, stream, priority + 1) 337 op.SetInput(1, right) 338 left = op 339 340 return (left, token) 341 342 343 def Parse(input): 344 stream = Stream(input) 345 r, t = _Expr(_GetToken(stream), stream, 0) 346 if t.token != '': 347 raise ValueError(t.string) 348 return r 349 350 351 class NodeGroup(object): 352 353 def __init__(self, title, children): 354 self.title = title 355 self.children = list(children) 356 self.description = '' 357 358 359 class NodeDescription(object): 360 361 def __init__(self, title, factory, description = ''): 362 self.title = title 363 self.factory = factory 364 self.description = description 365 366 367 NODE_DESCRIPTIONS = (NodeGroup('Operators', (NodeDescription('-x', lambda : UnaryOperator('-', 10)), 368 NodeDescription('x ^ y', lambda : Operator('^')), 369 NodeDescription('x * y', lambda : Operator('*')), 370 NodeDescription('x / y', lambda : Operator('/')), 371 NodeDescription('x + y', lambda : Operator('+')), 372 NodeDescription('x - y', lambda : Operator('-')), 373 NodeDescription('x == y', lambda : Operator('==')), 374 NodeDescription('x != y', lambda : Operator('!=')), 375 NodeDescription('x > y', lambda : Operator('>')), 376 NodeDescription('x < y', lambda : Operator('<')), 377 NodeDescription('x >= y', lambda : Operator('>=')), 378 NodeDescription('x <= y', lambda : Operator('<=')), 379 NodeDescription('x % y', lambda : Operator('%')), 380 NodeDescription('AND', lambda : Operator('&&')), 381 NodeDescription('OR', lambda : Operator('||')))), NodeGroup('Math', (NodeDescription('abs', lambda : FunctionCall('abs', 1), 'abs(x) - absolute value (drop the sign)'), 382 NodeDescription('sqrt', lambda : FunctionCall('sqrt', 1), 'sqrt(x) - square root of x'), 383 NodeDescription('sign', lambda : FunctionCall('sign', 1), 'sign(x) - sign (-1, 0 or 1) of the argument'), 384 NodeDescription('log2', lambda : FunctionCall('log2', 1), 'log2(x) - base 2 logarithm'), 385 NodeDescription('log10', lambda : FunctionCall('log10', 1), 'log10(x) - base 10 logarithm'), 386 NodeDescription('log', lambda : FunctionCall('log', 1), 'log(x) - natural logarithm'), 387 NodeDescription('ln', lambda : FunctionCall('ln', 1), 'ln(x) - natural logarithm'), 388 NodeDescription('exp', lambda : FunctionCall('exp', 1), 'exp(x) - natural exponent'), 389 NodeDescription('rint', lambda : FunctionCall('rint', 1), 'rint(x) - round value to an integer'), 390 NodeDescription('sin', lambda : FunctionCall('sin', 1), 'sin(x) - sine'), 391 NodeDescription('cos', lambda : FunctionCall('cos', 1), 'cos(x) - cosine'), 392 NodeDescription('tan', lambda : FunctionCall('tan', 1), 'tan(x) - tangent'), 393 NodeDescription('asin', lambda : FunctionCall('asin', 1), 'asin(x) - arcsine'), 394 NodeDescription('acos', lambda : FunctionCall('acos', 1), 'acos(x) - arccosine'), 395 NodeDescription('atan', lambda : FunctionCall('atan', 1), 'atan(x) - argtangent'), 396 NodeDescription('sinh', lambda : FunctionCall('sinh', 1), 'sinh(x) - hyperbolic sine'), 397 NodeDescription('cosh', lambda : FunctionCall('cosh', 1), 'cosh(x) - hyperbolic cosine'), 398 NodeDescription('tanh', lambda : FunctionCall('tanh', 1), 'tanh(x) - hyperbolic tangent'), 399 NodeDescription('asinh', lambda : FunctionCall('asinh', 1), 'asinh(x) - hyperbolic arcsine'), 400 NodeDescription('acosh', lambda : FunctionCall('acosh', 1), 'acosh(x) - hyperbolic arccosine'), 401 NodeDescription('atanh', lambda : FunctionCall('atanh', 1), 'atanh(x) - hyperbolic arctangent'), 402 NodeDescription('min', lambda : FunctionCall('min', 0), 'min(arg1...) - minimum of all arguments (one or more)'), 403 NodeDescription('max', lambda : FunctionCall('max', 0), 'max(arg1...) - maximum of all arguments (one or more)'), 404 NodeDescription('sum', lambda : FunctionCall('sum', 0), 'sum(arg1...) - sum of all arguments (one or more)'), 405 NodeDescription('avg', lambda : FunctionCall('avg', 0), 'avg(arg1...) - average of all arguments (one or more)'))))