diff --git a/src/Makefile b/src/Makefile index 5786cdb..18dd951 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,7 +1,11 @@ -files = main.c pebblisp.c tokens.c object.c env.c web.c plfunc.c hash.c threads.c -libs = -lreadline -lmicrohttpd -lpthread exe = pl +base_files = main.c pebblisp.c tokens.c object.c env.c hash.c +func_files = plfunc/web.c plfunc/plfunc.c plfunc/threads.c plfunc/plstring.c plfunc/pc.c +files:= $(base_files) $(func_files) + +libs = -lreadline -lmicrohttpd -lpthread + file_libs := $(files) $(libs) BINPREFIX ?= /usr/bin diff --git a/src/calc.c b/src/calc.c index fbdcb55..fb6a3db 100644 --- a/src/calc.c +++ b/src/calc.c @@ -2,9 +2,8 @@ #include #define HASHLESS_ENV -#include "pebcom.h" #include "object.h" -#include "pebbleobject.h" +#include "plfunc/pebbleobject.h" #include "calc.h" #define RESULT_LENGTH 128 diff --git a/src/env.c b/src/env.c index 4b6ea51..eb29e9b 100644 --- a/src/env.c +++ b/src/env.c @@ -5,9 +5,11 @@ #include #include "pebblisp.h" -#include "web.h" -#include "threads.h" -#include "plfunc.h" +#include "plfunc/web.h" +#include "plfunc/threads.h" +#include "plfunc/plfunc.h" +#include "plfunc/pc.h" +#include "plfunc/plstring.h" struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object*, int, struct Environment*), const char* help, const char* const* tests, size_t testLength); diff --git a/src/main.c b/src/main.c index d605ac7..97dad85 100644 --- a/src/main.c +++ b/src/main.c @@ -244,7 +244,7 @@ int main(int argc, const char* argv[]) readFile(SCRIPTDIR "/lib.pbl", &env); } - Object o = parseEval("(def prompt \"pebblisp::> \")", &env); + Object o = parseEval("(def prompt %%)", &env, nullTerminated("pebblisp::> ")); cleanObject(&o); o = parseEval("(def preprocess (fn (text) (text)))", &env); cleanObject(&o); diff --git a/src/object.c b/src/object.c index 973e4c8..d81ce63 100644 --- a/src/object.c +++ b/src/object.c @@ -6,7 +6,7 @@ #ifdef STANDALONE -#include "threads.h" +#include "plfunc/threads.h" size_t bytes = 0; @@ -281,11 +281,8 @@ int stringNObj(struct string* s, const Object* obj) appendf(s, "F%ld", obj->number); break; case TYPE_LAMBDA: { -#ifdef STANDALONE -#ifdef DEBUG - appendf(s, "\\x%d", obj->number); -#endif - char* docString = lambdaDocs(obj); +#if defined(STANDALONE) && !defined(DEBUG) + const char* docString = lambdaDocs(obj); if (docString) { inflate(s, strlen(docString)); appendf(s, "%s\n", docString); @@ -402,7 +399,7 @@ void cleanObject(Object* target) switch (target->type) { case TYPE_STRING: case TYPE_SYMBOL: - if ((target->string[-1] -= 1) == 0) { + if ((stringRefs(target) -= 1) == 0) { free(target->string - 1); } return; @@ -589,13 +586,17 @@ inline int isValidType(const Object test) Object cloneString(Object obj) { - obj.string[-1] += 1; + stringRefs(&obj) += 1; return obj; } Object cloneOther(const Object src) { - return src.other->clone ? src.other->clone(src.other) : src; + if (src.other->clone) { + return src.other->clone(src.other); + } + + return src; } Object cloneStruct(Object src); diff --git a/src/object.h b/src/object.h index 0ff4292..93218c3 100644 --- a/src/object.h +++ b/src/object.h @@ -77,6 +77,8 @@ static const char * const _name ## Symbol = _symbol; \ fnn(_name, _docs, __VA_ARGS__) #endif +#define stringRefs(STRING_OBJECT) (STRING_OBJECT)->string[-1] + enum errorCode { MISMATCHED_PARENS, NULL_ENV, diff --git a/src/pebblisp.c b/src/pebblisp.c index d0d816f..b590274 100644 --- a/src/pebblisp.c +++ b/src/pebblisp.c @@ -6,7 +6,7 @@ #ifdef STANDALONE -#include "web.h" +#include "plfunc/web.h" #endif diff --git a/src/pebblisp.h b/src/pebblisp.h index 80743bf..68a2b18 100644 --- a/src/pebblisp.h +++ b/src/pebblisp.h @@ -23,13 +23,11 @@ typedef struct Result { Object eval(const Object* obj, struct Environment* env); -Result parse(struct Slice* slices); - -Result readSeq(struct Slice* slices); +Result readSeq(struct Slice* slices, va_list* injections); Result parseAtom(struct Slice* slice); -Object parseEval(const char* input, struct Environment* env); +Object parseEval(const char* input, struct Environment* env, ...); Object evalList(const Object* obj, struct Environment* env); diff --git a/src/pebcom.c b/src/pebcom.c deleted file mode 100644 index a8ac25e..0000000 --- a/src/pebcom.c +++ /dev/null @@ -1,25 +0,0 @@ -#include "pebcom.h" - -#include - -Object doVibe(Object* params, int length, struct Environment* env) -{ - Object patternList = params[0]; - int l = listLength(&patternList); - uint32_t pattern[l]; - if (l > 0) { - int i = 0; - Object* pl = &patternList; - FOR_POINTER_IN_LIST(pl) { - if (POINTER->type == TYPE_NUMBER) { - pattern[i] = POINTER->number; - } - i++; - } - vibes_enqueue_custom_pattern( - (VibePattern) {.durations = pattern, .num_segments = l}); - return trueObject(); - } else { - return errorObject(NOT_A_LIST); - } -} diff --git a/src/pebcom.h b/src/pebcom.h deleted file mode 100644 index 5cc029e..0000000 --- a/src/pebcom.h +++ /dev/null @@ -1,3 +0,0 @@ -#include "pebblisp.h" - -Object doVibe(Object* params, int length, struct Environment* env); diff --git a/src/plfunc/pc.c b/src/plfunc/pc.c new file mode 100644 index 0000000..96fc91b --- /dev/null +++ b/src/plfunc/pc.c @@ -0,0 +1,88 @@ +#ifdef STANDALONE +#include "pc.h" + +#include +#include + +Object print(Object* params, int length, unused struct Environment* env) +{ + for (int i = 0; i < length; i++) { + _printObj(¶ms[i], 0); + } + return numberObject(0); +} + +Object numToChar(Object* params, unused int length, unused struct Environment* env) +{ + checkTypes(numToChar) + Object c = params[0]; + + 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); +} + +Object takeInput(Object* params, int length, unused struct Environment* env) +{ + Object prompt = params[0]; + if (length > 0 && prompt.type == TYPE_STRING) { + printf("%s", prompt.string); + } + char input[256] = ""; + if (fgets(input, 256, stdin)) { + return stringFromSlice(input, strlen(input) - 1); + } + return errorWithContext(NULL_PARSE, "fgets() error"); +} + +Object cd(Object* params, unused int length, unused struct Environment* env) +{ + checkTypes(cd) + + return numberObject(chdir(params[0].string)); +} + +Object cwd(unused Object* params, unused int length, unused struct Environment* env) +{ + char c[128]; + return nullTerminated(getcwd(c, sizeof(c))); +} + +Object systemCall(Object* params, unused int length, unused struct Environment* env) +{ + checkTypes(systemCall) + Object process = params[0]; + + if (isStringy(process)) { + return numberObject(system(process.string)); + } + return numberObject(255); +} + +Object readFileToObject(Object* params, unused int length, unused struct Environment* env) +{ + checkTypes(readFileToObject) + Object filename = params[0]; + + FILE* file = fopen(filename.string, "r"); + if (!file) { + throw(NULL_PARSE, "Error opening file at %s", filename.string); + } + + Object string = newObject(TYPE_STRING); + string.string = readFileToString(file); + return string; +} + +Object getEnvVar(Object* params, unused int length, unused struct Environment* env) +{ + checkTypes(getEnvVar) + const char* envVar = getenv(params[0].string); + if (envVar) { + return nullTerminated(envVar); + } + return stringFromSlice("", 0); +} +#endif // STANDALONE \ No newline at end of file diff --git a/src/plfunc/pc.h b/src/plfunc/pc.h new file mode 100644 index 0000000..9ea974f --- /dev/null +++ b/src/plfunc/pc.h @@ -0,0 +1,65 @@ +#ifdef STANDALONE + +#ifndef PEBBLISP_PC_H +#define PEBBLISP_PC_H + +#include "../pebblisp.h" + +fn(print, "prn", "Prints the string representation of the given object to stdout."); + +tfn(numToChar, "ch", + ({ expect(isNumber), returns(isStringy) }), + "Gets a string containing the ascii character for the given number value.", + "(ch 107)", "k", + "(ch 0x21)", "!", +); + +fn(printEnvO, "penv", + "Prints out the current scoped environment.\n" + "(penv) prints a mostly human-readable list of env variables.\n" + "(penv T) prints a mostly list of env variables, including pointer addresses.\n" + "Calling (penv) with no argument is equivalent to (penv F)" +); + +tfn(systemCall, "sys", + ({ expect(isStringy), returns(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", +); + +tfn(cd, "cd", + ({ expect(isStringy), anyType }), + "Change the current directory.", + "(cd \"/\") (cwd)", "/" +); + +tfn(cwd, "cwd", + ({ returns(isStringy) }), + "Get the current directory.", + "(cd \"/\") (cwd)", "/" +); + +/// @code +/// () => STRING +/// STRING => STRING +fn(takeInput, "inp", + "Take console input with an optional prompt. For example:\n" + "`(def x (input))` will wait for user input with no prompt.\n" + "`(def x (input \">> \"))` wait for input, but prompt the user with '>> '\n" +); + +tfn(readFileToObject, "rf", + ({ expect(isStringy), returns(isStringy) }), + "Read a file into a string object." +); + +tfn(getEnvVar, "env", + ({ expect(isStringy), returns(isStringy) }), + "Get a variable from the current environment\n" + "(env HOME) => /home/sagevaillancourt" +); + +#endif //PEBBLISP_PC_H + +#endif // STANDALONE diff --git a/src/pebbleobject.c b/src/plfunc/pebbleobject.c similarity index 87% rename from src/pebbleobject.c rename to src/plfunc/pebbleobject.c index c64f6b3..a8776b6 100644 --- a/src/pebbleobject.c +++ b/src/plfunc/pebbleobject.c @@ -1,7 +1,7 @@ #include "pebbleobject.h" -#include "calc.h" -#include "object.h" +#include "../calc.h" +#include "../object.h" Object pebbleOther(enum PebbleType type, void* data, void (* cleanup)(Object*), Object (* clone)(struct Other*)) @@ -160,3 +160,25 @@ Object subscribe(Object* params, int length, struct Environment* env) } return falseObject(); } + +Object doVibe(Object* params, int length, struct Environment* env) +{ + Object patternList = params[0]; + int l = listLength(&patternList); + uint32_t pattern[l]; + if (l > 0) { + int i = 0; + Object* pl = &patternList; + FOR_POINTER_IN_LIST(pl) { + if (POINTER->type == TYPE_NUMBER) { + pattern[i] = POINTER->number; + } + i++; + } + vibes_enqueue_custom_pattern( + (VibePattern) {.durations = pattern, .num_segments = l}); + return trueObject(); + } else { + return errorObject(NOT_A_LIST); + } +} diff --git a/src/pebbleobject.h b/src/plfunc/pebbleobject.h similarity index 89% rename from src/pebbleobject.h rename to src/plfunc/pebbleobject.h index b294a0c..425e635 100644 --- a/src/pebbleobject.h +++ b/src/plfunc/pebbleobject.h @@ -1,6 +1,6 @@ #include -#include "pebblisp.h" +#include "../pebblisp.h" enum PebbleType { WINDOW, TEXT_LAYER, P_ERROR @@ -32,3 +32,5 @@ Object addTextLayer(Object* params, int length, struct Environment* env); Object updateTextLayer(Object* params, int length, struct Environment* env); Object subscribe(Object* params, int length, struct Environment* env); + +Object doVibe(Object* params, int length, struct Environment* env); diff --git a/src/plfunc.c b/src/plfunc/plfunc.c similarity index 62% rename from src/plfunc.c rename to src/plfunc/plfunc.c index a9118d3..56346e0 100644 --- a/src/plfunc.c +++ b/src/plfunc/plfunc.c @@ -31,26 +31,6 @@ Object reduce(Object* params, unused int length, struct Environment* env) return total; } -Object charAt(Object* params, unused int length, unused struct Environment* env) -{ - checkTypes(charAt) - Object string = params[0]; - Object at = params[1]; - - Object c = withLen(1, string.type); - c.string[1] = '\0'; - - for (int i = 0; i < at.number; i++) { - if (string.string[i] == '\0') { - c.string[0] = '\0'; - return c; - } - } - - c.string[0] = string.string[at.number]; - return c; -} - Object filter(Object* params, unused int length, struct Environment* env) { checkTypes(filter) @@ -184,101 +164,6 @@ Object isErr(Object* params, unused int length, unused struct Environment* env) return boolObject(test.type == TYPE_ERROR); } -Object charVal(Object* params, unused int length, unused struct Environment* env) -{ - checkTypes(charVal) - Object test = params[0]; - - return numberObject(test.string[0]); -} - -Object chars(Object* params, unused int length, unused struct Environment* env) -{ - checkTypes(chars) - char c[2]; - c[1] = '\0'; - int i = 0; - Object list = listObject(); - while (params[0].string[i]) { - c[0] = params[0].string[i]; - nf_addToList(&list, stringFromSlice(c, 1)); - i++; - } - return list; -} - -size_t literalLen(const char* pattern) -{ - const char* cursor = pattern; - while (*cursor != '*' && *cursor != '\0') { - cursor++; - } - return cursor - pattern; -} - -/// Returns 1 on match, 0 otherwise -int stringComp(const char* string, const char* pattern) -{ - const char* cursor = string; - const char* patternCursor = pattern; - - while (*patternCursor) { - size_t len = literalLen(patternCursor); - if (len == 0) { - patternCursor += 1; - while (!stringComp(cursor, patternCursor)) { - cursor++; - if (*cursor == '\0') { - return 0; - } - } - } else { - if (strncmp(patternCursor, cursor, len) != 0) { - return 0; - } - patternCursor += len; - } - } - return 1; -} - -Object matches(Object* params, int length, unused struct Environment* env) -{ - checkTypes(matches) - return boolObject(stringComp(params[0].string, params[1].string)); -} - -Object slen(Object* params, int length, unused struct Environment* env) -{ - checkTypes(slen) - return numberObject(strlen(params[0].string)); -} - -Object substring(Object* params, int length, unused struct Environment* env) -{ - checkTypes(substring) - Object start = params[0]; // First char to include - Object end = params[1]; // First char to exclude - Object string = params[2]; - - long len = end.number - start.number; - size_t stringLen = strlen(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) { - len = stringLen - start.number; - } - - Object substr = withLen(len, TYPE_STRING); - snprintf(substr.string, len + 1, "%s", string.string + start.number); - return substr; -} - Object printEnvO(Object* params, int length, unused struct Environment* env) { int printPointers = 0; @@ -462,90 +347,3 @@ Object getTime(unused Object* params, unused int length, struct Environment* env return o; } -#ifdef STANDALONE - -#include - -Object print(Object* params, int length, unused struct Environment* env) -{ - for (int i = 0; i < length; i++) { - _printObj(¶ms[i], 0); - } - return numberObject(0); -} - -Object numToChar(Object* params, unused int length, unused struct Environment* env) -{ - checkTypes(numToChar) - Object c = params[0]; - - 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); -} - -Object takeInput(Object* params, int length, unused struct Environment* env) -{ - Object prompt = params[0]; - if (length > 0 && prompt.type == TYPE_STRING) { - printf("%s", prompt.string); - } - char input[256] = ""; - if (fgets(input, 256, stdin)) { - return stringFromSlice(input, strlen(input) - 1); - } - return errorWithContext(NULL_PARSE, "fgets() error"); -} - -Object cd(Object* params, unused int length, unused struct Environment* env) -{ - checkTypes(cd) - - return numberObject(chdir(params[0].string)); -} - -Object cwd(unused Object* params, unused int length, unused struct Environment* env) -{ - char c[128]; - return nullTerminated(getcwd(c, sizeof(c))); -} - -Object systemCall(Object* params, unused int length, unused struct Environment* env) -{ - checkTypes(systemCall) - Object process = params[0]; - - if (isStringy(process)) { - return numberObject(system(process.string)); - } - return numberObject(255); -} - -Object readFileToObject(Object* params, unused int length, unused struct Environment* env) -{ - checkTypes(readFileToObject) - Object filename = params[0]; - - FILE* file = fopen(filename.string, "r"); - if (!file) { - throw(NULL_PARSE, "Error opening file at %s", filename.string); - } - - Object string = newObject(TYPE_STRING); - string.string = readFileToString(file); - return string; -} - -Object getEnvVar(Object* params, unused int length, unused struct Environment* env) -{ - checkTypes(getEnvVar) - const char* envVar = getenv(params[0].string); - if (envVar) { - return nullTerminated(envVar); - } - return stringFromSlice("", 0); -} - -#endif // STANDALONE diff --git a/src/plfunc.h b/src/plfunc/plfunc.h similarity index 55% rename from src/plfunc.h rename to src/plfunc/plfunc.h index 0c78bbd..691a895 100644 --- a/src/plfunc.h +++ b/src/plfunc/plfunc.h @@ -1,7 +1,7 @@ #ifndef PEBBLISP_PLFUNC_H #define PEBBLISP_PLFUNC_H -#include "pebblisp.h" +#include "../pebblisp.h" fn(add, "+", "Add numbers", @@ -169,63 +169,6 @@ tfn(isErr, "iserr", "(iserr 5)", "F", ); -tfn(charAt, "chat", - ({ expect(isStringy), expect(isNumber), returns(isStringy) }), - "Get the char in the given string at the given index.", - "(chat \"Hello\" 1)", "e", - "(chat \"Hello\" 10)", "", -); - -tfn(charVal, "char", - ({ expect(isStringy), returns(isNumber) }), - "Get the ascii integer representaton of the given character.", - "(char \"h\")", "104", - "(char \"hello\")", "104", - "(char \"\")", "0", -); - -tfn(slen, "slen", - ({ expect(isStringy), returns(isNumber) }), - "Returns the length of the given string", - "(slen \"string\")", "6", - "(slen \"\")", "0", -); - -tfn(chars, "chars", - ({ expect(isStringy), returns(isListy) }), - "Get a list of all chars in the given string", - "(chars \"hello\")", "( h e l l o )", - "(chars \"\")", "( )", -); - -tfn(matches, "matches", - ({ expect(isStringy), expect(isStringy), returns(isBool) }), - "Check that a string matches a basic wildcard pattern\n" - "Note: Currently there is no way to match a literal asterisk", - "(matches \"Hiya\" \"Hiya\")", "T", - "(matches \"Howdy\" \"H*y\")", "T", - "(matches \"Yello\" \"*\")", "T", - "(matches \"S\" \"*S\")", "T", - "(matches \"S\" \"S*\")", "T", - "(matches \"\" \"*\")", "T", - "(matches \"ThisIsALongerOne\" \"This*ALong*One\")", "T", - "(matches \"ThisIsALongerOne\" \"*This*ALong*One*\")", "T", - ";", "", - "(matches \"Howdy\" \"*llo\")", "F", - "(matches \"Stink\" \"Stank\")", "F", -); - -tfn(substring, "substr", - ({ expect(isNumber), expect(isNumber), expect(isStringy), returns(isStringy) }), - "Get a substring from the given string.", - "(substr 1 3 \"Hello\")", "el", - "(substr 99 3 \"Hello\")", "BAD_PARAMS;", - "(substr 98 99 \"Hello\")", "BAD_PARAMS;", - "(substr 0 1 \"\")", "", - "(substr 0 99 \"Hey\")", "Hey", - "(substr 1 99 \"Heyyyy\")", "eyyyy", -); - /// STRING/SLIST => ANY fn(parseEvalO, "eval", "Evaluate the given string or quoted list. Uses the global scope.", @@ -238,63 +181,4 @@ tfn(getTime, "time", "Get a struct of the current time with fields (minute hour sec)." ); -#ifdef STANDALONE - -fn(print, "prn", "Prints the string representation of the given object to stdout."); - -tfn(numToChar, "ch", - ({ expect(isNumber), returns(isStringy) }), - "Gets a string containing the ascii character for the given number value.", - "(ch 107)", "k", - "(ch 0x21)", "!", -); - -fn(printEnvO, "penv", - "Prints out the current scoped environment.\n" - "(penv) prints a mostly human-readable list of env variables.\n" - "(penv T) prints a mostly list of env variables, including pointer addresses.\n" - "Calling (penv) with no argument is equivalent to (penv F)" -); - -tfn(systemCall, "sys", - ({ expect(isStringy), returns(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", -); - -tfn(cd, "cd", - ({ expect(isStringy), anyType }), - "Change the current directory.", - "(cd \"/\") (cwd)", "/" -); - -tfn(cwd, "cwd", - ({ returns(isStringy) }), - "Get the current directory.", - "(cd \"/\") (cwd)", "/" -); - -/// @code -/// () => STRING -/// STRING => STRING -fn(takeInput, "inp", - "Take console input with an optional prompt. For example:\n" - "`(def x (input))` will wait for user input with no prompt.\n" - "`(def x (input \">> \"))` wait for input, but prompt the user with '>> '\n" -); - -tfn(readFileToObject, "rf", - ({ expect(isStringy), returns(isStringy) }), - "Read a file into a string object." -); - -tfn(getEnvVar, "env", - ({ expect(isStringy), returns(isStringy) }), - "Get a variable from the current environment\n" - "(env HOME) => /home/sagevaillancourt" -); - -#endif // STANDALONE - #endif // PEBBLISP_PLFUNC_H diff --git a/src/plfunc/plstring.c b/src/plfunc/plstring.c new file mode 100644 index 0000000..8fc688c --- /dev/null +++ b/src/plfunc/plstring.c @@ -0,0 +1,118 @@ +#include "plstring.h" + +#include + +Object charVal(Object* params, unused int length, unused struct Environment* env) +{ + checkTypes(charVal) + Object test = params[0]; + + return numberObject(test.string[0]); +} + +Object chars(Object* params, unused int length, unused struct Environment* env) +{ + checkTypes(chars) + char c[2]; + c[1] = '\0'; + int i = 0; + Object list = listObject(); + while (params[0].string[i]) { + c[0] = params[0].string[i]; + nf_addToList(&list, stringFromSlice(c, 1)); + i++; + } + return list; +} + +size_t literalLen(const char* pattern) +{ + const char* cursor = pattern; + while (*cursor != '*' && *cursor != '\0') { + cursor++; + } + return cursor - pattern; +} + +/// Returns 1 on match, 0 otherwise +int stringComp(const char* string, const char* pattern) +{ + const char* cursor = string; + const char* patternCursor = pattern; + + while (*patternCursor) { + size_t len = literalLen(patternCursor); + if (len == 0) { + patternCursor += 1; + while (!stringComp(cursor, patternCursor)) { + cursor++; + if (*cursor == '\0') { + return 0; + } + } + } else { + if (strncmp(patternCursor, cursor, len) != 0) { + return 0; + } + patternCursor += len; + } + } + return 1; +} + +Object matches(Object* params, int length, unused struct Environment* env) +{ + checkTypes(matches) + return boolObject(stringComp(params[0].string, params[1].string)); +} + +Object slen(Object* params, int length, unused struct Environment* env) +{ + checkTypes(slen) + return numberObject(strlen(params[0].string)); +} + +Object substring(Object* params, int length, unused struct Environment* env) +{ + checkTypes(substring) + Object start = params[0]; // First char to include + Object end = params[1]; // First char to exclude + Object string = params[2]; + + long len = end.number - start.number; + size_t stringLen = strlen(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) { + len = stringLen - start.number; + } + + Object substr = withLen(len, TYPE_STRING); + snprintf(substr.string, len + 1, "%s", string.string + start.number); + return substr; +} + +Object charAt(Object* params, unused int length, unused struct Environment* env) +{ + checkTypes(charAt) + Object string = params[0]; + Object at = params[1]; + + Object c = withLen(1, string.type); + c.string[1] = '\0'; + + for (int i = 0; i < at.number; i++) { + if (string.string[i] == '\0') { + c.string[0] = '\0'; + return c; + } + } + + c.string[0] = string.string[at.number]; + return c; +} diff --git a/src/plfunc/plstring.h b/src/plfunc/plstring.h new file mode 100644 index 0000000..bf26922 --- /dev/null +++ b/src/plfunc/plstring.h @@ -0,0 +1,58 @@ +#include "../pebblisp.h" + +tfn(charAt, "chat", + ({ expect(isStringy), expect(isNumber), returns(isStringy) }), + "Get the char in the given string at the given index.", + "(chat \"Hello\" 1)", "e", + "(chat \"Hello\" 10)", "", +); + +tfn(charVal, "char", + ({ expect(isStringy), returns(isNumber) }), + "Get the ascii integer representaton of the given character.", + "(char \"h\")", "104", + "(char \"hello\")", "104", + "(char \"\")", "0", +); + +tfn(slen, "slen", + ({ expect(isStringy), returns(isNumber) }), + "Returns the length of the given string", + "(slen \"string\")", "6", + "(slen \"\")", "0", +); + +tfn(chars, "chars", + ({ expect(isStringy), returns(isListy) }), + "Get a list of all chars in the given string", + "(chars \"hello\")", "( h e l l o )", + "(chars \"\")", "( )", +); + +tfn(matches, "matches", + ({ expect(isStringy), expect(isStringy), returns(isBool) }), + "Check that a string matches a basic wildcard pattern\n" + "Note: Currently there is no way to match a literal asterisk", + "(matches \"Hiya\" \"Hiya\")", "T", + "(matches \"Howdy\" \"H*y\")", "T", + "(matches \"Yello\" \"*\")", "T", + "(matches \"S\" \"*S\")", "T", + "(matches \"S\" \"S*\")", "T", + "(matches \"\" \"*\")", "T", + "(matches \"ThisIsALongerOne\" \"This*ALong*One\")", "T", + "(matches \"ThisIsALongerOne\" \"*This*ALong*One*\")", "T", + ";", "", + "(matches \"Howdy\" \"*llo\")", "F", + "(matches \"Stink\" \"Stank\")", "F", +); + +tfn(substring, "substr", + ({ expect(isNumber), expect(isNumber), expect(isStringy), returns(isStringy) }), + "Get a substring from the given string.", + "(substr 1 3 \"Hello\")", "el", + "(substr 99 3 \"Hello\")", "BAD_PARAMS;", + "(substr 98 99 \"Hello\")", "BAD_PARAMS;", + "(substr 0 1 \"\")", "", + "(substr 0 99 \"Hey\")", "Hey", + "(substr 1 99 \"Heyyyy\")", "eyyyy", +); diff --git a/src/threads.c b/src/plfunc/threads.c similarity index 97% rename from src/threads.c rename to src/plfunc/threads.c index 371d8a1..3c84f00 100644 --- a/src/threads.c +++ b/src/plfunc/threads.c @@ -1,6 +1,4 @@ #include "threads.h" -#include "object.h" -#include "pebblisp.h" #include #include diff --git a/src/threads.h b/src/plfunc/threads.h similarity index 64% rename from src/threads.h rename to src/plfunc/threads.h index 49b40d9..8f3a52f 100644 --- a/src/threads.h +++ b/src/plfunc/threads.h @@ -3,7 +3,7 @@ #ifndef THREADS_H #define THREADS_H -#include "pebblisp.h" +#include "../pebblisp.h" Object clonePromise(Object src); @@ -13,11 +13,12 @@ int isDone(struct Promise* promise); int isPromise(Object src); -fn(async, "async", - "Run the given lambda on a separate thread, returning a promise.", +tfn(async, "async", + ({ expect(isFuncy), returns(isPromise)}), + "Run the given lambda on a separate thread, returning a promise.", // Disabled because the test ends while memory is still allocated, causing valgrind to report errors - ";(def sleepy (fn () ((sys \"sleep 0.02\") \"Hiya\"))) (def x (async sleepy)) x", "=> ", - "(def sleepy (fn () ((sys \"sleep 0.02\") \"Hiya\"))) (def x (async sleepy)) (await x)", "Hiya", + ";(def sleepy (fn () ((sys \"sleep 0.02\") \"Hiya\"))) (def x (async sleepy)) x", "=> ", + "(def sleepy (fn () ((sys \"sleep 0.02\") \"Hiya\"))) (def x (async sleepy)) (await x)", "Hiya", ); tfn(await, "await", diff --git a/src/web.c b/src/plfunc/web.c similarity index 98% rename from src/web.c rename to src/plfunc/web.c index 2d214b9..0960c38 100644 --- a/src/web.c +++ b/src/plfunc/web.c @@ -5,7 +5,7 @@ #include #include "web.h" -#include "tokens.h" +#include "../tokens.h" #ifdef _MHD_FLAGS_ENUM typedef enum MHD_Result HttpResult; @@ -123,7 +123,11 @@ answer_to_connection(void* cls, struct MHD_Connection* connection, void initialize() { - printf("Initializing...\n"); + static int initialized = 0; + if (!initialized) { + initialized = 1; + } + eprintf("Initializing...\n"); Object o = parseEval("(struct Request (queryParams username password))", global()); cleanObject(&o); requestDefinition = getStructIndex("Request"); @@ -131,11 +135,7 @@ void initialize() int start(int port) { - static int initialized = 0; - if (!initialized) { - initialize(); - initialized = 1; - } + initialize(); struct MHD_Daemon* daemon = MHD_start_daemon( MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD, diff --git a/src/web.h b/src/plfunc/web.h similarity index 97% rename from src/web.h rename to src/plfunc/web.h index 87209e1..777fc5a 100644 --- a/src/web.h +++ b/src/plfunc/web.h @@ -1,6 +1,6 @@ #ifdef STANDALONE -#include "pebblisp.h" +#include "../pebblisp.h" fn(startServer, "serve", "(serve) => 0\n"