Add dedicated plfunc directory.

Contains most native pl functions.
Consolidated pebcom into pebbleobject.
Broke out several pc (STANDALONE) and plstring functions into their own files.
This commit is contained in:
Sage Vaillancourt 2022-04-22 08:56:58 -04:00 committed by Sage Vaillancourt
parent 20f2d02f77
commit d40e551933
22 changed files with 399 additions and 387 deletions

View File

@ -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 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) file_libs := $(files) $(libs)
BINPREFIX ?= /usr/bin BINPREFIX ?= /usr/bin

View File

@ -2,9 +2,8 @@
#include <limits.h> #include <limits.h>
#define HASHLESS_ENV #define HASHLESS_ENV
#include "pebcom.h"
#include "object.h" #include "object.h"
#include "pebbleobject.h" #include "plfunc/pebbleobject.h"
#include "calc.h" #include "calc.h"
#define RESULT_LENGTH 128 #define RESULT_LENGTH 128

View File

@ -5,9 +5,11 @@
#include <string.h> #include <string.h>
#include "pebblisp.h" #include "pebblisp.h"
#include "web.h" #include "plfunc/web.h"
#include "threads.h" #include "plfunc/threads.h"
#include "plfunc.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, struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object*, int, struct Environment*), const char* help,
const char* const* tests, size_t testLength); const char* const* tests, size_t testLength);

View File

@ -244,7 +244,7 @@ int main(int argc, const char* argv[])
readFile(SCRIPTDIR "/lib.pbl", &env); readFile(SCRIPTDIR "/lib.pbl", &env);
} }
Object o = parseEval("(def prompt \"pebblisp::> \")", &env); Object o = parseEval("(def prompt %%)", &env, nullTerminated("pebblisp::> "));
cleanObject(&o); cleanObject(&o);
o = parseEval("(def preprocess (fn (text) (text)))", &env); o = parseEval("(def preprocess (fn (text) (text)))", &env);
cleanObject(&o); cleanObject(&o);

View File

