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:
parent
3f9d24c617
commit
68e062e2e0
101
src/calc.c
101
src/calc.c
|
@ -1,7 +1,73 @@
|
|||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
|
||||
#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;
|
||||
|
|
60
src/calc.h
60
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
|
||||
|
|
13
src/object.c
13
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:
|
||||
if(target->other->cleanup) {
|
||||
target->other->cleanup(target);
|
||||
free(target->other);
|
||||
}
|
||||
//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) {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
Loading…
Reference in New Issue