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 <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;
|
||||||
|
|
60
src/calc.h
60
src/calc.h
|
@ -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
|
||||||
|
|
13
src/object.c
13
src/object.c
|
@ -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) {
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -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);
|
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);
|
||||||
|
|
|
@ -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