Added TODO. Text size adjusts with code length
Can handle hex and binary. Other cleanup All tests pass
This commit is contained in:
parent
796a4cdc91
commit
a5ecb1b3aa
|
@ -1,6 +1,11 @@
|
|||
# PebbLisp
|
||||
A very basic LISP implementation with an interpreter and editor written for the Pebble.
|
||||
|
||||
# TODO
|
||||
- Call scripts from scripts
|
||||
- Call more pebble functions
|
||||
- Maybe hard-code more built-in functions to ease up on memory use
|
||||
|
||||
# Writing in PebbLisp
|
||||
PebbLisp includes several built-ins functionalities.
|
||||
|
||||
|
|
25
src/calc.c
25
src/calc.c
|
@ -52,6 +52,27 @@ static void cycle_tokens(ClickRecognizerRef recognizer, void *context)
|
|||
updateText();
|
||||
}
|
||||
|
||||
static const char *fonts[] = {
|
||||
FONT_KEY_GOTHIC_28_BOLD,
|
||||
FONT_KEY_GOTHIC_24_BOLD,
|
||||
FONT_KEY_GOTHIC_18_BOLD,
|
||||
FONT_KEY_GOTHIC_14_BOLD
|
||||
};
|
||||
|
||||
static const int smallestFont = 3;
|
||||
|
||||
static void adjustFont()
|
||||
{
|
||||
int i = 0;
|
||||
while(temptext[i++]) {}
|
||||
int f = i < 50 ? smallestFont - 3 :
|
||||
i < 100 ? smallestFont - 2 :
|
||||
i < 130 ? smallestFont - 1 :
|
||||
smallestFont;
|
||||
text_layer_set_font(s_input_text_layer,
|
||||
fonts_get_system_font(fonts[f]));
|
||||
}
|
||||
|
||||
/**
|
||||
* In normal token list, backspace if possible, otherwise return to script list
|
||||
* In function token list, return to normal token list
|
||||
|
@ -69,6 +90,7 @@ static void click_backspace(ClickRecognizerRef recognizer, void *context)
|
|||
using_func_tokens = false;
|
||||
updateText();
|
||||
}
|
||||
adjustFont();
|
||||
}
|
||||
|
||||
// Adds the current string to the main string
|
||||
|
@ -79,7 +101,9 @@ static void add_token()
|
|||
if(using_func_tokens) {
|
||||
using_func_tokens = false;
|
||||
}
|
||||
|
||||
updateText();
|
||||
adjustFont();
|
||||
}
|
||||
|
||||
/** Code running and saving **/
|
||||
|
@ -160,6 +184,7 @@ static void code_window_load(Window *window)
|
|||
if(persist_exists(current_code)) {
|
||||
persist_read_string(current_code, mytext, SMAX_LENGTH);
|
||||
updateText();
|
||||
adjustFont();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@ int current_code;
|
|||
// Currently selected button, starts on '('
|
||||
static int8_t selected_token = 1;
|
||||
|
||||
|
||||
// PebbLisp environment
|
||||
static struct Environment env;
|
||||
|
||||
|
@ -49,7 +48,7 @@ const char *tokens[] = {
|
|||
"+ ", "- ", "* ", "/ ",
|
||||
"1","2","3",
|
||||
"4","5","6",
|
||||
"7","8","9", "0",
|
||||
"7","8","9", "0", "x",
|
||||
"a", "b", "c", "d", "e",
|
||||
"= ", "< ", "> ",
|
||||
"\"",
|
||||
|
@ -63,7 +62,6 @@ const char *func_tokens[] = {
|
|||
"spent ", "window ",
|
||||
"max ", "min ",
|
||||
"sq ", "cube ", "exp "
|
||||
|
||||
};
|
||||
|
||||
bool using_func_tokens = false;
|
||||
|
|
|
@ -62,6 +62,7 @@ struct Environment envForLambda(const Object *params, const Object *arg_forms,
|
|||
const Object *march = arg_forms;
|
||||
for(int i = 0; i < paramCount; i++) {
|
||||
const char *newObjName = itemAt(params, i)->string;
|
||||
// Eval the `march` list
|
||||
const Object newEnvObj = eval(march, outer);
|
||||
addToEnv(&env, newObjName, newEnvObj); // Could use eval_forms?
|
||||
march = march->forward;
|
||||
|
@ -172,6 +173,10 @@ const char *codes[] = {
|
|||
) \
|
||||
))",
|
||||
|
||||
"(def r (fn (L) \
|
||||
(if (= L () \
|
||||
",
|
||||
|
||||
NULL
|
||||
};
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#define RESULT_LENGTH 20
|
||||
#define RESULT_LENGTH 30
|
||||
|
||||
#ifdef DEBUG
|
||||
#define printd(...) printf(__VA_ARGS__)
|
||||
|
@ -211,7 +211,7 @@ static const char *errorText[] = {
|
|||
"BAD_TYPE",
|
||||
"UNEXPECTED_FORM",
|
||||
"LISTS_NOT_SAME_SIZE",
|
||||
"SYMBOLS_CANT_START_WITH_DIGITS",
|
||||
"BAD_NUMBER",
|
||||
"UNSUPPORTED_NUMBER_TYPE",
|
||||
"NOT_A_SYMBOL",
|
||||
"ONLY_ONE_ARGUMENT",
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
#define MAX_ENV_ELM 50 // 50
|
||||
|
||||
#define FOR_POINTER_IN_LIST(_list) \
|
||||
if(_list && _list->type == TYPE_LIST) \
|
||||
for(Object *_element = _list->list; \
|
||||
_element != NULL;\
|
||||
_element = _element->forward)
|
||||
|
@ -32,7 +33,7 @@ enum errorCode {
|
|||
BAD_TYPE,
|
||||
UNEXPECTED_FORM,
|
||||
LISTS_NOT_SAME_SIZE,
|
||||
SYMBOLS_CANT_START_WITH_DIGITS,
|
||||
BAD_NUMBER,
|
||||
UNSUPPORTED_NUMBER_TYPE,
|
||||
NOT_A_SYMBOL,
|
||||
ONLY_ONE_ARGUMENT,
|
||||
|
@ -65,12 +66,11 @@ struct Object {
|
|||
Object *list;
|
||||
char *string;
|
||||
Object (*func)(Object, Object, struct Environment *);
|
||||
struct Lambda *lambda; // Maybe better as not a pointer?
|
||||
struct Lambda *lambda;
|
||||
enum errorCode err;
|
||||
};
|
||||
};
|
||||
|
||||
// Maybe better as pointers?
|
||||
struct Lambda {
|
||||
Object params;
|
||||
Object body;
|
||||
|
@ -81,7 +81,6 @@ void printList(const Object *list);
|
|||
void printObj(const Object *obj);
|
||||
void debugObj(const Object *obj);
|
||||
void printErr(const Object *obj);
|
||||
int getType(const Object *obj);
|
||||
|
||||
int isEmpty(const Object *obj);
|
||||
Object *tail(const Object *listObj);
|
||||
|
|
283
src/pebblisp.c
283
src/pebblisp.c
|
@ -24,15 +24,17 @@ Object evalDefArgs(const Object *argForms, struct Environment *env)
|
|||
|
||||
Object evalIfArgs(const Object *argForms, struct Environment *env)
|
||||
{
|
||||
return eval(argForms, env).number?
|
||||
return eval(argForms, env).number ?
|
||||
eval(argForms->forward, env) :
|
||||
eval(argForms->forward->forward, env);
|
||||
}
|
||||
|
||||
Object evalLambdaArgs(const Object *argForms)
|
||||
{
|
||||
// params // body
|
||||
return constructLambda(argForms, argForms? argForms->forward : NULL);
|
||||
return constructLambda(
|
||||
argForms, // Params
|
||||
argForms ? argForms->forward : NULL // Body
|
||||
);
|
||||
}
|
||||
|
||||
Object evalMapArgs(const Object *argForms, struct Environment *env)
|
||||
|
@ -42,12 +44,11 @@ Object evalMapArgs(const Object *argForms, struct Environment *env)
|
|||
|
||||
Object lambda = eval(argForms, env);
|
||||
const Object *inputList = argForms->forward;
|
||||
Object outputList = listObject();
|
||||
|
||||
if(lambda.type != TYPE_LAMBDA || inputList->type != TYPE_LIST)
|
||||
if(lambda.type != TYPE_LAMBDA)
|
||||
return errorObject(BAD_TYPE);
|
||||
|
||||
Object list = listObject();
|
||||
|
||||
FOR_POINTER_IN_LIST(inputList) {
|
||||
// Create a new list for each element,
|
||||
// since lambda evaluation looks for a list
|
||||
|
@ -57,13 +58,13 @@ Object evalMapArgs(const Object *argForms, struct Environment *env)
|
|||
struct Environment newEnv = envForLambda(params, &tempList, env);
|
||||
|
||||
// Add the lambda evaluation to the list
|
||||
nf_addToList(&list, eval(&lambda.lambda->body, &newEnv));
|
||||
nf_addToList(&outputList, eval(&lambda.lambda->body, &newEnv));
|
||||
deleteEnv(&newEnv);
|
||||
cleanObject(&tempList);
|
||||
}
|
||||
cleanObject(&lambda);
|
||||
|
||||
return list;
|
||||
return outputList;
|
||||
}
|
||||
|
||||
Object evalBuiltIns(const Object *first, const Object *rest,
|
||||
|
@ -72,6 +73,7 @@ Object evalBuiltIns(const Object *first, const Object *rest,
|
|||
if(first->type != TYPE_SYMBOL) {
|
||||
return errorObject(NOT_A_SYMBOL);
|
||||
}
|
||||
|
||||
if(strcmp(first->string, "def") == 0) {
|
||||
return evalDefArgs(rest, env);
|
||||
} else if(strcmp(first->string, "if") == 0) {
|
||||
|
@ -85,7 +87,7 @@ Object evalBuiltIns(const Object *first, const Object *rest,
|
|||
return errorObject(BUILT_IN_NOT_FOUND);
|
||||
}
|
||||
|
||||
void eval_forms(Object *destList, const Object *src, struct Environment *env)
|
||||
void evalForms(Object *destList, const Object *src, struct Environment *env)
|
||||
{
|
||||
int length = listLength(src) - 1; // Not counting first_form
|
||||
for(int i = 0; i < length; i++) { // Evaluates all in list
|
||||
|
@ -94,9 +96,82 @@ void eval_forms(Object *destList, const Object *src, struct Environment *env)
|
|||
}
|
||||
}
|
||||
|
||||
Object evalList(const Object *obj, struct Environment *env)
|
||||
{
|
||||
const int evalLength = listLength(obj);
|
||||
|
||||
if(evalLength == 0)
|
||||
return cloneObject(*obj);
|
||||
|
||||
if(evalLength == 1) {
|
||||
if(listLength(obj->list) == 0)
|
||||
return cloneObject(*obj);
|
||||
return eval(obj->list, env);
|
||||
}
|
||||
|
||||
Object *first_form = obj->list;
|
||||
|
||||
{ // Try to eval built-ins
|
||||
const Object builtIn =
|
||||
evalBuiltIns(first_form, first_form->forward, env);
|
||||
|
||||
if(!isError(builtIn, BUILT_IN_NOT_FOUND) &&
|
||||
!isError(builtIn, NOT_A_SYMBOL)) {
|
||||
return builtIn;
|
||||
}
|
||||
}
|
||||
|
||||
Object first_eval = eval(first_form, env);
|
||||
if(first_eval.type == TYPE_FUNC) {
|
||||
int length = evalLength - 1; // Not counting first_form
|
||||
Object rest[length];
|
||||
evalForms(rest, obj, env);
|
||||
|
||||
Object func_eval = rest[0];
|
||||
if(length == 1) {
|
||||
func_eval = first_eval.func(
|
||||
func_eval, errorObject(ONLY_ONE_ARGUMENT), env);
|
||||
// Consider it a partial function if only one arg
|
||||
if(isError(func_eval, ONLY_ONE_ARGUMENT))
|
||||
return cloneObject(*obj);
|
||||
} else {
|
||||
for(int i = 1; i < length; i++) {
|
||||
Object toClean = func_eval;
|
||||
func_eval = first_eval.func(func_eval, rest[i], env);
|
||||
cleanObject(&toClean);
|
||||
cleanObject(&rest[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return func_eval;
|
||||
|
||||
} else if (first_eval.type == TYPE_LAMBDA) {
|
||||
struct Environment newEnv = envForLambda(
|
||||
&first_eval.lambda->params,
|
||||
first_form->forward,
|
||||
env
|
||||
);
|
||||
Object ret = eval(&first_eval.lambda->body, &newEnv);
|
||||
deleteEnv(&newEnv);
|
||||
cleanObject(&first_eval);
|
||||
return ret;
|
||||
|
||||
} else {
|
||||
Object list = listObject();
|
||||
FOR_POINTER_IN_LIST(obj) {
|
||||
nf_addToList(&list, eval(POINTER, env));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
|
||||
Object eval(const Object *obj, struct Environment *env)
|
||||
{
|
||||
switch(obj->type) {
|
||||
case TYPE_ERROR:
|
||||
case TYPE_FUNC:
|
||||
return *obj;
|
||||
|
||||
case TYPE_NUMBER:
|
||||
case TYPE_BOOL:
|
||||
case TYPE_STRING:
|
||||
|
@ -106,79 +181,13 @@ Object eval(const Object *obj, struct Environment *env)
|
|||
return fetchFromEnvironment(obj->string, env);
|
||||
|
||||
case TYPE_LIST:
|
||||
{
|
||||
if(listLength(obj) == 0)
|
||||
return *obj;
|
||||
|
||||
if(listLength(obj) == 1)
|
||||
return eval(obj->list, env);
|
||||
|
||||
Object *first_form = obj->list;
|
||||
|
||||
{ // Try to eval built-ins
|
||||
const Object builtIn =
|
||||
evalBuiltIns(first_form, first_form->forward, env);
|
||||
|
||||
if(builtIn.type != TYPE_ERROR) {
|
||||
return builtIn;
|
||||
}
|
||||
}
|
||||
|
||||
Object first_eval = eval(first_form, env);
|
||||
if(first_eval.type == TYPE_FUNC) {
|
||||
int length = listLength(obj) - 1; // Not counting first_form
|
||||
Object rest[length];
|
||||
eval_forms(rest, obj, env);
|
||||
|
||||
Object func_eval = rest[0];
|
||||
if(length == 1) {
|
||||
func_eval = first_eval.func(
|
||||
func_eval, errorObject(ONLY_ONE_ARGUMENT), env);
|
||||
if(isError(func_eval, ONLY_ONE_ARGUMENT)) // Consider it a partial function
|
||||
return cloneObject(*obj);
|
||||
} else {
|
||||
for(int i = 1; i < length; i++) {
|
||||
Object toClean = func_eval;
|
||||
func_eval = first_eval.func(func_eval, rest[i], env);
|
||||
//if(i != length - 1)
|
||||
cleanObject(&toClean);
|
||||
cleanObject(&rest[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return func_eval;
|
||||
|
||||
} else if (first_eval.type == TYPE_LAMBDA) {
|
||||
struct Environment newEnv =
|
||||
envForLambda(&first_eval.lambda->params, first_form->forward, env);
|
||||
Object ret = eval(&first_eval.lambda->body, &newEnv);
|
||||
deleteEnv(&newEnv);
|
||||
cleanObject(&first_eval);
|
||||
return ret;
|
||||
|
||||
} else {
|
||||
Object list = listObject();
|
||||
FOR_POINTER_IN_LIST(obj) {
|
||||
nf_addToList(&list, eval(POINTER, env));
|
||||
}
|
||||
return list;
|
||||
}
|
||||
}
|
||||
return evalList(obj, env);
|
||||
|
||||
case TYPE_LAMBDA:
|
||||
return errorObject(UNEXPECTED_FORM);
|
||||
|
||||
default:
|
||||
return *obj;
|
||||
}
|
||||
}
|
||||
|
||||
Result result(Object obj, struct Slice *slices)
|
||||
{
|
||||
return (Result) {
|
||||
.obj = obj,
|
||||
.slices = slices
|
||||
};
|
||||
return errorObject(BAD_TYPE);
|
||||
}
|
||||
|
||||
Object catObjects(const Object obj1, const Object obj2, struct Environment *env)
|
||||
|
@ -202,6 +211,15 @@ Object catObjects(const Object obj1, const Object obj2, struct Environment *env)
|
|||
return o;
|
||||
}
|
||||
|
||||
Object listEquality(const Object *list1, const Object *list2)
|
||||
{
|
||||
FOR_POINTERS_IN_LISTS(list1, list2) {
|
||||
if(P1->type != P2->type || P1->number != P2->number)
|
||||
return boolObject(0);
|
||||
}
|
||||
return boolObject(1);
|
||||
}
|
||||
|
||||
Object _basicOp(const Object *obj1, const Object *obj2, const char op,
|
||||
struct Environment *env)
|
||||
{
|
||||
|
@ -225,7 +243,9 @@ Object _basicOp(const Object *obj1, const Object *obj2, const char op,
|
|||
case '=':
|
||||
if(obj1->type == TYPE_STRING || obj2->type == TYPE_STRING)
|
||||
return boolObject(!strcmp(obj1->string, obj2->string));
|
||||
return boolObject(n1 == n2);
|
||||
if(obj1->type == TYPE_LIST && obj2->type == TYPE_LIST)
|
||||
return listEquality(obj1, obj2);
|
||||
return boolObject(n1 == n2 && obj1->type == obj2->type);
|
||||
case '>':
|
||||
return boolObject(n1 > n2);
|
||||
case '<':
|
||||
|
@ -376,10 +396,10 @@ Result parse(struct Slice *slices)
|
|||
// todo check for null rest
|
||||
return readSeq(rest);
|
||||
} else { // todo error on missing close paren
|
||||
return result(parseAtom(token), rest);
|
||||
return (Result){parseAtom(token), rest};
|
||||
}
|
||||
} else {
|
||||
return result(errorObject(NULL_PARSE), NULL);
|
||||
return (Result){errorObject(NULL_PARSE), NULL};
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -390,7 +410,7 @@ Result readSeq(struct Slice *tokens)
|
|||
struct Slice *next = tokens;
|
||||
struct Slice *rest = next->text? &next[1] : NULL;
|
||||
if(next->text[0] == ')') {
|
||||
return result(res, rest);
|
||||
return (Result){res, rest};
|
||||
}
|
||||
Result r = parse(tokens);
|
||||
if(r.obj.type == TYPE_ERROR)
|
||||
|
@ -406,7 +426,7 @@ Object parseDecimal(struct Slice *s)
|
|||
int num = 0;
|
||||
for(int i = 0; i < s->length; i++) {
|
||||
if(!isDigit(s->text[i])) {
|
||||
return errorObject(SYMBOLS_CANT_START_WITH_DIGITS);
|
||||
return errorObject(BAD_NUMBER);
|
||||
}
|
||||
num *= 10;
|
||||
num += s->text[i] - '0';
|
||||
|
@ -414,22 +434,56 @@ Object parseDecimal(struct Slice *s)
|
|||
return numberObject(num);
|
||||
}
|
||||
|
||||
Object parseHex(struct Slice *s)
|
||||
{
|
||||
int num = 0;
|
||||
for(int i = 2; i < s->length; i++) {
|
||||
const char c = s->text[i];
|
||||
if(!isHex(c)) {
|
||||
return errorObject(BAD_NUMBER);
|
||||
}
|
||||
num *= 16;
|
||||
if(isDigit(c)) {
|
||||
num += c - '0';
|
||||
} else /* is hex */ {
|
||||
num += c - 'a' + 10;
|
||||
}
|
||||
}
|
||||
return numberObject(num);
|
||||
}
|
||||
|
||||
Object parseBin(struct Slice *s)
|
||||
{
|
||||
int num = 0;
|
||||
for(int i = 2; i < s->length; i++) {
|
||||
const char c = s->text[i];
|
||||
if(c != '0' && c != '1') {
|
||||
return errorObject(BAD_NUMBER);
|
||||
}
|
||||
num *= 2;
|
||||
num += c - '0';
|
||||
}
|
||||
return numberObject(num);
|
||||
}
|
||||
|
||||
Object parseAtom(struct Slice *s)
|
||||
{
|
||||
if(isDigit(s->text[0])) {
|
||||
if(s->text[0] != '0' || s->length == 1)
|
||||
const char c = s->text[0];
|
||||
if(isDigit(c)) {
|
||||
if(c != '0' || s->length == 1) {
|
||||
return parseDecimal(s);
|
||||
else {
|
||||
} else if(c == '0' && s->text[1] == 'x') {
|
||||
return parseHex(s);
|
||||
} else if(c == '0' && s->text[1] == 'b') {
|
||||
return parseBin(s);
|
||||
} else {
|
||||
return errorObject(UNSUPPORTED_NUMBER_TYPE);
|
||||
}
|
||||
|
||||
} else if (s->length == 1 && (s->text[0] == 'T' || s->text[0] == 't')) {
|
||||
} else if (s->length == 1 && (c == 'T' || c == 't')) {
|
||||
return boolObject(1);
|
||||
|
||||
} else if (s->length == 1 && (s->text[0] == 'F' || s->text[0] == 'f')) {
|
||||
} else if (s->length == 1 && (c == 'F' || c == 'f')) {
|
||||
return boolObject(0);
|
||||
|
||||
} else if (s->text[0] == '"' || s->text[0] == '\'') {
|
||||
} else if (c == '"' || c == '\'') {
|
||||
return objFromSlice(s->text, s->length);
|
||||
} else {
|
||||
return symFromSlice(s->text, s->length);
|
||||
|
@ -449,7 +503,7 @@ Object parseEval(const char *input, struct Environment *env)
|
|||
printd("start slice\n");
|
||||
if(debug) {
|
||||
while(debug->text) {
|
||||
char tok[MAX_TOK_LEN];
|
||||
char tok[100];
|
||||
copySlice(tok, debug);
|
||||
printd("slice: '%s'\n", tok);
|
||||
debug++;
|
||||
|
@ -461,32 +515,27 @@ Object parseEval(const char *input, struct Environment *env)
|
|||
int parens = 0;
|
||||
Object obj = numberObject(0);
|
||||
struct Slice *tok = tokens;
|
||||
if(tok[i].text[0] != '(') {
|
||||
Object parsed = parse(tok).obj;
|
||||
if(parsed.type == TYPE_ERROR)
|
||||
return parsed;
|
||||
obj = eval(&parsed, env);
|
||||
cleanObject(&parsed);
|
||||
} else {
|
||||
while(tok[i].text != NULL) {
|
||||
if(tok[i].text[0] == '(') {
|
||||
parens++;
|
||||
} else if(tok[i].text[0] == ')') {
|
||||
parens--;
|
||||
if(parens == 0) {
|
||||
cleanObject(&obj);
|
||||
Object parsed = parse(tok).obj;
|
||||
if(parsed.type == TYPE_ERROR)
|
||||
return parsed;
|
||||
tok = &tok[i + 1];
|
||||
i = -1;
|
||||
//printObj(&parsed);
|
||||
obj = eval(&parsed, env);
|
||||
cleanObject(&parsed);
|
||||
}
|
||||
}
|
||||
i++;
|
||||
while(tok[i].text != NULL) {
|
||||
if(tok[i].text[0] == '(') {
|
||||
parens++;
|
||||
} else if(tok[i].text[0] == ')') {
|
||||
parens--;
|
||||
}
|
||||
|
||||
if(parens == 0) {
|
||||
cleanObject(&obj);
|
||||
Object parsed = parse(tok).obj;
|
||||
if(parsed.type == TYPE_ERROR)
|
||||
return parsed;
|
||||
if(tok[i].text[0] == ')') {
|
||||
tok = &tok[i + 1];
|
||||
i = -1;
|
||||
}
|
||||
obj = eval(&parsed, env);
|
||||
cleanObject(&parsed);
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
free(tokens);
|
||||
return obj;
|
||||
|
|
|
@ -29,14 +29,12 @@ Object eval(const Object *obj, struct Environment *env);
|
|||
Result parse(struct Slice *slices);
|
||||
Result readSeq(struct Slice *slices);
|
||||
Object parseAtom(struct Slice *slice);
|
||||
void copySlice(char * dest, struct Slice *src);
|
||||
Object parseEval(const char *input, struct Environment *env);
|
||||
void eval_forms(Object *destList, const Object *src, struct Environment *env);
|
||||
void evalForms(Object *destList, const Object *src, struct Environment *env);
|
||||
void copySlice(char * dest, struct Slice *src);
|
||||
|
||||
Object evalLambdaArgs(const Object *arg_forms);
|
||||
|
||||
Result result(Object obj, struct Slice *slices);
|
||||
|
||||
// Slices
|
||||
void copySlice(char * dest, struct Slice *src);
|
||||
void debugSlice(struct Slice *s);
|
||||
|
@ -48,6 +46,7 @@ BASIC_OP(mul); BASIC_OP(dvi);
|
|||
BASIC_OP(mod); BASIC_OP(equ);
|
||||
BASIC_OP(gth); BASIC_OP(lth);
|
||||
#undef BASIC_OP
|
||||
|
||||
Object catObjects(const Object obj1, const Object obj2, struct Environment *env);
|
||||
Object filter(Object obj1, Object obj2, struct Environment *env);
|
||||
Object append(Object list, Object newElement, struct Environment *env);
|
||||
|
|
25
src/tests.sh
25
src/tests.sh
|
@ -1,5 +1,7 @@
|
|||
#!/bin/bash
|
||||
|
||||
VALCOM="valgrind -q --leak-check=full --track-origins=yes"
|
||||
|
||||
TOTAL_PASSES=0
|
||||
TOTAL_FAILS=0
|
||||
PASSES=0
|
||||
|
@ -24,11 +26,9 @@ endBlock() {
|
|||
|
||||
title() {
|
||||
echo "[1m$1[0m"
|
||||
# echo "$1" | sed 's/./-/g' | sed 's/$/-/g'
|
||||
}
|
||||
|
||||
pass() {
|
||||
# echo "[0;32m$1 test PASSED[0m"
|
||||
((PASSES++))
|
||||
((TOTAL_PASSES++))
|
||||
}
|
||||
|
@ -42,7 +42,7 @@ fail() {
|
|||
check() {
|
||||
local output
|
||||
if (($VALGRIND == 1)); then
|
||||
local output=$(valgrind -q --leak-check=full --track-origins=yes ./pebblisp "$2")
|
||||
local output=$($VALCOM ./pebblisp "$2")
|
||||
else
|
||||
local output=$(./pebblisp "$2")
|
||||
fi
|
||||
|
@ -62,6 +62,8 @@ title "Plain return tests"
|
|||
check "PlainRet" "10" "10"
|
||||
check "StrRetrn" "\"hey\"" "hey"
|
||||
check "SingleStrRetrn" "'hey'" "hey"
|
||||
check "HexReturn" "0x0f0f0" "61680"
|
||||
check "BinaryReturn" "0b011010011010011" "13523"
|
||||
endBlock
|
||||
|
||||
title "Arithmetic"
|
||||
|
@ -99,7 +101,7 @@ check "EmptLst2" "( )" "( )"
|
|||
check "ListIndex" "(at (+ 1 1) (1 2 1000 4 5))" "1000"
|
||||
check "EvalElems" "((* 10 10) 7)" "( 100 7 )"
|
||||
check "AppendToList" "(ap (1 20 300 4000 50000) 600000)" "( 1 20 300 4000 50000 600000 )"
|
||||
check "AppndToEnvList" "(def l (50000 4000 300 20)) (def l (ap l 1)) (l)" "( 50000 4000 300 20 1 )"
|
||||
check "AppndToEnvList" "(def l (50000 4000 300 20)) (def l (ap l 1)) l" "( 50000 4000 300 20 1 )"
|
||||
check "Rest(Tail)OfList" "(def l (50000 4000 300 20)) (rest l)" "( 4000 300 20 )"
|
||||
check "ListLength" "(def l (1 20 3 'abc' 'banana' (+ 10 5))) (len l)" "6"
|
||||
endBlock
|
||||
|
@ -158,7 +160,9 @@ title "ShouldError"
|
|||
check "LenOfNotList" "(len 5)" "NOT_A_LIST"
|
||||
check "NoMapList" "(map sq)" "( )"
|
||||
check "UnevenLists" "(+ (1 2) (1 2 3))" "LISTS_NOT_SAME_SIZE"
|
||||
check "BadSymbol" "(5df)" "SYMBOLS_START_WITH_LETTERS"
|
||||
check "BadNumber" "(5df)" "BAD_NUMBER"
|
||||
check "BadHex" "(0x0zf)" "BAD_NUMBER"
|
||||
check "BadBinary" "(0b01120)" "BAD_NUMBER"
|
||||
check "BadParens" "(hey()" "MISMATCHED_PARENS"
|
||||
check "BadParens2" "(hey)(" "MISMATCHED_PARENS"
|
||||
check "BadParens3" "((hey(" "MISMATCHED_PARENS"
|
||||
|
@ -175,10 +179,13 @@ endBlock
|
|||
echo ""
|
||||
|
||||
if [ "$TOTAL_FAILS" -ne "0" ]; then
|
||||
echo "[1;31m$TOTAL_FAILS Tests Failed[0m"
|
||||
else
|
||||
echo "$TOTAL_FAILS Tests Failed[0m"
|
||||
echo -n "[1;31m"
|
||||
fi
|
||||
echo "$TOTAL_FAILS Tests Failed[0m"
|
||||
|
||||
if [ "$TOTAL_PASSES" -ne "0" ]; then
|
||||
echo -n "[1;32m"
|
||||
fi
|
||||
echo "$TOTAL_PASSES Tests Passed[0m"
|
||||
|
||||
echo "[1;32m$TOTAL_PASSES Tests Passed[0m"
|
||||
echo ""
|
||||
|
|
|
@ -35,6 +35,10 @@ int isDigit(const char c) {
|
|||
return c >= '0' && c <= '9';
|
||||
}
|
||||
|
||||
int isHex(const char c) {
|
||||
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
|
||||
}
|
||||
|
||||
int isWhitespace(const char c) {
|
||||
return c == ' ' || c == '\t' || c == '\n';
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
|
||||
int isSingle(const char c);
|
||||
int isDigit(const char c);
|
||||
int isHex(const char c);
|
||||
struct Slice *nf_tokenize(const char *input);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue