More useful type-error messages.
BAD_PARAMS_ON -> BAD_PARAMS Replace printType() with getTypeName(). Use --ignore-config in testing. Add type-checking (and missing clone) to (await).
This commit is contained in:
parent
99a9b9a2b6
commit
deca6045ff
|
@ -68,11 +68,6 @@ unused static const char * const _name ## Tests[] = {__VA_ARGS__}; \
|
||||||
_Static_assert(array_length(_name ## Tests) % 2 == 0, "Array of test strings must have exactly one expected result for each test."); \
|
_Static_assert(array_length(_name ## Tests) % 2 == 0, "Array of test strings must have exactly one expected result for each test."); \
|
||||||
Object _name(Object* params, int length, struct Environment* env)
|
Object _name(Object* params, int length, struct Environment* env)
|
||||||
|
|
||||||
#define tfn(_name, _symbol, _type, _docs, ...) \
|
|
||||||
unused static int (*_name ## TypeChecks[])(Object) = UNPACK _type; \
|
|
||||||
static const char * const _name ## Symbol = _symbol; \
|
|
||||||
fnn(_name, _docs, __VA_ARGS__)
|
|
||||||
|
|
||||||
#define fn(_name, _symbol, _docs, ...) \
|
#define fn(_name, _symbol, _docs, ...) \
|
||||||
static const char * const _name ## Symbol = _symbol; \
|
static const char * const _name ## Symbol = _symbol; \
|
||||||
fnn(_name, _docs, __VA_ARGS__)
|
fnn(_name, _docs, __VA_ARGS__)
|
||||||
|
|
18
src/object.c
18
src/object.c
|
@ -142,7 +142,7 @@ static const char* errorText[] = {
|
||||||
"LAMBDA_ARGS_NOT_LIST",
|
"LAMBDA_ARGS_NOT_LIST",
|
||||||
"DID_NOT_FIND_SYMBOL",
|
"DID_NOT_FIND_SYMBOL",
|
||||||
"BAD_TYPE",
|
"BAD_TYPE",
|
||||||
"BAD_PARAMS_ON",
|
"BAD_PARAMS",
|
||||||
"BAD_NUMBER",
|
"BAD_NUMBER",
|
||||||
"UNSUPPORTED_NUMBER_TYPE",
|
"UNSUPPORTED_NUMBER_TYPE",
|
||||||
"NOT_ENOUGH_ARGUMENTS",
|
"NOT_ENOUGH_ARGUMENTS",
|
||||||
|
@ -332,14 +332,12 @@ char* stringObj(const Object* obj, size_t* length)
|
||||||
#if defined(DEBUG) || defined(STANDALONE)
|
#if defined(DEBUG) || defined(STANDALONE)
|
||||||
|
|
||||||
#define SIMPLE_TYPE(_type) case _type:\
|
#define SIMPLE_TYPE(_type) case _type:\
|
||||||
printf(#_type);\
|
return #_type;
|
||||||
return
|
|
||||||
|
|
||||||
void printType(const Object* obj)
|
const char* getTypeName(const Object* obj)
|
||||||
{
|
{
|
||||||
if (!obj) {
|
if (!obj) {
|
||||||
printf("NULL OBJECT");
|
return "NULL_OBJECT";
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
switch (obj->type) {
|
switch (obj->type) {
|
||||||
SIMPLE_TYPE(TYPE_NUMBER);
|
SIMPLE_TYPE(TYPE_NUMBER);
|
||||||
|
@ -353,14 +351,10 @@ void printType(const Object* obj)
|
||||||
SIMPLE_TYPE(TYPE_PROMISE);
|
SIMPLE_TYPE(TYPE_PROMISE);
|
||||||
SIMPLE_TYPE(TYPE_OTHER);
|
SIMPLE_TYPE(TYPE_OTHER);
|
||||||
SIMPLE_TYPE(TYPE_ERROR);
|
SIMPLE_TYPE(TYPE_ERROR);
|
||||||
case TYPE_LAMBDA:
|
SIMPLE_TYPE(TYPE_LAMBDA);
|
||||||
printf("TYPE_LAMBDA Params:\n");
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isValidType(*obj)) {
|
return "UNKNOWN_TYPE";
|
||||||
printf("UNKNOWN TYPE (%d)", obj->type);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void _printObj(const Object* obj, int newline)
|
void _printObj(const Object* obj, int newline)
|
||||||
|
|
|
@ -46,7 +46,7 @@ enum errorCode {
|
||||||
LAMBDA_ARGS_NOT_LIST,
|
LAMBDA_ARGS_NOT_LIST,
|
||||||
DID_NOT_FIND_SYMBOL,
|
DID_NOT_FIND_SYMBOL,
|
||||||
BAD_TYPE,
|
BAD_TYPE,
|
||||||
BAD_PARAMS_ON,
|
BAD_PARAMS,
|
||||||
BAD_NUMBER,
|
BAD_NUMBER,
|
||||||
UNSUPPORTED_NUMBER_TYPE,
|
UNSUPPORTED_NUMBER_TYPE,
|
||||||
NOT_ENOUGH_ARGUMENTS,
|
NOT_ENOUGH_ARGUMENTS,
|
||||||
|
@ -147,7 +147,7 @@ struct string {
|
||||||
*/
|
*/
|
||||||
char* stringObj(const Object* obj, size_t* length);
|
char* stringObj(const Object* obj, size_t* length);
|
||||||
|
|
||||||
void printType(const Object* obj);
|
const char* getTypeName(const Object* obj);
|
||||||
|
|
||||||
void printObj(const Object* obj);
|
void printObj(const Object* obj);
|
||||||
|
|
||||||
|
|
|
@ -520,15 +520,18 @@ Object parseEval(const char* input, struct Environment* env)
|
||||||
}
|
}
|
||||||
|
|
||||||
Object typeCheck(const char* funcName, Object* params, int length,
|
Object typeCheck(const char* funcName, Object* params, int length,
|
||||||
int (* typeChecks[])(Object), int typeLength, int* failed)
|
struct TypeCheck typeChecks[], int typeLength, int* failed)
|
||||||
{
|
{
|
||||||
*failed = 1;
|
*failed = 1;
|
||||||
if ((typeLength - 1) > length) {
|
if ((typeLength - 1) > length) {
|
||||||
return errorWithContext(NOT_ENOUGH_ARGUMENTS, funcName);
|
return errorWithContext(NOT_ENOUGH_ARGUMENTS, funcName);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < typeLength - 1; i++) {
|
for (int i = 0; i < typeLength - 1; i++) {
|
||||||
if (typeChecks[i] && !typeChecks[i](params[i])) { // TODO: Use pl func name instead of C function name.
|
if (typeChecks[i].checkFunc && !typeChecks[i].checkFunc(params[i])) { // TODO: Use pl func name instead of C function name.
|
||||||
return /*errorObject(BAD_TYPE); */ errorWithContextLineNo(BAD_PARAMS_ON, funcName, 0, NULL);
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*failed = 0;
|
*failed = 0;
|
||||||
|
|
|
@ -19,8 +19,17 @@ unused static const char * const _name ## Tests[] = {__VA_ARGS__}; \
|
||||||
_Static_assert(array_length(_name ## Tests) % 2 == 0, "Array of test strings must have exactly one expected result for each test."); \
|
_Static_assert(array_length(_name ## Tests) % 2 == 0, "Array of test strings must have exactly one expected result for each test."); \
|
||||||
Object _name(Object* params, int length, struct Environment* env)
|
Object _name(Object* params, int length, struct Environment* env)
|
||||||
|
|
||||||
|
struct TypeCheck {
|
||||||
|
int (*checkFunc)(Object);
|
||||||
|
const char* name;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define expect(_checker) {.checkFunc = (_checker), .name = #_checker}
|
||||||
|
#define returns(_checker) {.checkFunc = (_checker), .name = #_checker}
|
||||||
|
#define anyType {.checkFunc = NULL, .name = "AnyType"}
|
||||||
|
|
||||||
#define tfn(_name, _symbol, _type, _docs, ...) \
|
#define tfn(_name, _symbol, _type, _docs, ...) \
|
||||||
unused static int (*_name ## TypeChecks[])(Object) = UNPACK _type; \
|
unused static struct TypeCheck _name ## TypeChecks[] = UNPACK _type; \
|
||||||
static const char * const _name ## Symbol = _symbol; \
|
static const char * const _name ## Symbol = _symbol; \
|
||||||
fnn(_name, _docs, __VA_ARGS__)
|
fnn(_name, _docs, __VA_ARGS__)
|
||||||
|
|
||||||
|
@ -76,7 +85,7 @@ Object listEvalLambda(Object* lambda, const Object* remaining, int evalLength,
|
||||||
Object simpleFuncEval(Object func, Object arg1, Object arg2, struct Environment* env);
|
Object simpleFuncEval(Object func, Object arg1, Object arg2, struct Environment* env);
|
||||||
|
|
||||||
Object typeCheck(const char* funcName, Object* params, int length,
|
Object typeCheck(const char* funcName, Object* params, int length,
|
||||||
int (* typeChecks[])(Object), int typeLength, int* failed);
|
struct TypeCheck typeChecks[], int typeLength, int* failed);
|
||||||
|
|
||||||
#ifndef STANDALONE
|
#ifndef STANDALONE
|
||||||
#define DISABLE_TYPE_CHECKS
|
#define DISABLE_TYPE_CHECKS
|
||||||
|
@ -112,7 +121,7 @@ fn(def, "def",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(structAccess, "poss",
|
tfn(structAccess, "poss",
|
||||||
({ isStruct, isStringy, NULL }),
|
({ expect(isStruct), expect(isStringy), anyType }),
|
||||||
"Get the value of a struct's field",
|
"Get the value of a struct's field",
|
||||||
"(struct Post (title body))\n "
|
"(struct Post (title body))\n "
|
||||||
"(def p (Post \"TI\" \"BO\"))\n "
|
"(def p (Post \"TI\" \"BO\"))\n "
|
||||||
|
|
|
@ -251,7 +251,7 @@ Object substring(Object* params, int length, unused struct Environment* env)
|
||||||
|
|
||||||
int len = end.number - start.number;
|
int len = end.number - start.number;
|
||||||
if (len < 0 || start.number >= strlen(string.string)) {
|
if (len < 0 || start.number >= strlen(string.string)) {
|
||||||
return errorWithContext(BAD_PARAMS_ON, string.string);
|
return errorWithContext(BAD_PARAMS, string.string);
|
||||||
}
|
}
|
||||||
Object substr = withLen(len, TYPE_STRING);
|
Object substr = withLen(len, TYPE_STRING);
|
||||||
snprintf(substr.string, len + 1, "%s", string.string + start.number);
|
snprintf(substr.string, len + 1, "%s", string.string + start.number);
|
||||||
|
|
66
src/plfunc.h
66
src/plfunc.h
|
@ -68,40 +68,40 @@ fn(or, "|",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(catObjects, "cat",
|
tfn(catObjects, "cat",
|
||||||
({ NULL, isStringy }),
|
({ anyType, returns(isStringy) }),
|
||||||
"Concatenate string representations of the given objects.",
|
"Concatenate string representations of the given objects.",
|
||||||
"(cat \"Stuff: \" (1 2 3))", "Stuff: ( 1 2 3 )",
|
"(cat \"Stuff: \" (1 2 3))", "Stuff: ( 1 2 3 )",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(filter, "fil",
|
tfn(filter, "fil",
|
||||||
({ isFuncy, isListy, isListy }),
|
({ expect(isFuncy), expect(isListy), returns(isListy) }),
|
||||||
"Filter a list based on the given condition.",
|
"Filter a list based on the given condition.",
|
||||||
"(fil (fn (a) (< 50 a)) (25 60 100))", "( 60 100 )",
|
"(fil (fn (a) (< 50 a)) (25 60 100))", "( 60 100 )",
|
||||||
"(fil (fn (a) (< 0 (len a))) ( () (1) (1 2) () ))", "( ( 1 ) ( 1 2 ) )",
|
"(fil (fn (a) (< 0 (len a))) ( () (1) (1 2) () ))", "( ( 1 ) ( 1 2 ) )",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(append, "ap",
|
tfn(append, "ap",
|
||||||
({ isListy, NULL, isListy }),
|
({ expect(isListy), anyType, returns(isListy) }),
|
||||||
"Append the given element. Creates a new list.",
|
"Append the given element. Creates a new list.",
|
||||||
"(ap (1 2) 3)", "( 1 2 3 )",
|
"(ap (1 2) 3)", "( 1 2 3 )",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(prepend, "pre",
|
tfn(prepend, "pre",
|
||||||
({ isListy, NULL, isListy }),
|
({ expect(isListy), anyType, returns(isListy) }),
|
||||||
"Prepend the given element. Creates a new list",
|
"Prepend the given element. Creates a new list",
|
||||||
"(pre (2 3) 1)", "( 1 2 3 )",
|
"(pre (2 3) 1)", "( 1 2 3 )",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(len, "len",
|
tfn(len, "len",
|
||||||
({ isListy, isNumber }),
|
({ expect(isListy), returns(isNumber) }),
|
||||||
"Returns the length of the given list, or an error if the expression is not a list.",
|
"Returns the length of the given list, or an error if the expression is not a list.",
|
||||||
"(len (2 3))", "2",
|
"(len (2 3))", "2",
|
||||||
"(len ())", "0",
|
"(len ())", "0",
|
||||||
"(len \"string\")", "BAD_PARAMS_ON: len",
|
"(len \"string\")", "BAD_PARAMS;",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(reduce, "reduce",
|
tfn(reduce, "reduce",
|
||||||
({ NULL, isFuncy, NULL, NULL }),
|
({ anyType, expect(isFuncy), anyType, anyType }),
|
||||||
"Performs a simple reduction. Does not currently work with lambdas.\n"
|
"Performs a simple reduction. Does not currently work with lambdas.\n"
|
||||||
"Takes three arguments:\n"
|
"Takes three arguments:\n"
|
||||||
" - Values\n"
|
" - Values\n"
|
||||||
|
@ -112,30 +112,30 @@ tfn(reduce, "reduce",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(at, "at",
|
tfn(at, "at",
|
||||||
({ isNumber, isListy, NULL }),
|
({ expect(isNumber), expect(isListy), anyType }),
|
||||||
"Get item at the given index in the given list.",
|
"Get item at the given index in the given list.",
|
||||||
"(at 1 (1 2 3))", "2",
|
"(at 1 (1 2 3))", "2",
|
||||||
"(at 99 (1 2 3))", "INDEX_PAST_END",
|
"(at 99 (1 2 3))", "INDEX_PAST_END",
|
||||||
"(at 99 \"string\")", "BAD_PARAMS_ON: at",
|
"(at 99 \"string\")", "BAD_PARAMS;",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(rest, "rest",
|
tfn(rest, "rest",
|
||||||
({ isListy, isListy }),
|
({ expect(isListy), returns(isListy) }),
|
||||||
"Get the tail of a list. All but the first element.",
|
"Get the tail of a list. All but the first element.",
|
||||||
"(rest (1 2 3))", "( 2 3 )",
|
"(rest (1 2 3))", "( 2 3 )",
|
||||||
"(rest ())", "( )",
|
"(rest ())", "( )",
|
||||||
"(rest \"string\")", "BAD_PARAMS_ON: rest",
|
"(rest \"string\")", "BAD_PARAMS;",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(reverse, "rev",
|
tfn(reverse, "rev",
|
||||||
({ isListy, isListy }),
|
({ expect(isListy), returns(isListy) }),
|
||||||
"Reverse a list.",
|
"Reverse a list.",
|
||||||
"(rev (1 2 3))", "( 3 2 1 )",
|
"(rev (1 2 3))", "( 3 2 1 )",
|
||||||
"(rev \"string\")", "BAD_PARAMS_ON: reverse",
|
"(rev \"string\")", "BAD_PARAMS;",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(isNum, "isnum",
|
tfn(isNum, "isnum",
|
||||||
({ NULL, isBool }),
|
({ anyType, returns(isBool) }),
|
||||||
"Returns `T` only if the argument evaluates to a number.",
|
"Returns `T` only if the argument evaluates to a number.",
|
||||||
"(isnum 1)", "T",
|
"(isnum 1)", "T",
|
||||||
"(isnum (+ 5 5))", "T",
|
"(isnum (+ 5 5))", "T",
|
||||||
|
@ -144,7 +144,7 @@ tfn(isNum, "isnum",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(isList, "islist",
|
tfn(isList, "islist",
|
||||||
({ NULL, isBool }),
|
({ anyType, returns(isBool) }),
|
||||||
"Returns `T` only if the argument is a list.",
|
"Returns `T` only if the argument is a list.",
|
||||||
"(islist (1 2 3))", "T",
|
"(islist (1 2 3))", "T",
|
||||||
"(islist ())", "T",
|
"(islist ())", "T",
|
||||||
|
@ -152,7 +152,7 @@ tfn(isList, "islist",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(isString, "isstr",
|
tfn(isString, "isstr",
|
||||||
({ NULL, isBool }),
|
({ anyType, returns(isBool) }),
|
||||||
"Returns `T` only if the argument is a string.",
|
"Returns `T` only if the argument is a string.",
|
||||||
"(isstr \"Heyo\")", "T",
|
"(isstr \"Heyo\")", "T",
|
||||||
"(isstr \"\")", "T",
|
"(isstr \"\")", "T",
|
||||||
|
@ -161,21 +161,21 @@ tfn(isString, "isstr",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(isErr, "iserr",
|
tfn(isErr, "iserr",
|
||||||
({ NULL, isBool }),
|
({ anyType, returns(isBool) }),
|
||||||
"Check if the argument is an error.",
|
"Check if the argument is an error.",
|
||||||
"(iserr (at 10 ()))", "T",
|
"(iserr (at 10 ()))", "T",
|
||||||
"(iserr 5)", "F",
|
"(iserr 5)", "F",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(charAt, "chat",
|
tfn(charAt, "chat",
|
||||||
({ isStringy, isNumber, isStringy }),
|
({ expect(isStringy), expect(isNumber), returns(isStringy) }),
|
||||||
"Get the char in the given string at the given index.",
|
"Get the char in the given string at the given index.",
|
||||||
"(chat \"Hello\" 1)", "e",
|
"(chat \"Hello\" 1)", "e",
|
||||||
"(chat \"Hello\" 10)", "",
|
"(chat \"Hello\" 10)", "",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(charVal, "char",
|
tfn(charVal, "char",
|
||||||
({ isStringy, isNumber }),
|
({ expect(isStringy), returns(isNumber) }),
|
||||||
"Get the ascii integer representaton of the given character.",
|
"Get the ascii integer representaton of the given character.",
|
||||||
"(char \"h\")", "104",
|
"(char \"h\")", "104",
|
||||||
"(char \"hello\")", "104",
|
"(char \"hello\")", "104",
|
||||||
|
@ -183,21 +183,21 @@ tfn(charVal, "char",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(slen, "slen",
|
tfn(slen, "slen",
|
||||||
({ isStringy, isNumber }),
|
({ expect(isStringy), returns(isNumber) }),
|
||||||
"Returns the length of the given string",
|
"Returns the length of the given string",
|
||||||
"(slen \"string\")", "6",
|
"(slen \"string\")", "6",
|
||||||
"(slen \"\")", "0",
|
"(slen \"\")", "0",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(chars, "chars",
|
tfn(chars, "chars",
|
||||||
({ isStringy, isListy }),
|
({ expect(isStringy), returns(isListy) }),
|
||||||
"Get a list of all chars in the given string",
|
"Get a list of all chars in the given string",
|
||||||
"(chars \"hello\")", "( h e l l o )",
|
"(chars \"hello\")", "( h e l l o )",
|
||||||
"(chars \"\")", "( )",
|
"(chars \"\")", "( )",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(matches, "matches",
|
tfn(matches, "matches",
|
||||||
({ isStringy, isStringy, isBool }),
|
({ expect(isStringy), expect(isStringy), returns(isBool) }),
|
||||||
"Check that a string matches a basic wildcard pattern\n"
|
"Check that a string matches a basic wildcard pattern\n"
|
||||||
"Note: Currently there is no way to match a literal asterisk",
|
"Note: Currently there is no way to match a literal asterisk",
|
||||||
"(matches \"Hiya\" \"Hiya\")", "T",
|
"(matches \"Hiya\" \"Hiya\")", "T",
|
||||||
|
@ -214,11 +214,11 @@ tfn(matches, "matches",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(substring, "substr",
|
tfn(substring, "substr",
|
||||||
({ isNumber, isNumber, isStringy, isStringy }),
|
({ expect(isNumber), expect(isNumber), expect(isStringy), returns(isStringy) }),
|
||||||
"Get a substring from the given string.",
|
"Get a substring from the given string.",
|
||||||
"(substr 1 3 \"Hello\")", "el",
|
"(substr 1 3 \"Hello\")", "el",
|
||||||
"(substr 99 3 \"Hello\")", "BAD_PARAMS_ON: Hello;",
|
"(substr 99 3 \"Hello\")", "BAD_PARAMS;",
|
||||||
"(substr 98 99 \"Hello\")", "BAD_PARAMS_ON: Hello;",
|
"(substr 98 99 \"Hello\")", "BAD_PARAMS;",
|
||||||
"(substr 0 1 \"\")", "",
|
"(substr 0 1 \"\")", "",
|
||||||
"(substr 0 99 \"Hey\")", "Hey",
|
"(substr 0 99 \"Hey\")", "Hey",
|
||||||
"(substr 1 99 \"Heyyyy\")", "eyyyy",
|
"(substr 1 99 \"Heyyyy\")", "eyyyy",
|
||||||
|
@ -232,7 +232,7 @@ fn(parseEvalO, "eval",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(getTime, "time",
|
tfn(getTime, "time",
|
||||||
({ isStruct }),
|
({ returns(isStruct) }),
|
||||||
"Get a struct of the current time with fields (minute hour sec)."
|
"Get a struct of the current time with fields (minute hour sec)."
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -241,7 +241,7 @@ tfn(getTime, "time",
|
||||||
fn(print, "prn", "Prints the string representation of the given object to stdout.");
|
fn(print, "prn", "Prints the string representation of the given object to stdout.");
|
||||||
|
|
||||||
tfn(numToChar, "ch",
|
tfn(numToChar, "ch",
|
||||||
({ isNumber, isStringy }),
|
({ expect(isNumber), returns(isStringy) }),
|
||||||
"Gets a string containing the ascii character for the given number value.",
|
"Gets a string containing the ascii character for the given number value.",
|
||||||
"(ch 107)", "k",
|
"(ch 107)", "k",
|
||||||
"(ch 0x21)", "!",
|
"(ch 0x21)", "!",
|
||||||
|
@ -255,14 +255,14 @@ fn(printEnvO, "penv",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(systemCall, "sys",
|
tfn(systemCall, "sys",
|
||||||
({ isStringy, isNumber }),
|
({ expect(isStringy), returns(isNumber) }),
|
||||||
"Opens a shell and runs the given command, returning 0 if successful.\n"
|
"Opens a shell and runs the given command, returning 0 if successful.\n"
|
||||||
"If the argument is not a string, returns 255.\n",
|
"If the argument is not a string, returns 255.\n",
|
||||||
"(sys \"echo yee > /dev/null\")", "0",
|
"(sys \"echo yee > /dev/null\")", "0",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(loadFile, "loadfile",
|
tfn(loadFile, "loadfile",
|
||||||
({ isStringy, NULL }),
|
({ expect(isStringy), anyType }),
|
||||||
"Loads and parses the given file.\n"
|
"Loads and parses the given file.\n"
|
||||||
"Returns 0 if the file was loaded and parsed successfully. Otherwise 1.\n"
|
"Returns 0 if the file was loaded and parsed successfully. Otherwise 1.\n"
|
||||||
"(loadfile \"printdate.pl\")\n"
|
"(loadfile \"printdate.pl\")\n"
|
||||||
|
@ -271,12 +271,12 @@ tfn(loadFile, "loadfile",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(cd, "cd",
|
tfn(cd, "cd",
|
||||||
({ isStringy, NULL }),
|
({ expect(isStringy), anyType }),
|
||||||
"Change the current directory."
|
"Change the current directory."
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(cwd, "cwd",
|
tfn(cwd, "cwd",
|
||||||
({ isStringy }),
|
({ returns(isStringy) }),
|
||||||
"Get the current directory."
|
"Get the current directory."
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -290,12 +290,12 @@ fn(takeInput, "inp",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(readFileToObject, "rf",
|
tfn(readFileToObject, "rf",
|
||||||
({ isStringy, isStringy }),
|
({ expect(isStringy), returns(isStringy) }),
|
||||||
"Read a file into a string object."
|
"Read a file into a string object."
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(getEnvVar, "env",
|
tfn(getEnvVar, "env",
|
||||||
({ isStringy, isStringy }),
|
({ expect(isStringy), returns(isStringy) }),
|
||||||
"Get a variable from the current environment\n"
|
"Get a variable from the current environment\n"
|
||||||
"(env HOME) => /home/sagevaillancourt"
|
"(env HOME) => /home/sagevaillancourt"
|
||||||
);
|
);
|
||||||
|
|
|
@ -65,13 +65,13 @@ check() {
|
||||||
local exit_code=0
|
local exit_code=0
|
||||||
if $VALGRIND; then
|
if $VALGRIND; then
|
||||||
echo -ne "\n $1"
|
echo -ne "\n $1"
|
||||||
output="$($VALCOM ./pl "(loadfile \"examples/lib.pbl\") $2")"
|
output="$($VALCOM ./pl --ignore-config "(loadfile \"examples/lib.pbl\") $2")"
|
||||||
exit_code=$?
|
exit_code=$?
|
||||||
if [[ "$exit_code" == "0" ]]; then
|
if [[ "$exit_code" == "0" ]]; then
|
||||||
echo -ne "\r "
|
echo -ne "\r "
|
||||||
fi
|
fi
|
||||||
else
|
else
|
||||||
output="$(./pl "(loadfile \"examples/lib.pbl\") $2")"
|
output="$(./pl --ignore-config "(loadfile \"examples/lib.pbl\") $2")"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
@ -217,7 +217,7 @@ check "FuncReturningAFunc" "(def plusser (fn (outer) (fn (inner) (+ outer inner)
|
||||||
(plusFive 10)" "15"
|
(plusFive 10)" "15"
|
||||||
|
|
||||||
title "ShouldError"
|
title "ShouldError"
|
||||||
check "LenOfNotList" "(len 5)" regex "BAD_PARAMS_ON.*"
|
check "LenOfNotList" "(len 5)" regex "BAD_PARAMS.*"
|
||||||
check "NoMapList" "(map sq)" "NULL_MAP_ARGS"
|
check "NoMapList" "(map sq)" "NULL_MAP_ARGS"
|
||||||
check "BadNumber" "(5df)" regex "BAD_NUMBER.*"
|
check "BadNumber" "(5df)" regex "BAD_NUMBER.*"
|
||||||
check "BadHex" "(0x0zf)" regex "BAD_NUMBER.*"
|
check "BadHex" "(0x0zf)" regex "BAD_NUMBER.*"
|
||||||
|
|
|
@ -25,6 +25,11 @@ int isDone(struct Promise* promise)
|
||||||
return promise->done;
|
return promise->done;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int isPromise(Object src)
|
||||||
|
{
|
||||||
|
return src.type == TYPE_PROMISE;
|
||||||
|
}
|
||||||
|
|
||||||
void cleanPromise(struct Promise* promise)
|
void cleanPromise(struct Promise* promise)
|
||||||
{
|
{
|
||||||
promise->refs -= 1;
|
promise->refs -= 1;
|
||||||
|
@ -36,6 +41,7 @@ void cleanPromise(struct Promise* promise)
|
||||||
|
|
||||||
Object await(Object* params, int length, struct Environment* env)
|
Object await(Object* params, int length, struct Environment* env)
|
||||||
{
|
{
|
||||||
|
checkTypes(await)
|
||||||
struct Promise* promise = params[0].promise;
|
struct Promise* promise = params[0].promise;
|
||||||
if (!promise->done) { // TODO: Does `done` need a mutex or other lock?
|
if (!promise->done) { // TODO: Does `done` need a mutex or other lock?
|
||||||
pthread_join(promise->thread, NULL);
|
pthread_join(promise->thread, NULL);
|
||||||
|
@ -67,7 +73,7 @@ Object async(Object* params, int length, struct Environment* env)
|
||||||
.refs = 2,
|
.refs = 2,
|
||||||
.done = 0,
|
.done = 0,
|
||||||
.env = env,
|
.env = env,
|
||||||
.object = params[0], // TODO: Clone?
|
.object = cloneObject(params[0]),
|
||||||
};
|
};
|
||||||
env->refs += 1;
|
env->refs += 1;
|
||||||
|
|
||||||
|
|
|
@ -11,13 +11,16 @@ void cleanPromise(struct Promise* promise);
|
||||||
|
|
||||||
int isDone(struct Promise* promise);
|
int isDone(struct Promise* promise);
|
||||||
|
|
||||||
|
int isPromise(Object src);
|
||||||
|
|
||||||
fn(async, "async",
|
fn(async, "async",
|
||||||
"Run the given lambda on a separate thread, returning a promise.",
|
"Run the given lambda on a separate thread, returning a promise.",
|
||||||
"(def sleepy (fn () ((sys \"sleep 0.01\") \"Hiya\"))) (def x (async sleepy)) x", "<PENDING>",
|
"(def sleepy (fn () ((sys \"sleep 0.01\") \"Hiya\"))) (def x (async sleepy)) x", "<PENDING>",
|
||||||
"(def sleepy (fn () ((sys \"sleep 0.01\") \"Hiya\"))) (def x (async sleepy)) (await x)", "Hiya",
|
"(def sleepy (fn () ((sys \"sleep 0.01\") \"Hiya\"))) (def x (async sleepy)) (await x)", "Hiya",
|
||||||
);
|
);
|
||||||
|
|
||||||
fn(await, "await",
|
tfn(await, "await",
|
||||||
|
({ expect(isPromise), anyType }),
|
||||||
"Waits for a promise to resolve before proceeding.",
|
"Waits for a promise to resolve before proceeding.",
|
||||||
"(def sleepy (fn () ((sys \"sleep 0.01\") \"Hiya\"))) (def x (async sleepy)) (await x)", "Hiya",
|
"(def sleepy (fn () ((sys \"sleep 0.01\") \"Hiya\"))) (def x (async sleepy)) (await x)", "Hiya",
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue