/****** script.c This is the test/devel environment for the ts interpreter/scripting language. It provides a basic CLI for entering script to be interpreted live. ******/ #include #include #include #include #include // ULONG_MAX #define DIGIT 1 #define ALPHA 2 #define VAR_SET 1 #define RETURN 2 //#define TEST_FUNC "var1 = 2;\nvar2 = 4;\ndam = 2 * 2 / 2;\n.dam;\n" //#define TEST_FUNC "var1 = 8;\ndam = var1 * 2 / 4;\n.dam;\n" //#define TEST_FUNC "var1 = 2;var2 = 3;var3 = 7;dam = var1 * var2 * var3;dam = dam * var2;.dam;" #define TEST_FUNC "var1 = 2;var2 = 3;var3 = 7;var4 = 22;dam = var1 * var2 * var4 / var3;dam = dam * var2;.dam;" #define TEST_FULL_FUNC "test { var1 = 2;var2 = 3;var3 = 7;dam = var1 * var2 * var3;dam = dam * var2;.dam; }" #define TEST_FUNC_CALL "fcall { var = test();.var; }" #define ROLL_FUNC "roll { var=_roll($_1,$_2);.var; }" struct Arguments { const char *arg1; const char *arg2; const char *arg3; const char *arg4; }; typedef char *(*fptr)(char*); struct FuncTable { int size; char **keys; //void (**func)(); fptr *func; }; struct FuncTable *newFuncTable(int size) { struct FuncTable *func_table = malloc(sizeof(struct FuncTable)); func_table->size = size; func_table->keys = malloc(sizeof(char*)*size); //func_table->func = malloc(sizeof(void*)*size); func_table->func = malloc(sizeof(fptr*)*size); return func_table; } void freeFuncTable(struct FuncTable *func_table) { free(func_table->keys); free(func_table->func); free(func_table); } int getFuncIndex(struct FuncTable *func_table, char *key) { unsigned long int func = 0; int i = 0; while (func < ULONG_MAX && i < strlen(key)) { func += key[i++]; } return func % func_table->size; } void addFunc(struct FuncTable *func_table, char *key, void(*func)) { int index = getFuncIndex(func_table, key); func_table->keys[index] = key; func_table->func[index] = func; } fptr runFunc(struct FuncTable *func_table, char *key) { return func_table->func[getFuncIndex(func_table, key)]; } struct HashTable { int size; char **keys; void **values; }; struct FuncTable *g_functions; struct HashTable *newHashTable(int size) { struct HashTable *hash_table = malloc(sizeof(struct HashTable)); hash_table->size = size; hash_table->keys = malloc(sizeof(char*)*size); hash_table->values = malloc(sizeof(void*)*size); return hash_table; } void freeHashTable(struct HashTable *hash_table) { free(hash_table->keys); free(hash_table->values); free(hash_table); } int getHashIndex(struct HashTable *hash_table, char *key) { unsigned long int hash = 0; int i = 0; while (hash < ULONG_MAX && i < strlen(key)) { hash += key[i++]; } return hash % hash_table->size; } void addHash(struct HashTable *hash_table, char *key, void *value) { int index = getHashIndex(hash_table, key); hash_table->keys[index] = key; hash_table->values[index] = value; } void *getHash(struct HashTable *hash_table, char *key) { return hash_table->values[getHashIndex(hash_table, key)]; } struct VarTable { int size; char **keys; char **values; }; struct VarTable *function_table; char *parseAndCallFunction(struct VarTable *var_table, const char *buffer); struct VarTable *newVarTable(int size) { struct VarTable *var_table = malloc(sizeof(struct VarTable)); var_table->size = size; var_table->keys = malloc(sizeof(char*)*size); var_table->values = malloc(sizeof(char*)*size); return var_table; } void freeVarTable(struct VarTable *var_table) { // TODO: free key-value pairs free(var_table->keys); free(var_table->values); free(var_table); } int getVarIndex(struct VarTable *var_table, const char *key) { // int hash = 0; unsigned long int hash = 0; int i = 0; while (hash < ULONG_MAX && i < strlen(key)) { //while (i < strlen(key)) { //hash = hash << 8; hash += key[i++]; } return hash % var_table->size; } void addVar(struct VarTable *var_table, const char *key, char *value) { int index = getVarIndex(var_table, key); int key_length = strlen(key)+1; var_table->keys[index] = malloc(key_length); memcpy(var_table->keys[index], key, key_length); int value_length = strlen(value)+1; var_table->values[index] = malloc(value_length); memcpy(var_table->values[index], value, value_length); } void freeVar(struct VarTable *var_table, const char *key) { int index = getVarIndex(var_table, key); if (var_table->keys[index]) free(var_table->keys[index]); if (var_table->values[index]) free(var_table->values[index]); } char *getVar(struct VarTable *var_table, const char *key) { int index = getVarIndex(var_table, key); return var_table->values[index]; } struct Var { int bytes; char type; char operation; char *data; }; char* itoa(int value, char* result, int base) { // check that the base if valid if (base < 2 || base > 36) { *result = '\0'; return result; } char* ptr = result, *ptr1 = result, tmp_char; int tmp_value; do { tmp_value = value; value /= base; *ptr++ = "zyxwvutsrqponmlkjihgfedcba9876543210123456789abcdefghijklmnopqrstuvwxyz" [35 + (tmp_value - value * base)]; } while ( value ); // Apply negative sign if (tmp_value < 0) *ptr++ = '-'; *ptr-- = '\0'; while(ptr1 < ptr) { tmp_char = *ptr; *ptr--= *ptr1; *ptr1++ = tmp_char; } return result; } // TODO: replace Arguments with v_printf stuff void doOperation(struct VarTable *var_table, const char **args, int arg_count, char *return_string, const char *string) { printf("args: %d\n", arg_count); //printf("operation is %s\n", string); int s_i = 0; // string iterator int b_i = 0; // buffer iterator int bt_i = 0; // buffer type iterator char buffer[255]; // buffer that holds the portion being processed struct Var vars[31]; // max 31 variables int var_i = 0; int c_i = 0; // current var iterator char *f_return; // function call pointer while (string[s_i] != '\0') { switch (string[s_i]) { case '+': case '-': case '/': case '*': if (isdigit(buffer[0])) vars[var_i].type = DIGIT; else vars[var_i].type = ALPHA; buffer[b_i] = '\0'; if (vars[var_i].type == ALPHA) { if (arg_count > 0) printf("first part of arg0: %c\n", args[0][0]); if (buffer[b_i-1] == ')') { f_return = parseAndCallFunction(var_table, buffer); vars[var_i].bytes = strlen(f_return)+1; vars[var_i].data = malloc(vars[var_i].bytes); strcpy(vars[var_i].data, f_return); free(f_return); } else { vars[var_i].bytes = strlen(getVar(var_table, buffer))+1; vars[var_i].data = malloc(vars[var_i].bytes); strcpy(vars[var_i].data, getVar(var_table, buffer)); } } else { vars[var_i].bytes = strlen(buffer)+1; vars[var_i].data = malloc(vars[var_i].bytes); strcpy(vars[var_i].data, buffer); } memset(&buffer, '\0', 255); b_i = 0; vars[var_i].operation = string[s_i]; var_i++; break; case ';': buffer[b_i] = '\0'; // determine variable type if (isdigit(buffer[0])) vars[var_i].type = DIGIT; else vars[var_i].type = ALPHA; if (vars[var_i].type == ALPHA) { if (buffer[b_i-1] == ')') { f_return = parseAndCallFunction(var_table, buffer); vars[var_i].bytes = strlen(f_return)+1; vars[var_i].data = malloc(vars[var_i].bytes); strcpy(vars[var_i].data, f_return); free(f_return); } else { vars[var_i].bytes = strlen(getVar(var_table, buffer))+1; vars[var_i].data = malloc(vars[var_i].bytes); strcpy(vars[var_i].data, getVar(var_table, buffer)); } } else { vars[var_i].bytes = strlen(buffer)+1; vars[var_i].data = malloc(vars[var_i].bytes); strcpy(vars[var_i].data, buffer); } // "clear" buffer memset(&buffer, '\0', 255); b_i = 0; // ALRIGHT, now let's do the operaton c_i = 0; while (c_i < var_i) { switch (vars[c_i].operation) { case '+': itoa(atoi(vars[c_i].data) + atoi(vars[c_i+1].data), vars[c_i+1].data, 10); break; case '-': itoa(atoi(vars[c_i].data) - atoi(vars[c_i+1].data), vars[c_i+1].data, 10); break; case '*': itoa(atoi(vars[c_i].data) * atoi(vars[c_i+1].data), vars[c_i+1].data, 10); break; case '/': itoa(atoi(vars[c_i].data) / atoi(vars[c_i+1].data), vars[c_i+1].data, 10); break; } c_i++; } //printf(" = %s\n", vars[c_i].data); strcpy(return_string, vars[c_i].data); break; case ' ': break; default: buffer[b_i] = string[s_i]; b_i++; break; } s_i++; } while (var_i >= 0) { free(vars[var_i--].data); } } void doVarSet(struct Var *var, const char *string) { if (var->data) free(var); var->bytes = strlen(string)+1; var->data = malloc(var->bytes); memcpy(var->data, string, var->bytes); } // TODO: use vprintf stuff for arguments (or get byte offsets?) char *doFunction(const char *function, const char *arguments) { /* get our args from buffer, delimited by ',' */ int ba_i = 0; char args[2][31]; int a_i = 0; int arg_offset = 0; if (arguments[ba_i] != '\0') arg_offset++; while (arguments[ba_i] != '\0') { switch (arguments[ba_i]) { case ',': args[arg_offset][a_i] = '\0'; a_i = 0; arg_offset++; break; case ' ': break; default: args[arg_offset][a_i++] = arguments[ba_i]; break; } ba_i++; } args[arg_offset][a_i] = '\0'; /* okay, let's do the rolling. TODO: handle lack of args */ if (arg_offset > 0) printf("passed arg 0: %s\n", args[0]); printf("running function:\n%s\n-------\n", function); struct VarTable *local_vars = newVarTable(127); int b_i = 0; // buffer iterator int bt_i = 0; // buffer type iterator char buffer[255]; // buffer that holds the portion being processed char current_var[31]; // vars cannae have more than 31 chars char current_value[31]; current_var[0] = '\0'; int mode = 0; struct Var vars[31]; // max 31 variables int var_i = 0; int c_i = 0; // current var iterator int f_i = 0; // function char pos int l_i = 0; // line pos int l_c = 0; // line count size_t f_bytes = 0; char *f_return = malloc(1); while (function[f_i] != '\0') { switch(function[f_i]) { case '\n': l_c++; break; case ' ': // skip space break; case '=': buffer[b_i] = '\0'; b_i = 0; strcpy(current_var, buffer); memset(&buffer, 0, 255); mode = VAR_SET; break; case ';': if (mode == VAR_SET) { buffer[b_i++] = ';'; buffer[b_i] = '\0'; b_i = 0; doOperation(local_vars, &args, arg_offset, current_value, buffer); addVar(local_vars, current_var, current_value); } else if (mode == RETURN) { buffer[b_i] = '\0'; b_i = 0; strcpy(current_var, buffer); f_bytes = strlen(getVar(local_vars, current_var))+1; realloc(f_return, f_bytes); memcpy(f_return, getVar(local_vars, current_var), f_bytes); freeVarTable(local_vars); // free up all our local stuff return f_return; } break; case '.': if (b_i == 0) { mode = RETURN; } else { buffer[b_i++] = function[f_i]; } break; default: buffer[b_i++] = function[f_i]; break; } f_i++; // next char in func } freeVarTable(local_vars); // free up all our local stuff return f_return; } char *callFunction(const char *f_name, const char *f_args) { return (doFunction(getVar(function_table, f_name), f_args)); } char *parseAndCallFunction(struct VarTable *var_table, const char *buffer) { int b_i = 0; char f_name[32]; char f_args[63]; int f_i = 0; int mode = 0; while (buffer[b_i] != '\0') { switch (buffer[b_i]) { case '(': f_name[f_i] = '\0'; f_i = 0; mode = 1; break; case ')': f_args[f_i] = '\0'; f_i = 0; mode = 2; break; case ' ': break; default: if (mode == 0) { f_name[f_i++] = buffer[b_i]; } else if (mode > 0) { f_args[f_i++] = buffer[b_i]; // do something with args... } break; } b_i++; } /* convert args son */ // FIXME: hackish, q'n'd b_i = 0; char args[10][31]; int a_i = 0; int arg_offset = 0; while (f_args[b_i] != '\0') { switch (f_args[b_i]) { case ',': args[arg_offset][a_i] = '\0'; a_i = 0; arg_offset++; break; case ' ': break; default: args[arg_offset][a_i++] = f_args[b_i]; break; } b_i++; } args[arg_offset][a_i] = '\0'; if (f_args[0] != '\0') { a_i = 0; f_i = 0; b_i = 0; while (a_i <= arg_offset) { if (!isdigit(args[a_i][0])) { strcpy(args[a_i], getVar(var_table, args[a_i])); } //printf("%d = %s\n", a_i, args[a_i]); b_i = 0; while (args[a_i][b_i] != '\0') { f_args[f_i++] = args[a_i][b_i++]; } if (a_i != arg_offset) f_args[f_i++] = ','; a_i++; } } f_args[f_i] = '\0'; if (f_name[0] == '_') { //printf("trying to run %s\n", f_name); return runFunc(g_functions, f_name)(f_args); } else { // TODO: callFunction should parse f_args as well. return callFunction(f_name, f_args); } //return callFunction(f_name, NULL, NULL, NULL, NULL); } int addFunction(const char *buffer) { char func_name[32]; int f_size = 128; char *func_data = malloc(f_size*sizeof(char)); // start w/ 128 chars int f_i = 0; // func_data index offset int c_i = 0; // iterator for function_name int i = 0; // iterator for the buffer int depth = 0; while (buffer[i] != '\0') { switch (buffer[i]) { case ' ': break; case '{': if (depth == 0) func_name[c_i] = '\0'; depth++; break; case '}': depth--; break; default: if (depth == 0) { func_name[c_i++] = buffer[i]; } else if (depth > 0) { // resize if function surpasses our temp func_data buffer if (f_i+1 >= f_size) { f_size += 128; realloc(func_data, f_size); } func_data[f_i] = buffer[i]; f_i++; } break; } i++; } func_data[f_i] = '\0'; addVar(function_table, func_name, func_data); free(func_data); return 0; // should handle errors for malloc... } char *doRoll(const char *buffer) { /* get our args from buffer, delimited by ',' */ int b_i = 0; char args[2][31]; int a_i = 0; int arg_offset = 0; while (buffer[b_i] != '\0') { switch (buffer[b_i]) { case ',': args[arg_offset][a_i] = '\0'; a_i = 0; arg_offset++; break; case ' ': break; default: args[arg_offset][a_i++] = buffer[b_i]; break; } b_i++; } args[arg_offset][a_i] = '\0'; /* okay, let's do the rolling. TODO: handle lack of args */ int count = atoi(args[0]); int faces = atoi(args[1]); int total = 0; while (count > 0) { total += (rand() % faces + 1); count--; } /* return char array pointer that will be freed outside of this func */ char *f_return = malloc(24); itoa(total, f_return, 10); return f_return; } int main() { function_table = newVarTable(255); g_functions = newFuncTable(127); // function pointers addFunc(g_functions, "_roll", doRoll); /* #define'd function */ addFunction(TEST_FULL_FUNC); addFunction(ROLL_FUNC); char *f_return; f_return = callFunction("test", ""); printf("called func \"%s\" returned:\n%s\n", "test", f_return); free(f_return); /* free hand this ish */ addFunction("add { var = 1 + 2; .var; }"); f_return = callFunction("add", ""); printf("called func \"%s\" returned:\n%s\n", "add", f_return); free(f_return); addFunction(TEST_FUNC_CALL); f_return = callFunction("fcall", ""); printf("called func \"%s\" returned:\n%s\n", "fcall", f_return); free(f_return); char user_input[1024]; printf("Enter script:\n> "); //scanf("%s", user_input); fgets(user_input, 1024, stdin); f_return = doFunction(user_input, ""); printf("return:\n%s\n", f_return); free(f_return); freeVarTable(function_table); freeFuncTable(g_functions); /* char *f_return; f_return = doFunction(TEST_FUNC, NULL, NULL, NULL, NULL); printf("return: %s\n", f_return); free(f_return);*/ return 0; }