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

View File

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

View File

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

View File

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

View File

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

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.
*
* (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(&params[0], env);
const Object* inputList = &params[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(&params[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(&params[i]));
}
}
*failed = 0;

View File

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

View File

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