From 538205e8a5643fce9d7f6035613922263666dddc Mon Sep 17 00:00:00 2001 From: Matthieu Bessat Date: Sun, 15 May 2022 18:46:16 +0200 Subject: [PATCH] feat: add while loops --- Makefile | 2 + README.md | 17 +- examples/fibo.ltor | 9 + log.err | 304 ++++++++++++++++++++ sandbox.c | 41 ++- src/evaluator.c | 64 +++-- src/evaluator.h | 2 + src/funcs.c | 9 +- src/line_processing.c | 523 +++++++++++++++++++++++++++++------ src/line_processing.h | 29 ++ src/main.c | 93 ++----- src/operate.c | 16 +- src/operate.h | 3 +- src/stack.c | 56 ++-- src/stack.h | 16 +- src/state.c | 23 +- src/state.h | 38 ++- src/types.c | 14 + src/types.h | 4 + src/utils.c | 50 +++- src/utils.h | 7 +- src/var_store.c | 23 +- tests/test.c | 4 + tests/test_line_processing.c | 112 +++++++- tests/test_stack.c | 39 +++ tests/test_stack.h | 1 + tests/test_utils.c | 12 + tests/test_var_store.c | 18 ++ tests/test_var_store.h | 1 + 29 files changed, 1248 insertions(+), 282 deletions(-) create mode 100644 examples/fibo.ltor create mode 100644 log.err create mode 100644 tests/test_stack.c create mode 100644 tests/test_stack.h create mode 100644 tests/test_var_store.c create mode 100644 tests/test_var_store.h diff --git a/Makefile b/Makefile index 67e88fb..e180d25 100644 --- a/Makefile +++ b/Makefile @@ -10,6 +10,8 @@ build: test: gcc ${TEST_SRCS_ENC} ./tests/* -o ./bin/test ${CXXFLAGS_WITHOUT_PKGS} ./bin/test +test-no-run: + gcc ${TEST_SRCS_ENC} ./tests/* -o ./bin/test ${CXXFLAGS_WITHOUT_PKGS} sandbox: gcc ${TEST_SRCS_ENC} ./sandbox.c -o ./bin/sandbox ${CXXFLAGS_WITHOUT_PKGS} ./bin/sandbox diff --git a/README.md b/README.md index 64d0345..0359437 100644 --- a/README.md +++ b/README.md @@ -22,15 +22,22 @@ ToDo List: - [X] implement basic math functions - [X] implement random_int(min, max) - [X] implement print_number(message) +- [X] add unit tests +- [X] allow to set variables +- [X] read line comments +- [ ] add modulus operator '%' - [ ] implement input_number() - [ ] base of the CLI - [ ] evaluate expression from stdin - [ ] read a file -- [X] allow to set variables -- [X] read line comments -- [ ] conditionals -- [ ] add unit tests -- [ ] function call stack +- [ ] if statements +- [ ] while statements (with break) +- [ ] add functions support +- [ ] add config header file +- [ ] ability to modify keywords and customize the lang +- [ ] add strings support +- [ ] add list support + ## The language diff --git a/examples/fibo.ltor b/examples/fibo.ltor new file mode 100644 index 0000000..489a5a3 --- /dev/null +++ b/examples/fibo.ltor @@ -0,0 +1,9 @@ +set a to 1 +set b to 1 +set i to 0 +while i < 10 do + set c to a + set a to a+b + set b to c + print_number a +end diff --git a/log.err b/log.err new file mode 100644 index 0000000..907dabe --- /dev/null +++ b/log.err @@ -0,0 +1,304 @@ +./src/funcs.c:43:33: error: '#' is not followed by a macro parameter + 43 | #define SIMPLE_FUNC_BINDING(name) ({\ + | ^ +./src/funcs.c:51:1: warning: return type defaults to ‘int’ [-Wimplicit-int] + 51 | SIMPLE_FUNC_BINDING(sqrt) + | ^~~~~~~~~~~~~~~~~~~ +./src/funcs.c: In function ‘SIMPLE_FUNC_BINDING’: +./src/funcs.c:53:1: error: expected declaration specifiers before ‘SIMPLE_FUNC_BINDING’ + 53 | SIMPLE_FUNC_BINDING(exp) + | ^~~~~~~~~~~~~~~~~~~ +./src/funcs.c:114:1: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘{’ token + 114 | { + | ^ +./src/funcs.c:121:1: warning: empty declaration + 121 | struct FuncIntro { + | ^~~~~~ +./src/funcs.c:129:8: error: parameter ‘intros’ is initialized + 129 | struct FuncIntro intros[] = { + | ^~~~~~~~~ +./src/funcs.c:130:5: warning: braces around scalar initializer + 130 | {"abs", &abs_impl, 1}, + | ^ +./src/funcs.c:130:5: note: (near initialization for ‘intros’) +./src/funcs.c:130:6: warning: initialization of ‘struct FuncIntro *’ from incompatible pointer type ‘char *’ [-Wincompatible-pointer-types] + 130 | {"abs", &abs_impl, 1}, + | ^~~~~ +./src/funcs.c:130:6: note: (near initialization for ‘intros’) +./src/funcs.c:130:13: warning: excess elements in scalar initializer + 130 | {"abs", &abs_impl, 1}, + | ^ +./src/funcs.c:130:13: note: (near initialization for ‘intros’) +./src/funcs.c:130:24: warning: excess elements in scalar initializer + 130 | {"abs", &abs_impl, 1}, + | ^ +./src/funcs.c:130:24: note: (near initialization for ‘intros’) +./src/funcs.c:132:5: warning: braces around scalar initializer + 132 | {"sqrt", &sqrt_impl, 1}, + | ^ +./src/funcs.c:132:5: note: (near initialization for ‘intros’) +./src/funcs.c:132:6: warning: initialization of ‘struct FuncIntro *’ from incompatible pointer type ‘char *’ [-Wincompatible-pointer-types] + 132 | {"sqrt", &sqrt_impl, 1}, + | ^~~~~~ +./src/funcs.c:132:6: note: (near initialization for ‘intros’) +./src/funcs.c:132:15: error: ‘sqrt_impl’ undeclared (first use in this function) + 132 | {"sqrt", &sqrt_impl, 1}, + | ^~~~~~~~~ +./src/funcs.c:132:15: note: each undeclared identifier is reported only once for each function it appears in +./src/funcs.c:132:14: warning: excess elements in scalar initializer + 132 | {"sqrt", &sqrt_impl, 1}, + | ^ +./src/funcs.c:132:14: note: (near initialization for ‘intros’) +./src/funcs.c:132:26: warning: excess elements in scalar initializer + 132 | {"sqrt", &sqrt_impl, 1}, + | ^ +./src/funcs.c:132:26: note: (near initialization for ‘intros’) +./src/funcs.c:132:5: warning: excess elements in scalar initializer + 132 | {"sqrt", &sqrt_impl, 1}, + | ^ +./src/funcs.c:132:5: note: (near initialization for ‘intros’) +./src/funcs.c:133:5: warning: braces around scalar initializer + 133 | {"exp", &exp_impl, 1}, + | ^ +./src/funcs.c:133:5: note: (near initialization for ‘intros’) +./src/funcs.c:133:6: warning: initialization of ‘struct FuncIntro *’ from incompatible pointer type ‘char *’ [-Wincompatible-pointer-types] + 133 | {"exp", &exp_impl, 1}, + | ^~~~~ +./src/funcs.c:133:6: note: (near initialization for ‘intros’) +./src/funcs.c:133:14: error: ‘exp_impl’ undeclared (first use in this function) + 133 | {"exp", &exp_impl, 1}, + | ^~~~~~~~ +./src/funcs.c:133:13: warning: excess elements in scalar initializer + 133 | {"exp", &exp_impl, 1}, + | ^ +./src/funcs.c:133:13: note: (near initialization for ‘intros’) +./src/funcs.c:133:24: warning: excess elements in scalar initializer + 133 | {"exp", &exp_impl, 1}, + | ^ +./src/funcs.c:133:24: note: (near initialization for ‘intros’) +./src/funcs.c:133:5: warning: excess elements in scalar initializer + 133 | {"exp", &exp_impl, 1}, + | ^ +./src/funcs.c:133:5: note: (near initialization for ‘intros’) +./src/funcs.c:135:5: warning: braces around scalar initializer + 135 | {"sin", &sin_impl, 1}, + | ^ +./src/funcs.c:135:5: note: (near initialization for ‘intros’) +./src/funcs.c:135:6: warning: initialization of ‘struct FuncIntro *’ from incompatible pointer type ‘char *’ [-Wincompatible-pointer-types] + 135 | {"sin", &sin_impl, 1}, + | ^~~~~ +./src/funcs.c:135:6: note: (near initialization for ‘intros’) +./src/funcs.c:135:14: error: ‘sin_impl’ undeclared (first use in this function) + 135 | {"sin", &sin_impl, 1}, + | ^~~~~~~~ +./src/funcs.c:135:13: warning: excess elements in scalar initializer + 135 | {"sin", &sin_impl, 1}, + | ^ +./src/funcs.c:135:13: note: (near initialization for ‘intros’) +./src/funcs.c:135:24: warning: excess elements in scalar initializer + 135 | {"sin", &sin_impl, 1}, + | ^ +./src/funcs.c:135:24: note: (near initialization for ‘intros’) +./src/funcs.c:135:5: warning: excess elements in scalar initializer + 135 | {"sin", &sin_impl, 1}, + | ^ +./src/funcs.c:135:5: note: (near initialization for ‘intros’) +./src/funcs.c:136:5: warning: braces around scalar initializer + 136 | {"cos", &cos_impl, 1}, + | ^ +./src/funcs.c:136:5: note: (near initialization for ‘intros’) +./src/funcs.c:136:6: warning: initialization of ‘struct FuncIntro *’ from incompatible pointer type ‘char *’ [-Wincompatible-pointer-types] + 136 | {"cos", &cos_impl, 1}, + | ^~~~~ +./src/funcs.c:136:6: note: (near initialization for ‘intros’) +./src/funcs.c:136:14: error: ‘cos_impl’ undeclared (first use in this function); did you mean ‘abs_impl’? + 136 | {"cos", &cos_impl, 1}, + | ^~~~~~~~ + | abs_impl +./src/funcs.c:136:13: warning: excess elements in scalar initializer + 136 | {"cos", &cos_impl, 1}, + | ^ +./src/funcs.c:136:13: note: (near initialization for ‘intros’) +./src/funcs.c:136:24: warning: excess elements in scalar initializer + 136 | {"cos", &cos_impl, 1}, + | ^ +./src/funcs.c:136:24: note: (near initialization for ‘intros’) +./src/funcs.c:136:5: warning: excess elements in scalar initializer + 136 | {"cos", &cos_impl, 1}, + | ^ +./src/funcs.c:136:5: note: (near initialization for ‘intros’) +./src/funcs.c:137:5: warning: braces around scalar initializer + 137 | {"tan", &tan_impl, 1}, + | ^ +./src/funcs.c:137:5: note: (near initialization for ‘intros’) +./src/funcs.c:137:6: warning: initialization of ‘struct FuncIntro *’ from incompatible pointer type ‘char *’ [-Wincompatible-pointer-types] + 137 | {"tan", &tan_impl, 1}, + | ^~~~~ +./src/funcs.c:137:6: note: (near initialization for ‘intros’) +./src/funcs.c:137:14: error: ‘tan_impl’ undeclared (first use in this function) + 137 | {"tan", &tan_impl, 1}, + | ^~~~~~~~ +./src/funcs.c:137:13: warning: excess elements in scalar initializer + 137 | {"tan", &tan_impl, 1}, + | ^ +./src/funcs.c:137:13: note: (near initialization for ‘intros’) +./src/funcs.c:137:24: warning: excess elements in scalar initializer + 137 | {"tan", &tan_impl, 1}, + | ^ +./src/funcs.c:137:24: note: (near initialization for ‘intros’) +./src/funcs.c:137:5: warning: excess elements in scalar initializer + 137 | {"tan", &tan_impl, 1}, + | ^ +./src/funcs.c:137:5: note: (near initialization for ‘intros’) +./src/funcs.c:138:5: warning: braces around scalar initializer + 138 | {"ln", &ln_impl, 1}, + | ^ +./src/funcs.c:138:5: note: (near initialization for ‘intros’) +./src/funcs.c:138:6: warning: initialization of ‘struct FuncIntro *’ from incompatible pointer type ‘char *’ [-Wincompatible-pointer-types] + 138 | {"ln", &ln_impl, 1}, + | ^~~~ +./src/funcs.c:138:6: note: (near initialization for ‘intros’) +./src/funcs.c:138:13: error: ‘ln_impl’ undeclared (first use in this function) + 138 | {"ln", &ln_impl, 1}, + | ^~~~~~~ +./src/funcs.c:138:12: warning: excess elements in scalar initializer + 138 | {"ln", &ln_impl, 1}, + | ^ +./src/funcs.c:138:12: note: (near initialization for ‘intros’) +./src/funcs.c:138:22: warning: excess elements in scalar initializer + 138 | {"ln", &ln_impl, 1}, + | ^ +./src/funcs.c:138:22: note: (near initialization for ‘intros’) +./src/funcs.c:138:5: warning: excess elements in scalar initializer + 138 | {"ln", &ln_impl, 1}, + | ^ +./src/funcs.c:138:5: note: (near initialization for ‘intros’) +./src/funcs.c:139:5: warning: braces around scalar initializer + 139 | {"log", &log_impl, 1}, + | ^ +./src/funcs.c:139:5: note: (near initialization for ‘intros’) +./src/funcs.c:139:6: warning: initialization of ‘struct FuncIntro *’ from incompatible pointer type ‘char *’ [-Wincompatible-pointer-types] + 139 | {"log", &log_impl, 1}, + | ^~~~~ +./src/funcs.c:139:6: note: (near initialization for ‘intros’) +./src/funcs.c:139:14: error: ‘log_impl’ undeclared (first use in this function) + 139 | {"log", &log_impl, 1}, + | ^~~~~~~~ +./src/funcs.c:139:13: warning: excess elements in scalar initializer + 139 | {"log", &log_impl, 1}, + | ^ +./src/funcs.c:139:13: note: (near initialization for ‘intros’) +./src/funcs.c:139:24: warning: excess elements in scalar initializer + 139 | {"log", &log_impl, 1}, + | ^ +./src/funcs.c:139:24: note: (near initialization for ‘intros’) +./src/funcs.c:139:5: warning: excess elements in scalar initializer + 139 | {"log", &log_impl, 1}, + | ^ +./src/funcs.c:139:5: note: (near initialization for ‘intros’) +./src/funcs.c:141:5: warning: braces around scalar initializer + 141 | {"max", &max_impl, 2}, + | ^ +./src/funcs.c:141:5: note: (near initialization for ‘intros’) +./src/funcs.c:141:6: warning: initialization of ‘struct FuncIntro *’ from incompatible pointer type ‘char *’ [-Wincompatible-pointer-types] + 141 | {"max", &max_impl, 2}, + | ^~~~~ +./src/funcs.c:141:6: note: (near initialization for ‘intros’) +./src/funcs.c:141:14: error: ‘max_impl’ undeclared (first use in this function) + 141 | {"max", &max_impl, 2}, + | ^~~~~~~~ +./src/funcs.c:141:13: warning: excess elements in scalar initializer + 141 | {"max", &max_impl, 2}, + | ^ +./src/funcs.c:141:13: note: (near initialization for ‘intros’) +./src/funcs.c:141:24: warning: excess elements in scalar initializer + 141 | {"max", &max_impl, 2}, + | ^ +./src/funcs.c:141:24: note: (near initialization for ‘intros’) +./src/funcs.c:141:5: warning: excess elements in scalar initializer + 141 | {"max", &max_impl, 2}, + | ^ +./src/funcs.c:141:5: note: (near initialization for ‘intros’) +./src/funcs.c:142:5: warning: braces around scalar initializer + 142 | {"get_pi", &get_pi_impl, 0}, + | ^ +./src/funcs.c:142:5: note: (near initialization for ‘intros’) +./src/funcs.c:142:6: warning: initialization of ‘struct FuncIntro *’ from incompatible pointer type ‘char *’ [-Wincompatible-pointer-types] + 142 | {"get_pi", &get_pi_impl, 0}, + | ^~~~~~~~ +./src/funcs.c:142:6: note: (near initialization for ‘intros’) +./src/funcs.c:142:17: error: ‘get_pi_impl’ undeclared (first use in this function) + 142 | {"get_pi", &get_pi_impl, 0}, + | ^~~~~~~~~~~ +./src/funcs.c:142:16: warning: excess elements in scalar initializer + 142 | {"get_pi", &get_pi_impl, 0}, + | ^ +./src/funcs.c:142:16: note: (near initialization for ‘intros’) +./src/funcs.c:142:30: warning: excess elements in scalar initializer + 142 | {"get_pi", &get_pi_impl, 0}, + | ^ +./src/funcs.c:142:30: note: (near initialization for ‘intros’) +./src/funcs.c:142:5: warning: excess elements in scalar initializer + 142 | {"get_pi", &get_pi_impl, 0}, + | ^ +./src/funcs.c:142:5: note: (near initialization for ‘intros’) +./src/funcs.c:143:5: warning: braces around scalar initializer + 143 | {"", 0, 0} + | ^ +./src/funcs.c:143:5: note: (near initialization for ‘intros’) +./src/funcs.c:143:6: warning: initialization of ‘struct FuncIntro *’ from incompatible pointer type ‘char *’ [-Wincompatible-pointer-types] + 143 | {"", 0, 0} + | ^~ +./src/funcs.c:143:6: note: (near initialization for ‘intros’) +./src/funcs.c:143:10: warning: excess elements in scalar initializer + 143 | {"", 0, 0} + | ^ +./src/funcs.c:143:10: note: (near initialization for ‘intros’) +./src/funcs.c:143:13: warning: excess elements in scalar initializer + 143 | {"", 0, 0} + | ^ +./src/funcs.c:143:13: note: (near initialization for ‘intros’) +./src/funcs.c:143:5: warning: excess elements in scalar initializer + 143 | {"", 0, 0} + | ^ +./src/funcs.c:143:5: note: (near initialization for ‘intros’) +./src/funcs.c:146:12: error: storage class specified for parameter ‘nbOfFuncs’ + 146 | static int nbOfFuncs = sizeof(intros)/sizeof(struct FuncIntro); + | ^~~~~~~~~ +./src/funcs.c:146:1: error: parameter ‘nbOfFuncs’ is initialized + 146 | static int nbOfFuncs = sizeof(intros)/sizeof(struct FuncIntro); + | ^~~~~~ +./src/funcs.c:146:30: warning: ‘sizeof’ on array function parameter ‘intros’ will return size of ‘struct FuncIntro *’ [-Wsizeof-array-argument] + 146 | static int nbOfFuncs = sizeof(intros)/sizeof(struct FuncIntro); + | ^ +./src/funcs.c:129:18: note: declared here + 129 | struct FuncIntro intros[] = { + | ^~~~~~ +./src/funcs.c:152:1: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘{’ token + 152 | { + | ^ +./src/funcs.c:162:1: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘{’ token + 162 | { + | ^ +./src/funcs.c:51:1: warning: type of ‘sqrt’ defaults to ‘int’ [-Wimplicit-int] + 51 | SIMPLE_FUNC_BINDING(sqrt) + | ^~~~~~~~~~~~~~~~~~~ +./src/funcs.c:146:12: error: declaration for parameter ‘nbOfFuncs’ but no such parameter + 146 | static int nbOfFuncs = sizeof(intros)/sizeof(struct FuncIntro); + | ^~~~~~~~~ +./src/funcs.c:129:18: error: declaration for parameter ‘intros’ but no such parameter + 129 | struct FuncIntro intros[] = { + | ^~~~~~ +./src/funcs.c:196: error: expected ‘{’ at end of input +./src/funcs.c:51:1: warning: unused parameter ‘sqrt’ [-Wunused-parameter] + 51 | SIMPLE_FUNC_BINDING(sqrt) + | ^~~~~~~~~~~~~~~~~~~ +./src/funcs.c:146:12: warning: unused parameter ‘nbOfFuncs’ [-Wunused-parameter] + 146 | static int nbOfFuncs = sizeof(intros)/sizeof(struct FuncIntro); + | ^~~~~~~~~ +./src/funcs.c:196: warning: control reaches end of non-void function [-Wreturn-type] +./src/number_parsing.c: In function ‘parse_clean_positive_integer’: +./src/number_parsing.c:21:29: warning: binary constants are a C2X feature or GCC extension + 21 | (numeralIndex & 0b001111) + | ^~~~~~~~ +make: *** [Makefile:11: test] Error 1 diff --git a/sandbox.c b/sandbox.c index 01bd0bf..899af0c 100644 --- a/sandbox.c +++ b/sandbox.c @@ -23,12 +23,13 @@ int main () { // memcpy(&dst, &yes, 4); // printf("yes: %d \n", dst); - struct VariableStore* store = var_store_init(); + //struct VariableStore* store = var_store_init(); // int hashRes = var_store_hash_name(store, "HelloWorld++"); // printf("hashRes: %d \n", hashRes); - int val = 1234; - var_store_set(store, "foo", TYPE_INT, &val); + // int val = 1234; + // var_store_set(store, "foo", TYPE_INT, &val); + // var_store_get_key(store, "while i < 10 then"); //printf("Pos of var: %d \n", var_store_get_pos(store, "foo")); @@ -39,19 +40,33 @@ int main () { //printf("size of type %d \n", get_size_of_type(store->container[key].type)); //printf("%d \n", *((int*) store->container[key].dataPtr)); - int val2 = 0; - var_store_copy(store, "foo", &val2); + // int val2 = 0; + // var_store_copy(store, "foo", &val2); - printf("Value of var: %d \n", val2); + // printf("Value of var: %d \n", val2); - printf("==== \n"); - printf("==== \n"); + // printf("==== \n"); + // printf("==== \n"); + + // char* lines = "# hello world\n" + // "set x to 5\n" + // "set y to 1\n" + // "if !(x+y = 6) then\n" + // " print_number(x+y)\n" + // "end\n" + // "\n"; + + char* lines1 = "set x to 5\n" + "set y to 1\n" + "if !(x+y = 6) then\n" + " print_number(x+y)\n" + "end\n" + "\n"; + + printf("%s", lines1); + struct StateContainer* state = state_init(); - process_line(state, "# some random comment"); - process_line(state, "set VAR_A to 8.5"); - process_line(state, "set VAR_B to 1.5"); - process_line(state, "set VAR_C to VAR_A+VAR_B"); - process_line(state, "print_number(sqrt(VAR_C))"); + process_script(state, lines1); // struct List l1; diff --git a/src/evaluator.c b/src/evaluator.c index 2a9c839..007b9d1 100644 --- a/src/evaluator.c +++ b/src/evaluator.c @@ -205,7 +205,7 @@ int evaluator_reduce_var(struct StateContainer* state, struct List* evalList) { int varKey; list_get(evalList, patternPos, &varKey); - byte type = var_store_get_type_from_key(state->varStore, varKey); + byte type = var_store_get_type_from_key(state->varStore, varKey); byte varVal[get_size_of_type(type)]; var_store_copy_from_key(state->varStore, varKey, &varVal); @@ -355,6 +355,7 @@ Arguments: - Input String: char pointer (the source of the evaluation) - Result: int pointer (where the result of the evaluation will be written) */ +// FIXME: allow for multiple line expressions int evaluate(struct StateContainer* state, char* inputStr, int* resultPtr, unsigned char* typePtr) { int i = 0; int _len = strlen(inputStr); @@ -397,10 +398,14 @@ int evaluate(struct StateContainer* state, char* inputStr, int* resultPtr, unsig } // display the partition - printf("partitionPtr: %d \n", partitionPtr); + if (EVALUATOR_DEBUG_LEVEL >= 2) { + printf("partitionPtr: %d \n", partitionPtr); + } for (int j = 0; j < partitionPtr; j++) { - printf("start %d ", partitionStartPos[j]); - printf("stop %d ", partitionStopPos[j]); + if (EVALUATOR_DEBUG_LEVEL >= 2) { + printf("start %d ", partitionStartPos[j]); + printf("stop %d ", partitionStopPos[j]); + } int len = partitionStopPos[j] - partitionStartPos[j]; char buff[_len]; @@ -410,8 +415,9 @@ int evaluate(struct StateContainer* state, char* inputStr, int* resultPtr, unsig } } buff[len] = 0; - - printf("content %s \n", buff); + if (EVALUATOR_DEBUG_LEVEL >= 2) { + printf("content %s \n", buff); + } } // once we've lexed our expression, we want to analyse if it's righly parenthesized @@ -446,14 +452,16 @@ int evaluate(struct StateContainer* state, char* inputStr, int* resultPtr, unsig struct List evalList; // NOTICE: for some reason the struct don't reset after a usage list_reset(&evalList); - printf("\n - constructing list \n"); + if (EVALUATOR_DEBUG_LEVEL >= 2) printf("\n - constructing list \n"); // initializing the evaluation list for (int j = 0; j < partitionPtr; j++) { int startPos = partitionStartPos[j]; int stopPos = partitionStopPos[j]; - printf("=== %d\n", j); - printf("startPos %d, stopPos %d\n", startPos, stopPos); + if (EVALUATOR_DEBUG_LEVEL >= 2) { + printf("=== %d\n", j); + printf("startPos %d, stopPos %d\n", startPos, stopPos); + } int len = stopPos - startPos; // modifiy the start and stop pos to trim spaces @@ -485,8 +493,7 @@ int evaluate(struct StateContainer* state, char* inputStr, int* resultPtr, unsig buff[len] = 0; // terminate the buff // TODO: SPLIT INTO A FUNCTION "identify_token(char* str)" - - printf("buff: '%s' \n", buff); + if (EVALUATOR_DEBUG_LEVEL >= 2) printf("buff: '%s' \n", buff); char dumbValue = (char) 0; @@ -506,7 +513,7 @@ int evaluate(struct StateContainer* state, char* inputStr, int* resultPtr, unsig continue; } if (len == 1 && is_operator(buff[0])) { - printf("found op\n"); + if (EVALUATOR_DEBUG_LEVEL >= 2) printf("found op\n"); char opValue = buff[0]; list_set(&evalList, evalList.num_elements, TYPE_OPERATOR, &opValue); continue; @@ -526,13 +533,17 @@ int evaluate(struct StateContainer* state, char* inputStr, int* resultPtr, unsig // parse float success list_set(&evalList, evalList.num_elements, TYPE_FLOAT, &res); } - } + } if (st != 0) { // identify token // first try a variable then a func name // not a float, check if this is a common function name - short varKey = (short) var_store_get_key(state->varStore, buff); + if (EVALUATOR_DEBUG_LEVEL >= 2) printf("now going to identify token '%s' \n"); + if (EVALUATOR_DEBUG_LEVEL >= 2) var_store_print(state->varStore); + + int varKey = (int) var_store_get_key(state->varStore, buff); + if (varKey == -1) { // did not find the var name short funcID = identify_func_name(buff); @@ -549,15 +560,17 @@ int evaluate(struct StateContainer* state, char* inputStr, int* resultPtr, unsig list_set(&evalList, evalList.num_elements, TYPE_VAR_NAME, &varKey); } } - printf("end of a token id\n"); + if (EVALUATOR_DEBUG_LEVEL >= 2) printf("end of a token identification\n"); } // check the content of this thing - list_print(&evalList); - printf("Now going to actually evaluate \n"); + if (EVALUATOR_DEBUG_LEVEL >= 2) { + list_print(&evalList); + printf("Now going to actually evaluate \n"); + } while (evalList.num_elements > 1) { - list_print(&evalList); + if (EVALUATOR_DEBUG_LEVEL >= 2) list_print(&evalList); // int reduceVarOpStat = evaluator_reduce_var(state, &evalList); // int reduceFuncOpStat = evaluator_reduce_function_call(&evalList, 0); @@ -576,10 +589,12 @@ int evaluate(struct StateContainer* state, char* inputStr, int* resultPtr, unsig if (m == 3) stat = evaluator_reduce_not_pattern(&evalList); if (m == 4) stat = evaluator_reduce_operator_pattern(&evalList); if (m == 5) stat = evaluator_reduce_parenthesis_pattern(&evalList); - printf("Tried mod %d got stat %d \n", m, stat); if (stat > 0) { - printf("ERR Evaluator: mode %d reducing failed, dumping evalList: \n", m); - list_print(&evalList); + printf("ERR Evaluator: mode %d reducing failed \n", m); + if (EVALUATOR_DEBUG_LEVEL >= 2) { + printf("dumping evalList: \n"); + list_print(&evalList); + } } if (stat == 0) { didReduced = 1; @@ -589,8 +604,11 @@ int evaluate(struct StateContainer* state, char* inputStr, int* resultPtr, unsig if (!didReduced) { // all scans failed to find things to reduce // this is actually a failure because we can't do anything to get down to 1 element in the eval list - printf("ERR Evaluator: could not reduce more, dumping evalList: \n"); - list_print(&evalList); + printf("ERR Evaluator: could not reduce more \n"); + if (EVALUATOR_DEBUG_LEVEL >= 2) { + printf("dumping evalList: \n"); + list_print(&evalList); + } return 400; } diff --git a/src/evaluator.h b/src/evaluator.h index 7ff7693..6748bf8 100644 --- a/src/evaluator.h +++ b/src/evaluator.h @@ -2,6 +2,8 @@ #ifndef EVALUATOR_H_ #define EVALUATOR_H_ +#define EVALUATOR_DEBUG_LEVEL 1 + int evaluate(struct StateContainer* state, char* inputStr, int* resultPtr, unsigned char* typePtr); #endif diff --git a/src/funcs.c b/src/funcs.c index 9bb38fe..ac64897 100644 --- a/src/funcs.c +++ b/src/funcs.c @@ -259,13 +259,8 @@ int execute_func(short funcID, short argsLen, unsigned char* argsTypes, int* arg // call the function implementation // first cast the function ptr impl(resPtr, resTypePtr, argsTypes, argsValues); - if (*resTypePtr == TYPE_FLOAT) { - printf("Got TYPE_FLOAT with val: %f \n", get_float_from_int_rep(*resPtr)); - } else if (*resTypePtr == TYPE_INT) { - printf("Got TYPE_INT with val: %d \n", *resPtr); - } else { - printf("Got WTF NOTHING (%d) with val: %d \n", *resTypePtr, *resPtr); - } + + printf("Got %s \n", get_repr(*resTypePtr, resPtr)); return 0; } diff --git a/src/line_processing.c b/src/line_processing.c index 505709d..60baa43 100644 --- a/src/line_processing.c +++ b/src/line_processing.c @@ -1,23 +1,11 @@ #include "./utils.h" #include "./line_processing.h" +#include "./stack.h" #include "./evaluator.h" #include #include #include -int cmp_str_in_place(char* against, char* str, int from) -{ - - int i = 0; - while (against[i] != '\0') { - if (against[i] != str[from+i]) { - return 0; - } - i++; - } - return 1; -} - int is_char_accepted_in_var_name(char symbol) { int c = (int) symbol; @@ -55,15 +43,42 @@ int recognize_comment_statement(char* lineStr) return 1; } +// recognize in a string a keyword with a space before and a space afer OR the end of string after +/* +CASES: +[SPACE]NEEDLE$ +[SPACE]NEEDLE[SPACE+]$ -struct SetStatement { - short name_start; - short name_stop; - short expression_start; -}; +like +if machin then +while machin do +*/ +int recognize_termination_keyword(char* needle, char* subject, int from) +{ + int needleLen = strlen(needle); + int subLen = strlen(subject); + int remainLen = subLen - from; + if (remainLen < needleLen+1) { + return 0; + } + char terminationCandidate[remainLen]; + str_extract((char*) &terminationCandidate, subject, from, remainLen); + + // we could directly integrate the trim space process without extracting the + + char trimedTermination[remainLen]; + trim_space((char*) &trimedTermination, terminationCandidate); + + return strcmp(trimedTermination, needle) == 0; +} + +// recognize "set VAR_NAME to EVALUATION" int recognize_set_statement(char* lineStr, struct SetStatement* res) { + static char setStatementStartKeyword[] = "set"; + static char setStatementStopKeyword[] = "to"; + int i = 0; int mode = 1; int arg = 0; @@ -79,90 +94,49 @@ int recognize_set_statement(char* lineStr, struct SetStatement* res) mode 8 the expression */ while (lineStr[i] != '\0') { + //printf("mode: %d, char: '%c' \n", mode, lineStr[i]); if (mode == 1) { - if (lineStr[i] == 's') { + if (str_needle_at_pos(setStatementStartKeyword, lineStr, i)) { mode = 2; - arg = 0; + i += strlen(setStatementStartKeyword); continue; } - if (lineStr[i] != ' ') { - break; - } + if (lineStr[i] != ' ') break; } if (mode == 2) { - if (arg == 0 && lineStr[i] == 's') { - arg++; - i++; - continue; - } - if (arg == 1 && lineStr[i] == 'e') { - arg++; - i++; - continue; - } - if (arg == 2 && lineStr[i] == 't') { + if (arg > 0 && is_char_accepted_in_var_name(lineStr[i])) { mode = 3; arg = 0; + res->name_start = i; i++; continue; } - break; + if (lineStr[i] != ' ') break; + arg++; } if (mode == 3) { - if (arg > 0 && is_char_accepted_in_var_name(lineStr[i])) { + if (lineStr[i] == ' ') { mode = 4; arg = 0; - res->name_start = i; + res->name_stop = i; + i++; continue; } - if (lineStr[i] != ' ') { - break; - } - arg++; + if (!is_char_accepted_in_var_name(lineStr[i])) break; } if (mode == 4) { - if (lineStr[i] == ' ') { + if (str_needle_at_pos(setStatementStopKeyword, lineStr, i)) { mode = 5; - arg = 0; - res->name_stop = i; - continue; - } - if (!is_char_accepted_in_var_name(lineStr[i])) { - break; - } - } - if (mode == 5) { - if (arg > 0 && lineStr[i] == 't') { - mode = 6; - arg = 0; - continue; - } - if (lineStr[i] != ' ') { - break; - } - arg++; - } - if (mode == 6) { - if (arg == 0 && lineStr[i] == 't') { - arg++; - i++; - continue; - } - if (arg == 1 && lineStr[i] == 'o') { - mode = 7; - arg = 0; - i++; + i += strlen(setStatementStopKeyword); continue; } break; } - if (mode == 7) { - if (arg == 0 && lineStr[i] != ' ') { - break; - } + if (mode == 5) { + if (arg == 0 && lineStr[i] != ' ') break; if (arg > 0 && lineStr[i] != ' ') { // success - mode = 8; + mode = 6; res->expression_start = i; break; } @@ -171,23 +145,170 @@ int recognize_set_statement(char* lineStr, struct SetStatement* res) i++; } - // printf("End of set statement parsing \n"); - // printf("index %d \n", i); - // printf("mode %d \n", mode); - // printf("arg %d \n", arg); - if (mode != 8) { - // we've failed to recognize the pattern - return 0; - } - - // return a struct with anchors to the groups + if (mode != 6) return 0; return 1; } +// recognize "if EXPRESSION then \n begin (...) \n end" +// FIXME: does no allow for inline if +// FIXME: allow for multiline if statement +int recognize_if_statement(char* lineStr, struct IfStatement* res) +{ + static char ifStatementStartKeyword[] = "if"; + static char ifStatementStopKeyword[] = "then"; + + size_t len = strlen(lineStr); + if (len < strlen(ifStatementStartKeyword) + 1 + strlen(ifStatementStopKeyword)) return 0; + + //printf("try to reco if statement \n"); + int i = 0; + int mode = 1; + int arg = 0; + + /* + recognize pattern : ^\s*if {any string} then (\S)+ + mode 1 any space + mode 2 "if" + mode 3 any space + mode 4 expression + mode 5 any space + mode 6 "then" + mode 7 any space + mode 8 expression + if we reach the end of the line without getting anything we consider that we need smth after it + multiple modes: + */ + while (lineStr[i] != '\0') { + if (mode == 1) { + if (str_needle_at_pos(ifStatementStartKeyword, lineStr, i)) { + mode = 2; + i += strlen(ifStatementStartKeyword); + continue; + } + if (lineStr[i] != ' ') break; + } + if (mode == 2) { + if (arg > 0 && lineStr[i] != ' ') { + mode = 3; + arg = 0; + res->expression_start = i; + continue; + } + if (lineStr[i] != ' ') break; + arg++; + } + if (mode == 3) { + // in expression + if (arg > 0 && recognize_termination_keyword(ifStatementStopKeyword, lineStr, i)) { + // TODO: may be verify that the expression is valid? + // if an expression contains " then" it will not work that well... + // may be we need to verify that the rest of the line is empty + mode = 4; + arg = 0; + res->expression_stop = i; + continue; + } + arg++; + } + if (mode == 4) break; + + i++; + } + if (mode != 4) return 0; + return 1; +} + +// recognize "while EXPRESSION then \n begin (...) \n end" +int recognize_while_statement(char* lineStr, struct WhileStatement* res) +{ + static char whileStatementStartKeyword[] = "while"; + static char whileStatementStopKeyword[] = "do"; + + size_t len = strlen(lineStr); + if (len < strlen(whileStatementStartKeyword) + 1 + strlen(whileStatementStopKeyword)) return 0; + + //printf("try to reco if statement \n"); + int i = 0; + int mode = 1; + int arg = 0; + + while (lineStr[i] != '\0') { + //printf("i: %d, mode: %d, char: '%c' \n", i, mode, lineStr[i]); + if (mode == 1) { + if (str_needle_at_pos(whileStatementStartKeyword, lineStr, i)) { + mode = 2; + i += strlen(whileStatementStartKeyword); + continue; + } + if (lineStr[i] != ' ') break; + } + if (mode == 2) { + if (arg > 0 && lineStr[i] != ' ') { + mode = 3; + arg = 0; + res->expression_start = i; + continue; + } + if (lineStr[i] != ' ') break; + arg++; + } + if (mode == 3) { + // in expression + if ( + arg > 0 && + recognize_termination_keyword(whileStatementStopKeyword, lineStr, i) + ) { + mode = 4; + arg = 0; + res->expression_stop = i; + continue; + } + arg++; + } + if (mode == 4) break; + + i++; + } + if (mode != 4) return 0; + return 1; +} + +/* +Return 1 if it found a single word with only spaces +basically it does this check: +trim_space(lineStr) == word +*/ +int recognize_word(char* lineStr, char* word) +{ + int len = strlen(lineStr); + int expectedLen = strlen(word); + if (len < expectedLen) { + return 0; + } + // trim spaces on both sides + int start = 0; + int end = len-1; + while (lineStr[start] == ' ') { + start++; + } + while (lineStr[end] == ' ') { + end--; + } + end +=1; + + char dest[end-start]; + str_extract((char*) &dest, lineStr, start, end-start); + + return strcmp(dest, word) == 0; +} + +// Will interpret a line +// the string must be inputed without a new line at the end int process_line(struct StateContainer* state, char* str) { - printf("\n ======= PROCESSING LINE '%s' =======\n", str); + if (LINE_PROCESSING_DEBUG_LEVEL >= 1) printf("\n ======= PROCESSING LINE '%s' =======\n", str); + if (LINE_PROCESSING_DEBUG_LEVEL >= 2) printf("skipping: %d, blockStackLen: %d \n", state->skipping, state->blockStack->length); // process int len = strlen(str); // int startPos = 0; @@ -217,15 +338,97 @@ int process_line(struct StateContainer* state, char* str) //int stat = 0; if (recognize_comment_statement(str)) { - printf("Comment recognized \n"); + if (LINE_PROCESSING_DEBUG_LEVEL >= 2) printf("Comment recognized \n"); + state->linePtr++; return 1; } + if (is_full_of_space(str)) { + state->linePtr++; + return 1; + } + + if (recognize_word(str, "end")) { + byte lastBlockType; + int_stack_pop(state->blockStack, (int*) &lastBlockType); // FIXME: use a stack with size byte instead of int + if (!state->skipping && lastBlockType == BLOCK_WHILE) { + int lastLoopLine; + int_stack_pop(state->loopStack, &lastLoopLine); + + state->linePtr = lastLoopLine; + return 1; + } + if (LINE_PROCESSING_DEBUG_LEVEL >= 2) printf("recognize end state->blockStackAnchor: %d \n", state->blockStackAnchor); + if (state->skipping && int_stack_length(state->blockStack) == state->blockStackAnchor) { + // end of skipping + state->skipping = 0; + } + + state->linePtr++; + return 1; + } + if (recognize_word(str, "continue")) { + int lastLoopLine; + if (!int_stack_pop(state->loopStack, &lastLoopLine)) { + printf("Syntax error: unexpected continue, not in a loop \n"); + return 0; + } + state->linePtr = lastLoopLine; + + // find the last while if the block stack and pop blocks up to that point + byte lastBlockType; + while (lastBlockType != BLOCK_WHILE) { + if (!int_stack_pop(state->blockStack, &lastBlockType)) { + printf("Syntax error: unexpected continue \n"); + } + } + int_stack_pop(state->blockStack, &lastBlockType); + + return 1; + } + if (recognize_word(str, "break")) { + int lastLoopLine; + if (!int_stack_pop(state->loopStack, &lastLoopLine)) { + printf("Syntax error: unexpected break, not in a loop \n"); + return 0; + } + + // go into skip mode + // find the stack anchor coresponding to the last while + // we will need to search in the block stack without poping + int lastLoopAnchor = state->blockStack->length-1; + while (lastLoopAnchor >= 0) { + if (state->blockStack->data[lastLoopAnchor] == BLOCK_WHILE) break; + lastLoopAnchor--; + } + state->blockStackAnchor = lastLoopAnchor+1; + state->skipping = 1; + return 1; + } + + struct IfStatement ifStatementParsing; + byte ifRecognized = recognize_if_statement(str, &ifStatementParsing); + + struct WhileStatement whileStatementParsing; + byte whileRecognized = recognize_while_statement(str, &whileStatementParsing); + + if (state->skipping) { + // we've finish to analyse structure-aware blocks + state->linePtr++; + return 1; + } + struct SetStatement setStatementParsing; if (recognize_set_statement(str, &setStatementParsing)) { - printf("Set statement recognized \n"); + if (LINE_PROCESSING_DEBUG_LEVEL >= 2) { + printf("Set statement recognized (%d, %d, %d) \n", + setStatementParsing.name_start, + setStatementParsing.name_stop, + setStatementParsing.expression_start); + } + // handle the set statement (set a variable) int nameLen = setStatementParsing.name_stop-setStatementParsing.name_start; - char* name = (char*) malloc(sizeof(char) * nameLen); + char* name = (char*) malloc(sizeof(char) * (nameLen+1)); for (int z = 0; z < nameLen; z++) { name[z] = str[setStatementParsing.name_start + z]; } @@ -233,13 +436,84 @@ int process_line(struct StateContainer* state, char* str) int res; byte resType; - int evalStat = evaluate(state, str + setStatementParsing.expression_start, &res, &resType); + int expressLen = len - setStatementParsing.expression_start; + char express[expressLen]; + str_extract((char*) &express, str, setStatementParsing.expression_start, expressLen); + if (LINE_PROCESSING_DEBUG_LEVEL >= 2) printf("Got expression '%s' \n", express); + + int evalStat = evaluate(state, (char*) &express, &res, &resType); if (evalStat != 0) { printf("Syntax error for line \"%s\"\n", str); + return 0; } - printf("==> Set '%s' to %s \n", name, get_repr(resType, &res)); + if (LINE_PROCESSING_DEBUG_LEVEL >= 2) printf("==> Set '%s' to %s \n", name, get_repr(resType, &res)); var_store_set(state->varStore, name, resType, &res); + + state->linePtr++; + return 1; + } + + if (ifRecognized) { + if (LINE_PROCESSING_DEBUG_LEVEL >= 2) printf("If statement recognized express (%d:%d)\n", ifStatementParsing.expression_start, ifStatementParsing.expression_stop); + + // expression to evaluate + int expressLen = ifStatementParsing.expression_stop - ifStatementParsing.expression_start; + char dest[len]; + str_extract((char*) &dest, str, ifStatementParsing.expression_start, expressLen); + // printf("expression to eval in if '%s' \n", dest); + int res; + byte resType; + int evalStat = evaluate(state, (char*) &dest, &res, &resType); + if (evalStat != 0) { + printf("Syntax error for line \"%s\"\n", str); + return 0; + } + + byte doFollow = convert_to_bool(resType, res); + if (LINE_PROCESSING_DEBUG_LEVEL >= 2) printf("Got %s, doFollow: %d \n", get_repr(resType, &res), doFollow); + + if (!doFollow) { + state->blockStackAnchor = int_stack_length(state->blockStack); + state->skipping = 1; + } + + int_stack_push(state->blockStack, BLOCK_IF); + + state->linePtr++; + return 1; + } + + if (whileRecognized) { + if (LINE_PROCESSING_DEBUG_LEVEL >= 2) printf("While statement recognized express (%d:%d)\n", whileStatementParsing.expression_start, whileStatementParsing.expression_stop); + + // expression to evaluate + int expressLen = whileStatementParsing.expression_stop - whileStatementParsing.expression_start; + char dest[len]; + str_extract((char*) &dest, str, whileStatementParsing.expression_start, expressLen); + // printf("expression to eval in if '%s' \n", dest); + int res; + byte resType; + int evalStat = evaluate(state, (char*) &dest, &res, &resType); + if (evalStat != 0) { + printf("Syntax error for line \"%s\"\n", str); + return 0; + } + byte doFollow = convert_to_bool(resType, res); + if (LINE_PROCESSING_DEBUG_LEVEL >= 2) printf("Got %s, doFollow: %d \n", get_repr(resType, &res), doFollow); + + if (doFollow) { + // follow the line + int_stack_push(state->loopStack, state->linePtr); + } + if (!doFollow) { + state->blockStackAnchor = int_stack_length(state->blockStack); + state->skipping = 1; + } + + int_stack_push(state->blockStack, BLOCK_WHILE); + + state->linePtr++; return 1; } @@ -251,5 +525,70 @@ int process_line(struct StateContainer* state, char* str) printf("Syntax error for line \"%s\"\n", str); return 0; } + state->linePtr++; + return 1; +} + + +int process_script(struct StateContainer* state, char* script) +{ + int lineCount = 0; + int i = 0; + while (script[i] != '\0') { + if (script[i] == '\n') { + lineCount += 1; + } + i++; + } + if (LINE_PROCESSING_DEBUG_LEVEL >= 1) printf("\n ==================================================================== \n"); + if (LINE_PROCESSING_DEBUG_LEVEL >= 1) printf(" ======== NEW SCRIPT ======== \n"); + if (LINE_PROCESSING_DEBUG_LEVEL >= 1) printf(" ==================================================================== \n"); + if (LINE_PROCESSING_DEBUG_LEVEL >= 1) printf("line count: %d \n", lineCount); + + // FIXME: conditional line ptr reset + state->linePtr = 0; + + int lineStarts[lineCount]; + int lineEnds[lineCount]; + lineStarts[0] = 0; + + i = 0; + int lineIndex = 0; + while (script[i] != '\0') { + if (script[i] == '\n') { + lineEnds[lineIndex] = i; + //printf("end of a line: s: %d e: %d \n", lineStarts[lineIndex], lineEnds[lineIndex]); + if (script[i+1] != '\0') { + lineStarts[lineIndex+1] = i+1; + } + lineIndex++; + } + i++; + } + + // for (int j = 0; j < lineCount; j++) { + // int lineLen = (lineEnds[j] - lineStarts[j]); + // char line[lineLen+1]; + // str_extract(&line, script, lineStarts[j], lineLen); + // // printf("line %d %d \n", lineStarts[j], lineEnds[j]); + // // printf("line : '%s' \n", line); + // } + + while (state->running && state->linePtr != lineCount) { + if (state->linePtr >= lineCount) { + printf("Invalid line %d \n", state->linePtr); + break; + } + int lineLen = (lineEnds[state->linePtr] - lineStarts[state->linePtr]); + char line[lineLen+1]; + str_extract((char*) &line, script, lineStarts[state->linePtr], lineLen); + + int stat = process_line(state, line); + if (!stat) { + printf("Abnormal end of run at line %d: '%s' \n", state->linePtr, line); + return 0; + } + } + return 1; } diff --git a/src/line_processing.h b/src/line_processing.h index 10700f9..5fedb35 100644 --- a/src/line_processing.h +++ b/src/line_processing.h @@ -2,6 +2,35 @@ #ifndef LINE_PROCESSING_H_ #define LINE_PROCESSING_H_ +#define LINE_PROCESSING_DEBUG_LEVEL 1 + +#define BLOCK_IF 2 +#define BLOCK_WHILE 3 + +int recognize_word(char* lineStr, char* word); +int recognize_termination_keyword(char* needle, char* subject, int from); + +struct SetStatement { + short name_start; + short name_stop; + short expression_start; +}; +int recognize_set_statement(char* lineStr, struct SetStatement* res); + +struct IfStatement { + short expression_start; + short expression_stop; +}; +int recognize_if_statement(char* lineStr, struct IfStatement* res); + +struct WhileStatement { + short expression_start; + short expression_stop; +}; +int recognize_while_statement(char* lineStr, struct WhileStatement* res); + +int process_script(struct StateContainer* state, char* script); + int process_line(struct StateContainer* state, char* str); #endif diff --git a/src/main.c b/src/main.c index 951bbdf..98c5510 100644 --- a/src/main.c +++ b/src/main.c @@ -1,4 +1,4 @@ -#include +#include #include #include #include "./stack.h" @@ -7,78 +7,39 @@ #include "./evaluator.h" #include "./utils.h" #include "./types.h" -//int create_stack(int a, int b) +#include "./line_processing.h" -void test_list() -{ - // test dynamic list - struct List l1; +byte load_file(char* filename, char** resultPtr) { + FILE* f = fopen(filename, "rb"); - printf("0: %d \n", list_append_int(&l1, 844)); - printf("1: %d \n", list_get_type(&l1, 0)); - - int val; - printf("2: %d \n", list_get_int(&l1, 0, &val)); - - printf("final value: %d \n", val); - - printf("3: %d \n", list_append_int(&l1, 855)); - printf("4: %d \n", list_append_int(&l1, 866)); - printf("5: %d \n", list_append_int(&l1, 877)); - - printf("\n"); - - for (int i = 0; i < l1.num_elements; i++) { - printf("- elem: %d, ptr: %d, type: %d \n", i, l1.ptrArray[i] , l1.typeArray[i]); + if (!f) { + return 0; } - printf("\n"); - - list_get_int(&l1, 0, &val); - printf("6: %d \n", val); - - list_delete(&l1, 0); - - for (int i = 0; i < l1.num_elements; i++) { - printf("- elem: %d, ptr: %d, type: %d \n", i, l1.ptrArray[i] , l1.typeArray[i]); + fseek(f, 0, SEEK_END); + long length = ftell(f); + fseek (f, 0, SEEK_SET); + char* buffer = (char*) malloc(length); + if (!buffer) { + return 0; } - // 844, 855, 866, 877 - list_get_int(&l1, 2, &val); - printf("6: %d \n", val); + if (buffer) { + fread(buffer, 1, length, f); + } + fclose(f); + + *resultPtr = buffer; + return 1; } -int main () { +int main (int argc, char** argv) { + // if (argc == 1) { + // printf("One arg '%s'", argv[0]); - char *inputStr2 = "((1+1))*MAX(4, 5)"; + // return 0; + // } - int res = 0; - int resType = 0; - int evalStatus = evaluate(inputStr2, &res, &resType); - printf("evaluation status is: %d \n", evalStatus); - printf("result type is: %d \n", resType); - - if (resType == TYPE_FLOAT) { - printf("result is float: %f \n", get_float_from_int_rep(res)); - } - - if (resType == TYPE_INT) { - printf("result is int: %d \n", res); - } - - // example of the use of stack - // struct IntStack exStack; - // int_stack_push(&exStack, 233); - // int_stack_push(&exStack, 298); - // int_stack_push(&exStack, 299); - - // int res; - // int_stack_pop(&exStack, &res); - // printf("pop stack: %d \n", res); - - // int_stack_pop(&exStack, &res); - // printf("pop stack: %d \n", res); - - // int_stack_pop(&exStack, &res); - // printf("pop stack: %d \n", res); + // printf("Invalid usage use --help to document your self\n"); + printf("CLI not implemented yet ¯\\_(ツ)_/¯ \n"); + return 1; } - diff --git a/src/operate.c b/src/operate.c index 4392171..d933c36 100644 --- a/src/operate.c +++ b/src/operate.c @@ -18,16 +18,6 @@ int is_operator(char candidate) { ); } -int convert_to_int(byte type, int repr) { - if (type == TYPE_FLOAT) return (int) *((int*) &repr); - return repr; -} - -float convert_to_float(byte type, int repr) { - if (type == TYPE_INT) return (float) repr; - return *((float*) &repr); -} - int operate( byte operator, byte typeA, int aRepr, @@ -79,7 +69,7 @@ int operate( a = convert_to_float(typeA, aRepr); b = convert_to_float(typeB, bRepr); - printf("Appling operation: %f %c %f \n", a, operator, b); + if (OPERATE_DEBUG_LEVEL) printf("Appling operation: %f %c %f \n", a, operator, b); if (operator == '+') { res = a+b; @@ -96,13 +86,13 @@ int operate( } else { return 2; } - printf("Got float: %f \n", res); + if (OPERATE_DEBUG_LEVEL) printf("Got float: %f \n", res); // get int representation of float *resPtr = *(int *)(&res); return 0; } if (typeA == TYPE_INT && typeB == TYPE_INT) { - printf("Appling operation %d %c %d \n", aRepr, operator, bRepr); + if (OPERATE_DEBUG_LEVEL) printf("Appling operation %d %c %d \n", aRepr, operator, bRepr); *typeRes = TYPE_INT; int res = 0; if (operator == '+') { diff --git a/src/operate.h b/src/operate.h index e00387f..77b2570 100644 --- a/src/operate.h +++ b/src/operate.h @@ -1,7 +1,8 @@ - #ifndef OPERATOR_H_ #define OPERATOR_H_ +#define OPERATE_DEBUG_LEVEL 0 + int is_operator(char candidate); int operate( diff --git a/src/stack.c b/src/stack.c index 14f9a1f..5035757 100644 --- a/src/stack.c +++ b/src/stack.c @@ -1,35 +1,55 @@ +#include "./utils.h" #include "./stack.h" +#include +#include -int int_stack_push(struct IntStack* stack, int valueToPush) +struct IntStack* int_stack_init() { - if (stack->end_pos == STACK_LEN) { - // return a stack overflow error - return 1; - } + struct IntStack* stack = (struct IntStack*) malloc(sizeof(struct IntStack)); + stack->length = 0; + stack->allocated = 8; + stack->data = (int*) malloc(stack->allocated * sizeof(int)); - stack->data[stack->end_pos] = valueToPush; - stack->end_pos = stack->end_pos + 1; - - return 0; + return stack; } -int int_stack_pop(struct IntStack* stack, int* valuePtr) +byte int_stack_push(struct IntStack* stack, int valueToPush) { - if (stack->end_pos == 0) { - // return a stack underflow error - return 1; + if (stack->length == stack->allocated) { + stack->allocated *= 2; + stack->data = realloc(stack->data, stack->allocated * sizeof(int)); } - *valuePtr = stack->end_pos; + stack->data[stack->length] = valueToPush; + stack->length++; - stack->end_pos = stack->end_pos - 1; - *valuePtr = stack->data[stack->end_pos]; + return 1; +} - return 0; +byte int_stack_pop(struct IntStack* stack, int* valuePtr) +{ + if (stack->length == 0) { + // error, nothing to pop + return 0; + } + + *valuePtr = stack->data[stack->length-1]; + + stack->length--; + + return 1; } int int_stack_length(struct IntStack* stack) { - return stack->end_pos; + return stack->length; } +void int_stack_print(struct IntStack* stack) +{ + printf("= STACK REPORT \n"); + for (int i = 0; i < stack->length; i++) { + printf("%d; ", stack->data[i]); + } + printf("\n"); +} diff --git a/src/stack.h b/src/stack.h index 6bc3ea2..135ad0c 100644 --- a/src/stack.h +++ b/src/stack.h @@ -1,17 +1,21 @@ +#include "./utils.h" #ifndef STACK_H_ #define STACK_H_ -#define STACK_LEN 255 - struct IntStack { - int end_pos; - int data[STACK_LEN]; + int length; + int allocated; + int* data; }; -int int_stack_push(struct IntStack* stack, int valueToPush); +struct IntStack* int_stack_init(); -int int_stack_pop(struct IntStack* stack, int* valuePtr); +byte int_stack_push(struct IntStack* stack, int valueToPush); + +byte int_stack_pop(struct IntStack* stack, int* valuePtr); int int_stack_length(struct IntStack* stack); +void int_stack_print(struct IntStack* stack); + #endif diff --git a/src/state.c b/src/state.c index e1ae293..e36e16f 100644 --- a/src/state.c +++ b/src/state.c @@ -3,23 +3,20 @@ #include "./state.h" #include -// #define STATE_LEN 255 -// #define VARIABLES_LEN 255 - -// struct ProcessingState { -// int linePtr; - -// int variableStore[VARIABLES_LEN]; -// int functionStore[255]; -// //int stringStore[255] -// int data[STACK_LEN]; -// }; - struct StateContainer* state_init() { struct StateContainer* ptr = (struct StateContainer*) malloc(sizeof(struct StateContainer)); ptr->varStore = var_store_init(); + ptr->linePtr = 0; + + ptr->loopStack = int_stack_init(); + ptr->blockStack = int_stack_init(); + + ptr->blockStackAnchor = 0; + ptr->skipping = 0; + + ptr->running = 1; + return ptr; } - diff --git a/src/state.h b/src/state.h index c8c08a5..377b08a 100644 --- a/src/state.h +++ b/src/state.h @@ -5,22 +5,48 @@ we store: - the functions names and their references */ #include "./utils.h" +#include "./stack.h" #include "./var_store.h" #ifndef STATE_H_ #define STATE_H_ struct StateContainer { + struct VariableStore* varStore; + + // if true, continue to look for new lines to execute + byte running; + // the line we are currently processing int linePtr; - // usefull when we are in SKIP_BLOCK mode, we'will count the nb of block (begin/end) - int blocStack; - // FULL, SKIP_BLOC, etc - byte mode; - struct VariableStore* varStore; + + // store the head address of loops + struct IntStack* loopStack; + + // a stack that tracks the blocks we are in, 0 for a if, 1 for a while.. + struct IntStack* blockStack; + + // formely known as blockStackLengthBeforeSkip + int blockStackAnchor; + // if true tell the interpreter to skip until the block stack is at the blockStackLengthBeforeSkip + byte skipping; + // struct FunctionStore* funcStore; // struct StringStore* strStore ?? }; struct StateContainer* state_init(); -#endif \ No newline at end of file +#endif + +/* +proto proof that just a if block counter is not sufficient to really track every if blocks + +if true then -> la on dit que ifBlocks = 0 +if false then -> la on dit que ifBlocks = 1 +smth +if smth then ifBlocks = 2 +end ifBlocks = 1 +end ifBlocks = 0 +end ifBlocks = -1 -> on peut pas retrouver le block d'avant le skip +conclusion: on ne peut pas juste utiliser un compteur à la con, ça ne marche pas il faut obligatoirement utiliser une stack +*/ diff --git a/src/types.c b/src/types.c index e3681c4..7187dea 100644 --- a/src/types.c +++ b/src/types.c @@ -53,3 +53,17 @@ char* get_repr(byte type, void* valPtr) sprintf(res, "UNKNOWN(%d)", type); return res; } + +int convert_to_int(byte type, int repr) { + if (type == TYPE_FLOAT) return (int) *((int*) &repr); + return repr; +} + +byte convert_to_bool(byte type, int repr) { + return (byte) (convert_to_int(type, repr) == 1); +} + +float convert_to_float(byte type, int repr) { + if (type == TYPE_INT) return (float) repr; + return *((float*) &repr); +} diff --git a/src/types.h b/src/types.h index 51fcd8a..f6236f5 100644 --- a/src/types.h +++ b/src/types.h @@ -17,4 +17,8 @@ int get_size_of_type(byte type); char* get_repr(byte type, void* data); +byte convert_to_bool(byte type, int repr); +int convert_to_int(byte type, int repr); +float convert_to_float(byte type, int repr); + #endif diff --git a/src/utils.c b/src/utils.c index c0dece5..9a73776 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,4 +1,5 @@ #include +#include #include "./utils.h" int get_int_rep_from_float(float ft) @@ -262,8 +263,51 @@ int is_full_of_space(char* str) return 1; } -char* trim_space(char* str) +void trim_space(char* dst, char* subject) { - // TODO: implements me - return str; + // trim spaces on both sides + int start = 0; + int end = strlen(subject)-1; + while (subject[start] == ' ') start++; + while (subject[end] == ' ') end--; + end += 1; + + str_extract(dst, subject, start, end-start); +} + +// FIXME: not protected against memory overflow +void str_extract(char* dst, char* subject, int initial, int length) +{ + for (int i = 0; i < length; i++) { + dst[i] = subject[initial+i]; + } + dst[length] = '\0'; +} + +byte str_needle_at_pos(char* needle, char* subject, int atPos) { + size_t needleLen = strlen(needle); + if (strlen(subject) < needleLen) { + return 0; + } + char ahead[strlen(needle)+1]; + str_extract((char*) &ahead, subject, atPos, needleLen); + return strcmp(needle, ahead) == 0; +} + +byte str_starts_with(char* needle, char* subject) { + return str_needle_at_pos(needle, subject, 0); +} + +// FIXME: dead code +int cmp_str_in_place(char* against, char* str, int from) +{ + + int i = 0; + while (against[i] != '\0') { + if (against[i] != str[from+i]) { + return 0; + } + i++; + } + return 1; } diff --git a/src/utils.h b/src/utils.h index bc2770e..5d4320c 100644 --- a/src/utils.h +++ b/src/utils.h @@ -38,6 +38,11 @@ float m_tan(float x); int is_full_of_space(char* str); -char* trim_space(char* str); +void str_extract(char* res, char* subject, int initial, int length); + +byte str_needle_at_pos(char* needle, char* subject, int pos); +byte str_starts_with(char* needle, char* subject); + +void trim_space(char* dst, char* subject); #endif diff --git a/src/var_store.c b/src/var_store.c index ba5e290..0bed8fc 100644 --- a/src/var_store.c +++ b/src/var_store.c @@ -34,6 +34,8 @@ int var_store_hash_name(struct VariableStore* store, char* varName) hash += (get_int_rep_from_char(varName[i])*integer_pow(9, i)) % MAX_INTEGER; i++; } + // FIXME: when reallocating, we should copy all variables to their new key + // because we use modulus operator, the hash will change whenever we reallocated the table return hash % store->allocatedLength; } @@ -70,6 +72,9 @@ byte var_store_set(struct VariableStore* store, char* varName, byte type, void* while (1) { if (store->container[key].type == 0) { + // we finally found an empty space, but we didn't found a variable with the same name + // we have a new variable + store->length++; break; } if (strcmp(store->container[key].namePtr, varName) == 0) { @@ -89,10 +94,10 @@ byte var_store_set(struct VariableStore* store, char* varName, byte type, void* store->container[key].namePtr = namePtr; store->container[key].dataPtr = dataPtr; - store->length++; if (2*store->length >= store->allocatedLength) { // do smth to double the store->allocatedLength // e.g reallocate the store->container + // FIXME: copy all variables to their new keys store->allocatedLength = 2*store->allocatedLength; store->container = (struct VariableContainer*) realloc(store->container, sizeof(struct VariableContainer) * store->allocatedLength); if (store->container == NULL) { @@ -182,3 +187,19 @@ float var_store_get_float(struct VariableStore* store, char* varName) var_store_copy(store, varName, &val); return val; } + +void var_store_print(struct VariableStore* store) +{ + printf("== VarStore report (%d items) ==\n", store->length); + for (int i = 0; i < store->allocatedLength; i++) { + if (store->container[i].type != 0) { + printf( + "key: %d, '%s' = %s; ", + i, + store->container[i].namePtr, + get_repr(store->container[i].type, store->container[i].dataPtr) + ); + } + } + printf("== end of report\n"); +} \ No newline at end of file diff --git a/tests/test.c b/tests/test.c index 5b8c3be..eb0ac15 100644 --- a/tests/test.c +++ b/tests/test.c @@ -1,11 +1,15 @@ #include "./test_utils.h" #include "./test_evaluation.h" #include "./test_line_processing.h" +#include "./test_stack.h" +#include "./test_var_store.h" #include int main() { printf("== UNIT TESTS == \n"); + test_var_store(); + test_stack(); test_utils(); test_evaluation(); test_line_processing(); diff --git a/tests/test_line_processing.c b/tests/test_line_processing.c index 5de92ae..2974a9a 100644 --- a/tests/test_line_processing.c +++ b/tests/test_line_processing.c @@ -7,28 +7,112 @@ #include "../src/evaluator.h" #include "../src/state.h" #include "../src/var_store.h" +#include "../src/line_processing.h" void test_line_processing() { printf("== test line processing == \n"); - struct StateContainer* state = state_init(); + // test string manipulation + assert(!recognize_word("", "hello")); + assert(!recognize_word(" hell", "hello")); + assert(recognize_word("hello", "hello")); + assert(recognize_word("hello ", "hello")); + assert(recognize_word(" hello ", "hello")); + assert(recognize_word(" hello ", "hello")); + assert(recognize_word(" hello ", "hello")); - assert(process_line(state, "#")); - assert(process_line(state, "# some random comment")); - assert(process_line(state, "set VAR_A to 8.5")); - printf("%d \n", var_store_get_int(state->varStore, "VAR_A")); - assert(float_almost_equal(8.5, var_store_get_float(state->varStore, "VAR_A"))); + assert(recognize_termination_keyword("end", "01 end", 2)); + assert(!recognize_termination_keyword("end", "01 end == something end", 2)); + assert(!recognize_termination_keyword("end", "xend", 0)); + assert(!recognize_termination_keyword("end", "end", 0)); + assert(recognize_termination_keyword("end", " end", 0)); - assert(process_line(state, "set VAR_B to 1.5")); - assert(float_almost_equal(1.5, var_store_get_float(state->varStore, "VAR_B"))); + struct SetStatement res; + assert(recognize_set_statement("set x to 1", &res)); + assert(recognize_set_statement("set someBigThings_yes to (wow)+yes", &res)); + assert(!recognize_set_statement("set to ", &res)); + assert(!recognize_set_statement("set thing uo to yes", &res)); + assert(recognize_set_statement("set varToThing_to to yes", &res)); - assert(process_line(state, "set VAR_C to 142")); - assert(142 == var_store_get_int(state->varStore, "VAR_C")); + struct IfStatement resIf; + assert(recognize_if_statement("if something is not cool then", &resIf)); + assert(recognize_if_statement("if some_then_var then", &resIf)); - assert(process_line(state, "set VAR_D to VAR_A+VAR_B")); - assert(float_almost_equal(10, var_store_get_float(state->varStore, "VAR_D"))); + struct WhileStatement resWhile; + assert(recognize_while_statement("while i < 10 do", &resWhile)); + assert(recognize_while_statement("while !(i = 10) do", &resWhile)); + assert(!recognize_while_statement(" while 1 then", &resWhile)); - assert(process_line(state, "set VAR_D to VAR_A+VAR_B")); - assert(float_almost_equal(10, var_store_get_float(state->varStore, "VAR_D"))); + struct StateContainer* state1 = state_init(); + + assert(process_line(state1, "#")); + assert(process_line(state1, "# some random comment")); + assert(process_line(state1, "set VAR_A to 8.5")); + printf("%d \n", var_store_get_int(state1->varStore, "VAR_A")); + assert(float_almost_equal(8.5, var_store_get_float(state1->varStore, "VAR_A"))); + + assert(process_line(state1, "set VAR_B to 1.5")); + assert(float_almost_equal(1.5, var_store_get_float(state1->varStore, "VAR_B"))); + + assert(process_line(state1, "set VAR_C to 142")); + assert(142 == var_store_get_int(state1->varStore, "VAR_C")); + + assert(process_line(state1, "set VAR_D to VAR_A+VAR_B")); + assert(float_almost_equal(10, var_store_get_float(state1->varStore, "VAR_D"))); + + assert(process_line(state1, "set VAR_D to VAR_A+VAR_B")); + assert(float_almost_equal(10, var_store_get_float(state1->varStore, "VAR_D"))); + + assert(process_line(state1, "if VAR_D = 19 & 1 then")); + + + char* lines1 = "# hello world\n" + "set x to 5\n" + "set y to 1\n" + "if (x+y) = 6 then\n" + " set res to (1024)\n" + "end\n" + "\n"; + struct StateContainer* state2 = state_init(); + process_script(state2, lines1); + + assert(!state2->skipping); + assert(1024 == var_store_get_int(state2->varStore, "res")); + assert(5 == var_store_get_int(state2->varStore, "x")); + assert(1 == var_store_get_int(state2->varStore, "y")); + + char* lines2 = "set x to 5\n" + "set y to 1\n" + "set z to -1\n" + "if (x+y) = 6 then\n" + " if z = -1 then\n" + " set res to (1024)\n" + " end\n" + " set res to (res+1)\n" + "end\n"; + process_script(state2, lines2); + + assert(!state2->skipping); + assert(1025 == var_store_get_int(state2->varStore, "res")); + + char* lines3 = "set res to -42\n" + "if 0 then\n" + " set res to 42\n" + "end\n" + "\n"; + process_script(state2, lines3); + + assert(!state2->skipping); + assert(-42 == var_store_get_int(state2->varStore, "res")); + + char* lines4 = "set i to 0\n" + "while !(i = 9) do\n" + " set i to i+1\n" + "end\n"; + process_script(state2, lines4); + + assert(!state2->skipping); + printf("got i: %d \n", var_store_get_int(state2->varStore, "i")); + assert(9 == var_store_get_int(state2->varStore, "i")); } diff --git a/tests/test_stack.c b/tests/test_stack.c new file mode 100644 index 0000000..d48c1d7 --- /dev/null +++ b/tests/test_stack.c @@ -0,0 +1,39 @@ +#include +#include +#include +#include "../src/stack.h" +#include "../src/utils.h" + +void test_stack() +{ + printf("== test stack == \n"); + int res; + + struct IntStack* stack1 = int_stack_init(); + assert(int_stack_push(stack1, 255)); + assert(1 == int_stack_length(stack1)); + assert(int_stack_push(stack1, 742)); + assert(2 == int_stack_length(stack1)); + + assert(int_stack_pop(stack1, &res)); + assert(742 == res); + assert(1 == int_stack_length(stack1)); + + assert(int_stack_push(stack1, 0)); + assert(int_stack_push(stack1, -1)); + assert(int_stack_push(stack1, -2)); + assert(int_stack_push(stack1, 854)); + assert(int_stack_push(stack1, 1024)); + assert(int_stack_push(stack1, 2024)); + assert(int_stack_push(stack1, 2025)); + assert(int_stack_push(stack1, 2026)); + assert(16 == stack1->allocated); + printf("%d \n", int_stack_length(stack1)); + assert(9 == int_stack_length(stack1)); + + int_stack_print(stack1); + + assert(int_stack_pop(stack1, &res)); + assert(2026 == res); + assert(8 == int_stack_length(stack1)); +} \ No newline at end of file diff --git a/tests/test_stack.h b/tests/test_stack.h new file mode 100644 index 0000000..f559c81 --- /dev/null +++ b/tests/test_stack.h @@ -0,0 +1 @@ +void test_stack(); diff --git a/tests/test_utils.c b/tests/test_utils.c index 7813538..7653637 100644 --- a/tests/test_utils.c +++ b/tests/test_utils.c @@ -62,4 +62,16 @@ void test_utils() // char src2[] = "hello"; // char* trimed2 = trim_space(&src2); // assert(strcmp("hello", trimed2) == 0); + + char src[] = " termination "; + char dst[strlen(src)]; + trim_space((char*) &dst, src); + assert(strcmp("termination", dst) == 0); + + char src2[] = "termination"; + char dst2[strlen(src2)]; + trim_space((char*) &dst2, src2); + assert(strcmp("termination", dst2) == 0); + + } \ No newline at end of file diff --git a/tests/test_var_store.c b/tests/test_var_store.c new file mode 100644 index 0000000..8e9d6ba --- /dev/null +++ b/tests/test_var_store.c @@ -0,0 +1,18 @@ +#include +#include +#include +#include "../src/var_store.h" + +void test_var_store() +{ + printf("== test var store == \n"); + + struct VariableStore* store = var_store_init(); + + int iDest = 0; + for (int i = 0; i < 10; i++) { + var_store_set(store, "i", TYPE_INT, &i); + var_store_copy(store, "i", (void*) &iDest); + assert(i == iDest); + } +} diff --git a/tests/test_var_store.h b/tests/test_var_store.h new file mode 100644 index 0000000..cc54af6 --- /dev/null +++ b/tests/test_var_store.h @@ -0,0 +1 @@ +void test_var_store();