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:
parent
cadb57c0f2
commit
c142730837
30
src/calc.c
30
src/calc.c
|
@ -2,6 +2,7 @@
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
|
||||||
#include "pebcom.h"
|
#include "pebcom.h"
|
||||||
|
#include "object.h"
|
||||||
#include "pebbleobject.h"
|
#include "pebbleobject.h"
|
||||||
#include "calc.h"
|
#include "calc.h"
|
||||||
|
|
||||||
|
@ -205,9 +206,14 @@ static void add_token()
|
||||||
static void calculate()
|
static void calculate()
|
||||||
{
|
{
|
||||||
Object obj = parseEval(current_code_text, &env);
|
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);
|
snprintf(result_text, RESULT_LENGTH, RESULT_PREFIX "%s", temp);
|
||||||
text_layer_set_text(s_result_text_layer, result_text);
|
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)
|
static void click_save(ClickRecognizerRef recognizer, void *context)
|
||||||
{
|
{
|
||||||
int8_t i = strlen(displayed_code);
|
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';
|
displayed_code[i-(1 + j)] = '\0';
|
||||||
|
printf("%s", displayed_code);
|
||||||
}
|
}
|
||||||
|
|
||||||
persist_write_string(current_script_num, 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;
|
return CELL_HEIGHT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void menu_load(Window *window)
|
static void script_menu_load(Window *window)
|
||||||
{
|
{
|
||||||
Layer *window_layer = window_get_root_layer(window);
|
Layer *window_layer = window_get_root_layer(window);
|
||||||
GRect bounds = layer_get_bounds(window_layer);
|
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));
|
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);
|
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)
|
Object add_window(Object obj1, Object _, struct Environment *env)
|
||||||
{
|
{
|
||||||
printf("ADD_WINDOW\n");
|
printf("ADD_WINDOW\n");
|
||||||
if(obj1.type == TYPE_STRING) {
|
stringObj(header, &obj1);
|
||||||
strcpy(header, obj1.string);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!s_custom_window) {
|
if(!s_custom_window) {
|
||||||
s_custom_window = window_create();
|
s_custom_window = window_create();
|
||||||
|
@ -483,12 +490,14 @@ static void init(void)
|
||||||
env = pebbleEnv();
|
env = pebbleEnv();
|
||||||
s_script_menu_window = window_create();
|
s_script_menu_window = window_create();
|
||||||
window_set_window_handlers(s_script_menu_window, (WindowHandlers) {
|
window_set_window_handlers(s_script_menu_window, (WindowHandlers) {
|
||||||
.load = menu_load,
|
.load = script_menu_load,
|
||||||
.unload = menu_unload
|
.unload = script_menu_unload
|
||||||
});
|
});
|
||||||
window_stack_push(s_script_menu_window, true);
|
window_stack_push(s_script_menu_window, true);
|
||||||
app_message_open(inbox_size, outbox_size);
|
app_message_open(inbox_size, outbox_size);
|
||||||
app_message_register_inbox_received(inbox_received_callback);
|
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)
|
static void deinit(void)
|
||||||
|
@ -501,7 +510,6 @@ static void deinit(void)
|
||||||
int main(void)
|
int main(void)
|
||||||
{
|
{
|
||||||
init();
|
init();
|
||||||
tiny_font = fonts_load_custom_font(resource_get_handle(RESOURCE_ID_FONT_TINY_11));
|
|
||||||
app_event_loop();
|
app_event_loop();
|
||||||
deinit();
|
deinit();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@
|
||||||
#include "pebblisp.h"
|
#include "pebblisp.h"
|
||||||
|
|
||||||
#define SMAX_LENGTH 256
|
#define SMAX_LENGTH 256
|
||||||
#define RESULT_LENGTH 20
|
|
||||||
#define CELL_HEIGHT 44
|
#define CELL_HEIGHT 44
|
||||||
|
|
||||||
#define SCRIPT_COUNT 5
|
#define SCRIPT_COUNT 5
|
||||||
|
|
12
src/env.c
12
src/env.c
|
@ -42,7 +42,7 @@ Object fetchFromEnvironment(const char *name, struct Environment *env)
|
||||||
return fetchFromEnvironment(name, env->outer);
|
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,
|
struct Environment envForLambda(const Object *params, const Object *arg_forms,
|
||||||
|
@ -200,11 +200,15 @@ struct symFunc {
|
||||||
|
|
||||||
struct Environment defaultEnv()
|
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 = {
|
struct Environment e = {
|
||||||
.outer = NULL,
|
.outer = NULL,
|
||||||
.strings = calloc(sizeof(char*), MAX_ENV_ELM),
|
.strings = strings,
|
||||||
.objects = malloc(sizeof(Object) * MAX_ENV_ELM),
|
.objects = objects,
|
||||||
.size = MAX_ENV_ELM
|
.size = size,
|
||||||
};
|
};
|
||||||
|
|
||||||
struct symFunc symFuncs[] = {
|
struct symFunc symFuncs[] = {
|
||||||
|
|
75
src/object.c
75
src/object.c
|
@ -1,10 +1,7 @@
|
||||||
#include "object.h"
|
#include "object.h"
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#define RESULT_LENGTH 30
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
#define printd(...) printf(__VA_ARGS__)
|
#define printd(...) printf(__VA_ARGS__)
|
||||||
#else
|
#else
|
||||||
|
@ -108,7 +105,7 @@ inline int isEmpty(const Object *obj)
|
||||||
case TYPE_OTHER:
|
case TYPE_OTHER:
|
||||||
return obj->other == NULL;
|
return obj->other == NULL;
|
||||||
case TYPE_ERROR:
|
case TYPE_ERROR:
|
||||||
return obj->err == 0;
|
return obj->error->code == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -122,8 +119,6 @@ inline int isEmpty(const Object *obj)
|
||||||
*/
|
*/
|
||||||
void allocObject(Object **spot, const Object src)
|
void allocObject(Object **spot, const Object src)
|
||||||
{
|
{
|
||||||
if(!spot)
|
|
||||||
return;
|
|
||||||
*spot = malloc(sizeof(struct Object));
|
*spot = malloc(sizeof(struct Object));
|
||||||
**spot = src;
|
**spot = src;
|
||||||
(*spot)->forward = NULL;
|
(*spot)->forward = NULL;
|
||||||
|
@ -232,8 +227,6 @@ void printErr(const Object *obj)
|
||||||
{
|
{
|
||||||
if(!obj || obj->type != TYPE_ERROR)
|
if(!obj || obj->type != TYPE_ERROR)
|
||||||
return;
|
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 dest The string to copy the list text into
|
||||||
* @param obj The list Object to make a string from
|
* @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)
|
if(!dest || !obj)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
switch(obj->type) {
|
switch(obj->type) {
|
||||||
case TYPE_NUMBER:
|
case TYPE_NUMBER:
|
||||||
snprintf(dest, RESULT_LENGTH, "%d", obj->number);
|
snprintf(dest, len, "%d", obj->number);
|
||||||
break;
|
break;
|
||||||
case TYPE_BOOL:
|
case TYPE_BOOL:
|
||||||
snprintf(dest, RESULT_LENGTH, "%s", obj->number ? "T" : "F");
|
snprintf(dest, len, "%s", obj->number ? "T" : "F");
|
||||||
break;
|
break;
|
||||||
case TYPE_STRING:
|
case TYPE_STRING:
|
||||||
snprintf(dest, RESULT_LENGTH, "%s", obj->string);
|
snprintf(dest, len, "%s", obj->string);
|
||||||
break;
|
break;
|
||||||
case TYPE_SYMBOL:
|
case TYPE_SYMBOL:
|
||||||
snprintf(dest, RESULT_LENGTH, "`%s`", obj->string);
|
snprintf(dest, len, "`%s`", obj->string);
|
||||||
break;
|
break;
|
||||||
case TYPE_LIST:
|
case TYPE_LIST:
|
||||||
stringList(dest, obj);
|
stringList(dest, obj);
|
||||||
break;
|
break;
|
||||||
case TYPE_ERROR:
|
case TYPE_ERROR: {
|
||||||
#ifdef STANDALONE
|
int code = obj->error->code;
|
||||||
snprintf(dest, RESULT_LENGTH, "%s", errorText[(int)(obj->err)]);
|
if (obj->error->context && obj->error->context[0] != '\0') {
|
||||||
#else
|
snprintf(dest, len, "'%s': %s",
|
||||||
snprintf(dest, RESULT_LENGTH, "E%d", obj->err);
|
errorText[code], obj->error->context);
|
||||||
#endif
|
} else {
|
||||||
|
snprintf(dest, len, "%s", errorText[code]);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case TYPE_FUNC:
|
case TYPE_FUNC:
|
||||||
case TYPE_LAMBDA:
|
case TYPE_LAMBDA:
|
||||||
snprintf(dest, RESULT_LENGTH, "X%d", obj->number);
|
snprintf(dest, len, "X%d", obj->number);
|
||||||
break;
|
break;
|
||||||
case TYPE_OTHER:
|
case TYPE_OTHER:
|
||||||
snprintf(dest, RESULT_LENGTH, "%p", obj->other->data);
|
snprintf(dest, len, "%p", obj->other->data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(!isValidType(*obj))
|
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;
|
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
|
* Prints a given Object only if the DEBUG flag is set
|
||||||
* @param obj The Object to debug
|
* @param obj The Object to debug
|
||||||
|
@ -451,16 +450,19 @@ void cleanObject(Object *target)
|
||||||
cleanObject(&target->lambda->body);
|
cleanObject(&target->lambda->body);
|
||||||
free(target->lambda);
|
free(target->lambda);
|
||||||
break;
|
break;
|
||||||
|
case TYPE_ERROR:
|
||||||
|
free(target->error->context);
|
||||||
|
free(target->error);
|
||||||
|
target->error = NULL;
|
||||||
|
break;
|
||||||
case TYPE_OTHER:
|
case TYPE_OTHER:
|
||||||
if(target->other->cleanup) {
|
if(target->other->cleanup) {
|
||||||
target->other->cleanup(target);
|
target->other->cleanup(target);
|
||||||
}
|
}
|
||||||
//free(target->other);
|
|
||||||
break;
|
break;
|
||||||
case TYPE_BOOL:
|
case TYPE_BOOL:
|
||||||
case TYPE_NUMBER:
|
case TYPE_NUMBER:
|
||||||
case TYPE_FUNC:
|
case TYPE_FUNC:
|
||||||
case TYPE_ERROR:
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -650,11 +652,12 @@ inline Object cloneObject(const Object src)
|
||||||
case TYPE_STRING:
|
case TYPE_STRING:
|
||||||
case TYPE_SYMBOL:
|
case TYPE_SYMBOL:
|
||||||
return cloneString(src);
|
return cloneString(src);
|
||||||
|
case TYPE_ERROR:
|
||||||
|
return errorWithContext(src.error->code, src.error->context);
|
||||||
case TYPE_BOOL:
|
case TYPE_BOOL:
|
||||||
case TYPE_NUMBER:
|
case TYPE_NUMBER:
|
||||||
case TYPE_FUNC:
|
case TYPE_FUNC:
|
||||||
case TYPE_OTHER:
|
case TYPE_OTHER:
|
||||||
case TYPE_ERROR:
|
|
||||||
; // Fall through to plain return
|
; // 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)
|
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)
|
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)
|
inline Object errorObject(enum errorCode err)
|
||||||
{
|
{
|
||||||
Object o = newObject(TYPE_ERROR);
|
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;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
18
src/object.h
18
src/object.h
|
@ -1,8 +1,11 @@
|
||||||
#ifndef OBJECT_H
|
#ifndef OBJECT_H
|
||||||
#define 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 MAX_ENV_ELM 100
|
||||||
|
#define RESULT_LENGTH 128
|
||||||
|
|
||||||
#define FOR_POINTER_IN_LIST(_list) \
|
#define FOR_POINTER_IN_LIST(_list) \
|
||||||
if(_list && _list->type == TYPE_LIST) \
|
if(_list && _list->type == TYPE_LIST) \
|
||||||
|
@ -44,8 +47,6 @@ enum errorCode {
|
||||||
INDEX_PAST_END,
|
INDEX_PAST_END,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define MALLOC_FLAG 64
|
|
||||||
|
|
||||||
typedef enum Type {
|
typedef enum Type {
|
||||||
TYPE_NUMBER,
|
TYPE_NUMBER,
|
||||||
TYPE_BOOL,
|
TYPE_BOOL,
|
||||||
|
@ -63,6 +64,7 @@ struct Lambda;
|
||||||
struct Environment;
|
struct Environment;
|
||||||
struct Slice;
|
struct Slice;
|
||||||
struct Other;
|
struct Other;
|
||||||
|
struct Error;
|
||||||
|
|
||||||
struct Object {
|
struct Object {
|
||||||
Type type;
|
Type type;
|
||||||
|
@ -74,10 +76,15 @@ struct Object {
|
||||||
Object (*func)(Object, Object, struct Environment *);
|
Object (*func)(Object, Object, struct Environment *);
|
||||||
struct Lambda *lambda;
|
struct Lambda *lambda;
|
||||||
struct Other *other;
|
struct Other *other;
|
||||||
enum errorCode err;
|
struct Error *error;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Error {
|
||||||
|
enum errorCode code;
|
||||||
|
char* context;
|
||||||
|
};
|
||||||
|
|
||||||
struct Lambda {
|
struct Lambda {
|
||||||
Object params;
|
Object params;
|
||||||
Object body;
|
Object body;
|
||||||
|
@ -89,6 +96,7 @@ struct Other {
|
||||||
void *data;
|
void *data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
char* stringNObj(char *dest, const Object *obj, size_t len);
|
||||||
char* stringObj(char *dest, const Object *obj);
|
char* stringObj(char *dest, const Object *obj);
|
||||||
void printList(const Object *list);
|
void printList(const Object *list);
|
||||||
void printType(const Object *obj);
|
void printType(const Object *obj);
|
||||||
|
@ -135,6 +143,8 @@ Object boolObject(int b);
|
||||||
Object numberObject(int num);
|
Object numberObject(int num);
|
||||||
Object otherObject();
|
Object otherObject();
|
||||||
Object errorObject(enum errorCode err);
|
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 constructLambda(const Object *params, const Object *body);
|
||||||
|
|
||||||
// Object version of listLength()
|
// Object version of listLength()
|
||||||
|
|
|
@ -171,14 +171,21 @@ Object listEvalFunc(
|
||||||
|
|
||||||
Object func_result = rest[0];
|
Object func_result = rest[0];
|
||||||
if(length == 1) {
|
if(length == 1) {
|
||||||
|
Object oneArg = errorObject(ONLY_ONE_ARGUMENT);
|
||||||
func_result = function->func(
|
func_result = function->func(
|
||||||
func_result, errorObject(ONLY_ONE_ARGUMENT), env);
|
func_result, oneArg, env);
|
||||||
// Return a partial function if more parameters are required
|
// Return a partial function if more parameters are required
|
||||||
// Otherwise, return the function result
|
// Otherwise, return the function result
|
||||||
cleanObject(&rest[0]);
|
cleanObject(&rest[0]);
|
||||||
return isError(func_result, ONLY_ONE_ARGUMENT) ?
|
if (isError(func_result, ONLY_ONE_ARGUMENT)) {
|
||||||
cloneObject(*list) :
|
// These functions modify their second argument,
|
||||||
func_result;
|
// so we don't clean oneArg here
|
||||||
|
cleanObject(&func_result);
|
||||||
|
return cloneObject(*list);
|
||||||
|
} else {
|
||||||
|
cleanObject(&oneArg);
|
||||||
|
return func_result;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// With two args, will apply function once
|
// With two args, will apply function once
|
||||||
// With more than two args, apply the function repeatedly
|
// 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;
|
Object *first_form = obj->list;
|
||||||
|
|
||||||
{ // Try to eval built-ins
|
{ // Try to eval built-ins
|
||||||
const Object builtIn =
|
Object builtIn = evalBuiltIns(first_form, first_form->forward, env);
|
||||||
evalBuiltIns(first_form, first_form->forward, env);
|
|
||||||
|
|
||||||
if(!isError(builtIn, BUILT_IN_NOT_FOUND) &&
|
if(!isError(builtIn, BUILT_IN_NOT_FOUND) &&
|
||||||
!isError(builtIn, NOT_A_SYMBOL)) {
|
!isError(builtIn, NOT_A_SYMBOL)) {
|
||||||
return builtIn;
|
return builtIn;
|
||||||
}
|
}
|
||||||
|
cleanObject(&builtIn);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Evaluate the list based on the first element's type
|
// 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)
|
Object eval(const Object *obj, struct Environment *env)
|
||||||
{
|
{
|
||||||
switch(obj->type) {
|
switch(obj->type) {
|
||||||
case TYPE_ERROR:
|
|
||||||
case TYPE_FUNC:
|
case TYPE_FUNC:
|
||||||
return *obj;
|
case TYPE_ERROR:
|
||||||
|
|
||||||
case TYPE_OTHER:
|
case TYPE_OTHER:
|
||||||
case TYPE_NUMBER:
|
case TYPE_NUMBER:
|
||||||
case TYPE_BOOL:
|
case TYPE_BOOL:
|
||||||
|
@ -702,9 +707,12 @@ Object parseAtom(struct Slice *s)
|
||||||
|
|
||||||
Object parseEval(const char *input, struct Environment *env)
|
Object parseEval(const char *input, struct Environment *env)
|
||||||
{
|
{
|
||||||
struct Slice *tokens = nf_tokenize(input);
|
char *err;
|
||||||
if(!tokens) {
|
struct Slice *tokens = nf_tokenize(input, &err);
|
||||||
return errorObject(MISMATCHED_PARENS);
|
if(err) {
|
||||||
|
Object o = errorWithContext(MISMATCHED_PARENS, err);
|
||||||
|
free(err);
|
||||||
|
return o;
|
||||||
}
|
}
|
||||||
if(!tokens->text) {
|
if(!tokens->text) {
|
||||||
return symFromSlice(" ", 1);
|
return symFromSlice(" ", 1);
|
||||||
|
@ -738,10 +746,11 @@ Object parseEval(const char *input, struct Environment *env)
|
||||||
cleanObject(&obj);
|
cleanObject(&obj);
|
||||||
Object parsed = parse(tok).obj;
|
Object parsed = parse(tok).obj;
|
||||||
if(parsed.type == TYPE_ERROR) {
|
if(parsed.type == TYPE_ERROR) {
|
||||||
obj = parsed;
|
obj = parsed; // TODO Check necessity
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if(tok[i].text[0] == ')') {
|
if(tok[i].text[0] == ')') {
|
||||||
|
// Skip `tok` past end of list that just closed
|
||||||
tok = &tok[i + 1];
|
tok = &tok[i + 1];
|
||||||
i = -1;
|
i = -1;
|
||||||
}
|
}
|
||||||
|
@ -829,8 +838,8 @@ int main(int argc, const char* argv[])
|
||||||
{
|
{
|
||||||
struct Environment env = defaultEnv();
|
struct Environment env = defaultEnv();
|
||||||
readFile(SCRIPTDIR "/lib.pbl", &env);
|
readFile(SCRIPTDIR "/lib.pbl", &env);
|
||||||
FILE *file = fopen(argv[1], "r");
|
|
||||||
if(argc >= 2) {
|
if(argc >= 2) {
|
||||||
|
FILE *file = fopen(argv[1], "r");
|
||||||
if(file) {
|
if(file) {
|
||||||
// Executing a file
|
// Executing a file
|
||||||
loadArgsIntoEnv(argc, argv, &env);
|
loadArgsIntoEnv(argc, argv, &env);
|
||||||
|
|
19
src/tests.sh
19
src/tests.sh
|
@ -51,9 +51,9 @@ check() {
|
||||||
fi
|
fi
|
||||||
if $VALGRIND; then
|
if $VALGRIND; then
|
||||||
echo -ne "\n $1\r"
|
echo -ne "\n $1\r"
|
||||||
local output=$($VALCOM ./pl "(loadfile \"examples/lib.pbl\") $2")
|
local output="$($VALCOM ./pl "(loadfile \"examples/lib.pbl\") $2")"
|
||||||
else
|
else
|
||||||
local output=$(./pl "(loadfile \"examples/lib.pbl\") $2")
|
local output="$(./pl "(loadfile \"examples/lib.pbl\") $2")"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ "$output" == "$3" ]; then
|
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 "BadNumber" "(5df)" "BAD_NUMBER"
|
||||||
check "BadHex" "(0x0zf)" "BAD_NUMBER"
|
check "BadHex" "(0x0zf)" "BAD_NUMBER"
|
||||||
check "BadBinary" "(0b01120)" "BAD_NUMBER"
|
check "BadBinary" "(0b01120)" "BAD_NUMBER"
|
||||||
check "BadParens" "(hey()" "MISMATCHED_PARENS"
|
check "BadParens" "(hey()" \
|
||||||
check "BadParens2" "(hey)(" "MISMATCHED_PARENS"
|
"'MISMATCHED_PARENS': (loadfile \"examples/lib.pbl\") (hey()"
|
||||||
check "BadParens3" "((hey(" "MISMATCHED_PARENS"
|
check "BadParens2" "(hey)(" \
|
||||||
check "BadParens4" ")))hey" "MISMATCHED_PARENS"
|
"'MISMATCHED_PARENS': (loadfile \"examples/lib.pbl\") (hey)("
|
||||||
check "BadParens5" "hey))(" "MISMATCHED_PARENS"
|
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
|
endBlock
|
||||||
|
|
||||||
title "Eval"
|
title "Eval"
|
||||||
|
|
29
src/tokens.c
29
src/tokens.c
|
@ -1,5 +1,6 @@
|
||||||
#include "tokens.h"
|
#include "tokens.h"
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
#ifdef STANDALONE
|
#ifdef STANDALONE
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
@ -8,9 +9,13 @@
|
||||||
#else
|
#else
|
||||||
#include <pebble.h>
|
#include <pebble.h>
|
||||||
#undef printd
|
#undef printd
|
||||||
|
#undef printf
|
||||||
|
#define printf(...) APP_LOG(APP_LOG_LEVEL_DEBUG, __VA_ARGS__)
|
||||||
#define printd(...) printf(__VA_ARGS__)
|
#define printd(...) printf(__VA_ARGS__)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#define ERR_LEN 256
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Grammar:
|
* Grammar:
|
||||||
* token
|
* token
|
||||||
|
@ -48,12 +53,20 @@ int notWhitespace(const char c) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return needs to be freed, if not null
|
// 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;
|
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 i = 0;
|
||||||
int slice = 0;
|
int slice = 0;
|
||||||
|
@ -73,6 +86,9 @@ struct Slice *nf_tokenize(const char *input)
|
||||||
} else if (input[i] == ')') {
|
} else if (input[i] == ')') {
|
||||||
parens--;
|
parens--;
|
||||||
if(parens < 0) {
|
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);
|
free(slices);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -82,7 +98,6 @@ struct Slice *nf_tokenize(const char *input)
|
||||||
|
|
||||||
if(isSingle(input[i])) {
|
if(isSingle(input[i])) {
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
} else if(input[i] == '"' || input[i] == '\'') {
|
} else if(input[i] == '"' || input[i] == '\'') {
|
||||||
const char quote = input[i];
|
const char quote = input[i];
|
||||||
while(input[++i] != quote && input[i] != '\0') {
|
while(input[++i] != quote && input[i] != '\0') {
|
||||||
|
@ -99,7 +114,10 @@ struct Slice *nf_tokenize(const char *input)
|
||||||
slice++;
|
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);
|
free(slices);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
@ -107,5 +125,6 @@ struct Slice *nf_tokenize(const char *input)
|
||||||
slices[slice].text = NULL;
|
slices[slice].text = NULL;
|
||||||
slices[slice].length = 0;
|
slices[slice].length = 0;
|
||||||
|
|
||||||
|
*err = NULL;
|
||||||
return slices;
|
return slices;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,6 @@
|
||||||
int isSingle(const char c);
|
int isSingle(const char c);
|
||||||
int isDigit(const char c);
|
int isDigit(const char c);
|
||||||
int isHex(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
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue