فهرست منبع

resource: make expression language functions configurable

Daniele Bartolini 1 سال پیش
والد
کامیت
d87c148c16
3فایلهای تغییر یافته به همراه124 افزوده شده و 85 حذف شده
  1. 56 84
      src/resource/expression_language.cpp
  2. 47 1
      src/resource/expression_language.h
  3. 21 0
      src/resource/expression_language.inl

+ 56 - 84
src/resource/expression_language.cpp

@@ -1,7 +1,7 @@
 #include "core/error/error.h"
 #include "core/error/error.h"
 #include "core/math/math.h"
 #include "core/math/math.h"
 #include "core/strings/string.inl"
 #include "core/strings/string.inl"
-#include "resource/expression_language.h"
+#include "resource/expression_language.inl"
 #include <alloca.h>
 #include <alloca.h>
 #include <limits.h> // UINT_MAX
 #include <limits.h> // UINT_MAX
 #include <stdlib.h> // strtof
 #include <stdlib.h> // strtof
@@ -49,18 +49,6 @@ namespace expression_language
 		OP_MATCH_2D
 		OP_MATCH_2D
 	};
 	};
 
 
-	static inline float pop(Stack &stack)
-	{
-		CE_ASSERT(stack.size > 0, "Stack underflow");
-		return stack.data[--stack.size];
-	}
-
-	static inline void push(Stack &stack, float f)
-	{
-		CE_ASSERT(stack.size < stack.capacity, "Stack overflow");
-		stack.data[stack.size++] = f;
-	}
-
 	inline float fmax(float a, float b)
 	inline float fmax(float a, float b)
 	{
 	{
 		return a > b ? a : b;
 		return a > b ? a : b;
@@ -82,7 +70,7 @@ namespace expression_language
 	}
 	}
 
 
 	/// Computes the function specified by @a op_code on the @a stack.
 	/// Computes the function specified by @a op_code on the @a stack.
-	static inline void compute_function(OpCode op_code, Stack &stack)
+	static inline void default_compute_function(int op_code, Stack &stack)
 	{
 	{
 #define POP() pop(stack)
 #define POP() pop(stack)
 #define PUSH(f) push(stack, f)
 #define PUSH(f) push(stack, f)
@@ -123,8 +111,11 @@ namespace expression_language
 		return fu.f;
 		return fu.f;
 	}
 	}
 
 
-	bool run(const unsigned *byte_code, const float *variables, Stack &stack)
+	bool run(const unsigned *byte_code, const float *variables, Stack &stack, ComputeFunction compute_function)
 	{
 	{
+		if (compute_function == NULL)
+			compute_function = default_compute_function;
+
 		const unsigned *p = byte_code;
 		const unsigned *p = byte_code;
 		while (true) {
 		while (true) {
 			unsigned bc = *p++;
 			unsigned bc = *p++;
@@ -205,38 +196,8 @@ namespace expression_language
 		};
 		};
 	};
 	};
 
 
-	/// Describes a function.
-	struct Function
-	{
-		Function()
-		{
-		}
-
-		Function(OpCode op_code, unsigned precedence, unsigned arity)
-			: op_code(op_code)
-			, precedence(precedence)
-			, arity(arity)
-		{
-		}
-
-		OpCode op_code;      ///< The opcode of the function.
-		unsigned precedence; ///< The precedence of the function operator.
-		unsigned arity;      ///< The number of arguments that the function takes.
-	};
-
-	/// Represents the environment in which we are compiling -- the available variables,
-	/// constants and functions.
-	struct CompileEnvironment
+	namespace compile_env
 	{
 	{
-		unsigned num_variables;
-		const char **variable_names;
-		unsigned num_constants;
-		const char **constant_names;
-		const float *constant_values;
-		unsigned num_functions;
-		const char **function_names;
-		const Function *function_values;
-
 		/// Finds a string in @a strings matching @a s of length @a len and returns its index.
 		/// Finds a string in @a strings matching @a s of length @a len and returns its index.
 		/// Returns UINT_MAX if no such string is found.
 		/// Returns UINT_MAX if no such string is found.
 		static unsigned find_string(const char *s, unsigned len, unsigned num_strings, const char **strings)
 		static unsigned find_string(const char *s, unsigned len, unsigned num_strings, const char **strings)
@@ -248,33 +209,34 @@ namespace expression_language
 		}
 		}
 
 
 		/// Finds a token representing the identifier in the environment.
 		/// Finds a token representing the identifier in the environment.
