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
|
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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
|
19
src/object.c
19
src/object.c
|
@ -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);
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
#ifdef STANDALONE
|
#ifdef STANDALONE
|
||||||
|
|
||||||
#include "web.h"
|
#include "plfunc/web.h"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
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 "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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
|
@ -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(¶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
|
#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
|
|
@ -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 "threads.h"
|
||||||
#include "object.h"
|
|
||||||
#include "pebblisp.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
|
@ -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,11 +13,12 @@ int isDone(struct Promise* promise);
|
||||||
|
|
||||||
int isPromise(Object src);
|
int isPromise(Object src);
|
||||||
|
|
||||||
fn(async, "async",
|
tfn(async, "async",
|
||||||
"Run the given lambda on a separate thread, returning a promise.",
|
({ 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
|
// 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>",
|
||||||
"(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)) (await x)", "Hiya",
|
||||||
);
|
);
|
||||||
|
|
||||||
tfn(await, "await",
|
tfn(await, "await",
|
|
@ -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;
|
initialize();
|
||||||
if (!initialized) {
|
|
||||||
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,
|
|
@ -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"
|
Loading…
Reference in New Issue