From 68e062e2e0273c182fa38ea0b7e87412480777e5 Mon Sep 17 00:00:00 2001 From: = <=> Date: Sun, 9 Aug 2020 20:03:02 +0100 Subject: [PATCH] Add TYPE_OTHER, subscriptions, and PebbleObjects Move most file-local variables from calc.h to calc.c (so calc.h can be included) Quick hack to get simple TYPE_LAMBDA evaluated Add pebcom files for time/vibe/eventually battery etc. functions Add TYPE_OTHER, so that pebble-specific (and potentially other) objects can be used in PebbLisp Created PebbleObject/PebbleType to work as a sort of tagged pointer, to be used as a TYPE_OTHER object Add some functions for creating/editing windows and textlayers from within PebbLisp Added subscribe(), enabling lambdas to be run at set time intervals --- src/calc.c | 101 ++++++++++++++++++++++++++++++++++++ src/calc.h | 60 +-------------------- src/object.c | 15 ++++-- src/object.h | 4 +- src/pebbleobject.c | 126 +++++++++++++++++++++++++++++++++++++++++++++ src/pebbleobject.h | 32 ++++++++++++ src/pebblisp.c | 2 +- src/pebcom.c | 46 +++++++++++++++++ src/pebcom.h | 8 +++ 9 files changed, 330 insertions(+), 64 deletions(-) create mode 100644 src/pebbleobject.c create mode 100644 src/pebbleobject.h create mode 100644 src/pebcom.c create mode 100644 src/pebcom.h diff --git a/src/calc.c b/src/calc.c index 9d17aa8..2ca4e06 100644 --- a/src/calc.c +++ b/src/calc.c @@ -1,7 +1,73 @@ #include #include + +#include "pebcom.h" +#include "pebbleobject.h" #include "calc.h" +Window *s_menu_window; +Window *s_code_window; + +// Custom layers +Window *s_custom_window; +TextLayer *s_heading_text_layer; +char header[30] = ""; + +// Code layers +TextLayer *s_input_text_layer; +TextLayer *s_result_text_layer; + +// Menu layers +MenuLayer *s_menu_layer; +TextLayer *s_list_message_layer; + +int current_code; + +// Currently selected button, starts on '(' +static int8_t selected_token = 1; + +// PebbLisp environment +static struct Environment env; + +// Live code text +char mytext[SMAX_LENGTH] = ""; + +// The actual displayed code text +char temptext[SMAX_LENGTH] = ""; + +// The result of execution +char resulttext[RESULT_LENGTH] = ""; + +const char *tokens[] = { + " ", "(", ")", + "+ ", "- ", "* ", "/ ", + "1","2","3", + "4","5","6", + "7","8","9", "0", "x", + "a", "b", "c", "d", "e", + "= ", "< ", "> ", + "\"", + "s", + "cat", "map", "fil", + "fn", "def", "if", "\n", + "...", + "END" +}; + +const char *func_tokens[] = { + "spent ", "window ", + "max ", "min ", + "sq ", "cube ", "exp ", + "sc ", "cw ", "pw ", "rw ", "atl ", "utl ", + "sec ", "mnt ", "hr ", "hrt ", "vibe ", "sub " +}; + +const uint32_t inbox_size = 1024; +const uint32_t outbox_size = 1024; + +bool using_func_tokens = false; + + /** Text Editing **/ // Get the number of tokens in the current list @@ -173,6 +239,18 @@ static void code_click_subscribe(void *context) window_single_click_subscribe(BUTTON_ID_BACK, click_backspace); } +#define FORCE_TEXT \ + "(def time (fn (a) (utl tt "\ + "(cat (hrt 0 0) \":\" (mnt 0 0))" \ + ")))" \ + "(def ww (cw 0 0))" \ + "(def tt (atl ww \"OI\"))" \ + "(pw ww 0)" \ + "(sub time 0)" + +// Remove to re-enable +#undef FORCE_TEXT + static void code_window_load(Window *window) { Layer *window_layer = window_get_root_layer(window); @@ -199,11 +277,23 @@ static void code_window_load(Window *window) window_stack_push(s_code_window, true); // If possible, load the previous code text + #ifdef FORCE_TEXT + strncpy(mytext, FORCE_TEXT, SMAX_LENGTH); + updateText(); + adjustFont(); + #else if(persist_exists(current_code)) { persist_read_string(current_code, mytext, SMAX_LENGTH); updateText(); adjustFont(); } + #endif +} + +void debug_result(const char* d) { + snprintf(resulttext, RESULT_LENGTH, "%s", d); + text_layer_set_text(s_result_text_layer, resulttext); + psleep(300); } static Object run_script(Object script_num, Object obj, struct Environment *e) { @@ -365,6 +455,17 @@ static struct Environment pebbleEnv() // Needs two args addFunc("window", &add_window, &e); addFunc("sc", &run_script, &e); + addFunc("cw", &createWindow, &e); + addFunc("pw", &pushWindow, &e); + addFunc("rw", &deleteWindow, &e); + addFunc("atl", &addTextLayer, &e); + addFunc("utl", &updateTextLayer, &e); + addFunc("sec", &getSeconds, &e); + addFunc("mnt", &getMinutes, &e); + addFunc("hr", &getHours, &e); + addFunc("hrt", &getTwelveHours, &e); + addFunc("vibe", &doVibe, &e); + addFunc("sub", &subscribe, &e); parseEval("(def win (fn (a) (window a 1)))", &e); parseEval("(def s (fn (a) (sc a 0)))", &e); return e; diff --git a/src/calc.h b/src/calc.h index 6dbef7a..de59109 100644 --- a/src/calc.h +++ b/src/calc.h @@ -10,64 +10,6 @@ #define SCRIPT_COUNT 5 -Window *s_menu_window; -Window *s_code_window; - -// Custom layers -Window *s_custom_window; -TextLayer *s_heading_text_layer; -char header[30] = ""; - -// Code layers -TextLayer *s_input_text_layer; -TextLayer *s_result_text_layer; - -// Menu layers -MenuLayer *s_menu_layer; -TextLayer *s_list_message_layer; - -int current_code; - -// Currently selected button, starts on '(' -static int8_t selected_token = 1; - -// PebbLisp environment -static struct Environment env; - -// Live code text -char mytext[SMAX_LENGTH] = ""; - -// The actual displayed code text -char temptext[SMAX_LENGTH] = ""; - -// The result of execution -char resulttext[RESULT_LENGTH] = ""; - -const char *tokens[] = { - " ", "(", ")", - "+ ", "- ", "* ", "/ ", - "1","2","3", - "4","5","6", - "7","8","9", "0", "x", - "a", "b", "c", "d", "e", - "= ", "< ", "> ", - "\"", - "s", - "cat", "map", "fil", - "fn", "def", "if", "\n", - "...", - "END" -}; - -const char *func_tokens[] = { - "spent ", "window ", - "max ", "min ", - "sq ", "cube ", "exp " -}; - -const uint32_t inbox_size = 1024; -const uint32_t outbox_size = 1024; - -bool using_func_tokens = false; +void debug_result(const char *d); #endif diff --git a/src/object.c b/src/object.c index 5082fa3..00b61f5 100644 --- a/src/object.c +++ b/src/object.c @@ -218,7 +218,8 @@ static const char *errorText[] = { "NOT_A_SYMBOL", "ONLY_ONE_ARGUMENT", "NOT_A_LIST", - "SCRIPT_NOT_FOUND" + "SCRIPT_NOT_FOUND", + "NO_CLONE_SPECIFIED" }; /** @@ -439,8 +440,10 @@ void cleanObject(Object *target) free(target->lambda); break; case TYPE_OTHER: - target->other->cleanup(target); - free(target->other); + if(target->other->cleanup) { + target->other->cleanup(target); + } + //free(target->other); break; case TYPE_BOOL: case TYPE_NUMBER: @@ -619,6 +622,12 @@ inline Object cloneList(const Object src) return list; } +inline Object cloneOther(const Object src) { + return src.other->clone? + src.other->clone(src.other) : + src; +} + inline Object cloneObject(const Object src) { switch(src.type) { diff --git a/src/object.h b/src/object.h index 79a0855..f24b63b 100644 --- a/src/object.h +++ b/src/object.h @@ -38,7 +38,8 @@ enum errorCode { NOT_A_SYMBOL, ONLY_ONE_ARGUMENT, NOT_A_LIST, - SCRIPT_NOT_FOUND + SCRIPT_NOT_FOUND, + NO_CLONE_SPECIFIED }; #define MALLOC_FLAG 64 @@ -82,6 +83,7 @@ struct Lambda { struct Other { void (*cleanup)(Object*); + Object (*clone)(struct Other*); void *data; }; diff --git a/src/pebbleobject.c b/src/pebbleobject.c new file mode 100644 index 0000000..2439160 --- /dev/null +++ b/src/pebbleobject.c @@ -0,0 +1,126 @@ +#include "pebbleobject.h" +#include "calc.h" +#include "object.h" + +Object pebbleOther( + enum PebbleType type, + void* data, + void (*cleanup)(Object*), + Object (*clone)(struct Other*)) +{ + struct Object o = otherObject(); + struct PebbleObject *po = malloc(sizeof(struct PebbleObject)); + o.other->data = po; + o.other->cleanup = cleanup; + o.other->clone = clone; + po->type = type; + po->ptr = data; + return o; +} + +enum PebbleType getPebbleType(const Object o1) +{ + if(o1.type == TYPE_OTHER) { + struct PebbleObject *po = o1.other->data; + enum PebbleType t = po->type; + return t; + } + return P_ERROR; +} + +struct PebbleObject* accessPebbleObject(Object obj) { + if(getPebbleType(obj) != P_ERROR) { + return obj.other->data; + } + return NULL; +} + +void custom_window_load(Window *window) { +} + +void custom_window_unload(Window *window) { + window_destroy(window); +} + +Object createWindow(Object o1, Object o2, struct Environment *env) { + Window *window = window_create(); + WindowHandlers wh = { + .load = custom_window_load, + .unload = custom_window_unload }; + window_set_window_handlers(window, wh); + return pebbleOther(WINDOW, window, NULL, NULL); +} + +Object deleteWindow(Object window, Object o2, struct Environment *env) { + /* Maintain a list of layers to delete? */ + if(getPebbleType(window) == WINDOW) { + window_stack_remove(accessPebbleObject(window)->window, true); + return boolObject(1); + } + return boolObject(0); +} + +Object pushWindow(Object window, Object o2, struct Environment *env) { + if(getPebbleType(window) == WINDOW) { + window_stack_push(accessPebbleObject(window)->window, true); + return boolObject(1); + } + return boolObject(0); +} + +Object updateTextLayer(Object textLayer, Object text, struct Environment *env) { + if(getPebbleType(textLayer) == TEXT_LAYER) { + struct PebbleObject *po = accessPebbleObject(textLayer); + stringObj(po->textLayer->text, &text); + text_layer_set_text(po->textLayer->layer, po->textLayer->text); + return boolObject(1); + } + return boolObject(0); +} + +Object addTextLayer(Object window, Object text, struct Environment *env) { + if(getPebbleType(window) != WINDOW) { + return errorObject(0); + } + Layer *window_layer = window_get_root_layer(accessPebbleObject(window)->window); + GRect bounds = layer_get_bounds(window_layer); + + struct PLTextLayer *textLayer = malloc(sizeof(struct PLTextLayer)); + textLayer->text = calloc(sizeof(char), 100); + + textLayer->layer = text_layer_create(bounds); + stringObj(textLayer->text, &text); + text_layer_set_text(textLayer->layer, textLayer->text); + text_layer_set_font(textLayer->layer, + fonts_get_system_font(FONT_KEY_BITHAM_42_BOLD)); + layer_add_child(window_layer, text_layer_get_layer(textLayer->layer)); + + return pebbleOther(TEXT_LAYER, textLayer, NULL, NULL); +} + + +Object subscription; +struct Environment *subscriptionEnv; + +static void subscriptionHandler(struct tm *tick_time, TimeUnits changed) { + eval(&subscription, subscriptionEnv); +} + +Object subscribe(Object function, Object time, struct Environment *env) { + if(function.type == TYPE_LAMBDA) { + subscription = cloneObject(function); + subscriptionEnv = env; + int unit = time.type != TYPE_NUMBER ? MINUTE_UNIT : + time.number == 1 ? SECOND_UNIT : + time.number == 2 ? MINUTE_UNIT : + time.number == 3 ? HOUR_UNIT : + time.number == 4 ? DAY_UNIT : + time.number == 5 ? MONTH_UNIT : + time.number == 6 ? YEAR_UNIT : + MINUTE_UNIT; + tick_timer_service_subscribe(unit, subscriptionHandler); + return boolObject(1); + } + return boolObject(0); +} + diff --git a/src/pebbleobject.h b/src/pebbleobject.h new file mode 100644 index 0000000..1902d59 --- /dev/null +++ b/src/pebbleobject.h @@ -0,0 +1,32 @@ +#include +#include "pebblisp.h" + +enum PebbleType { + WINDOW, + TEXT_LAYER, + BOUNDS, + P_ERROR +}; + +struct PLTextLayer { + TextLayer *layer; + char *text; +}; + +typedef struct PebbleObject PebbleObject; +struct PebbleObject { + enum PebbleType type; + union { + Window *window; + struct PLTextLayer *textLayer; + GRect bounds; + void *ptr; + }; +}; + +Object createWindow(Object o1, Object o2, struct Environment *env); +Object deleteWindow(Object window, Object o2, struct Environment *env); +Object pushWindow(Object o1, Object o2, struct Environment *env); +Object addTextLayer(Object window, Object text, struct Environment *env); +Object updateTextLayer(Object textLayer, Object text, struct Environment *env); +Object subscribe(Object function, Object time, struct Environment *env); diff --git a/src/pebblisp.c b/src/pebblisp.c index aeea5de..124d099 100644 --- a/src/pebblisp.c +++ b/src/pebblisp.c @@ -185,7 +185,7 @@ Object eval(const Object *obj, struct Environment *env) return evalList(obj, env); case TYPE_LAMBDA: - return errorObject(UNEXPECTED_FORM); + return eval(&obj->lambda->body, env); } return errorObject(BAD_TYPE); diff --git a/src/pebcom.c b/src/pebcom.c new file mode 100644 index 0000000..1678334 --- /dev/null +++ b/src/pebcom.c @@ -0,0 +1,46 @@ +#include +#include "pebcom.h" + +struct tm* getTime() { + time_t t = time(NULL); + return localtime(&t); +} + +Object getSeconds(Object o1, Object o2, struct Environment *env) { + return numberObject(getTime()->tm_sec); +} + +Object getMinutes(Object o1, Object o2, struct Environment *env) { + return numberObject(getTime()->tm_min); +} + +Object getHours(Object o1, Object o2, struct Environment *env) { + return numberObject(getTime()->tm_hour); +} + +Object getTwelveHours(Object o1, Object o2, struct Environment *env) { + int hour = (getTime()->tm_hour % 12) ?: 12; + return numberObject(hour); +} + +Object doVibe(Object patternList, Object o2, struct Environment *env) { + int length = listLength(&patternList); + uint32_t pattern[length]; + if(length > 0) { + int i = 0; + Object *pl = &patternList; + FOR_POINTER_IN_LIST(pl) { + if(POINTER->type == TYPE_NUMBER) { + pattern[i] = POINTER->number; + } + i++; + } + vibes_enqueue_custom_pattern((VibePattern) { + .durations = pattern, + .num_segments = length + }); + return boolObject(1); + } else { + return errorObject(NOT_A_LIST); + } +} diff --git a/src/pebcom.h b/src/pebcom.h new file mode 100644 index 0000000..ce43692 --- /dev/null +++ b/src/pebcom.h @@ -0,0 +1,8 @@ +#include "pebblisp.h" + +Object getSeconds(Object o1, Object o2, struct Environment *env); +Object getMinutes(Object o1, Object o2, struct Environment *env); +Object getHours(Object o1, Object o2, struct Environment *env); +Object getTwelveHours(Object o1, Object o2, struct Environment *env); + +Object doVibe(Object patternList, Object o2, struct Environment *env);