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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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