-		Token token_for_identifier(const char *identifier, unsigned len) const
+		Token token_for_identifier(const CompileEnvironment &env, const char *identifier, unsigned len, Token default_token = Token(Token::NUMBER, 0.0f))
 		{
 		{
 			unsigned i;
 			unsigned i;
-			if ((i = find_string(identifier, len, num_variables, variable_names)) != UINT_MAX) {
+			if ((i = find_string(identifier, len, env.num_variables, env.variable_names)) != UINT_MAX) {
 				return Token(Token::VARIABLE, i);
 				return Token(Token::VARIABLE, i);
-			} else if ((i = find_string(identifier, len, num_constants, constant_names)) != UINT_MAX) {
-				return Token(Token::NUMBER, constant_values[i]);
-			} else if ((i = find_string(identifier, len, num_functions, function_names)) != UINT_MAX) {
+			} else if ((i = find_string(identifier, len, env.num_constants, env.constant_names)) != UINT_MAX) {
+				return Token(Token::NUMBER, env.constant_values[i]);
+			} else if ((i = find_string(identifier, len, env.num_functions, env.function_names)) != UINT_MAX) {
 				return Token(Token::FUNCTION, i);
 				return Token(Token::FUNCTION, i);
 			} else {
 			} else {
-				CE_FATAL("Unknown identifier: %s", identifier);
-				return Token();
+				// CE_FATAL("Unknown identifier: %s", identifier);
+				return default_token;
 			}
 			}
 		}
 		}
 
 
 		/// Finds a token representing the identifier in the environment.
 		/// Finds a token representing the identifier in the environment.
-		Token token_for_identifier(const char *identifier) const
+		Token token_for_identifier(const CompileEnvironment &env, const char *identifier)
 		{
 		{
-			return token_for_identifier(identifier, strlen32(identifier));
+			return token_for_identifier(env, identifier, strlen32(identifier));
 		}
 		}
 
 
 		/// True if there is a function matching the specified identifier.
 		/// True if there is a function matching the specified identifier.
-		bool has_function(char *identifier) const
+		bool has_function(const CompileEnvironment &env, char *identifier)
 		{
 		{
-			return find_string(identifier, strlen32(identifier), num_functions, function_names) != UINT_MAX;
+			return find_string(identifier, strlen32(identifier), env.num_functions, env.function_names) != UINT_MAX;
 		}
 		}
-	};
+
+	} // namespace compile_env
 
 
 	/// Tokenizes the source code @a p into a sequence of tokens. The environment @a env
 	/// Tokenizes the source code @a p into a sequence of tokens. The environment @a env
 	/// is used for looking up source code identifiers.
 	/// is used for looking up source code identifiers.
