Add some runtime type-checking.

Adjust simple-ops structure.
This commit is contained in:
Sage Vaillancourt 2022-03-28 13:19:34 -04:00 committed by Sage Vaillancourt
parent e22e022cd8
commit 66a07e395c
8 changed files with 276 additions and 225 deletions

View File

@ -393,8 +393,8 @@ struct Environment defaultEnv()
{"/", &dvi},
{"%", &mod},
{"=", &equ},
{">", &gth},
{"<", &lth},
{">", &greaterThan},
{"<", &lessThan},
{"&", &and},
{"|", &or},
pf("cat", catObjects),

View File

@ -178,7 +178,7 @@ static const char* errorText[] = {"MISMATCHED_PARENS",
"LAMBDA_ARGS_NOT_LIST",
"DID_NOT_FIND_SYMBOL",
"BAD_TYPE",
"LISTS_NOT_SAME_SIZE",
"BAD_PARAMS_ON",
"BAD_NUMBER",
"UNSUPPORTED_NUMBER_TYPE",
"NOT_ENOUGH_ARGUMENTS",
@ -299,7 +299,7 @@ int stringNObj(char* dest, const Object* obj, const size_t len)
dest += stringf(dest, len, "E[%d]", (int)code);
#else
if (obj->error->context && obj->error->context[0] != '\0') {
dest += stringf(dest, len, "'%s': %s", errorText[code],
dest += stringf(dest, len, "%s: %s", errorText[code],
obj->error->context);
} else if (code >= 0 && code <= INDEX_PAST_END) {
dest += stringf(dest, len, "%s", errorText[code]);
@ -602,6 +602,11 @@ inline Object startList(const Object start)
return list;
}
inline int isNumber(const Object test)
{
return test.type == TYPE_NUMBER;
}
inline int isListy(const Object test)
{
return test.type == TYPE_LIST || test.type == TYPE_SLIST;
@ -612,6 +617,16 @@ inline int isStringy(const Object test)
return test.type == TYPE_STRING || test.type == TYPE_SYMBOL;
}
inline int isBool(const Object test)
{
return test.type == TYPE_BOOL;
}
inline int isFuncy(const Object test)
{
return test.type == TYPE_LAMBDA || test.type == TYPE_FUNC;
}
inline int isValidType(const Object test)
{
switch (test.type) {
@ -823,7 +838,11 @@ inline enum errorCode getErrorCode(const Object obj)
inline void errorAddContext(Object* o, const char* context, int lineNo, const char* fileName)
{
o->error->context = calloc(sizeof(char), RESULT_LENGTH);
sprintf(o->error->context, "%s %s:%d", context, fileName, lineNo);
char* cursor = o->error->context;
cursor += sprintf(cursor, "%s", context);
if (fileName) {
sprintf(cursor, " %s:%d", fileName, lineNo);
}
}
inline Object errorWithContextLineNo(enum errorCode code, const char* context, int lineNo, const char* fileName)

View File

@ -25,13 +25,6 @@
_element = _element->forward)
#define POINTER _element
#define FOR_POINTERS_IN_LISTS(_list, _list2) \
for(Object *_element = (_list)->list, *_element2 = (_list2)->list; \
_element != NULL && _element2 != NULL; \
_element = _element->forward, _element2 = _element2->forward)
#define P1 POINTER
#define P2 _element2
#ifdef PBL_PLATFORM_APLITE
#define LOW_MEM
#endif
@ -50,7 +43,7 @@ enum errorCode {
LAMBDA_ARGS_NOT_LIST,
DID_NOT_FIND_SYMBOL,
BAD_TYPE,
LISTS_NOT_SAME_SIZE,
BAD_PARAMS_ON,
BAD_NUMBER,
UNSUPPORTED_NUMBER_TYPE,
NOT_ENOUGH_ARGUMENTS,
@ -169,10 +162,16 @@ void allocObject(Object** spot, Object src);
void appendList(Object* dest, const Object* src);
int isNumber(Object test);
int isListy(Object test);
int isStringy(Object test);
int isBool(Object test);
int isFuncy(Object test);
int isValidType(Object test);
int isError(Object obj, enum errorCode err);

View File

@ -714,8 +714,8 @@ int main(int argc, const char* argv[])
}
deleteEnv(&env);
shredDictionary();
// fprintf(stderr, "TOTAL ALLOCATIONS: %d\n", getAllocations());
// fprintf(stderr, "TOTAL BYTES: %zu\n", getBytes());
// fprintf(stderr, "\nHEAP-ALLOCATED OBJECTS: %d\n", getAllocations());
// fprintf(stderr, "TOTAL OBJECT.C ALLOC: %zu\n", getBytes());
}
#endif

View File

@ -18,8 +18,9 @@ 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."); \
Object _name(Object* params, int length, struct Environment* env)
// GCC warns without the attribute, even when typeChecks are used
#define tfn(_name, _type, _docs, ...) \
static const Type _name ## Type[] = UNPACK _type; \
__attribute__((unused)) static int (*_name ## TypeChecks[])(Object) = UNPACK _type; \
fn(_name, _docs, __VA_ARGS__)
#define fnn(_name, _symbol, _docs, ...) \

View File

@ -3,8 +3,33 @@
#include "plfunc.h"
Object typeCheck(const char* funcName, Object* params, int length, int (*typeChecks[])(Object), int typeLength, int* failed)
{
*failed = 1;
if ((typeLength - 1) > length ) {
return errorObject(NOT_ENOUGH_ARGUMENTS);
}
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.
return /*errorObject(BAD_TYPE); */ errorWithContextLineNo(BAD_PARAMS_ON, funcName, 0, NULL);
}
}
*failed = 0;
return numberObject(0);
}
#ifndef DISABLE_TYPE_CHECKS
#define checkTypes(FUNC) int FAILED; Object ERROR = typeCheck(#FUNC, params, length, FUNC ## TypeChecks, array_length(FUNC ## TypeChecks), &FAILED); \
if (FAILED) { \
return ERROR; \
}
#else
#define checkTypes(FUNC) ;
#endif
Object reduce(Object* params, int length, struct Environment* env)
{
checkTypes(reduce)
Object list = params[0];
const Object func = params[1];
Object total = params[2];
@ -22,6 +47,7 @@ Object reduce(Object* params, int length, struct Environment* env)
Object charAt(Object* params, int length, struct Environment* env)
{
checkTypes(charAt)
Object string = params[0];
Object at = params[1];
@ -41,6 +67,7 @@ Object charAt(Object* params, int length, struct Environment* env)
Object filter(Object* params, int length, struct Environment* env)
{
checkTypes(filter)
Object condition = params[0];
Object list = params[1];
@ -61,6 +88,7 @@ Object filter(Object* params, int length, struct Environment* env)
Object append(Object* params, int length, struct Environment* env)
{
checkTypes(append)
Object list = params[0];
Object newElement = params[1];
@ -71,6 +99,7 @@ Object append(Object* params, int length, struct Environment* env)
Object prepend(Object* params, int length, struct Environment* env)
{
checkTypes(prepend)
Object list = params[0];
Object newElement = params[1];
@ -82,6 +111,7 @@ Object prepend(Object* params, int length, struct Environment* env)
Object at(Object* params, int length, struct Environment* env)
{
checkTypes(at)
Object index = params[0];
Object list = params[1];
@ -95,6 +125,7 @@ Object at(Object* params, int length, struct Environment* env)
Object rest(Object* params, int length, struct Environment* env)
{
checkTypes(rest)
Object list = params[0];
if (!isListy(list)) {
return errorObject(NOT_A_LIST);
@ -113,11 +144,9 @@ Object rest(Object* params, int length, struct Environment* env)
Object reverse(Object* params, int length, struct Environment* ignore2)
{
checkTypes(reverse)
Object _list = params[0];
if (!isListy(_list)) {
return errorObject(NOT_A_LIST);
}
const Object* list = &_list;
Object rev = listObject();
@ -136,6 +165,7 @@ Object reverse(Object* params, int length, struct Environment* ignore2)
Object isNum(Object* params, int length, struct Environment* env)
{
checkTypes(isNum)
Object test = params[0];
return test.type == TYPE_NUMBER ? boolObject(1) : boolObject(0);
@ -143,6 +173,7 @@ Object isNum(Object* params, int length, struct Environment* env)
Object isList(Object* params, int length, struct Environment* env)
{
checkTypes(isList)
Object test = params[0];
return test.type == TYPE_LIST ? boolObject(1) : boolObject(0);
@ -150,6 +181,7 @@ Object isList(Object* params, int length, struct Environment* env)
Object isString(Object* params, int length, struct Environment* env)
{
checkTypes(isString)
Object test = params[0];
return test.type == TYPE_STRING ? boolObject(1) : boolObject(0);
@ -157,6 +189,7 @@ Object isString(Object* params, int length, struct Environment* env)
Object charVal(Object* params, int length, struct Environment* env)
{
checkTypes(charVal)
Object test = params[0];
return numberObject(test.string[0]);
@ -164,6 +197,7 @@ Object charVal(Object* params, int length, struct Environment* env)
Object isErr(Object* params, int length, struct Environment* env)
{
checkTypes(isErr)
Object test = params[0];
return test.type == TYPE_ERROR ? boolObject(1) : boolObject(0);
@ -189,17 +223,11 @@ Object parseEvalO(Object* params, int length, struct Environment* env)
}
}
Object listEquality(const Object* list1, const Object* list2)
{
FOR_POINTERS_IN_LISTS(list1, list2) {
if (P1->type != P2->type || P1->number != P2->number) {
return boolObject(0);
}
}
return boolObject(1);
}
#ifdef STANDALONE
#define CAT_MAX 1024
#else
#define CAT_MAX 64
#endif
Object _catObjects(Object obj1, Object obj2, struct Environment* env)
{
@ -222,6 +250,8 @@ Object _catObjects(Object obj1, Object obj2, struct Environment* env)
Object catObjects(Object* params, int length, struct Environment* env)
{
checkTypes(catObjects)
Object string = stringFromSlice("", 0);
if (length == 0) {
return string;
@ -234,89 +264,12 @@ Object catObjects(Object* params, int length, struct Environment* env)
return string;
}
Object _basicOp(const Object* obj1, const Object* obj2, const char op,
struct Environment* env)
{
const int n1 = obj1->number;
const int n2 = obj2->number;
switch (op) {
case '&':
return boolObject(n1 != 0 && n2 != 0);
case '|':
return boolObject(n1 != 0 || n2 != 0);
case '=':
if (bothAre(TYPE_STRING, obj1, obj2)) {
return boolObject(!strcmp(obj1->string, obj2->string));
}
if (bothAre(TYPE_LIST, obj1, obj2)) {
return listEquality(obj1, obj2);
}
return boolObject(n1 == n2 && areSameType(obj1, obj2));
case '>':
return boolObject(n1 > n2);
case '<':
return boolObject(n1 < n2);
default:
return *obj1;
}
}
Object basicOp(const Object* obj1, const Object* obj2, const char op,
struct Environment* env)
{
if (isError(*obj2, NOT_ENOUGH_ARGUMENTS)) {
return *obj2;
}
int lists = (obj1->type == TYPE_LIST) + (obj2->type == TYPE_LIST);
if (lists == 0) {
return _basicOp(obj1, obj2, op, env);
} else if (lists == 1) { // Single operand is applied to each element in list
const Object* listObj = (obj1->type == TYPE_LIST) ? obj1 : obj2;
const Object* singleObj = (obj1->type == TYPE_LIST) ? obj2 : obj1;
Object newList = listObject();
FOR_POINTER_IN_LIST(listObj) {
Object adding = eval(POINTER, env);
nf_addToList(&newList, _basicOp(&adding, singleObj, op, env));
}
return newList;
} else { // 2 lists with the op applied to matching indices of both lists
if (listLength(obj1) == listLength(obj2)) {
Object newList = listObject();
FOR_POINTERS_IN_LISTS(obj1, obj2) {
const Object ev1 = eval(P1, env);
const Object ev2 = eval(P2, env);
nf_addToList(&newList, _basicOp(&ev1, &ev2, op, env));
}
return newList;
} else {
return errorObject(LISTS_NOT_SAME_SIZE);
}
}
}
Object len(Object* params, int length, struct Environment* env)
{
Object obj1 = params[0];
checkTypes(len)
if (!isListy(obj1)) {
return errorObject(NOT_A_LIST);
}
Object o = numberObject(listLength(&obj1));
return o;
}
#define BASIC_OP(_name, _char) \
Object _name(Object* params, int length, struct Environment* env) \
{ \
Object obj1 = params[0]; \
Object obj2 = params[1]; \
return basicOp(&obj1, &obj2, _char, env); \
return numberObject(listLength(&params[0]));
}
#define BASIC_MATH(_name, _op) \
@ -342,17 +295,86 @@ BASIC_MATH(dvi, /=)
BASIC_MATH(mod, %=)
BASIC_OP(equ, '=')
int areEqual(const Object* obj1, const Object* obj2);
BASIC_OP(gth, '>')
int listEquality(const Object* list1, const Object* list2)
{
Object* element1, *element2;
for (element1 = (list1)->list, element2 = (list2)->list;
element1 != ((void*) 0) && element2 != ((void*) 0);
element1 = element1->forward, element2 = element2->forward) {
if (!areEqual(element1, element2)) {
return 0;
}
}
return (element1 == NULL && element2 == NULL);
}
BASIC_OP(lth, '<')
int areEqual(const Object* obj1, const Object* obj2)
{
const int n1 = obj1->number;
const int n2 = obj2->number;
BASIC_OP(and, '&')
if (bothAre(TYPE_STRING, obj1, obj2)) {
return !strcmp(obj1->string, obj2->string);
}
if (bothAre(TYPE_LIST, obj1, obj2)) {
return listEquality(obj1, obj2);
}
return n1 == n2 && areSameType(obj1, obj2);
}
BASIC_OP(or, '|')
Object equ(Object* params, int length, struct Environment* env)
{
if (length < 2) {
return errorObject(NOT_ENOUGH_ARGUMENTS);
}
int bool = 1;
for (int i = 0; i < length - 1; i++) {
if (!areEqual(&params[i], &params[i + 1])) {
bool = 0;
break;
}
}
return boolObject(bool);
}
#undef BASIC_OP
Object or(Object* params, int length, struct Environment* env)
{
if (length < 2) {
return errorObject(NOT_ENOUGH_ARGUMENTS);
}
int bool = 0;
for (int i = 0; i < length - 1; i++) {
if (params[i].number || params[i + 1].number) {
bool = 1;
break;
}
}
return boolObject(bool);
}
#define BASIC_COMPARISON(NAME, OP)\
Object NAME(Object* params, int length, struct Environment* env) \
{ \
if (length < 2) { \
return errorObject(NOT_ENOUGH_ARGUMENTS); \
} \
int bool = 1; \
for (int i = 0; i < length - 1; i++) { \
if (!(params[i].number OP params[i + 1].number)) { \
bool = 0; \
break; \
} \
} \
return boolObject(bool); \
}
BASIC_COMPARISON(greaterThan, >)
BASIC_COMPARISON(lessThan, <)
BASIC_COMPARISON(and, &&)
#ifdef STANDALONE
@ -366,6 +388,7 @@ Object print(Object* params, int length, struct Environment* env)
Object numToChar(Object* params, int length, struct Environment* env)
{
checkTypes(numToChar)
Object c = params[0];
if (c.type != TYPE_NUMBER) {
@ -396,6 +419,7 @@ Object takeInput(Object* params, int length, struct Environment* env)
Object loadFile(Object* params, int length, struct Environment* env)
{
checkTypes(loadFile)
Object filename = params[0];
if (isStringy(filename)) {
@ -407,6 +431,7 @@ Object loadFile(Object* params, int length, struct Environment* env)
Object systemCall(Object* params, int length, struct Environment* env)
{
checkTypes(systemCall)
Object process = params[0];
if (isStringy(process)) {
@ -430,8 +455,9 @@ char* readFileToString(FILE* input)
size_t capacity = 128;
char* string = malloc(sizeof(char) * capacity);
int c;
int i = 0;
int i = 1; // Skip refCount
string[0] = 1; // Set refCount
while ((c = fgetc(input)) != EOF) {
string[i] = c;
i++;
@ -439,16 +465,19 @@ char* readFileToString(FILE* input)
char* prev = string;
capacity *= 2;
string = malloc(sizeof(char) * capacity);
memcpy(string, prev, sizeof(char) * capacity / 2);
string += 1;
memcpy(string, prev, sizeof(char) * (capacity / 2));
free(prev);
}
}
string[i] = '\0';
return string;
return string + 1; // Offset past refCount
}
Object readFileToObject(Object* params, int length, struct Environment* env)
{
checkTypes(readFileToObject)
Object filename = params[0];
if (filename.type != TYPE_STRING) {

View File

@ -18,9 +18,9 @@ BASIC_OP(mod);
BASIC_OP(equ);
BASIC_OP(gth);
BASIC_OP(greaterThan);
BASIC_OP(lth);
BASIC_OP(lessThan);
BASIC_OP(and);
@ -28,119 +28,119 @@ BASIC_OP(or);
#undef BASIC_OP
/// ANY => STRING
fn(catObjects,
"Concatenate string versions of the given objects.",
"(cat \"Stuff: \" (1 2 3))", "Stuff: ( 1 2 3 )",
tfn(catObjects,
({ NULL, isStringy }),
"Concatenate string versions of the given objects.",
"(cat \"Stuff: \" (1 2 3))", "Stuff: ( 1 2 3 )",
);
/// FUNCY, LIST => LIST
fn(filter,
"Filter a list based on the given condition.",
"(fil (fn (a) (< 50 a)) (25 60 100))", "( 60 100 )",
"(fil (fn (a) (< 0 (len a))) ( () (1) (1 2) () ))", "( ( 1 ) ( 1 2 ) )",
tfn(filter,
({ isFuncy, isListy, isListy }),
"Filter a list based on the given condition.",
"(fil (fn (a) (< 50 a)) (25 60 100))", "( 60 100 )",
"(fil (fn (a) (< 0 (len a))) ( () (1) (1 2) () ))", "( ( 1 ) ( 1 2 ) )",
);
/// LIST, ANY => LIST
fn(append,
"Append the given element. Creates a new list.",
"(ap (1 2) 3)", "( 1 2 3 )",
tfn(append,
({ isListy, NULL, isListy }),
"Append the given element. Creates a new list.",
"(ap (1 2) 3)", "( 1 2 3 )",
);
/// LIST, ANY => LIST
fn(prepend,
"Prepend the given element. Creates a new list",
"(pre (2 3) 1)", "( 1 2 3 )",
tfn(prepend,
({ isListy, NULL, isListy }),
"Prepend the given element. Creates a new list",
"(pre (2 3) 1)", "( 1 2 3 )",
);
tfn(len,
({ TYPE_LIST, TYPE_NUMBER }),
({ isListy, isNumber }),
"Returns the length of the given list, or a NOT_A_LIST error if the expression is not a list.",
"(len (2 3))", "2",
"(len ())", "0",
"(len \"string\")", "NOT_A_LIST",
"(len \"string\")", "BAD_PARAMS_ON: len",
);
/// LIST, FUNCY, ANY => ANY
fn(reduce,
"Performs a simple reduction. Does not currently work with lambdas.\n"
"Takes three arguments:\n"
" - Values\n"
" - A function to apply to each value\n"
" - An initial value",
"(reduce 5 + 6)", "11",
"(reduce (1 2 3) + 0)", "6",
tfn(reduce,
({ NULL, isFuncy, NULL, NULL }),
"Performs a simple reduction. Does not currently work with lambdas.\n"
"Takes three arguments:\n"
" - Values\n"
" - A function to apply to each value\n"
" - An initial value",
"(reduce 5 + 6)", "11",
"(reduce (1 2 3) + 0)", "6",
);
/// NUMBER, LIST => ANY
fn(at,
"Get item at the given index in the given list.",
"(at 1 (1 2 3))", "2",
"(at 99 (1 2 3))", "INDEX_PAST_END",
"(at 99 \"string\")", "INDEX_PAST_END",
tfn(at,
({ isNumber, isListy, NULL }),
"Get item at the given index in the given list.",
"(at 1 (1 2 3))", "2",
"(at 99 (1 2 3))", "INDEX_PAST_END",
"(at 99 \"string\")", "BAD_PARAMS_ON: at",
);
/// LIST => LIST
fn(rest,
"Get the tail of a list. All but the first element.",
"(rest (1 2 3))", "( 2 3 )",
"(rest ())", "( )",
"(rest \"string\")", "NOT_A_LIST",
tfn(rest,
({ isListy, isListy }),
"Get the tail of a list. All but the first element.",
"(rest (1 2 3))", "( 2 3 )",
"(rest ())", "( )",
"(rest \"string\")", "BAD_PARAMS_ON: rest",
);
/// LIST => LIST
fn(reverse,
"Reverse a list.",
"(rev (1 2 3))", "( 3 2 1 )",
"(rev \"string\")", "NOT_A_LIST",
tfn(reverse,
({ isListy, isListy }),
"Reverse a list.",
"(rev (1 2 3))", "( 3 2 1 )",
"(rev \"string\")", "BAD_PARAMS_ON: reverse",
);
/// ANY => BOOL
fn(isNum,
"Returns `T` only if the argument evaluates to a number.",
"(isnum 1)", "T",
"(isnum (+ 5 5))", "T",
"(isnum '(+ 5 5))", "F",
"(isnum \"Hello\")", "F",
tfn(isNum,
({ NULL, isBool }),
"Returns `T` only if the argument evaluates to a number.",
"(isnum 1)", "T",
"(isnum (+ 5 5))", "T",
"(isnum '(+ 5 5))", "F",
"(isnum \"Hello\")", "F",
);
/// ANY => BOOL
fn(isList,
"Returns `T` only if the argument is a list.",
"(islist (1 2 3))", "T",
"(islist ())", "T",
"(islist \"Stringy\")", "F",
tfn(isList,
({ NULL, isBool }),
"Returns `T` only if the argument is a list.",
"(islist (1 2 3))", "T",
"(islist ())", "T",
"(islist \"Stringy\")", "F",
);
/// ANY => BOOL
fn(isString,
"Returns `T` only if the argument is a string.",
"(isstr \"Heyo\")", "T",
"(isstr \"\")", "T",
"(isstr (cat 5 5))", "T",
"(isstr 10)", "F",
tfn(isString,
({ NULL, isBool }),
"Returns `T` only if the argument is a string.",
"(isstr \"Heyo\")", "T",
"(isstr \"\")", "T",
"(isstr (cat 5 5))", "T",
"(isstr 10)", "F",
);
/// ANY => BOOL
fn(isErr,
"Check if the argument is an error.",
"(iserr (at 10 ()))", "T",
"(iserr 5)", "F",
tfn(isErr,
({ NULL, isBool }),
"Check if the argument is an error.",
"(iserr (at 10 ()))", "T",
"(iserr 5)", "F",
);
/// STRING => STRING
fn(charAt,
"Get the char in the given string at the given index.",
"(chat \"Hello\" 1)", "e",
"(chat \"Hello\" 10)", "",
tfn(charAt,
({isStringy, isNumber, isStringy}),
"Get the char in the given string at the given index.",
"(chat \"Hello\" 1)", "e",
"(chat \"Hello\" 10)", "",
);
/// STRING => NUMBER
fn(charVal,
"Get the ascii integer representaton of the given character.",
"(char \"h\")", "104",
"(char \"hello\")", "104",
"(char \"\")", "0",
tfn(charVal,
({ isStringy, isNumber }),
"Get the ascii integer representaton of the given character.",
"(char \"h\")", "104",
"(char \"hello\")", "104",
"(char \"\")", "0",
);
/// STRING/SLIST => ANY
@ -161,27 +161,29 @@ fn(possessive,
fn(print, "Prints the string representation of the given object to stdout.");
fn(numToChar,
"Gets a string containing the ascii character for the given number value.",
"(ch 107)", "k",
"(ch 0x21)", "!",
tfn(numToChar,
({ isNumber, isStringy }),
"Gets a string containing the ascii character for the given number value.",
"(ch 107)", "k",
"(ch 0x21)", "!",
);
fn(printEnvO, "Prints out the current scoped environment.");
fn(systemCall,
"Opens a shell and runs the given command, returning 0 if successful.\n"
"If the argument is not a string, returns 255.\n",
"(sys \"echo yee > /dev/null\")", "0",
"(sys 5)", "255",
tfn(systemCall,
({ isStringy, isNumber }),
"Opens a shell and runs the given command, returning 0 if successful.\n"
"If the argument is not a string, returns 255.\n",
"(sys \"echo yee > /dev/null\")", "0",
);
fn(loadFile,
"Loads and parses the given file.\n"
"Returns 0 if the file was loaded and parsed successfully. Otherwise 1.\n"
"(loadfile \"printdate.pl\")\n"
"Mon 21 Mar 2022 10:35:03 AM EDT\n"
"=> 0"
tfn(loadFile,
({ isStringy, NULL }),
"Loads and parses the given file.\n"
"Returns 0 if the file was loaded and parsed successfully. Otherwise 1.\n"
"(loadfile \"printdate.pl\")\n"
"Mon 21 Mar 2022 10:35:03 AM EDT\n"
"=> 0"
);
/// @code
@ -201,9 +203,9 @@ fn(help,
"(? \"+\") => \"(+ 1 2) => 3\""
);
/// STRING => STRING
fn(readFileToObject,
"Read a file into a string object."
tfn(readFileToObject,
({ isStringy, isStringy }),
"Read a file into a string object."
);
#endif // STANDALONE

View File

@ -107,6 +107,7 @@ check "ChainDiv" "(/ 1493856 741 96 7)" "3"
title "Comparison"
check "GratrThn" "(> 23847123 19375933)" "T"
check "GratrThnMulti" "(> 9999 55 1 0)" "T"
check "LessThan" "(< 23847123 19375933)" "F"
check "Equality" "(= 987654321 987654321 )" "T"
check "StringEquality" '(= "Bean" "Bean" )' "T"
@ -215,18 +216,18 @@ check "FuncReturningAFunc" "(def plusser (fn (outer) (fn (inner) (+ outer inner)
(plusFive 10)" "15"
title "ShouldError"
check "LenOfNotList" "(len 5)" "NOT_A_LIST"
check "LenOfNotList" "(len 5)" regex "BAD_PARAMS_ON.*"
check "NoMapList" "(map sq)" "( )"
check "BadNumber" "(5df)" regex "BAD_NUMBER.*"
check "BadHex" "(0x0zf)" regex "BAD_NUMBER.*"
check "BadBinary" "(0b01120)" regex "BAD_NUMBER.*"
check "UnsupportedNumber" "(00000)" regex "UNSUPPORTED_NUMBER.*"
check "BadParens1" "(hey()" regex "'MISMATCHED_PARENS.*"
check "BadParens2" "(hey)(" regex "'MISMATCHED_PARENS.*"
check "BadParens3" "((hey(" regex "'MISMATCHED_PARENS.*"
check "BadParens4" ")))hey" regex "'MISMATCHED_PARENS.*"
check "BadParens5" "hey))(" regex "'MISMATCHED_PARENS.*"
check "BadParens6" '(ey")"' regex "'MISMATCHED_PARENS.*"
check "BadParens1" "(hey()" regex "MISMATCHED_PARENS.*"
check "BadParens2" "(hey)(" regex "MISMATCHED_PARENS.*"
check "BadParens3" "((hey(" regex "MISMATCHED_PARENS.*"
check "BadParens4" ")))hey" regex "MISMATCHED_PARENS.*"
check "BadParens5" "hey))(" regex "MISMATCHED_PARENS.*"
check "BadParens6" '(ey")"' regex "MISMATCHED_PARENS.*"
title "ListArithmetic" disabled
check "UnevenLists" "(+ (1 2) (1 2 3))" "LISTS_NOT_SAME_SIZE"