More robust addToTable().

Add several pl string functions: `slen`, `chars`, `matches`, and `substr`.
Add support for comments in native tests.
This commit is contained in:
Sage Vaillancourt 2022-04-05 15:27:41 -04:00 committed by Sage Vaillancourt
parent eb2dde72e3
commit b419826b8e
7 changed files with 184 additions and 39 deletions

View File

@ -1,6 +1,7 @@
#include <stdio.h> #include <stdio.h>
#include <limits.h> #include <limits.h>
#define HASHLESS_ENV
#include "pebcom.h" #include "pebcom.h"
#include "object.h" #include "object.h"
#include "pebbleobject.h" #include "pebbleobject.h"

View File

@ -8,10 +8,6 @@
#include "web.h" #include "web.h"
#include "plfunc.h" #include "plfunc.h"
#define printd(...) ;
#define HASH_ENV
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);
@ -60,7 +56,7 @@ struct symFunc {
#define pf(_func) buildFuncSym(_func ## Symbol, &(_func)) #define pf(_func) buildFuncSym(_func ## Symbol, &(_func))
#endif #endif
#ifdef HASH_ENV // Hash-based env #ifndef HASHLESS_ENV // Hash-based env
Object* fetch(const char* name, struct Environment* env) Object* fetch(const char* name, struct Environment* env)
{ {
while (env) { while (env) {
@ -104,18 +100,10 @@ struct Environment envForLambda(const Object* params, const Object* arg_forms, i
struct Environment env = { struct Environment env = {
.outer = NULL, .outer = NULL,
.table = buildTable(4), .table = buildTable(paramCount * 2),
.refs = 1, .refs = 1,
}; };
if (paramCount == 0) {
if (outer) {
outer->refs += 1;
return *outer;
}
return env;
}
const Object* march = arg_forms; const Object* march = arg_forms;
for (int i = 0; i < paramCount; i++) { for (int i = 0; i < paramCount; i++) {
const char* newObjName = itemAt(params, i)->string; const char* newObjName = itemAt(params, i)->string;
@ -186,6 +174,10 @@ struct Environment defaultEnv()
#ifndef PBL_PLATFORM_APLITE #ifndef PBL_PLATFORM_APLITE
pf(rest), pf(rest),
pf(charAt), pf(charAt),
pf(chars),
pf(matches),
pf(slen),
pf(substring),
pf(isNum), pf(isNum),
pf(isList), pf(isList),
pf(isString), pf(isString),
@ -447,6 +439,10 @@ struct Environment defaultEnvPreAllocated(struct EnvElement* elements)
#ifndef PBL_PLATFORM_APLITE #ifndef PBL_PLATFORM_APLITE
pf(rest), pf(rest),
pf(charAt), pf(charAt),
pf(chars),
pf(matches),
pf(slen),
pf(substring),
pf(isNum), pf(isNum),
pf(isList), pf(isList),
pf(isString), pf(isString),
@ -488,12 +484,6 @@ struct Environment defaultEnvPreAllocated(struct EnvElement* elements)
e.elements[j].symbol = NULL; e.elements[j].symbol = NULL;
} }
#ifdef CACHE
for (i = 0; i < CACHE_SIZE + 1; i++) {
cache[i] = NULL;
}
#endif
helpInitialized = 1; helpInitialized = 1;
return e; return e;
} }
@ -618,12 +608,12 @@ void printColored(const char* code)
int isQuote = 0; int isQuote = 0;
printf("[%dm", getColor(depth)); printf("[%dm", getColor(depth));
while (code[c]) { while (code[c]) {
if (code[c] == '(' && !isQuote) { if ((code[c] == '(' || code[c] == '{') && !isQuote) {
depth += 1; depth += 1;
printf("[%dm", getColor(depth)); printf("[%dm", getColor(depth));
} else if (code[c] == ')' && !isQuote) { } else if ((code[c] == ')' || code[c] == '}') && !isQuote) {
depth -= 1; depth -= 1;
printf(")[%dm", getColor(depth)); printf("%c[%dm", code[c], getColor(depth));
c++; c++;
continue; continue;
} else if (code[c] == '"') { } else if (code[c] == '"') {
@ -668,6 +658,10 @@ Object help(Object* params, int length, struct Environment* env)
for (int ti = 0; ti < h.testCount; ti += 2) { for (int ti = 0; ti < h.testCount; ti += 2) {
const char* test = h.tests[ti]; const char* test = h.tests[ti];
const char* expected = h.tests[ti + 1]; const char* expected = h.tests[ti + 1];
if (test[0] == ';') {
textCursor += sprintf(textCursor, "\n%s %s", test + 1, expected);
continue;
}
textCursor += sprintf(textCursor, "\n %s => ", test); textCursor += sprintf(textCursor, "\n %s => ", test);
if (expected[0] == '\0') { if (expected[0] == '\0') {
textCursor += sprintf(textCursor, "<empty string>"); textCursor += sprintf(textCursor, "<empty string>");
@ -697,12 +691,19 @@ int runTests(int detailed)
for (int ti = 0; ti < h.testCount; ti += 2) { for (int ti = 0; ti < h.testCount; ti += 2) {
const char* test = h.tests[ti]; const char* test = h.tests[ti];
const char* expected = h.tests[ti + 1]; const char* expected = h.tests[ti + 1];
if (test[0] == ';') {
continue;
}
struct Environment env = defaultEnv(); struct Environment env = defaultEnv();
Object o = parseEval(test, &env); Object o = parseEval(test, &env);
size_t length; size_t length;
char* result = stringObj(&o, &length); char* result = stringObj(&o, &length);
cleanObject(&o); cleanObject(&o);
if (strcmp(result, expected) != 0) { int expectedLen = 0;
while(expected[expectedLen] && expected[expectedLen] != ';') {
expectedLen++;
}
if (strncmp(result, expected, expectedLen) != 0) {
failureCount++; failureCount++;
printf("Test failed!\n"); printf("Test failed!\n");
printf("%s\n", test); printf("%s\n", test);

View File

@ -14,6 +14,8 @@
(def nl (ch 10)) (def nl (ch 10))
(def fore (fn (f list) ((map f list) "")))
(def first (fn (list) (at 0 list))) (def first (fn (list) (at 0 list)))
(def up "..") (def up "..")
@ -21,7 +23,7 @@
(def config (cat ~ "/.pebblisp.pbl")) (def config (cat ~ "/.pebblisp.pbl"))
(def r (fn () (loadfile config))) (def reloadConfig (fn () (loadfile config)))
(def hour (fn (ti) ( (def hour (fn (ti) (
(def h (% ti.hour 12)) (def h (% ti.hour 12))
@ -30,25 +32,42 @@
(def zero (fn (num) (cat (if (< num 10) "0" "") num))) (def zero (fn (num) (cat (if (< num 10) "0" "") num)))
(def clock (fn (ti) (cat (hour ti) ":" (zero ti.minute) ":" (zero ti.sec)))) (def string (fn (a) (cat "" a)))
(struct Alias (name value)) (struct Alias (name value))
(def aliases ( (def aliases (
(Alias "ls" "ls --color") (Alias "ls" "ls --color")
(Alias "alias" "(alias)")
(Alias "pbl" "vim ~/.pebblisp.pbl")
(Alias "r" "(reloadConfig)")
)) ))
(def alias (fn () (
(reduce (map (fn (a) (cat nl a.name " -> " a.value)) aliases) cat "")
)))
(def preprocess (fn (text) ( (def preprocess (fn (text) (
(def matches (fil (fn (a) (= text a.name)) aliases)) (def matches (fil (fn (a) (= text a.name)) aliases))
(def match (first matches)) (def match (first matches))
(if (iserr match) text match.value) (if (iserr match) text match.value)
))) )))
(def clock (fn (ti) (cat (hour ti) ":" (zero ti.minute) ":" (zero ti.sec))))
(def cleanDir (fn () (
(def di (cwd))
(if (matches di (cat ~ "*"))
(cat "~" (substr (slen ~) 999 di))
di
)
)))
(def prompt (fn (a) ( (def prompt (fn (a) (
(def ti (time)) (def ti (time))
(def d (cwd)) (def d (cleanDir))
(cat nl (cat nl
esc "]0; " d (ch 7) esc "]0; " d (ch 7)
bold red "[sage] " blue (clock) " " reset cyan d nl bold red "[sage] " blue (clock ti) " " reset cyan d nl
bold green "pebblisp ~> " reset) bold green "pebblisp ~> " reset)
))) )))

View File

@ -21,7 +21,7 @@ struct ObjectTable buildTable(size_t capacity)
struct ObjectTable table = { struct ObjectTable table = {
.count = 0, .count = 0,
.capacity = capacity, .capacity = capacity,
.elements = malloc(sizeof(struct EnvElement) * capacity), .elements = capacity ? malloc(sizeof(struct EnvElement) * capacity) : NULL,
}; };
for (int i = 0; i < table.capacity; i++) { for (int i = 0; i < table.capacity; i++) {
table.elements[i].symbol = NULL; table.elements[i].symbol = NULL;
@ -35,16 +35,8 @@ struct ObjectTable buildTable(size_t capacity)
/// \param object Should already be cloned /// \param object Should already be cloned
void addToTable(struct ObjectTable* table, char* name, Object object) void addToTable(struct ObjectTable* table, char* name, Object object)
{ {
size_t h = hash(name) % table->capacity; if (table->capacity < (table->count + 1) * 2) {
while(table->elements[h].symbol) { struct ObjectTable newTable = buildTable(table->capacity ? table->capacity * 2 : 4);
h = (h + 1) % table->capacity;
}
table->elements[h].symbol = name;
table->elements[h].object = object;
table->count += 1;
if (table->capacity < table->count * 2) {
struct ObjectTable newTable = buildTable(table->capacity * 2);
for (int i = 0; i < table->capacity; i++) { for (int i = 0; i < table->capacity; i++) {
if (table->elements[i].symbol) { if (table->elements[i].symbol) {
addToTable(&newTable, table->elements[i].symbol, table->elements[i].object); addToTable(&newTable, table->elements[i].symbol, table->elements[i].object);
@ -53,10 +45,21 @@ void addToTable(struct ObjectTable* table, char* name, Object object)
free(table->elements); free(table->elements);
*table = newTable; *table = newTable;
} }
size_t h = hash(name) % table->capacity;
while(table->elements[h].symbol) {
h = (h + 1) % table->capacity;
}
table->elements[h].symbol = name;
table->elements[h].object = object;
table->count += 1;
} }
Object* getFromTable(struct ObjectTable* table, const char* name) Object* getFromTable(struct ObjectTable* table, const char* name)
{ {
if (table->capacity == 0) {
return NULL;
}
size_t h = hash(name) % table->capacity; size_t h = hash(name) % table->capacity;
while(table->elements[h].symbol) { while(table->elements[h].symbol) {
if (strcmp(name, table->elements[h].symbol) == 0) { if (strcmp(name, table->elements[h].symbol) == 0) {

View File

@ -698,6 +698,7 @@ void handler(int nSignum, siginfo_t* si, void* vcontext)
exit(139); exit(139);
} }
// TODO: Track crashes in ~/.plcrash or some such
int main(int argc, const char* argv[]) int main(int argc, const char* argv[])
{ {
struct Environment env = defaultEnv(); struct Environment env = defaultEnv();

View File

@ -180,6 +180,84 @@ Object charVal(Object* params, unused int length, unused struct Environment* env
return numberObject(test.string[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];
int len = end.number - start.number;
if (len < 0 || start.number >= strlen(string.string)) {
return errorWithContext(BAD_PARAMS_ON, string.string);
}
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;

View File

@ -182,6 +182,48 @@ tfn(charVal, "char",
"(char \"\")", "0", "(char \"\")", "0",
); );
tfn(slen, "slen",
({ isStringy, isNumber }),
"Returns the length of the given string",
"(slen \"string\")", "6",
"(slen \"\")", "0",
);
tfn(chars, "chars",
({ isStringy, isListy }),
"Get a list of all chars in the given string",
"(chars \"hello\")", "( h e l l o )",
"(chars \"\")", "( )",
);
tfn(matches, "matches",
({ isStringy, isStringy, 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",
({ isNumber, isNumber, isStringy, isStringy }),
"Get a substring from the given string.",
"(substr 1 3 \"Hello\")", "el",
"(substr 99 3 \"Hello\")", "BAD_PARAMS_ON: Hello;",
"(substr 98 99 \"Hello\")", "BAD_PARAMS_ON: Hello;",
"(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.", "Evaluate the given string or quoted list.",