@@ -302,7 +264,7 @@ namespace expression_language
 				const char *identifier = p;
 				const char *identifier = p;
 				while ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p == '_') || (*p >= '0' && *p <= '9'))
 				while ((*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z') || (*p == '_') || (*p >= '0' && *p <= '9'))
 					p++;
 					p++;
-				token = env.token_for_identifier(identifier, u32(p - identifier));
+				token = compile_env::token_for_identifier(env, identifier, u32(p - identifier));
 				binary = true;
 				binary = true;
 				// Operators
 				// Operators
 			} else {
 			} else {
@@ -310,18 +272,18 @@ namespace expression_language
 				case '(': token = Token(Token::LEFT_PARENTHESIS); binary = false; break;
 				case '(': token = Token(Token::LEFT_PARENTHESIS); binary = false; break;
 				case ')': token = Token(Token::RIGHT_PARENTHESIS); binary = true; break;
 				case ')': token = Token(Token::RIGHT_PARENTHESIS); binary = true; break;
 				case ' ': case '\t': case '\n': case '\r': break;
 				case ' ': case '\t': case '\n': case '\r': break;
-				case '-': token = env.token_for_identifier(binary ? "-" : "u-"); binary = false; break;
-				case '+': token = env.token_for_identifier(binary ? "+" : "u+"); binary = false; break;
+				case '-': token = compile_env::token_for_identifier(env, binary ? "-" : "u-"); binary = false; break;
+				case '+': token = compile_env::token_for_identifier(env, binary ? "+" : "u+"); binary = false; break;
 
 
 				default: {
 				default: {
 					char s2[3] = {*p, *(p + 1), 0};
 					char s2[3] = {*p, *(p + 1), 0};
 
 
-					if (s2[1] && env.has_function(s2)) {
-						token = env.token_for_identifier(s2);
+					if (s2[1] && compile_env::has_function(env, s2)) {
+						token = compile_env::token_for_identifier(env, s2);
 						++p;
 						++p;
 					} else {
 					} else {
 						char s1[2] = {*p, 0};
 						char s1[2] = {*p, 0};
-						token = env.token_for_identifier(s1);
+						token = compile_env::token_for_identifier(env, s1);
 					}
 					}
 
 
 					binary = false;
 					binary = false;
@@ -370,7 +332,7 @@ namespace expression_language
 				continue;
 				continue;
 
 
 			stack.size = arity;
 			stack.size = arity;
-			compute_function(f.op_code, stack);
+			env.compute_function(f.op_code, stack);
 			unsigned results = stack.size;
 			unsigned results = stack.size;
 			int to_remove = int(arity + 1) - int(results);
 			int to_remove = int(arity + 1) - int(results);
 			if (to_remove > 0) {
 			if (to_remove > 0) {
@@ -501,29 +463,11 @@ namespace expression_language
 	}
 	}
 
 
 	unsigned compile(const char *source
 	unsigned compile(const char *source
-		, unsigned num_variables
-		, const char **variables
-		, unsigned num_constants
-		, const char **constant_names
-		, const float *constant_values
+		, CompileEnvironment &env
 		, unsigned *byte_code
 		, unsigned *byte_code
 		, unsigned capacity
 		, unsigned capacity
 		)
 		)
 	{
 	{
-		const char *function_names[NUM_DEFAULT_FUNCTIONS];
-		Function functions[NUM_DEFAULT_FUNCTIONS];
-		unsigned num_functions = setup_functions(function_names, functions, NUM_DEFAULT_FUNCTIONS);
-
-		CompileEnvironment env;
-		env.num_variables = num_variables;
-		env.variable_names = variables;
-		env.num_constants = num_constants;
-		env.constant_names = constant_names;
-		env.constant_values = constant_values;
-		env.num_functions = num_functions;
-		env.function_names = function_names;
-		env.function_values = functions;
-
 		unsigned num_tokens = tokenize(source, env, NULL, 0);
 		unsigned num_tokens = tokenize(source, env, NULL, 0);
 
 
 		// Change alloca to some other temp memory allocator if you want to
 		// Change alloca to some other temp memory allocator if you want to
@@ -570,6 +514,34 @@ namespace expression_language
 		return generate_bytecode(rpl, num_rpl, env, byte_code, capacity);
 		return generate_bytecode(rpl, num_rpl, env, byte_code, capacity);
 	}
 	}
 
 
+	unsigned compile(const char *source
+		, unsigned num_variables
+		, const char **variables
+		, unsigned num_constants
+		, const char **constant_names
+		, const float *constant_values
+		, unsigned *byte_code
+		, unsigned capacity
+		)
+	{
+		const char *function_names[NUM_DEFAULT_FUNCTIONS];
+		Function functions[NUM_DEFAULT_FUNCTIONS];
+		unsigned num_functions = setup_functions(function_names, functions, NUM_DEFAULT_FUNCTIONS);
+
+		CompileEnvironment env;
+		env.num_variables = num_variables;
+		env.variable_names = variables;
+		env.num_constants = num_constants;
+		env.constant_names = constant_names;
+		env.constant_values = constant_values;
+		env.num_functions = num_functions;
+		env.function_names = function_names;
+		env.function_values = functions;
+		env.compute_function = default_compute_function;
+
+		return compile(source, env, byte_code, capacity);
+	}
+
 } // namespace expression_language
 } // namespace expression_language
 #endif // if CROWN_CAN_COMPILE
 #endif // if CROWN_CAN_COMPILE
 
 

+ 47 - 1
src/resource/expression_language.h

@@ -45,16 +45,62 @@ namespace expression_language
 		}
 		}
 	};
 	};
 
 
