Add throw() macro.

Simplifies the creation of complex error messages.
This commit is contained in:
Sage Vaillancourt 2022-04-13 15:16:32 -04:00 committed by Sage Vaillancourt
parent 9415b27f41
commit 54410030f7
8 changed files with 59 additions and 48 deletions

View File

@ -75,7 +75,7 @@ Object fetchFromEnvironment(const char* name, struct Environment* env)
Object o = deStrip(*object); Object o = deStrip(*object);
return cloneObject(o); 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) void addToEnv(struct Environment* env, const char* name, const Object obj)

View File

@ -82,9 +82,16 @@ fn(segfault, "seg",
); );
fn(help, "?", fn(help, "?",
"Gets a string with help text for the given function.\n" "Gets a string with help text or a string representation for the object.\n"
"For example:\n" "\n"
" (? islist) => \"(islist (1 2 3)) => T\"" " Function help:\n"
" (? islist) => \"(islist (1 2 3)) => T\"\n"
"\n"
" Struct fields:\n"
" (? Output) => \"{ stdout stderr }\"\n"
"\n"
" Other objects:\n"
" (? \"Hello\") => \"Hello\""
); );

View File

@ -66,7 +66,10 @@
(def zero (fn (num) (cat (if (< num 10) "0" "") num))) (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 () (def cleanDir (fn ()
"Get a string of the current directory" ( "Get a string of the current directory" (

View File

@ -734,12 +734,14 @@ inline Object constructLambda(const Object* params, const Object* docs, const Ob
return errorObject(NULL_LAMBDA_LIST); return errorObject(NULL_LAMBDA_LIST);
} }
if (params->type != TYPE_LIST || body->type != TYPE_LIST) { if (params->type != TYPE_LIST) {
return errorObject(LAMBDA_ARGS_NOT_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) { 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); Object o = newObject(TYPE_LAMBDA);

View File

@ -147,6 +147,8 @@ struct string {
size_t capacity; size_t capacity;
}; };
const char* getTypeName(const Object* obj);
/** /**
* Returns a dynamically-sized string representation of the given object. * 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 #ifdef SIMPLE_ERRORS
#define errorWithContext(code, context) errorObject(code) #define errorWithContext(code, context) errorObject(code)
#define errorAddContext(x, y, z, a) ; #define errorAddContext(x, y, z, a) ;
#define throw(_code, ...) return errorObject(_code)
#else #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__) #define errorWithContext(_code, _context) errorWithContextLineNo(_code, _context, __LINE__, __FILE__)
void errorAddContext(Object* o, const char* context, int lineNo, const char* fileName); void errorAddContext(Object* o, const char* context, int lineNo, const char* fileName);

View File

@ -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. * 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) Object evalStructArgs(const Object* symbol, const Object* fields, unused struct Environment* env)
{ {
const char* name = symbol->string; const char* name = symbol->string;
if (!isListy(*fields)) { if (!fields || !isListy(*fields)) {
return errorObject(NOT_A_LIST); throw(NOT_A_LIST, "In definition of struct %s, expected a list of fields.", name);
} }
int fieldCount = listLength(fields); 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) Object mapO(Object* params, int length, struct Environment* env)
{ {
if (length < 2) { 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(&params[0], env); Object lambda = eval(&params[0], env);
const Object* inputList = &params[1]; const Object* inputList = &params[1];
if (!isFuncy(lambda)) { if (!isFuncy(lambda)) {
return errorObject(BAD_TYPE); throw(BAD_TYPE, "First argument of (map) should be func-like.");
} }
BuildListNamed(outputList); BuildListNamed(outputList);
@ -181,7 +181,7 @@ Object funcyEval(Object* funcy, const Object* passedArguments, int evalLength,
{ {
if (!funcy) { if (!funcy) {
eprintf("HIGHLY ILLEGAL NULL FUNC-LIKE!!!\n"); eprintf("HIGHLY ILLEGAL NULL FUNC-LIKE!!!\n");
return errorObject(BAD_TYPE); throw(BAD_TYPE, "Expected func-like object, but received null");
} }
switch (funcy->type) { switch (funcy->type) {
case TYPE_LAMBDA: case TYPE_LAMBDA:
@ -190,7 +190,7 @@ Object funcyEval(Object* funcy, const Object* passedArguments, int evalLength,
return listEvalFunc(funcy, passedArguments, evalLength, env); return listEvalFunc(funcy, passedArguments, evalLength, env);
default: default:
eprintf("HIGHLY ILLEGAL NOT-FUNC IN funcyEval()!!!\n"); 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 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) 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); throw(NULL_PARSE, "Could not find field named `%s`", field.string);
return errorObject(NULL_PARSE);
} }
Result parse(struct Slice* slices) Result parse(struct Slice* slices)
@ -448,6 +447,7 @@ Result parseAtom(struct Slice* s)
} else if (c == '"'/* || c == '\''*/) { } else if (c == '"'/* || c == '\''*/) {
return (Result) { objFromSlice(s->text, s->length), s }; return (Result) { objFromSlice(s->text, s->length), s };
} else if (s->text[s->length] == '.') { } else if (s->text[s->length] == '.') {
/*
struct InlinedFunction f ={ struct InlinedFunction f ={
.func = structAccess, .func = structAccess,
.arguments = malloc(sizeof(Object) * 2), .arguments = malloc(sizeof(Object) * 2),
@ -458,7 +458,7 @@ Result parseAtom(struct Slice* s)
f.arguments[1] = stringFromSlice(next->text, next->length); f.arguments[1] = stringFromSlice(next->text, next->length);
return (Result) { list, next }; return (Result) { list, next };
/* */
Object structAccessFunc = newObject(TYPE_FUNC); Object structAccessFunc = newObject(TYPE_FUNC);
structAccessFunc.func = &structAccess; structAccessFunc.func = &structAccess;
Object list = startList(structAccessFunc); Object list = startList(structAccessFunc);
@ -468,7 +468,6 @@ Result parseAtom(struct Slice* s)
Object structField = stringFromSlice(next->text, next->length); Object structField = stringFromSlice(next->text, next->length);
nf_addToList(&list, structField); nf_addToList(&list, structField);
return (Result) { list, next }; return (Result) { list, next };
*/
} }
return (Result) { symFromSlice(s->text, s->length), s }; return (Result) { symFromSlice(s->text, s->length), s };
} }
@ -541,15 +540,13 @@ Object typeCheck(const char* funcName, Object* params, int length,
{ {
*failed = 1; *failed = 1;
if ((typeLength - 1) > length) { 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++) { for (int i = 0; i < typeLength - 1; i++) {
if (typeChecks[i].checkFunc && !typeChecks[i].checkFunc(params[i])) { if (typeChecks[i].checkFunc && !typeChecks[i].checkFunc(params[i])) {
char context[128]; throw(BAD_PARAMS, "When calling (%s), expected %s at param %d, but received %s.",
sprintf(context, "When calling (%s), expected %s, but received %s.", funcName, typeChecks[i].name, funcName, typeChecks[i].name, i, getTypeName(&params[i]));
getTypeName(&params[i]));
return errorWithContextLineNo(BAD_PARAMS, context, 0, NULL);
} }
} }
*failed = 0; *failed = 0;

View File

@ -102,28 +102,26 @@ Object at(Object* params, unused int length, unused struct Environment* env)
Object list = params[1]; Object list = params[1];
const Object* found = itemAt(&list, index.number); const Object* found = itemAt(&list, index.number);
if (found) { if (found) {
return cloneObject(*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) Object rest(Object* params, unused int length, unused struct Environment* env)
{ {
checkTypes(rest) checkTypes(rest)
Object list = params[0]; Object list = params[0];
if (!isListy(list)) {
return errorObject(NOT_A_LIST);
}
Object ret = listObject(); BuildListNamed(ret);
Object* l = &list; Object* l = &list;
FOR_POINTER_IN_LIST(l) { FOR_POINTER_IN_LIST(l) {
if (POINTER == l->list) { if (POINTER == l->list) {
continue; continue;
} }
nf_addToList(&ret, cloneObject(*POINTER)); addToList(ret, cloneObject(*POINTER));
} }
return ret; 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 end = params[1]; // First char to exclude
Object string = params[2]; Object string = params[2];
int len = end.number - start.number; long len = end.number - start.number;
size_t stringLen = strlen(string.string); size_t stringLen = strlen(string.string);
if (len < 0 || start.number > stringLen) { if (len < 0) {
return errorWithContext(BAD_PARAMS, string.string); 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) { if (len > stringLen - start.number) {
@ -300,7 +301,7 @@ Object parseEvalO(Object* params, unused int length, struct Environment* env)
case TYPE_STRING: case TYPE_STRING:
return parseEval(text.string, env); return parseEval(text.string, env);
default: 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) int areEqual(const Object* obj1, const Object* obj2)
{ {
const int n1 = obj1->number; const long n1 = obj1->number;
const int n2 = obj2->number; const long n2 = obj2->number;
if (bothAre(TYPE_STRING, obj1, obj2)) { if (bothAre(TYPE_STRING, obj1, obj2)) {
return !strcmp(obj1->string, obj2->string); return !strcmp(obj1->string, obj2->string);
@ -445,8 +446,8 @@ Object getTime(unused Object* params, unused int length, struct Environment* env
{ {
if (timeStructDefinition == -1) { if (timeStructDefinition == -1) {
parseEval("(struct Time (minute hour sec))", env); parseEval("(struct Time (minute hour sec))", env);
}
timeStructDefinition = getStructIndex("Time"); timeStructDefinition = getStructIndex("Time");
}
time_t t = time(NULL); time_t t = time(NULL);
struct tm tm = *localtime(&t); struct tm tm = *localtime(&t);
Object o = structObject(timeStructDefinition); Object o = structObject(timeStructDefinition);
@ -473,8 +474,8 @@ Object numToChar(Object* params, unused int length, unused struct Environment* e
checkTypes(numToChar) checkTypes(numToChar)
Object c = params[0]; Object c = params[0];
if (c.type != TYPE_NUMBER || c.number > 255 || c.number < 0) { if (c.number > 255 || c.number < 0) {
return errorObject(BAD_NUMBER); throw(BAD_NUMBER, "Char values should be between 0 and 255 (inclusive), but received %ld", c.number);
} }
char ch[1] = { c.number }; char ch[1] = { c.number };
return stringFromSlice(ch, 1); return stringFromSlice(ch, 1);
@ -546,13 +547,9 @@ Object readFileToObject(Object* params, unused int length, unused struct Environ
checkTypes(readFileToObject) checkTypes(readFileToObject)
Object filename = params[0]; Object filename = params[0];
if (filename.type != TYPE_STRING) {
return errorObject(NULL_PARSE);
}
FILE* file = fopen(filename.string, "r"); FILE* file = fopen(filename.string, "r");
if (!file) { if (!file) {
return errorObject(NULL_PARSE); throw(NULL_PARSE, "Error opening file at %s", filename.string);
} }
Object string = newObject(TYPE_STRING); Object string = newObject(TYPE_STRING);

View File

@ -217,7 +217,7 @@ check "Lambda Returning a Lambda" "(def plusser (fn (outer) (fn (inner) (+ outer
title "ShouldError" title "ShouldError"
check "Len of Not-List" "(len 5)" regex "BAD_PARAMS.*" 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 Number" "(5df)" regex "BAD_NUMBER.*"
check "Bad Hex" "(0x0zf)" regex "BAD_NUMBER.*" check "Bad Hex" "(0x0zf)" regex "BAD_NUMBER.*"
check "Bad Binary" "(0b01120)" regex "BAD_NUMBER.*" check "Bad Binary" "(0b01120)" regex "BAD_NUMBER.*"