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
This commit is contained in:
= 2020-08-09 20:03:02 +01:00
parent 3f9d24c617
commit 68e062e2e0
9 changed files with 330 additions and 64 deletions

View File

@ -1,7 +1,73 @@
#include <stdio.h> #include <stdio.h>
#include <limits.h> #include <limits.h>
#include "pebcom.h"
#include "pebbleobject.h"
#include "calc.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 **/ /** Text Editing **/
// Get the number of tokens in the current list // 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); 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) static void code_window_load(Window *window)
{ {
Layer *window_layer = window_get_root_layer(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); window_stack_push(s_code_window, true);
// If possible, load the previous code text // If possible, load the previous code text
#ifdef FORCE_TEXT
strncpy(mytext, FORCE_TEXT, SMAX_LENGTH);
updateText();
adjustFont();
#else
if(persist_exists(current_code)) { if(persist_exists(current_code)) {
persist_read_string(current_code, mytext, SMAX_LENGTH); persist_read_string(current_code, mytext, SMAX_LENGTH);
updateText(); updateText();
adjustFont(); 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) { static Object run_script(Object script_num, Object obj, struct Environment *e) {
@ -365,6 +455,17 @@ static struct Environment pebbleEnv()
// Needs two args // Needs two args
addFunc("window", &add_window, &e); addFunc("window", &add_window, &e);
addFunc("sc", &run_script, &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 win (fn (a) (window a 1)))", &e);
parseEval("(def s (fn (a) (sc a 0)))", &e); parseEval("(def s (fn (a) (sc a 0)))", &e);
return e; return e;

View File

@ -10,64 +10,6 @@
#define SCRIPT_COUNT 5 #define SCRIPT_COUNT 5
Window *s_menu_window; void debug_result(const char *d);
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;
#endif #endif

View File

@ -218,7 +218,8 @@ static const char *errorText[] = {
"NOT_A_SYMBOL", "NOT_A_SYMBOL",
"ONLY_ONE_ARGUMENT", "ONLY_ONE_ARGUMENT",
"NOT_A_LIST", "NOT_A_LIST",
"SCRIPT_NOT_FOUND" "SCRIPT_NOT_FOUND",
"NO_CLONE_SPECIFIED"
}; };
/** /**
@ -439,8 +440,10 @@ void cleanObject(Object *target)
free(target->lambda); free(target->lambda);
break; break;
case TYPE_OTHER: case TYPE_OTHER:
if(target->other->cleanup) {
target->other->cleanup(target); target->other->cleanup(target);
free(target->other); }
//free(target->other);
break; break;
case TYPE_BOOL: case TYPE_BOOL:
case TYPE_NUMBER: case TYPE_NUMBER:
@ -619,6 +622,12 @@ inline Object cloneList(const Object src)
return list; return list;
} }
inline Object cloneOther(const Object src) {
return src.other->clone?
src.other->clone(src.other) :
src;
}
inline Object cloneObject(const Object src) inline Object cloneObject(const Object src)
{ {
switch(src.type) { switch(src.type) {

View File

@ -38,7 +38,8 @@ enum errorCode {
NOT_A_SYMBOL, NOT_A_SYMBOL,
ONLY_ONE_ARGUMENT, ONLY_ONE_ARGUMENT,
NOT_A_LIST, NOT_A_LIST,
SCRIPT_NOT_FOUND SCRIPT_NOT_FOUND,
NO_CLONE_SPECIFIED
}; };
#define MALLOC_FLAG 64 #define MALLOC_FLAG 64
@ -82,6 +83,7 @@ struct Lambda {
struct Other { struct Other {
void (*cleanup)(Object*); void (*cleanup)(Object*);
Object (*clone)(struct Other*);
void *data; void *data;
}; };

126
src/pebbleobject.c Normal file
View File

@ -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);
}

32
src/pebbleobject.h Normal file
View File

@ -0,0 +1,32 @@
#include <pebble.h>
#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);

View File

@ -185,7 +185,7 @@ Object eval(const Object *obj, struct Environment *env)
return evalList(obj, env); return evalList(obj, env);
case TYPE_LAMBDA: case TYPE_LAMBDA:
return errorObject(UNEXPECTED_FORM); return eval(&obj->lambda->body, env);
} }
return errorObject(BAD_TYPE); return errorObject(BAD_TYPE);

46
src/pebcom.c Normal file
View File

@ -0,0 +1,46 @@
#include <pebble.h>
#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);
}
}

8
src/pebcom.h Normal file
View File

@ -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);