expression_language.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486
  1. #include "core/error/error.h"
  2. #include "core/math/math.h"
  3. #include "resource/expression_language.h"
  4. #include <alloca.h>
  5. #include <string.h>
  6. #include <limits.h>
  7. #include <stdlib.h>
  8. namespace skinny { namespace expression_language {
  9. /// Byte code constants.
  10. ///
  11. /// If the upper 12 bits of the byte code do not match one of these values, the operation is
  12. /// BC_PUSH_FLOAT and the byte code specify the 32 bit float to push. If the upper 12 bits
  13. /// match one of these values (which are all NaNs, so they should never appear as regular
  14. /// floats), the operation will instead be the one matching.
  15. ///
  16. /// The remaining 20 bits of the byte code are used for the id of functions and variables.
  17. enum ByteCode {BC_FUNCTION = 0x7f800000, BC_PUSH_VAR = 0x7f900000, BC_END = 0x7fa00000};
  18. /// Returns the byte code operation part of the byte code word.
  19. static inline unsigned bc_mask(unsigned i) {return i & 0xfff00000;}
  20. /// Returns the id part of the byte code word.
  21. static inline unsigned id_mask(unsigned i) {return i & 0x000fffff;}
  22. /// Returns true if the byte code word is a BC_PUSH_FLOAT operation.
  23. static inline bool is_bc_push_float(unsigned i) {return (i & 0x7f80000) != 0x7f8;}
  24. /// Opcodes for functions
  25. enum OpCode {OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_UNARY_MINUS, OP_NOP, OP_SIN, OP_COS, OP_ABS, OP_MATCH, OP_MATCH2D};
  26. static inline float pop(Stack &stack) {CE_ASSERT(stack.size > 0, "Stack underflow"); return stack.data[--stack.size];}
  27. static inline void push(Stack &stack, float f) {CE_ASSERT(stack.size < stack.capacity, "Stack overflow"); stack.data[stack.size++] = f;}
  28. inline float fmax(float a, float b)
  29. {
  30. return a > b ? a : b;
  31. }
  32. inline float length(float a, float b)
  33. {
  34. return sqrtf((b - a) * (b - a));
  35. }
  36. inline float match(float a, float b)
  37. {
  38. return fmax(1.0f-length(a, b), 0.0f);
  39. }
  40. inline float match2d(float a, float b, float c, float d)
  41. {
  42. return fmax(1.0f-length(a, b), 0.0f);
  43. }
  44. /// Computes the function specified by @a op_code on the @a stack.
  45. static inline void compute_function(OpCode op_code, Stack &stack)
  46. {
  47. #define POP() pop(stack)
  48. #define PUSH(f) push(stack, f)
  49. float a,b,c,d;
  50. switch(op_code) {
  51. case OP_ADD: b=POP(); a=POP(); PUSH(a+b); break;
  52. case OP_SUB: b=POP(); a=POP(); PUSH(a-b); break;
  53. case OP_MUL: b=POP(); a=POP(); PUSH(a*b); break;
  54. case OP_DIV: b=POP(); a=POP(); PUSH(a/b); break;
  55. case OP_UNARY_MINUS: PUSH(-POP()); break;
  56. case OP_SIN: PUSH(sinf(POP())); break;
  57. case OP_COS: PUSH(cosf(POP())); break;
  58. case OP_ABS: a = POP(); PUSH(fabs(a)); break;
  59. case OP_MATCH: b=POP(); a=POP(); PUSH(match(a, b)); break;
  60. case OP_MATCH2D: d=POP(); c=POP(); b=POP(); a=POP(); PUSH(match2d(a,b,c,d)); break;
  61. case OP_NOP: break;
  62. default:
  63. CE_FATAL("Unknown opcode");
  64. }
  65. #undef POP
  66. #undef PUSH
  67. }
  68. /// Union to cast through to convert between float and unsigned.
  69. union FloatAndUnsigned
  70. {
  71. float f;
  72. unsigned u;
  73. };
  74. static inline float unsigned_to_float(unsigned u) {FloatAndUnsigned fu; fu.u = u; return fu.f;}
  75. #ifdef CAN_COMPILE
  76. static inline unsigned float_to_unsigned(float f) {FloatAndUnsigned fu; fu.f = f; return fu.u;}
  77. #ifdef WIN32
  78. float strtof(const char *nptr, char **endptr) {return (float)strtod(nptr, endptr);}
  79. #endif
  80. /// Represents a token in the expression language. The tokens are used
  81. /// both during the tokenization phase and as a representation of the
  82. /// program. (A list of tokens in reverse polish notation form.)
  83. struct Token
  84. {
  85. enum TokenType {EMPTY, NUMBER, FUNCTION, VARIABLE, LEFT_PARENTHESIS, RIGHT_PARENTHESIS};
  86. Token() {}
  87. Token(TokenType type) : type(type) {}
  88. Token(TokenType type, unsigned id) : type(type), id(id) {}
  89. Token(TokenType type, float value) : type(type), value(value) {}
  90. TokenType type; ///< Identifies the type of the token
  91. union {
  92. unsigned id; ///< Id for FUNCTION and VARIABLE tokens
  93. float value; ///< Numeric value for NUMBER tokens
  94. };
  95. };
  96. /// Describes a function.
  97. struct Function
  98. {
  99. Function() {}
  100. Function(OpCode op_code, unsigned precedence, unsigned arity) : op_code(op_code), precedence(precedence), arity(arity) {}
  101. OpCode op_code; ///< The opcode of the function
  102. unsigned precedence; ///< The precedence of the function operator.
  103. unsigned arity; ///< The number of arguments that the function takes.
  104. };
  105. /// Represents the environment in which we are compiling -- the available variables,
  106. /// constants and functions.
  107. struct CompileEnvironment
  108. {
  109. unsigned num_variables;
  110. const char **variable_names;
  111. unsigned num_constants;
  112. const char **constant_names;
  113. const float *constant_values;
  114. unsigned num_functions;
  115. const char **function_names;
  116. const Function *function_values;
  117. /// Finds a string in @a strings matching @a s of length @a len and returns its index.
  118. /// Returns UINT_MAX if no such string is found.
  119. static unsigned find_string(const char *s, unsigned len, unsigned num_strings, const char **strings)
  120. {
  121. for (unsigned i=0; i<num_strings; ++i)
  122. if (strncmp(s, strings[i], len) == 0 && strlen(strings[i]) == len)
  123. return i;
  124. return UINT_MAX;
  125. }
  126. /// Finds a token representing the identifier in the environment.
  127. Token token_for_identifier(const char *identifier, unsigned len) const
  128. {
  129. unsigned i;
  130. if ( (i=find_string(identifier, len, num_variables, variable_names)) != UINT_MAX)
  131. return Token(Token::VARIABLE, i);
  132. else if ( (i=find_string(identifier, len, num_constants, constant_names)) != UINT_MAX)
  133. return Token(Token::NUMBER, constant_values[i]);
  134. else if ( (i=find_string(identifier, len, num_functions, function_names)) != UINT_MAX)
  135. return Token(Token::FUNCTION, i);
  136. else {
  137. CE_FATAL("Unknown identifier: %s", identifier);
  138. return Token();
  139. }
  140. }
  141. /// Finds a token representing the identifier in the environment.
  142. Token token_for_identifier(const char *identifier) const
  143. {
  144. return token_for_identifier(identifier, strlen(identifier));
  145. }
  146. /// True if there is a function matching the specified identifier.
  147. bool has_function(char * identifier) const
  148. {
  149. return find_string(identifier, strlen(identifier), num_functions, function_names) != UINT_MAX;
  150. }
  151. };
  152. /// Tokenizes the source code @a p into a sequence of tokens. The environment @a env
  153. /// is used for looking up source code identifiers.
  154. /// Returns the total number of tokens. If the returned number is greater than the @a capcity, only
  155. /// the first @a capacity items will be converted.
  156. static unsigned tokenize(const char *p, const CompileEnvironment &env, Token *tokens, unsigned capacity)
  157. {
  158. // Determines if the next + or - is a binary or unary operator.
  159. bool binary = false;
  160. unsigned num_tokens = 0;
  161. unsigned overflow_tokens = 0;
  162. while (*p != 0) {
  163. Token token(Token::EMPTY);
  164. // Numbers
  165. if (*p >= '0' && *p <= '9') {
  166. char *out;
  167. token = Token(Token::NUMBER, strtof(p, &out));
  168. p = out;
  169. binary = true;
  170. // Identifiers
  171. } else if ( (*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p == '_') ) {
  172. const char *identifier = p;
  173. while ( (*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p == '_') || (*p >= '0' && *p <= '9'))
  174. p++;
  175. token = env.token_for_identifier(identifier, p-identifier);
  176. binary = true;
  177. // Operators
  178. } else {
  179. switch (*p) {
  180. case '(': token = Token(Token::LEFT_PARENTHESIS); binary = false; break;
  181. case ')': token = Token(Token::RIGHT_PARENTHESIS); binary = true; break;
  182. case ' ': case '\t': case '\n': case '\r': break;
  183. case '-': token = env.token_for_identifier(binary ? "-" : "u-"); binary = false; break;
  184. case '+': token = env.token_for_identifier(binary ? "+" : "u+"); binary = false; break;
  185. default: {
  186. char s1[2] = {*p,0};
  187. char s2[3] = {*p, *(p+1), 0};
  188. if (s2[1] && env.has_function(s2)) {
  189. token = env.token_for_identifier(s2);
  190. ++p;
  191. } else
  192. token = env.token_for_identifier(s1);
  193. binary = false;
  194. break;
  195. }
  196. }
  197. ++p;
  198. }
  199. if (token.type != Token::EMPTY) {
  200. if (num_tokens == capacity)
  201. ++overflow_tokens;
  202. else
  203. tokens[num_tokens++] = token;
  204. }
  205. }
  206. return num_tokens + overflow_tokens;
  207. }
  208. /// Performs constant folding on the program represented by @a rpl which is a
  209. /// sequence of tokens in reverse polish notation. Any function found in the
  210. /// token stream which only takes constant arguments is replaced by the
  211. /// result of evaluating the function over the constant arguments.
  212. static void fold_constants(Token *rpl, unsigned &num_tokens, CompileEnvironment &env)
  213. {
  214. static const int MAX_ARITY = 4;
  215. float stack_data[MAX_ARITY];
  216. for (unsigned i=0; i<num_tokens; ++i) {
  217. if (rpl[i].type != Token::FUNCTION)
  218. continue;
  219. Stack stack(stack_data, MAX_ARITY);
  220. bool constant_arguments = true;
  221. Function f = env.function_values[rpl[i].id];
  222. unsigned arity = f.arity;
  223. CE_ASSERT(arity <= MAX_ARITY, "MAX_ARITY too small");
  224. CE_ASSERT(i >= arity, "Too few arguments to function");
  225. unsigned arg_start = i - arity;
  226. for (unsigned j=0; j<arity && constant_arguments; ++j) {
  227. constant_arguments = constant_arguments && rpl[i-j-1].type == Token::NUMBER;
  228. stack.data[j] = rpl[arg_start+j].value;
  229. }
  230. if (!constant_arguments)
  231. continue;
  232. stack.size = arity;
  233. compute_function(f.op_code, stack);
  234. unsigned results = stack.size;
  235. int to_remove = int(arity + 1) - int(results);
  236. if (to_remove > 0) {
  237. memmove(&rpl[arg_start], &rpl[arg_start+to_remove], sizeof(Token)*(num_tokens-arg_start-to_remove));
  238. num_tokens -= to_remove;
  239. }
  240. for (unsigned res = 0; res<stack.size; ++res)
  241. rpl[arg_start + res] = Token(Token::NUMBER, stack.data[res]);
  242. i = arg_start - 1;
  243. }
  244. }
  245. /// Generates bytecode from a program in RPL token stream form.
  246. /// Returns the number of byte_code tokens generated. If the returned number is > capacity, only the first
  247. /// capacity items are generated.
  248. static unsigned generate_bytecode(Token *rpl, unsigned num_tokens, const CompileEnvironment &env, unsigned *byte_code, unsigned capacity)
  249. {
  250. unsigned size = 0;
  251. unsigned overflow = 0;
  252. for (unsigned i=0; i<num_tokens; ++i) {
  253. Function f;
  254. Token t = rpl[i];
  255. unsigned op;
  256. switch (t.type) {
  257. case Token::NUMBER:
  258. op = float_to_unsigned(t.value);
  259. break;
  260. case Token::VARIABLE:
  261. op = BC_PUSH_VAR + t.id;
  262. break;
  263. case Token::FUNCTION:
  264. f = env.function_values[t.id];
  265. op = BC_FUNCTION + f.op_code;
  266. break;
  267. default:
  268. CE_FATAL("Unknown token");
  269. break;
  270. }
  271. if (size < capacity)
  272. byte_code[size++] = op;
  273. else
  274. ++overflow;
  275. }
  276. unsigned op = BC_END;
  277. if (size < capacity)
  278. byte_code[size++] = op;
  279. else
  280. ++overflow;
  281. return size + overflow;
  282. }
  283. /// Represents an item on the function call stack.
  284. /// This object is comparable so that functions that have higher precedence are executed
  285. /// before others.
  286. struct FunctionStackItem
  287. {
  288. FunctionStackItem() {}
  289. FunctionStackItem(Token t, int p, int pl) : token(t), precedence(p), par_level(pl) {}
  290. inline int cmp(const FunctionStackItem &f) const {
  291. if (par_level != f.par_level) return par_level - f.par_level;
  292. return precedence - f.precedence;
  293. }
  294. inline bool operator<(const FunctionStackItem &other) const {return cmp(other) < 0;}
  295. inline bool operator<=(const FunctionStackItem &other) const {return cmp(other) <= 0;}
  296. inline bool operator==(const FunctionStackItem &other) const {return cmp(other) == 0;}
  297. inline bool operator>=(const FunctionStackItem &other) const {return cmp(other) >= 0;}
  298. inline bool operator>(const FunctionStackItem &other) const {return cmp(other) > 0;}
  299. Token token;
  300. int precedence;
  301. int par_level;
  302. };
  303. const int NUM_DEFAULT_FUNCTIONS = 12;
  304. /// Sets up the functions that should be usable in the language.
  305. static unsigned setup_functions(const char **names, Function *functions, unsigned capacity)
  306. {
  307. CE_ASSERT(capacity >= NUM_DEFAULT_FUNCTIONS, "Not enough space for default functions");
  308. names[0] = ","; functions[0] = Function(OP_NOP, 1, 0);
  309. names[1] = "+"; functions[1] = Function(OP_ADD, 12, 2);
  310. names[2] = "-"; functions[2] = Function(OP_SUB, 12, 2);
  311. names[3] = "*"; functions[3] = Function(OP_MUL, 13, 2);
  312. names[4] = "/"; functions[4] = Function(OP_DIV, 13, 2);
  313. names[5] = "u-"; functions[5] = Function(OP_UNARY_MINUS, 16, 1);
  314. names[6] = "u+"; functions[6] = Function(OP_NOP, 16, 0);
  315. names[7] = "sin"; functions[7] = Function(OP_SIN, 17, 1);
  316. names[8] = "cos"; functions[8] = Function(OP_COS, 17, 1);
  317. names[9] = "abs"; functions[9] = Function(OP_ABS, 17, 1);
  318. names[10] = "match"; functions[10] = Function(OP_MATCH, 17, 2);
  319. names[11] = "match2d"; functions[11] = Function(OP_MATCH2D, 17, 4);
  320. return NUM_DEFAULT_FUNCTIONS;
  321. }
  322. unsigned compile(const char *source, unsigned num_variables, const char **variables,
  323. unsigned num_constants, const char **constant_names, const float *constant_values,
  324. unsigned *byte_code, unsigned capacity)
  325. {
  326. const char *function_names[NUM_DEFAULT_FUNCTIONS];
  327. Function functions[NUM_DEFAULT_FUNCTIONS];
  328. unsigned num_functions = setup_functions(function_names, functions, NUM_DEFAULT_FUNCTIONS);
  329. CompileEnvironment env;
  330. env.num_variables = num_variables;
  331. env.variable_names = variables;
  332. env.num_constants = num_constants;
  333. env.constant_names = constant_names;
  334. env.constant_values = constant_values;
  335. env.num_functions = num_functions;
  336. env.function_names = function_names;
  337. env.function_values = functions;
  338. unsigned num_tokens = tokenize(source, env, NULL, 0);
  339. // Change alloca to some other temp memory allocator if you want to
  340. Token *tokens = (Token *)alloca(sizeof(Token) * num_tokens);
  341. tokenize(source, env, tokens, num_tokens);
  342. Token *rpl = (Token *)alloca(sizeof(Token) * num_tokens);
  343. unsigned num_rpl = 0;
  344. FunctionStackItem *function_stack = (FunctionStackItem *)alloca(sizeof(FunctionStackItem)*num_tokens);
  345. unsigned num_function_stack = 0;
  346. int par_level = 0;
  347. for (unsigned i=0; i<num_tokens; ++i) {
  348. Token &token = tokens[i];
  349. switch (token.type) {
  350. case Token::NUMBER:
  351. case Token::VARIABLE:
  352. rpl[num_rpl++] = token;
  353. break;
  354. case Token::LEFT_PARENTHESIS:
  355. ++par_level;
  356. break;
  357. case Token::RIGHT_PARENTHESIS:
  358. --par_level;
  359. break;
  360. case Token::FUNCTION: {
  361. FunctionStackItem f(token, env.function_values[token.id].precedence, par_level);
  362. while (num_function_stack>0 && function_stack[num_function_stack-1] >= f)
  363. rpl[num_rpl++] = function_stack[--num_function_stack].token;
  364. function_stack[num_function_stack++] = f;
  365. break;
  366. }
  367. default:
  368. CE_FATAL("Unknown token");
  369. break;
  370. }
  371. }
  372. while (num_function_stack>0)
  373. rpl[num_rpl++] = function_stack[--num_function_stack].token;
  374. fold_constants(rpl, num_rpl, env);
  375. return generate_bytecode(rpl, num_rpl, env, byte_code, capacity);
  376. }
  377. #endif // CAN_COMPILE
  378. bool is_constant(const unsigned *byte_code)
  379. {
  380. unsigned bc = *byte_code++;
  381. if (!is_bc_push_float(bc))
  382. return false;
  383. bc = *byte_code;
  384. return bc_mask(bc) == BC_END;
  385. }
  386. float constant_value(const unsigned *byte_code)
  387. {
  388. unsigned bc = *byte_code;
  389. if (is_bc_push_float(bc))
  390. return unsigned_to_float(bc);
  391. else {
  392. CE_FATAL("Not a static expression");
  393. return 0;
  394. }
  395. }
  396. bool run(const unsigned *byte_code, const float *variables, Stack &stack)
  397. {
  398. const unsigned *p = byte_code;
  399. while (true) {
  400. unsigned bc = *p++;
  401. unsigned op = bc_mask(bc);
  402. unsigned id = id_mask(bc);
  403. switch (op) {
  404. case BC_PUSH_VAR:
  405. if (stack.size == stack.capacity) return false;
  406. stack.data[stack.size++] = variables[id];
  407. break;
  408. case BC_FUNCTION:
  409. compute_function((OpCode)id, stack);
  410. break;
  411. case BC_END:
  412. return true;
  413. default: // BC_PUSH_FLOAT
  414. if (stack.size == stack.capacity) return false;
  415. stack.data[stack.size++] = unsigned_to_float(bc);
  416. break;
  417. }
  418. }
  419. }
  420. }} // skinny::expression_langauge