Skip to content

Instantly share code, notes, and snippets.

@Lokno
Last active October 19, 2020 01:40
Show Gist options
  • Select an option

  • Save Lokno/88d2cf65788968402882fabc36e9c3b0 to your computer and use it in GitHub Desktop.

Select an option

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
//
// 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