@ -6,7 +6,7 @@
#ifdef STANDALONE #ifdef STANDALONE
#include "threads.h" #include "plfunc/threads.h"
size_t bytes = 0; size_t bytes = 0;
@ -281,11 +281,8 @@ int stringNObj(struct string* s, const Object* obj)
appendf(s, "F%ld", obj->number); appendf(s, "F%ld", obj->number);
break; break;
case TYPE_LAMBDA: { case TYPE_LAMBDA: {
#ifdef STANDALONE #if defined(STANDALONE) && !defined(DEBUG)
#ifdef DEBUG const char* docString = lambdaDocs(obj);
appendf(s, "\\x%d", obj->number);
#endif
char* docString = lambdaDocs(obj);
if (docString) { if (docString) {
inflate(s, strlen(docString)); inflate(s, strlen(docString));
appendf(s, "%s\n", docString); appendf(s, "%s\n", docString);
@ -402,7 +399,7 @@ void cleanObject(Object* target)
switch (target->type) { switch (target->type) {
case TYPE_STRING: case TYPE_STRING:
case TYPE_SYMBOL: case TYPE_SYMBOL:
if ((target->string[-1] -= 1) == 0) { if ((stringRefs(target) -= 1) == 0) {
free(target->string - 1); free(target->string - 1);
} }
return; return;
@ -589,13 +586,17 @@ inline int isValidType(const Object test)
Object cloneString(Object obj) Object cloneString(Object obj)
{ {
obj.string[-1] += 1; stringRefs(&obj) += 1;
return obj; return obj;
} }
Object cloneOther(const Object src) 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); Object cloneStruct(Object src);

View File

@ -77,6 +77,8 @@ static const char * const _name ## Symbol = _symbol; \
fnn(_name, _docs, __VA_ARGS__) fnn(_name, _docs, __VA_ARGS__)
#endif #endif
#define stringRefs(STRING_OBJECT) (STRING_OBJECT)->string[-1]
enum errorCode { enum errorCode {
MISMATCHED_PARENS, MISMATCHED_PARENS,
NULL_ENV, NULL_ENV,

View File

@ -6,7 +6,7 @@
#ifdef STANDALONE #ifdef STANDALONE
#include "web.h" #include "plfunc/web.h"
#endif #endif

View File

@ -23,13 +23,11 @@ typedef struct Result {
Object eval(const Object* obj, struct Environment* env); Object eval(const Object* obj, struct Environment* env);
Result parse(struct Slice* slices); Result readSeq(struct Slice* slices, va_list* injections);
Result readSeq(struct Slice* slices);
Result parseAtom(struct Slice* slice); 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); Object evalList(const Object* obj, struct Environment* env);

View File

@ -1,25 +0,0 @@
#include "pebcom.h"
#include <pebble.h>
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);
}
}

View File

@ -1,3 +0,0 @@
#include "pebblisp.h"
Object doVibe(Object* params, int length, struct Environment* env);

88
src/plfunc/pc.c Normal file
View File

@ -0,0 +1,88 @@
#ifdef STANDALONE
#include "pc.h"
#include <unistd.h>
#include <string.h>
Object print(Object* params, int length, unused struct Environment* env)
{
for (int i = 0; i < length; i++) {
_printObj(&params[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

65
src/plfunc/pc.h Normal file
View File

@ -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

View File

@ -1,7 +1,7 @@
#include "pebbleobject.h" #include "pebbleobject.h"
#include "calc.h" #include "../calc.h"
#include "object.h" #include "../object.h"
Object pebbleOther(enum PebbleType type, void* data, void (* cleanup)(Object*), Object pebbleOther(enum PebbleType type, void* data, void (* cleanup)(Object*),
Object (* clone)(struct Other*)) Object (* clone)(struct Other*))
@ -160,3 +160,25 @@ Object subscribe(Object* params, int length, struct Environment* env)
} }
return falseObject(); 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);
}
}

View File

@ -1,6 +1,6 @@
#include <pebble.h> #include <pebble.h>
#include "pebblisp.h" #include "../pebblisp.h"
enum PebbleType { enum PebbleType {
WINDOW, TEXT_LAYER, P_ERROR 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 updateTextLayer(Object* params, int length, struct Environment* env);
Object subscribe(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);

View File

@ -31,26 +31,6 @@ Object reduce(Object* params, unused int length, struct Environment* env)
return total; 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) Object filter(Object* params, unused int length, struct Environment* env)
{ {
checkTypes(filter) checkTypes(filter)
@ -184,101 +164,6 @@ Object isErr(Object* params, unused int length, unused struct Environment* env)
return boolObject(test.type == TYPE_ERROR); 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) Object printEnvO(Object* params, int length, unused struct Environment* env)
{ {
int printPointers = 0; int printPointers = 0;
@ -462,90 +347,3 @@ Object getTime(unused Object* params, unused int length, struct Environment* env
return o; return o;
} }
#ifdef STANDALONE
#include <unistd.h>
Object print(Object* params, int length, unused struct Environment* env)
{
for (int i = 0; i < length; i++) {
_printObj(&params[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

View File

@ -1,7 +1,7 @@
#ifndef PEBBLISP_PLFUNC_H #ifndef PEBBLISP_PLFUNC_H
#define PEBBLISP_PLFUNC_H #define PEBBLISP_PLFUNC_H
#include "pebblisp.h" #include "../pebblisp.h"
fn(add, "+", fn(add, "+",
"Add numbers", "Add numbers",
@ -169,63 +169,6 @@ tfn(isErr, "iserr",
"(iserr 5)", "F", "(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 /// STRING/SLIST => ANY
fn(parseEvalO, "eval", fn(parseEvalO, "eval",
"Evaluate the given string or quoted list. Uses the global scope.", "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)." "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 #endif // PEBBLISP_PLFUNC_H

118
src/plfunc/plstring.c Normal file
View File

@ -0,0 +1,118 @@
#include "plstring.h"
#include <string.h>
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;
}

58
src/plfunc/plstring.h Normal file
View File

@ -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",
);

View File

@ -1,6 +1,4 @@
#include "threads.h" #include "threads.h"
#include "object.h"
#include "pebblisp.h"
#include <stdlib.h> #include <stdlib.h>
#include <pthread.h> #include <pthread.h>

View File

@ -3,7 +3,7 @@
#ifndef THREADS_H #ifndef THREADS_H
#define THREADS_H #define THREADS_H
#include "pebblisp.h" #include "../pebblisp.h"
Object clonePromise(Object src); Object clonePromise(Object src);
@ -13,7 +13,8 @@ int isDone(struct Promise* promise);
int isPromise(Object src); int isPromise(Object src);
fn(async, "async", tfn(async, "async",
({ expect(isFuncy), returns(isPromise)}),
"Run the given lambda on a separate thread, returning a promise.", "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 // 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", "=> <PENDING>", ";(def sleepy (fn () ((sys \"sleep 0.02\") \"Hiya\"))) (def x (async sleepy)) x", "=> <PENDING>",

View File

@ -5,7 +5,7 @@
#include <stdio.h> #include <stdio.h>
#include "web.h" #include "web.h"
#include "tokens.h" #include "../tokens.h"
#ifdef _MHD_FLAGS_ENUM #ifdef _MHD_FLAGS_ENUM
typedef enum MHD_Result HttpResult; typedef enum MHD_Result HttpResult;
@ -123,7 +123,11 @@ answer_to_connection(void* cls, struct MHD_Connection* connection,
void initialize() 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()); Object o = parseEval("(struct Request (queryParams username password))", global());
cleanObject(&o); cleanObject(&o);
requestDefinition = getStructIndex("Request"); requestDefinition = getStructIndex("Request");
@ -131,11 +135,7 @@ void initialize()
int start(int port) int start(int port)
{ {
static int initialized = 0;
if (!initialized) {
initialize(); initialize();
initialized = 1;
}
struct MHD_Daemon* daemon = MHD_start_daemon( struct MHD_Daemon* daemon = MHD_start_daemon(
MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD, MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD,

View File

@ -1,6 +1,6 @@
#ifdef STANDALONE #ifdef STANDALONE
#include "pebblisp.h" #include "../pebblisp.h"
fn(startServer, "serve", fn(startServer, "serve",
"(serve) => 0\n" "(serve) => 0\n"