From 54410030f737e5cf930d24fea6b41973b765f3d4 Mon Sep 17 00:00:00 2001 From: Sage Vaillancourt Date: Wed, 13 Apr 2022 15:16:32 -0400 Subject: [PATCH] Add throw() macro. Simplifies the creation of complex error messages. --- src/env.c | 2 +- src/env.h | 13 ++++++++++--- src/examples/pebblisp.pbl | 5 ++++- src/object.c | 10 ++++++---- src/object.h | 5 +++++ src/pebblisp.c | 31 ++++++++++++++----------------- src/plfunc.c | 39 ++++++++++++++++++--------------------- src/tests.sh | 2 +- 8 files changed, 59 insertions(+), 48 deletions(-) diff --git a/src/env.c b/src/env.c index 6cbf786..d5891ef 100644 --- a/src/env.c +++ b/src/env.c @@ -75,7 +75,7 @@ Object fetchFromEnvironment(const char* name, struct Environment* env) Object o = deStrip(*object); return cloneObject(o); } - return errorWithContext(DID_NOT_FIND_SYMBOL, name); + throw(DID_NOT_FIND_SYMBOL, "%s", name); } void addToEnv(struct Environment* env, const char* name, const Object obj) diff --git a/src/env.h b/src/env.h index c49425a..935bb51 100644 --- a/src/env.h +++ b/src/env.h @@ -82,9 +82,16 @@ fn(segfault, "seg", ); fn(help, "?", - "Gets a string with help text for the given function.\n" - "For example:\n" - " (? islist) => \"(islist (1 2 3)) => T\"" + "Gets a string with help text or a string representation for the object.\n" + "\n" + " Function help:\n" + " (? islist) => \"(islist (1 2 3)) => T\"\n" + "\n" + " Struct fields:\n" + " (? Output) => \"{ stdout stderr }\"\n" + "\n" + " Other objects:\n" + " (? \"Hello\") => \"Hello\"" ); diff --git a/src/examples/pebblisp.pbl b/src/examples/pebblisp.pbl index 6b6f1a9..617d4a3 100644 --- a/src/examples/pebblisp.pbl +++ b/src/examples/pebblisp.pbl @@ -66,7 +66,10 @@ (def zero (fn (num) (cat (if (< num 10) "0" "") num))) -(def clock (fn (ti) (cat (hour ti) ":" (zero ti.minute) ":" (zero ti.sec)))) +(def clock (fn (ti) + "Get a simple H:MM:SS time string" + (cat (hour ti) ":" (zero ti.minute) ":" (zero ti.sec)) +)) (def cleanDir (fn () "Get a string of the current directory" ( diff --git a/src/object.c b/src/object.c index aecf243..32e90c4 100644 --- a/src/object.c +++ b/src/object.c @@ -734,12 +734,14 @@ inline Object constructLambda(const Object* params, const Object* docs, const Ob return errorObject(NULL_LAMBDA_LIST); } - if (params->type != TYPE_LIST || body->type != TYPE_LIST) { - return errorObject(LAMBDA_ARGS_NOT_LIST); + if (params->type != TYPE_LIST) { + throw(LAMBDA_ARGS_NOT_LIST, "params are %s", getTypeName(params)); + } + if (body->type != TYPE_LIST) { + throw(LAMBDA_ARGS_NOT_LIST, "body is %s", getTypeName(body)); } - if (docs && docs->type != TYPE_STRING) { - return errorWithContext(BAD_TYPE, "fn docstring must be a string"); + throw(BAD_TYPE, "fn docstring must be a string, but received %s", getTypeName(docs)); } Object o = newObject(TYPE_LAMBDA); diff --git a/src/object.h b/src/object.h index 21d6da7..3d8bde2 100644 --- a/src/object.h +++ b/src/object.h @@ -147,6 +147,8 @@ struct string { size_t capacity; }; +const char* getTypeName(const Object* obj); + /** * Returns a dynamically-sized string representation of the given object. * @@ -249,7 +251,10 @@ Object errorWithContextLineNo(enum errorCode code, const char* context, int line #ifdef SIMPLE_ERRORS #define errorWithContext(code, context) errorObject(code) #define errorAddContext(x, y, z, a) ; +#define throw(_code, ...) return errorObject(_code) #else +char ERROR_CONTEXT[128]; +#define throw(_code, ...) sprintf(ERROR_CONTEXT, __VA_ARGS__); return errorWithContext(_code, ERROR_CONTEXT) #define errorWithContext(_code, _context) errorWithContextLineNo(_code, _context, __LINE__, __FILE__) void errorAddContext(Object* o, const char* context, int lineNo, const char* fileName); diff --git a/src/pebblisp.c b/src/pebblisp.c index d717b4b..4ccf682 100644 --- a/src/pebblisp.c +++ b/src/pebblisp.c @@ -38,14 +38,14 @@ Object def(Object* params, unused int length, unused struct Environment* env) * * Not a typical pl function because I don't feel like adding more syntactic sugar right now. * - * (struct point (x y)) + * (struct Point (x y)) */ Object evalStructArgs(const Object* symbol, const Object* fields, unused struct Environment* env) { const char* name = symbol->string; - if (!isListy(*fields)) { - return errorObject(NOT_A_LIST); + if (!fields || !isListy(*fields)) { + throw(NOT_A_LIST, "In definition of struct %s, expected a list of fields.", name); } int fieldCount = listLength(fields); @@ -83,14 +83,14 @@ Object evalIfArgs(const Object* argForms, struct Environment* env) Object mapO(Object* params, int length, struct Environment* env) { if (length < 2) { - return errorObject(NULL_MAP_ARGS); + throw(NULL_MAP_ARGS, "(map) expects at least 2 parameters, but only received %d.", length); } Object lambda = eval(¶ms[0], env); const Object* inputList = ¶ms[1]; if (!isFuncy(lambda)) { - return errorObject(BAD_TYPE); + throw(BAD_TYPE, "First argument of (map) should be func-like."); } BuildListNamed(outputList); @@ -181,7 +181,7 @@ Object funcyEval(Object* funcy, const Object* passedArguments, int evalLength, { if (!funcy) { eprintf("HIGHLY ILLEGAL NULL FUNC-LIKE!!!\n"); - return errorObject(BAD_TYPE); + throw(BAD_TYPE, "Expected func-like object, but received null"); } switch (funcy->type) { case TYPE_LAMBDA: @@ -190,7 +190,7 @@ Object funcyEval(Object* funcy, const Object* passedArguments, int evalLength, return listEvalFunc(funcy, passedArguments, evalLength, env); default: eprintf("HIGHLY ILLEGAL NOT-FUNC IN funcyEval()!!!\n"); - return errorObject(BAD_TYPE); + throw(BAD_TYPE, "Expected func-like object, but received %s", getTypeName(funcy)); } } @@ -296,7 +296,7 @@ Object eval(const Object* obj, struct Environment* env) return evalList(obj, env); } - return errorWithContext(BAD_TYPE, "eval()"); + throw(BAD_TYPE, "Object being evaluated has a type number of %d", obj->type); } Object structAccess(Object* params, unused int length, unused struct Environment* env) @@ -313,8 +313,7 @@ Object structAccess(Object* params, unused int length, unused struct Environment } } - printf("Could not find field name `%s`\n", field.string); - return errorObject(NULL_PARSE); + throw(NULL_PARSE, "Could not find field named `%s`", field.string); } Result parse(struct Slice* slices) @@ -448,6 +447,7 @@ Result parseAtom(struct Slice* s) } else if (c == '"'/* || c == '\''*/) { return (Result) { objFromSlice(s->text, s->length), s }; } else if (s->text[s->length] == '.') { + /* struct InlinedFunction f ={ .func = structAccess, .arguments = malloc(sizeof(Object) * 2), @@ -458,7 +458,7 @@ Result parseAtom(struct Slice* s) f.arguments[1] = stringFromSlice(next->text, next->length); return (Result) { list, next }; - /* + */ Object structAccessFunc = newObject(TYPE_FUNC); structAccessFunc.func = &structAccess; Object list = startList(structAccessFunc); @@ -468,7 +468,6 @@ Result parseAtom(struct Slice* s) Object structField = stringFromSlice(next->text, next->length); nf_addToList(&list, structField); return (Result) { list, next }; - */ } return (Result) { symFromSlice(s->text, s->length), s }; } @@ -541,15 +540,13 @@ Object typeCheck(const char* funcName, Object* params, int length, { *failed = 1; if ((typeLength - 1) > length) { - return errorWithContext(NOT_ENOUGH_ARGUMENTS, funcName); + throw(NOT_ENOUGH_ARGUMENTS, "%s requires %d arguments, but only received %d", funcName, typeLength - 1, length); } for (int i = 0; i < typeLength - 1; i++) { if (typeChecks[i].checkFunc && !typeChecks[i].checkFunc(params[i])) { - char context[128]; - sprintf(context, "When calling (%s), expected %s, but received %s.", funcName, typeChecks[i].name, - getTypeName(¶ms[i])); - return errorWithContextLineNo(BAD_PARAMS, context, 0, NULL); + throw(BAD_PARAMS, "When calling (%s), expected %s at param %d, but received %s.", + funcName, typeChecks[i].name, i, getTypeName(¶ms[i])); } } *failed = 0; diff --git a/src/plfunc.c b/src/plfunc.c index ae9bd66..298f7fb 100644 --- a/src/plfunc.c +++ b/src/plfunc.c @@ -102,28 +102,26 @@ Object at(Object* params, unused int length, unused struct Environment* env) Object list = params[1]; const Object* found = itemAt(&list, index.number); + if (found) { return cloneObject(*found); - } else { - return errorObject(INDEX_PAST_END); } + + return errorObject(INDEX_PAST_END); } Object rest(Object* params, unused int length, unused struct Environment* env) { checkTypes(rest) Object list = params[0]; - if (!isListy(list)) { - return errorObject(NOT_A_LIST); - } - Object ret = listObject(); + BuildListNamed(ret); Object* l = &list; FOR_POINTER_IN_LIST(l) { if (POINTER == l->list) { continue; } - nf_addToList(&ret, cloneObject(*POINTER)); + addToList(ret, cloneObject(*POINTER)); } return ret; } @@ -258,10 +256,13 @@ Object substring(Object* params, int length, unused struct Environment* env) Object end = params[1]; // First char to exclude Object string = params[2]; - int len = end.number - start.number; + long len = end.number - start.number; size_t stringLen = strlen(string.string); - if (len < 0 || start.number > stringLen) { - return errorWithContext(BAD_PARAMS, string.string); + if (len < 0) { + throw(BAD_PARAMS, "substr start index (%ld) is higher than its end index (%ld)!", start.number, end.number); + } + if (start.number > stringLen) { + throw(BAD_PARAMS, "substr start index (%ld) is higher than the string length (%lu)", start.number, stringLen); } if (len > stringLen - start.number) { @@ -300,7 +301,7 @@ Object parseEvalO(Object* params, unused int length, struct Environment* env) case TYPE_STRING: return parseEval(text.string, env); default: - return errorObject(CAN_ONLY_EVAL_STRINGS); + throw(CAN_ONLY_EVAL_STRINGS, "Tried to (eval) a %s, instead of a string or symbol list", getTypeName(&text)); } } @@ -377,8 +378,8 @@ int listEquality(const Object* list1, const Object* list2) int areEqual(const Object* obj1, const Object* obj2) { - const int n1 = obj1->number; - const int n2 = obj2->number; + const long n1 = obj1->number; + const long n2 = obj2->number; if (bothAre(TYPE_STRING, obj1, obj2)) { return !strcmp(obj1->string, obj2->string); @@ -445,8 +446,8 @@ Object getTime(unused Object* params, unused int length, struct Environment* env { if (timeStructDefinition == -1) { parseEval("(struct Time (minute hour sec))", env); + timeStructDefinition = getStructIndex("Time"); } - timeStructDefinition = getStructIndex("Time"); time_t t = time(NULL); struct tm tm = *localtime(&t); Object o = structObject(timeStructDefinition); @@ -473,8 +474,8 @@ Object numToChar(Object* params, unused int length, unused struct Environment* e checkTypes(numToChar) Object c = params[0]; - if (c.type != TYPE_NUMBER || c.number > 255 || c.number < 0) { - return errorObject(BAD_NUMBER); + if (c.number > 255 || c.number < 0) { + throw(BAD_NUMBER, "Char values should be between 0 and 255 (inclusive), but received %ld", c.number); } char ch[1] = { c.number }; return stringFromSlice(ch, 1); @@ -546,13 +547,9 @@ Object readFileToObject(Object* params, unused int length, unused struct Environ checkTypes(readFileToObject) Object filename = params[0]; - if (filename.type != TYPE_STRING) { - return errorObject(NULL_PARSE); - } - FILE* file = fopen(filename.string, "r"); if (!file) { - return errorObject(NULL_PARSE); + throw(NULL_PARSE, "Error opening file at %s", filename.string); } Object string = newObject(TYPE_STRING); diff --git a/src/tests.sh b/src/tests.sh index f7dc918..c947876 100755 --- a/src/tests.sh +++ b/src/tests.sh @@ -217,7 +217,7 @@ check "Lambda Returning a Lambda" "(def plusser (fn (outer) (fn (inner) (+ outer title "ShouldError" check "Len of Not-List" "(len 5)" regex "BAD_PARAMS.*" -check "Map With No List" "(map sq)" "NULL_MAP_ARGS" +check "Map With No List" "(map sq)" regex "NULL_MAP_ARGS.*" check "Bad Number" "(5df)" regex "BAD_NUMBER.*" check "Bad Hex" "(0x0zf)" regex "BAD_NUMBER.*" check "Bad Binary" "(0b01120)" regex "BAD_NUMBER.*"