+	float pop(Stack &stack);
+	void push(Stack &stack, float f);
+
+	typedef void (*ComputeFunction)(int op_code, Stack &stack);
+
 	/// Runs the @a byte_code using the @a stack as execution stack.
 	/// Runs the @a byte_code using the @a stack as execution stack.
 	/// @a variables is a list of variable values to use for the execution.
 	/// @a variables is a list of variable values to use for the execution.
 	/// They should match the list of variable names supplied to the compile function.
 	/// They should match the list of variable names supplied to the compile function.
-	bool run(const unsigned *byte_code, const float *variables, Stack &stack);
+	bool run(const unsigned *byte_code, const float *variables, Stack &stack, ComputeFunction compute_function = NULL);
 
 
 } // namespace expression_language
 } // namespace expression_language
 
 
 #if CROWN_CAN_COMPILE
 #if CROWN_CAN_COMPILE
 namespace expression_language
 namespace expression_language
 {
 {
+	/// Describes a function.
+	struct Function
+	{
+		Function()
+		{
+		}
+
+		Function(int op_code, unsigned precedence, unsigned arity)
+			: op_code(op_code)
+			, precedence(precedence)
+			, arity(arity)
+		{
+		}
+
+		int op_code;         ///< The opcode of the function.
+		unsigned precedence; ///< The precedence of the function operator.
+		unsigned arity;      ///< The number of arguments that the function takes.
+	};
+
+	/// Represents the environment in which we are compiling -- the available variables,
+	/// constants and functions.
+	struct CompileEnvironment
+	{
+		unsigned num_variables;
+		const char **variable_names;
+		unsigned num_constants;
+		const char **constant_names;
+		const float *constant_values;
+		unsigned num_functions;
+		const char **function_names;
+		const Function *function_values;
+		ComputeFunction compute_function;
+	};
+
+	///
+	unsigned compile(const char *source
+		, CompileEnvironment &env
+		, unsigned *byte_code
+		, unsigned capacity
+		);
+
 	/// Compiles the @a source and stores the result in the @a byte_code.
 	/// Compiles the @a source and stores the result in the @a byte_code.
 	/// @a variables is a list of variable names. The position of the variable in
 	/// @a variables is a list of variable names. The position of the variable in
 	/// the list should match the position when @a variables is sent to the run
 	/// the list should match the position when @a variables is sent to the run

+ 21 - 0
src/resource/expression_language.inl

@@ -0,0 +1,21 @@
+#include "resource/expression_language.h"
+
+namespace crown
+{
+namespace expression_language
+{
+	inline float pop(Stack &stack)
+	{
+		CE_ASSERT(stack.size > 0, "Stack underflow");
+		return stack.data[--stack.size];
+	}
+
+	inline void push(Stack &stack, float f)
+	{
+		CE_ASSERT(stack.size < stack.capacity, "Stack overflow");
+		stack.data[stack.size++] = f;
+	}
+
+} // namespace expression_language
+
+} // namespace crown