diff --git a/src/env.c b/src/env.c index eb29e9b..82c9f39 100644 --- a/src/env.c +++ b/src/env.c @@ -132,9 +132,21 @@ struct Environment envForLambda(const Object* params, const Object* arguments, i const Object* param = params->list; const Object* argument = arguments; for (int i = 0; i < paramCount && param; i++) { + const char* paramName = param->string; + if (paramName[0] == '.' && paramName[1] == '.' && paramName[2] == '.' && paramName[3] != '\0') { + paramName = ¶mName[3]; + Object BuildListNamed(varargs); + while (argument) { + addToList(varargs, eval(argument, outer)); + argument = argument->forward; + } + addToEnv(&env, paramName, varargs); + cleanObject(&varargs); + break; + } + Object newEnvObj = eval(argument, outer); - const char* paramName = param->string; addToEnv(&env, paramName, newEnvObj); cleanObject(&newEnvObj); diff --git a/src/examples/lib.pbl b/src/examples/lib.pbl index 26e7316..efc943f 100644 --- a/src/examples/lib.pbl +++ b/src/examples/lib.pbl @@ -1,4 +1,5 @@ #!/usr/bin/pl + (def string (fn (a) (cat "" a))) (def switch (fn (val dict) ( @@ -27,9 +28,19 @@ (def nl (ch 10)) -(def prnl (fn (_txt) - "Print with an appended newline." ( - (prn (cat _txt nl)) +(def prnlambda (fn (text) (prn text))) + +(def prnl (fn (...text) + "Print the given elements with an appended newline." ( + (for-each prnlambda text) + (prn nl) +))) + +(def join (fn (separator text) + "With the given separator, join the given elements as a string. (join \"_\" (1 2 3)) => 1_2_3" ( + (def initial (first text)) + (def concat (fn (left right) (cat left separator right))) + (reduce (rest text) concat initial) ))) (def _indexOf (fn (txt search) ( @@ -80,18 +91,22 @@ (+ (fib (- a 1)) (fib (- a 2))) ))) -(def max (fn (a b) - "Return the larger of the given values" - (if (> a b) a b) +(def _max (fn (a b) (if (> a b) a b))) + +(def max (fn (...values) + "Return the largest of the given values" + (reduce values _max (first values)) )) -(def min (fn (a b) - "Return the smaller of the given values" - (if (< a b) a b) +(def _min (fn (a b) (if (< a b) a b))) + +(def min (fn (...values) + "Return the smallest of the given values" + (reduce values _min (first values)) )) (def for-each (fn (action list) - "Apply the given action to each element in list." ( + "Apply the given action to each element in list, and return an empty string." ( (map action list) "" ))) @@ -149,14 +164,4 @@ (def endsWith (fn (text pattern) ( (matches text (cat "*" pattern)) -))) - -; Switch expression -; Doesn't yet work with lambdas -;(def switch (fn (val pair_list) -; (if (= 0 (len pair_list)) "no match" -; (if (= val (at 0 (at 0 pair_list))) (at 1 (at 0 pair_list)) ( -; (switch val (rest pair_list)) -; )) -; ) -;)) +))) \ No newline at end of file diff --git a/src/main.c b/src/main.c index bd92b5e..371e741 100644 --- a/src/main.c +++ b/src/main.c @@ -244,7 +244,7 @@ int main(int argc, const char* argv[]) readFile(SCRIPTDIR "/lib.pbl", &env); } - Object o = parseEval("(def prompt %%)", &env, nullTerminated("pebblisp::> ")); + Object o = parseEval("(def prompt %%)", &env); cleanObject(&o); o = parseEval("(def preprocess (fn (text) (text)))", &env); cleanObject(&o); diff --git a/src/object.c b/src/object.c index 9981f6c..587d035 100644 --- a/src/object.c +++ b/src/object.c @@ -227,7 +227,7 @@ void stringStruct(struct string* s, const Object* obj) */ int stringNObj(struct string* s, const Object* obj) { - inflate(s, 16); // In most cases this is enough to fit the appended string. + inflate(s, 32); // In most cases this is enough to fit the appended string. switch (obj->type) { case TYPE_NUMBER: appendf(s, "%ld", obj->number); diff --git a/src/pebblisp.c b/src/pebblisp.c index b128d20..48a6f9a 100644 --- a/src/pebblisp.c +++ b/src/pebblisp.c @@ -10,16 +10,14 @@ #endif -Object singleDef(Object* string, Object* value, struct Environment* env) +Object singleDef(const char* name, Object* value, struct Environment* env) { - const char* name = string->string; - Object finalValue = eval(value, env); addToEnv(env, name, finalValue); cleanObject(&finalValue); - return cloneObject(*string); + return nullTerminated(name); } Object listDef(Object* nameList, Object* valueList, struct Environment* env) @@ -29,7 +27,17 @@ Object listDef(Object* nameList, Object* valueList, struct Environment* env) if (!value) { break; } - singleDef(POINTER, value, env); + const char* name = POINTER->string; + if (name[0] == '.' && name[1] == '.' && name[2] == '.' && name[3] != '\0') { + Object BuildListNamed(varargs); + while (value) { + addToList(varargs, *value); + value = value->forward; + } + addToEnv(env, &name[3], varargs); + break; + } + addToEnv(env, name, eval(value, env)); value = value->forward; } return cloneObject(*nameList); @@ -40,7 +48,7 @@ Object listDef(Object* nameList, Object* valueList, struct Environment* env) * * If `argForms` (symbol) and `argForms->forward` (value) are lists of the same * length, define each symbol element with the corresponding value element. - * I.e. `(def (a b) (5 20))` would store `a` as `5` and `b` as `20`. + * I.e. `(def '(a b) (5 20))` would store `a` as `5` and `b` as `20`. * * @param argForms The symbol(s) and value(s) to define in the environment * @param env The environment to add the new definition to @@ -49,7 +57,7 @@ Object listDef(Object* nameList, Object* valueList, struct Environment* env) Object def(Object* params, unused int length, struct Environment* env) { if (isStringy(params[0])) { - return singleDef(¶ms[0], ¶ms[1], env); + return singleDef(params[0].string, ¶ms[1], env); } if (length == 2 && isListy(params[0]) && isListy(params[1])) { @@ -125,8 +133,8 @@ Object mapO(Object* params, int length, struct Environment* env) Object lambda = eval(¶ms[0], env); const Object* inputList = ¶ms[1]; - if (!isFuncy(lambda)) { - throw(BAD_TYPE, "First argument of (map) should be func-like."); + if (lambda.type != TYPE_LAMBDA) { + throw(BAD_TYPE, "First argument of (map) must be a lambda!"); // TODO: why no function? } Object BuildListNamed(outputList); @@ -567,7 +575,9 @@ Object parseEval(const char* input, struct Environment* env) free(err.context); return o; } + if (!tokens->text) { + free(tokens); return symFromSlice(" ", 1); } diff --git a/src/pebblisp.h b/src/pebblisp.h index 68a2b18..761efd2 100644 --- a/src/pebblisp.h +++ b/src/pebblisp.h @@ -23,11 +23,11 @@ typedef struct Result { Object eval(const Object* obj, struct Environment* env); -Result readSeq(struct Slice* slices, va_list* injections); +Result readSeq(struct Slice* slices); 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); diff --git a/src/tokens.c b/src/tokens.c index 9e514cc..f51060e 100644 --- a/src/tokens.c +++ b/src/tokens.c @@ -11,17 +11,21 @@ */ // Is the char a standalone token? -static const char singleTokens[] = "()'?."; +static const char singleTokens[] = "()'?"; -int isSingle(const char c) +int isSingle(const char *c) { int i = 0; while (singleTokens[i] != '\0') { - if (singleTokens[i] == c) { + if (singleTokens[i] == c[0]) { return 1; } i++; } + // It's not a single if it's next to another '.' + if (c[0] == '.' && c[1] != '.' && c[-1] != '.') { + return 1; + } return 0; } @@ -91,7 +95,7 @@ struct Slice* nf_tokenize(const char* input, struct Error* err) slices[slice].text = &input[i]; slices[slice].lineNumber = lineNumber; - if (isSingle(input[i])) { + if (isSingle(&input[i])) { i++; } else if (input[i] == ';') { while (input[i] && input[i] != '\n') { @@ -145,7 +149,7 @@ void processString(const char* input, struct Error* err, struct Slice* slices, i void collectSymbol(const char* input, int* i, int* length) { - while (!isWhitespace(input[++(*i)]) && !isSingle(input[(*i)]) && input[(*i)] != '"' && input[(*i)] != '\0') { + while (!isWhitespace(input[++(*i)]) && !isSingle(&input[(*i)]) && input[(*i)] != '"' && input[(*i)] != '\0') { (*length)++; } } diff --git a/src/tokens.h b/src/tokens.h index fba01c5..2fbfa08 100644 --- a/src/tokens.h +++ b/src/tokens.h @@ -3,7 +3,7 @@ #include "pebblisp.h" -int isSingle(char c); +int isSingle(const char *c); int isDigit(char c);