Add throw() macro.
Simplifies the creation of complex error messages.
This commit is contained in:
parent
9415b27f41
commit
54410030f7
|
@ -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)
|
||||
|
|
13
src/env.h
13
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\""
|
||||
);
|
||||
|
||||
|
||||
|
|
|
@ -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" (
|
||||
|
|
10
src/object.c
10
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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
39
src/plfunc.c
39
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");
|
||||
}
|
||||
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);
|
||||
|
|
|
@ -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.*"
|
||||
|
|
Loading…
Reference in New Issue