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:
parent
eb2dde72e3
commit
b419826b8e
|
@ -1,6 +1,7 @@
|
|||
#include <stdio.h>
|
||||
#include <limits.h>
|
||||
|
||||
#define HASHLESS_ENV
|
||||
#include "pebcom.h"
|
||||
#include "object.h"
|
||||
#include "pebbleobject.h"
|
||||
|
|
49
src/env.c
49
src/env.c
|
@ -8,10 +8,6 @@
|
|||
#include "web.h"
|
||||
#include "plfunc.h"
|
||||
|
||||
#define printd(...) ;
|
||||
|
||||
#define HASH_ENV
|
||||
|
||||
struct symFunc buildFuncSym(const char* symbol, Object (* func)(Object*, int, struct Environment*), const char* help,
|
||||
const char* const* tests, size_t testLength);
|
||||
|
||||
|
@ -60,7 +56,7 @@ struct symFunc {
|
|||
#define pf(_func) buildFuncSym(_func ## Symbol, &(_func))
|
||||
#endif
|
||||
|
||||
#ifdef HASH_ENV // Hash-based env
|
||||
#ifndef HASHLESS_ENV // Hash-based env
|
||||
Object* fetch(const char* name, struct Environment* env)
|
||||
{
|
||||
while (env) {
|
||||
|
@ -104,18 +100,10 @@ struct Environment envForLambda(const Object* params, const Object* arg_forms, i
|
|||
|
||||
struct Environment env = {
|
||||
.outer = NULL,
|
||||
.table = buildTable(4),
|
||||
.table = buildTable(paramCount * 2),
|
||||
.refs = 1,
|
||||
};
|
||||
|
||||
if (paramCount == 0) {
|
||||
if (outer) {
|
||||
outer->refs += 1;
|
||||
return *outer;
|
||||
}
|
||||
return env;
|
||||
}
|
||||
|
||||
const Object* march = arg_forms;
|
||||
for (int i = 0; i < paramCount; i++) {
|
||||
const char* newObjName = itemAt(params, i)->string;
|
||||
|
@ -186,6 +174,10 @@ struct Environment defaultEnv()
|
|||
#ifndef PBL_PLATFORM_APLITE
|
||||
pf(rest),
|
||||
pf(charAt),
|
||||
pf(chars),
|
||||
pf(matches),
|
||||
pf(slen),
|
||||
pf(substring),
|
||||
pf(isNum),
|
||||
pf(isList),
|
||||
pf(isString),
|
||||
|
@ -447,6 +439,10 @@ struct Environment defaultEnvPreAllocated(struct EnvElement* elements)
|
|||
#ifndef PBL_PLATFORM_APLITE
|
||||
pf(rest),
|
||||
pf(charAt),
|
||||
pf(chars),
|
||||
pf(matches),
|
||||
pf(slen),
|
||||
pf(substring),
|
||||
pf(isNum),
|
||||
pf(isList),
|
||||
pf(isString),
|
||||
|
@ -488,12 +484,6 @@ struct Environment defaultEnvPreAllocated(struct EnvElement* elements)
|
|||
e.elements[j].symbol = NULL;
|
||||
}
|
||||
|
||||
#ifdef CACHE
|
||||
for (i = 0; i < CACHE_SIZE + 1; i++) {
|
||||
cache[i] = NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
helpInitialized = 1;
|
||||
return e;
|
||||
}
|
||||
|
@ -618,12 +608,12 @@ void printColored(const char* code)
|
|||
int isQuote = 0;
|
||||
printf("[%dm", getColor(depth));
|
||||
while (code[c]) {
|
||||
if (code[c] == '(' && !isQuote) {
|
||||
if ((code[c] == '(' || code[c] == '{') && !isQuote) {
|
||||
depth += 1;
|
||||
printf("[%dm", getColor(depth));
|
||||
} else if (code[c] == ')' && !isQuote) {
|
||||
} else if ((code[c] == ')' || code[c] == '}') && !isQuote) {
|
||||
depth -= 1;
|
||||
printf(")[%dm", getColor(depth));
|
||||
printf("%c[%dm", code[c], getColor(depth));
|
||||
c++;
|
||||
continue;
|
||||
} 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) {
|
||||
const char* test = h.tests[ti];
|
||||
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);
|
||||
if (expected[0] == '\0') {
|
||||
textCursor += sprintf(textCursor, "<empty string>");
|
||||
|
@ -697,12 +691,19 @@ int runTests(int detailed)
|
|||
for (int ti = 0; ti < h.testCount; ti += 2) {
|
||||
const char* test = h.tests[ti];
|
||||
const char* expected = h.tests[ti + 1];
|
||||
if (test[0] == ';') {
|
||||
continue;
|
||||
}
|
||||
struct Environment env = defaultEnv();
|
||||
Object o = parseEval(test, &env);
|
||||
size_t length;
|
||||
char* result = stringObj(&o, &length);
|
||||
cleanObject(&o);
|
||||
if (strcmp(result, expected) != 0) {
|
||||
int expectedLen = 0;
|
||||
while(expected[expectedLen] && expected[expectedLen] != ';') {
|
||||
expectedLen++;
|
||||
}
|
||||
if (strncmp(result, expected, expectedLen) != 0) {
|
||||
failureCount++;
|
||||
printf("Test failed!\n");
|
||||
printf("%s\n", test);
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
|
||||
(def nl (ch 10))
|
||||
|
||||
(def fore (fn (f list) ((map f list) "")))
|
||||
|
||||
(def first (fn (list) (at 0 list)))
|
||||
|
||||
(def up "..")
|
||||
|
@ -21,7 +23,7 @@
|
|||
|
||||
(def config (cat ~ "/.pebblisp.pbl"))
|
||||
|
||||
(def r (fn () (loadfile config)))
|
||||
(def reloadConfig (fn () (loadfile config)))
|
||||
|
||||
(def hour (fn (ti) (
|
||||
(def h (% ti.hour 12))
|
||||
|
@ -30,25 +32,42 @@
|
|||
|
||||
(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))
|
||||
(def aliases (
|
||||
(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 matches (fil (fn (a) (= text a.name)) aliases))
|
||||
(def match (first matches))
|
||||
(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 ti (time))
|
||||
(def d (cwd))
|
||||
(def d (cleanDir))
|
||||
|
||||
(cat nl
|
||||
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)
|
||||
)))
|
||||
|
|
25
src/hash.c
25
src/hash.c
|
@ -21,7 +21,7 @@ struct ObjectTable buildTable(size_t capacity)
|
|||
struct ObjectTable table = {
|
||||
.count = 0,
|
||||
.capacity = capacity,
|
||||
.elements = malloc(sizeof(struct EnvElement) * capacity),
|
||||
.elements = capacity ? malloc(sizeof(struct EnvElement) * capacity) : NULL,
|
||||
};
|
||||
for (int i = 0; i < table.capacity; i++) {
|
||||
table.elements[i].symbol = NULL;
|
||||
|
@ -35,16 +35,8 @@ struct ObjectTable buildTable(size_t capacity)
|
|||
/// \param object Should already be cloned
|
||||
void addToTable(struct ObjectTable* table, char* name, Object object)
|
||||
{
|
||||
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;
|
||||
|
||||
if (table->capacity < table->count * 2) {
|
||||
struct ObjectTable newTable = buildTable(table->capacity * 2);
|
||||
if (table->capacity < (table->count + 1) * 2) {
|
||||
struct ObjectTable newTable = buildTable(table->capacity ? table->capacity * 2 : 4);
|
||||
for (int i = 0; i < table->capacity; i++) {
|
||||
if (table->elements[i].symbol) {
|
||||
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);
|
||||
*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)
|
||||
{
|
||||
if (table->capacity == 0) {
|
||||
return NULL;
|
||||
}
|
||||
size_t h = hash(name) % table->capacity;
|
||||
while(table->elements[h].symbol) {
|
||||
if (strcmp(name, table->elements[h].symbol) == 0) {
|
||||
|
|
|
@ -698,6 +698,7 @@ void handler(int nSignum, siginfo_t* si, void* vcontext)
|
|||
exit(139);
|
||||
}
|
||||
|
||||
// TODO: Track crashes in ~/.plcrash or some such
|
||||
int main(int argc, const char* argv[])
|
||||
{
|
||||
struct Environment env = defaultEnv();
|
||||
|
|
78
src/plfunc.c
78
src/plfunc.c
|
@ -180,6 +180,84 @@ Object charVal(Object* params, unused int length, unused struct Environment* env
|
|||
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)
|
||||
{
|
||||
int printPointers = 0;
|
||||
|
|
42
src/plfunc.h
42
src/plfunc.h
|
@ -182,6 +182,48 @@ tfn(charVal, "char",
|
|||
"(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
|
||||
fn(parseEvalO, "eval",
|
||||
"Evaluate the given string or quoted list.",
|
||||
|
|
Loading…
Reference in New Issue