Last active
October 19, 2020 01:40
-
-
Save Lokno/88d2cf65788968402882fabc36e9c3b0 to your computer and use it in GitHub Desktop.
evaluate() evaluates an expression passed in as a char array using the Lua scripting language
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // | |
| // evaluate() evaluates an expression passed in as a char array using the Lua scripting language | |
| // The expression may reference a set of floating-point variables past in as an pair of | |
| // arrays corresponding to the key, or name, of the variable and its value. | |
| // | |
| // Tested with Lua-5.4.0 | |
| // | |
| // Depends on stb_ds.h from https://github.com/nothings/stb | |
| #define STB_DS_IMPLEMENTATION | |
| #include "stb_ds.h" | |
| #include <lua.h> | |
| #include <lauxlib.h> | |
| #include <lualib.h> | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <float.h> | |
| int char_in_str(const char c, const char* str) | |
| { | |
| int found = 0; | |
| for( char* p = (char*)str; *p != 0; ++p ) | |
| { | |
| if( c == *p ) | |
| { | |
| found = 1; | |
| break; | |
| } | |
| } | |
| return found; | |
| } | |
| // wrapper for strstr() that checks that the character immediately before | |
| // and immediately after each matching substring is not in the list | |
| // of characters var_char | |
| char* strstrpad( const char * str1, const char * str2 ) | |
| { | |
| static const char* var_char = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789._"; | |
| size_t len1; | |
| size_t len2 = strlen(str2); | |
| char* pch = (char*)str1; | |
| char* pchend = pch; | |
| do | |
| { | |
| len1 = strlen(str1); | |
| pch = strstr(pchend,str2); | |
| pchend = pch+len2; | |
| } | |
| while( pch != NULL && (( ((pch-str1) > 0u) && char_in_str(*(pch-1u), var_char) ) || | |
| ( ((pchend-str1) < (len1-1u)) && char_in_str(*pchend, var_char) )) ); | |
| return pch; | |
| } | |
| // dst is a pointer to a dynamic array from stb_ds.h | |
| // Based on https://stackoverflow.com/questions/779875/what-function-is-to-replace-a-substring-from-a-string-in-c | |
| void str_replace(char** dst, char *orig, const char *rep, const char *with) { | |
| char* ins; // the next insert point | |
| char* tmp; // varies | |
| int len_rep; // length of rep (the string to remove) | |
| int len_with; // length of with (the string to replace rep with) | |
| int len_front; // distance between rep and end of last rep | |
| int count; // number of replacements | |
| if( *dst != NULL ) arrdeln(*dst,0,arrlen(*dst)); | |
| // sanity checks and initialization | |
| if (!orig || !rep) | |
| { | |
| fprintf(stderr, "\nERROR in str_replace(): NULL input\n"); | |
| return; | |
| } | |
| len_rep = strlen(rep); | |
| if (len_rep == 0) // empty rep causes infinite loop during count | |
| { | |
| fprintf(stderr, "\nERROR in str_replace(): empty replacement string\n"); | |
| return; | |
| } | |
| if (!with) with = ""; | |
| len_with = strlen(with); | |
| // count the number of replacements needed | |
| ins = orig; | |
| for (count = 0; (tmp = strstrpad(ins, rep)); ++count) | |
| { | |
| ins = tmp + len_rep; | |
| } | |
| arraddnptr(*dst, strlen(orig) + (len_with - len_rep) * count + 1); | |
| tmp = *dst; | |
| // first time through the loop, all the variable are set correctly | |
| // from here on, | |
| // tmp points to the end of the result string | |
| // ins points to the next occurrence of rep in orig | |
| // orig points to the remainder of orig after "end of rep" | |
| while (count--) { | |
| ins = strstrpad(orig, rep); | |
| len_front = ins - orig; | |
| tmp = strncpy(tmp, orig, len_front) + len_front; | |
| tmp = strcpy(tmp, with) + len_with; | |
| orig += len_front + len_rep; // move to next "end of rep" | |
| } | |
| strcpy(tmp, orig); | |
| } | |
| float evaluate( char* expression, char* const * keys, const float* values, int n ) | |
| { | |
| static const char* func_header = "function equation(etab)\n return "; | |
| static const char* func_footer = "\nend\n"; | |
| static const char* replace_prefix = "etab['"; | |
| static const char* replace_postfix = "']"; | |
| int len_prefix = strlen(replace_prefix); | |
| int len_postfix = strlen(replace_postfix); | |
| int len_func_header = strlen(func_header); | |
| int len_func_footer = strlen(func_footer); | |
| char* expression_str = NULL; | |
| char* curr_str = NULL; | |
| char* new_str = NULL; | |
| char* swap_ptr = NULL; | |
| lua_State *L; | |
| if( expression == NULL ) | |
| { | |
| fprintf(stderr, "\nERROR in evaluate(): NULL expression\n"); | |
| return FLT_MAX; | |
| } | |
| if( n > 0 && (keys == NULL || values == NULL) ) | |
| { | |
| fprintf(stderr, "\nERROR in evaluate(): non-zero n value with NULL keys/values\n"); | |
| return FLT_MAX; | |
| } | |
| arraddnptr(expression_str,len_func_header); | |
| strncpy(expression_str, func_header, len_func_header); | |
| char* temp_str = NULL; | |
| // Add tab_in[''] around keys in expression | |
| for( int i = 0; i < n; ++i ) | |
| { | |
| if( keys[i] == NULL ) | |
| { | |
| fprintf(stderr, "\nERROR in evaluate(): NULL string in keys\n"); | |
| if( expression_str != NULL ) arrfree(expression_str); | |
| return FLT_MAX; | |
| } | |
| if( temp_str != NULL ) arrdeln(temp_str,0,arrlen(temp_str)); | |
| int len_key = strlen(keys[i]); | |
| if( | |
| strcmp(keys[i],"etab") == 0 || | |
| strcmp(keys[i],"and") == 0 || | |
| strcmp(keys[i],"break") == 0 || | |
| strcmp(keys[i],"do") == 0 || | |
| strcmp(keys[i],"else") == 0 || | |
| strcmp(keys[i],"elseif") == 0 || | |
| strcmp(keys[i],"end") == 0 || | |
| strcmp(keys[i],"false") == 0 || | |
| strcmp(keys[i],"for") == 0 || | |
| strcmp(keys[i],"function") == 0 || | |
| strcmp(keys[i],"goto") == 0 || | |
| strcmp(keys[i],"if") == 0 || | |
| strcmp(keys[i],"in") == 0 || | |
| strcmp(keys[i],"local") == 0 || | |
| strcmp(keys[i],"nil") == 0 || | |
| strcmp(keys[i],"not") == 0 || | |
| strcmp(keys[i],"or") == 0 || | |
| strcmp(keys[i],"repeat") == 0 || | |
| strcmp(keys[i],"return") == 0 || | |
| strcmp(keys[i],"then") == 0 || | |
| strcmp(keys[i],"true") == 0 || | |
| strcmp(keys[i],"until") == 0 || | |
| strcmp(keys[i],"while") == 0 | |
| ) | |
| { | |
| fprintf(stderr, "\nERROR in evaluate(): variable name '%s' is reserved\n", keys[i]); | |
| if( expression_str != NULL ) arrfree(expression_str); | |
| if( temp_str != NULL ) arrfree(temp_str); | |
| return FLT_MAX; | |
| } | |
| arraddnptr(temp_str,len_prefix+len_key+len_postfix+1); | |
| strncpy(temp_str, replace_prefix, len_prefix); | |
| strncpy(temp_str+len_prefix, keys[i], len_key); | |
| strncpy(temp_str+len_prefix+len_key, replace_postfix, len_postfix); | |
| temp_str[len_prefix+len_key+len_postfix] = 0; | |
| str_replace(&new_str, curr_str == NULL ? expression : curr_str, keys[i], temp_str); | |
| if( new_str == NULL ) | |
| { | |
| fprintf(stderr, "\nERROR in evaluate(): Could Not Allocate Memory\n"); | |
| if( expression_str != NULL ) arrfree(expression_str); | |
| if( temp_str != NULL ) arrfree(temp_str); | |
| return FLT_MAX; | |
| } | |
| // swap buffers | |
| swap_ptr = curr_str; | |
| curr_str = new_str; | |
| new_str = swap_ptr; | |
| } | |
| int len_expression = strlen(expression); | |
| if( curr_str != NULL ) len_expression = strlen(curr_str); | |
| arraddnptr(expression_str,len_expression+len_func_footer+1); | |
| strncpy(expression_str+len_func_header, curr_str != NULL ? curr_str : expression, len_expression); | |
| strncpy(expression_str+len_func_header+len_expression, func_footer, len_func_footer); | |
| expression_str[len_func_header+len_expression+len_func_footer] = 0; | |
| if( curr_str != NULL ) arrfree(curr_str); | |
| if( new_str != NULL ) arrfree(new_str); | |
| if( temp_str != NULL ) arrfree(temp_str); | |
| L = luaL_newstate(); | |
| luaL_openlibs(L); | |
| if (luaL_loadstring(L, expression_str)) | |
| { | |
| fprintf(stderr, "\nERROR in evaluate():\n %s: %s\n\n", "luaL_loadstring() failed", lua_tostring(L, -1)); | |
| arrfree(expression_str); | |
| return FLT_MAX; | |
| } | |
| if (lua_pcall(L, 0, 0, 0)) | |
| { | |
| fprintf(stderr, "\nERROR in evaluate():\n %s: %s\n\n", "lua_pcall() failed", lua_tostring(L, -1)); | |
| arrfree(expression_str); | |
| return FLT_MAX; | |
| } | |
| lua_getglobal(L, "equation"); | |
| lua_newtable(L); | |
| for( int i = 0; i < n; ++i ) | |
| { | |
| lua_pushstring(L, keys[i]); | |
| lua_pushnumber(L, values[i]); | |
| lua_settable(L, -3); | |
| } | |
| if(lua_pcall(L, 1, 1, 0)) | |
| { | |
| fprintf(stderr, "\nERROR in evaluate():\n %s: %s\n\n", "lua_pcall() failed", lua_tostring(L, -1)); | |
| arrfree(expression_str); | |
| return FLT_MAX; | |
| } | |
| float ret = lua_tonumber(L, -1); | |
| lua_close(L); | |
| arrfree(expression_str); | |
| return ret; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment