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.
This commit is contained in:
= 2021-07-21 16:26:04 +01:00
parent cadb57c0f2
commit c142730837
9 changed files with 148 additions and 75 deletions

View File

@ -2,6 +2,7 @@
#include <limits.h>
#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();
}

View File

@ -5,7 +5,6 @@
#include "pebblisp.h"
#define SMAX_LENGTH 256
#define RESULT_LENGTH 20
#define CELL_HEIGHT 44
#define SCRIPT_COUNT 5

View File

@ -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[] = {

View File

@ -1,10 +1,7 @@
#include "object.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#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;
}

View File

@ -1,8 +1,11 @@
#ifndef OBJECT_H
#define OBJECT_H
#define MAX_TOK_CNT 1024 // 128
#include <stdlib.h>
#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()

View File

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

View File

@ -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"

View File

@ -1,5 +1,6 @@
#include "tokens.h"
#include <stdlib.h>
#include <string.h>
#ifdef STANDALONE
#include <stdio.h>
@ -8,9 +9,13 @@
#else
#include <pebble.h>
#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;
}

View File

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