From c142730837d40c39a796163d2d22e7fca65b1230 Mon Sep 17 00:00:00 2001 From: = <=> Date: Wed, 21 Jul 2021 16:26:04 +0100 Subject: [PATCH] Errors can have context to make them more useful. External: Error changes. User windows will show string reps of all object types Internal: Some re-org and renames in calc. Add stringNObj for more controlled string-receiving. Note: currently aplite has a hard time with larger scripts, possibly because of recent changes. Not sure how much can be done about it, but I'm investigating. --- src/calc.c | 30 ++++++++++++-------- src/calc.h | 1 - src/env.c | 12 +++++--- src/object.c | 75 +++++++++++++++++++++++++++++++------------------- src/object.h | 18 +++++++++--- src/pebblisp.c | 37 +++++++++++++++---------- src/tests.sh | 19 ++++++++----- src/tokens.c | 29 +++++++++++++++---- src/tokens.h | 2 +- 9 files changed, 148 insertions(+), 75 deletions(-) diff --git a/src/calc.c b/src/calc.c index ffc251f..ad88870 100644 --- a/src/calc.c +++ b/src/calc.c @@ -2,6 +2,7 @@ #include #include "pebcom.h" +#include "object.h" #include "pebbleobject.h" #include "calc.h" @@ -205,9 +206,14 @@ static void add_token() static void calculate() { Object obj = parseEval(current_code_text, &env); - char temp[RESULT_LENGTH-2] = ""; + char temp[RESULT_LENGTH + 2] = ""; - stringObj(temp, &obj); + stringNObj(temp, &obj, RESULT_LENGTH); + if(obj.type == TYPE_ERROR) { + text_layer_set_font(s_result_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_14_BOLD)); + } else { + text_layer_set_font(s_result_text_layer, fonts_get_system_font(FONT_KEY_GOTHIC_18_BOLD)); + } snprintf(result_text, RESULT_LENGTH, RESULT_PREFIX "%s", temp); text_layer_set_text(s_result_text_layer, result_text); } @@ -230,8 +236,11 @@ static void click_select(ClickRecognizerRef recognizer, void *context) static void click_save(ClickRecognizerRef recognizer, void *context) { int8_t i = strlen(displayed_code); - for(unsigned j = 0; j < strlen(getToken(selected_token)); j++) { + unsigned token_len = strlen(getToken(selected_token)); + printf("%s", displayed_code); + for(unsigned j = 0; j < token_len; j++) { displayed_code[i-(1 + j)] = '\0'; + printf("%s", displayed_code); } persist_write_string(current_script_num, displayed_code); @@ -374,7 +383,7 @@ static int16_t get_cell_height_callback(struct MenuLayer *menu_layer, return CELL_HEIGHT; } -static void menu_load(Window *window) +static void script_menu_load(Window *window) { Layer *window_layer = window_get_root_layer(window); GRect bounds = layer_get_bounds(window_layer); @@ -396,7 +405,7 @@ static void menu_load(Window *window) layer_add_child(window_layer, menu_layer_get_layer(s_script_select_layer)); } -static void menu_unload(Window *window) +static void script_menu_unload(Window *window) { menu_layer_destroy(s_script_select_layer); } @@ -426,9 +435,7 @@ static void custom_load(Window *window) Object add_window(Object obj1, Object _, struct Environment *env) { printf("ADD_WINDOW\n"); - if(obj1.type == TYPE_STRING) { - strcpy(header, obj1.string); - } + stringObj(header, &obj1); if(!s_custom_window) { s_custom_window = window_create(); @@ -483,12 +490,14 @@ static void init(void) env = pebbleEnv(); s_script_menu_window = window_create(); window_set_window_handlers(s_script_menu_window, (WindowHandlers) { - .load = menu_load, - .unload = menu_unload + .load = script_menu_load, + .unload = script_menu_unload }); window_stack_push(s_script_menu_window, true); app_message_open(inbox_size, outbox_size); app_message_register_inbox_received(inbox_received_callback); + tiny_font = fonts_load_custom_font( + resource_get_handle(RESOURCE_ID_FONT_TINY_11)); } static void deinit(void) @@ -501,7 +510,6 @@ static void deinit(void) int main(void) { init(); - tiny_font = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FONT_TINY_11)); app_event_loop(); deinit(); } diff --git a/src/calc.h b/src/calc.h index de59109..5044222 100644 --- a/src/calc.h +++ b/src/calc.h @@ -5,7 +5,6 @@ #include "pebblisp.h" #define SMAX_LENGTH 256 -#define RESULT_LENGTH 20 #define CELL_HEIGHT 44 #define SCRIPT_COUNT 5 diff --git a/src/env.c b/src/env.c index 25356a1..2959295 100644 --- a/src/env.c +++ b/src/env.c @@ -42,7 +42,7 @@ Object fetchFromEnvironment(const char *name, struct Environment *env) return fetchFromEnvironment(name, env->outer); } - return errorObject(DID_NOT_FIND_SYMBOL); + return errorWithContext(DID_NOT_FIND_SYMBOL, name); } struct Environment envForLambda(const Object *params, const Object *arg_forms, @@ -200,11 +200,15 @@ struct symFunc { struct Environment defaultEnv() { + char** strings = calloc(sizeof(char*), MAX_ENV_ELM); + Object* objects = malloc(sizeof(Object) * MAX_ENV_ELM); + char size = MAX_ENV_ELM; + struct Environment e = { .outer = NULL, - .strings = calloc(sizeof(char*), MAX_ENV_ELM), - .objects = malloc(sizeof(Object) * MAX_ENV_ELM), - .size = MAX_ENV_ELM + .strings = strings, + .objects = objects, + .size = size, }; struct symFunc symFuncs[] = { diff --git a/src/object.c b/src/object.c index 153aeb0..749d703 100644 --- a/src/object.c +++ b/src/object.c @@ -1,10 +1,7 @@ #include "object.h" -#include #include #include -#define RESULT_LENGTH 30 - #ifdef DEBUG #define printd(...) printf(__VA_ARGS__) #else @@ -108,7 +105,7 @@ inline int isEmpty(const Object *obj) case TYPE_OTHER: return obj->other == NULL; case TYPE_ERROR: - return obj->err == 0; + return obj->error->code == 0; } return 0; @@ -122,8 +119,6 @@ inline int isEmpty(const Object *obj) */ void allocObject(Object **spot, const Object src) { - if(!spot) - return; *spot = malloc(sizeof(struct Object)); **spot = src; (*spot)->forward = NULL; @@ -232,8 +227,6 @@ void printErr(const Object *obj) { if(!obj || obj->type != TYPE_ERROR) return; - - printf("%s\n", errorText[(int)(obj->err)]); } /** @@ -276,49 +269,55 @@ void stringList(char *dest, const Object *obj) * @param dest The string to copy the list text into * @param obj The list Object to make a string from */ -char* stringObj(char *dest, const Object *obj) -{ +char* stringNObj(char *dest, const Object *obj, const size_t len) { if(!dest || !obj) return NULL; switch(obj->type) { case TYPE_NUMBER: - snprintf(dest, RESULT_LENGTH, "%d", obj->number); + snprintf(dest, len, "%d", obj->number); break; case TYPE_BOOL: - snprintf(dest, RESULT_LENGTH, "%s", obj->number ? "T" : "F"); + snprintf(dest, len, "%s", obj->number ? "T" : "F"); break; case TYPE_STRING: - snprintf(dest, RESULT_LENGTH, "%s", obj->string); + snprintf(dest, len, "%s", obj->string); break; case TYPE_SYMBOL: - snprintf(dest, RESULT_LENGTH, "`%s`", obj->string); + snprintf(dest, len, "`%s`", obj->string); break; case TYPE_LIST: stringList(dest, obj); break; - case TYPE_ERROR: - #ifdef STANDALONE - snprintf(dest, RESULT_LENGTH, "%s", errorText[(int)(obj->err)]); - #else - snprintf(dest, RESULT_LENGTH, "E%d", obj->err); - #endif + case TYPE_ERROR: { + int code = obj->error->code; + if (obj->error->context && obj->error->context[0] != '\0') { + snprintf(dest, len, "'%s': %s", + errorText[code], obj->error->context); + } else { + snprintf(dest, len, "%s", errorText[code]); + } break; + } case TYPE_FUNC: case TYPE_LAMBDA: - snprintf(dest, RESULT_LENGTH, "X%d", obj->number); + snprintf(dest, len, "X%d", obj->number); break; case TYPE_OTHER: - snprintf(dest, RESULT_LENGTH, "%p", obj->other->data); + snprintf(dest, len, "%p", obj->other->data); break; } if(!isValidType(*obj)) - snprintf(dest, RESULT_LENGTH, "BAD_TYPE(%d) X%d", obj->type, obj->number); + snprintf(dest, len, "BAD_TYPE(%d) X%d", obj->type, obj->number); return dest; } +char* stringObj(char *dest, const Object *obj) { + return stringNObj(dest, obj, RESULT_LENGTH); +} + /** * Prints a given Object only if the DEBUG flag is set * @param obj The Object to debug @@ -451,16 +450,19 @@ void cleanObject(Object *target) cleanObject(&target->lambda->body); free(target->lambda); break; + case TYPE_ERROR: + free(target->error->context); + free(target->error); + target->error = NULL; + break; case TYPE_OTHER: if(target->other->cleanup) { target->other->cleanup(target); } - //free(target->other); break; case TYPE_BOOL: case TYPE_NUMBER: case TYPE_FUNC: - case TYPE_ERROR: break; } @@ -650,11 +652,12 @@ inline Object cloneObject(const Object src) case TYPE_STRING: case TYPE_SYMBOL: return cloneString(src); + case TYPE_ERROR: + return errorWithContext(src.error->code, src.error->context); case TYPE_BOOL: case TYPE_NUMBER: case TYPE_FUNC: case TYPE_OTHER: - case TYPE_ERROR: ; // Fall through to plain return } @@ -715,7 +718,7 @@ inline Object constructLambda(const Object *params, const Object *body) inline int isError(const Object obj, const enum errorCode err) { - return obj.type == TYPE_ERROR && obj.err == err; + return obj.type == TYPE_ERROR && obj.error->code == err; } inline int bothAre(const enum Type type, const Object *obj1, const Object *obj2) @@ -743,7 +746,23 @@ inline Object otherObject() inline Object errorObject(enum errorCode err) { Object o = newObject(TYPE_ERROR); - o.err = err; + o.error = malloc(sizeof(struct Error)); + o.error->code = err; + o.error->context = NULL; + + return o; +} + +inline void errorAddContext(Object *o, const char* context) +{ + o->error->context = calloc(sizeof(char), RESULT_LENGTH); + strncpy(o->error->context, context, RESULT_LENGTH); +} + +inline Object errorWithContext(enum errorCode err, const char* context) +{ + Object o = errorObject(err); + errorAddContext(&o, context); return o; } diff --git a/src/object.h b/src/object.h index ff4e220..b69cd20 100644 --- a/src/object.h +++ b/src/object.h @@ -1,8 +1,11 @@ #ifndef OBJECT_H #define OBJECT_H -#define MAX_TOK_CNT 1024 // 128 +#include + +#define MAX_TOK_CNT 1024 #define MAX_ENV_ELM 100 +#define RESULT_LENGTH 128 #define FOR_POINTER_IN_LIST(_list) \ if(_list && _list->type == TYPE_LIST) \ @@ -44,8 +47,6 @@ enum errorCode { INDEX_PAST_END, }; -#define MALLOC_FLAG 64 - typedef enum Type { TYPE_NUMBER, TYPE_BOOL, @@ -63,6 +64,7 @@ struct Lambda; struct Environment; struct Slice; struct Other; +struct Error; struct Object { Type type; @@ -74,10 +76,15 @@ struct Object { Object (*func)(Object, Object, struct Environment *); struct Lambda *lambda; struct Other *other; - enum errorCode err; + struct Error *error; }; }; +struct Error { + enum errorCode code; + char* context; +}; + struct Lambda { Object params; Object body; @@ -89,6 +96,7 @@ struct Other { void *data; }; +char* stringNObj(char *dest, const Object *obj, size_t len); char* stringObj(char *dest, const Object *obj); void printList(const Object *list); void printType(const Object *obj); @@ -135,6 +143,8 @@ Object boolObject(int b); Object numberObject(int num); Object otherObject(); Object errorObject(enum errorCode err); +Object errorWithContext(enum errorCode err, const char* context); +void errorAddContext(Object *o, const char* context); Object constructLambda(const Object *params, const Object *body); // Object version of listLength() diff --git a/src/pebblisp.c b/src/pebblisp.c index 28cd455..ca027e9 100644 --- a/src/pebblisp.c +++ b/src/pebblisp.c @@ -171,14 +171,21 @@ Object listEvalFunc( Object func_result = rest[0]; if(length == 1) { + Object oneArg = errorObject(ONLY_ONE_ARGUMENT); func_result = function->func( - func_result, errorObject(ONLY_ONE_ARGUMENT), env); + func_result, oneArg, env); // Return a partial function if more parameters are required // Otherwise, return the function result cleanObject(&rest[0]); - return isError(func_result, ONLY_ONE_ARGUMENT) ? - cloneObject(*list) : - func_result; + if (isError(func_result, ONLY_ONE_ARGUMENT)) { + // These functions modify their second argument, + // so we don't clean oneArg here + cleanObject(&func_result); + return cloneObject(*list); + } else { + cleanObject(&oneArg); + return func_result; + } } else { // With two args, will apply function once // With more than two args, apply the function repeatedly @@ -251,13 +258,13 @@ Object evalList(const Object *obj, struct Environment *env) Object *first_form = obj->list; { // Try to eval built-ins - const Object builtIn = - evalBuiltIns(first_form, first_form->forward, env); + Object builtIn = evalBuiltIns(first_form, first_form->forward, env); if(!isError(builtIn, BUILT_IN_NOT_FOUND) && !isError(builtIn, NOT_A_SYMBOL)) { return builtIn; } + cleanObject(&builtIn); } // Evaluate the list based on the first element's type @@ -288,10 +295,8 @@ Object evalList(const Object *obj, struct Environment *env) Object eval(const Object *obj, struct Environment *env) { switch(obj->type) { - case TYPE_ERROR: case TYPE_FUNC: - return *obj; - + case TYPE_ERROR: case TYPE_OTHER: case TYPE_NUMBER: case TYPE_BOOL: @@ -702,9 +707,12 @@ Object parseAtom(struct Slice *s) Object parseEval(const char *input, struct Environment *env) { - struct Slice *tokens = nf_tokenize(input); - if(!tokens) { - return errorObject(MISMATCHED_PARENS); + char *err; + struct Slice *tokens = nf_tokenize(input, &err); + if(err) { + Object o = errorWithContext(MISMATCHED_PARENS, err); + free(err); + return o; } if(!tokens->text) { return symFromSlice(" ", 1); @@ -738,10 +746,11 @@ Object parseEval(const char *input, struct Environment *env) cleanObject(&obj); Object parsed = parse(tok).obj; if(parsed.type == TYPE_ERROR) { - obj = parsed; + obj = parsed; // TODO Check necessity break; } if(tok[i].text[0] == ')') { + // Skip `tok` past end of list that just closed tok = &tok[i + 1]; i = -1; } @@ -829,8 +838,8 @@ int main(int argc, const char* argv[]) { struct Environment env = defaultEnv(); readFile(SCRIPTDIR "/lib.pbl", &env); - FILE *file = fopen(argv[1], "r"); if(argc >= 2) { + FILE *file = fopen(argv[1], "r"); if(file) { // Executing a file loadArgsIntoEnv(argc, argv, &env); diff --git a/src/tests.sh b/src/tests.sh index 0ea24ce..71a99bb 100755 --- a/src/tests.sh +++ b/src/tests.sh @@ -51,9 +51,9 @@ check() { fi if $VALGRIND; then echo -ne "\n $1\r" - local output=$($VALCOM ./pl "(loadfile \"examples/lib.pbl\") $2") + local output="$($VALCOM ./pl "(loadfile \"examples/lib.pbl\") $2")" else - local output=$(./pl "(loadfile \"examples/lib.pbl\") $2") + local output="$(./pl "(loadfile \"examples/lib.pbl\") $2")" fi if [ "$output" == "$3" ]; then @@ -186,11 +186,16 @@ check "UnevenLists" "(+ (1 2) (1 2 3))" "LISTS_NOT_SAME_SIZE" check "BadNumber" "(5df)" "BAD_NUMBER" check "BadHex" "(0x0zf)" "BAD_NUMBER" check "BadBinary" "(0b01120)" "BAD_NUMBER" -check "BadParens" "(hey()" "MISMATCHED_PARENS" -check "BadParens2" "(hey)(" "MISMATCHED_PARENS" -check "BadParens3" "((hey(" "MISMATCHED_PARENS" -check "BadParens4" ")))hey" "MISMATCHED_PARENS" -check "BadParens5" "hey))(" "MISMATCHED_PARENS" +check "BadParens" "(hey()" \ + "'MISMATCHED_PARENS': (loadfile \"examples/lib.pbl\") (hey()" +check "BadParens2" "(hey)(" \ + "'MISMATCHED_PARENS': (loadfile \"examples/lib.pbl\") (hey)(" +check "BadParens3" "((hey(" \ + "'MISMATCHED_PARENS': (loadfile \"examples/lib.pbl\") ((hey(" +check "BadParens4" ")))hey" \ + "'MISMATCHED_PARENS': (loadfile \"examples/lib.pbl\") )))hey" +check "BadParens5" "hey))(" \ + "'MISMATCHED_PARENS': (loadfile \"examples/lib.pbl\") hey))(" endBlock title "Eval" diff --git a/src/tokens.c b/src/tokens.c index 29d4771..9e4863e 100644 --- a/src/tokens.c +++ b/src/tokens.c @@ -1,5 +1,6 @@ #include "tokens.h" #include +#include #ifdef STANDALONE #include @@ -8,9 +9,13 @@ #else #include #undef printd +#undef printf +#define printf(...) APP_LOG(APP_LOG_LEVEL_DEBUG, __VA_ARGS__) #define printd(...) printf(__VA_ARGS__) #endif +#define ERR_LEN 256 + /* * Grammar: * token @@ -48,12 +53,20 @@ int notWhitespace(const char c) { } // Return needs to be freed, if not null -struct Slice *nf_tokenize(const char *input) +struct Slice *nf_tokenize(const char *input, char **err) { - if(!input) + if(!input) { + *err = malloc(sizeof(char) * ERR_LEN); + strcpy(*err, "no input"); return NULL; + } - struct Slice *slices = malloc(sizeof(struct Slice) * MAX_TOK_CNT); + int token_count = MAX_TOK_CNT; + struct Slice *slices = malloc(sizeof(struct Slice) * token_count); + while(slices == NULL) { + token_count /= 2; + slices = malloc(sizeof(struct Slice) * token_count); + } int i = 0; int slice = 0; @@ -73,6 +86,9 @@ struct Slice *nf_tokenize(const char *input) } else if (input[i] == ')') { parens--; if(parens < 0) { + *err = malloc(sizeof(char) * ERR_LEN); + int start = i > ERR_LEN ? i - ERR_LEN : 0; + strncpy(*err, &input[start], ERR_LEN); free(slices); return NULL; } @@ -82,7 +98,6 @@ struct Slice *nf_tokenize(const char *input) if(isSingle(input[i])) { i++; - } else if(input[i] == '"' || input[i] == '\'') { const char quote = input[i]; while(input[++i] != quote && input[i] != '\0') { @@ -99,7 +114,10 @@ struct Slice *nf_tokenize(const char *input) slice++; } - if(parens){ + if(parens != 0){ + *err = malloc(sizeof(char) * ERR_LEN); + int start = i > ERR_LEN ? i - ERR_LEN : 0; + strncpy(*err, &input[start], ERR_LEN); free(slices); return NULL; } @@ -107,5 +125,6 @@ struct Slice *nf_tokenize(const char *input) slices[slice].text = NULL; slices[slice].length = 0; + *err = NULL; return slices; } diff --git a/src/tokens.h b/src/tokens.h index 18483f8..a4c50c7 100644 --- a/src/tokens.h +++ b/src/tokens.h @@ -6,6 +6,6 @@ int isSingle(const char c); int isDigit(const char c); int isHex(const char c); -struct Slice *nf_tokenize(const char *input); +struct Slice *nf_tokenize(const char *input, char **err); #endif