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:
parent
20f2d02f77
commit
d40e551933
|
@ -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
|
||||
|
|
|
@ -2,9 +2,8 @@
|
|||
#include <limits.h>
|
||||
|
||||
#define HASHLESS_ENV
|
||||
#include "pebcom.h"
|
||||
#include "object.h"
|
||||
#include "pebbleobject.h"
|
||||
#include "plfunc/pebbleobject.h"
|
||||
#include "calc.h"
|
||||
|
||||
#define RESULT_LENGTH 128
|
||||
|
|
|
@ -5,9 +5,11 @@
|
|||
#include <string.h>
|
||||
|
||||
#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);
|
||||
|
|
|
@ -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);
|
||||
|
|
19
src/object.c
19
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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#ifdef STANDALONE
|
||||
|
||||
#include "web.h"
|
||||
#include "plfunc/web.h"
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
25
src/pebcom.c
25
src/pebcom.c
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
#include "pebblisp.h"
|
||||
|
||||
Object doVibe(Object* params, int length, struct Environment* env);
|
|
@ -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(¶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
|
|
@ -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
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
#include <pebble.h>
|
||||
|
||||
#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);
|
|
@ -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 <unistd.h>
|
||||
|
||||
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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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",
|
||||
);
|
|
@ -1,6 +1,4 @@
|
|||
#include "threads.h"
|
||||
#include "object.h"
|
||||
#include "pebblisp.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <pthread.h>
|
|
@ -3,7 +3,7 @@
|
|||
#ifndef THREADS_H
|
||||
#define THREADS_H
|
||||
|
||||
#include "pebblisp.h"
|
||||
#include "../pebblisp.h"
|
||||
|
||||
Object clonePromise(Object src);
|
||||
|
||||
|
@ -13,7 +13,8 @@ int isDone(struct Promise* promise);
|
|||
|
||||
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.",
|
||||
// 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>",
|
|
@ -5,7 +5,7 @@
|
|||
#include <stdio.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
struct MHD_Daemon* daemon = MHD_start_daemon(
|
||||
MHD_USE_AUTO | MHD_USE_INTERNAL_POLLING_THREAD,
|
|
@ -1,6 +1,6 @@
|
|||
#ifdef STANDALONE
|
||||
|
||||
#include "pebblisp.h"
|
||||
#include "../pebblisp.h"
|
||||
|
||||
fn(startServer, "serve",
|
||||
"(serve) => 0\n"
|
Loading…
Reference in New Issue