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);
|
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)
|
||||||
|
|
13
src/env.h
13
src/env.h
|
@ -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\""
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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" (
|
||||||
|
|
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);
|
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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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(¶ms[0], env);
|
Object lambda = eval(¶ms[0], env);
|
||||||
const Object* inputList = ¶ms[1];
|
const Object* inputList = ¶ms[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(¶ms[i]));
|
||||||
getTypeName(¶ms[i]));
|
|
||||||
return errorWithContextLineNo(BAD_PARAMS, context, 0, NULL);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*failed = 0;
|
*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];
|
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);
|
||||||
|
|
|
@ -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.*"
|
||||||
|
|
Loading…
Reference in New Issue