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
|
# PebbLisp
|
||||||
A very basic LISP implementation with an interpreter and editor written for the Pebble.
|
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
|
# Writing in PebbLisp
|
||||||
PebbLisp includes several built-ins functionalities.
|
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();
|
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 normal token list, backspace if possible, otherwise return to script list
|
||||||
* In function token list, return to normal token 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;
|
using_func_tokens = false;
|
||||||
updateText();
|
updateText();
|
||||||
}
|
}
|
||||||
|
adjustFont();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds the current string to the main string
|
// Adds the current string to the main string
|
||||||
|
@ -79,7 +101,9 @@ static void add_token()
|
||||||
if(using_func_tokens) {
|
if(using_func_tokens) {
|
||||||
using_func_tokens = false;
|
using_func_tokens = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
updateText();
|
updateText();
|
||||||
|
adjustFont();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Code running and saving **/
|
/** Code running and saving **/
|
||||||
|
@ -160,6 +184,7 @@ static void code_window_load(Window *window)
|
||||||
if(persist_exists(current_code)) {
|
if(persist_exists(current_code)) {
|
||||||
persist_read_string(current_code, mytext, SMAX_LENGTH);
|
persist_read_string(current_code, mytext, SMAX_LENGTH);
|
||||||
updateText();
|
updateText();
|
||||||
|
adjustFont();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,6 @@ int current_code;
|
||||||
// Currently selected button, starts on '('
|
// Currently selected button, starts on '('
|
||||||
static int8_t selected_token = 1;
|
static int8_t selected_token = 1;
|
||||||
|
|
||||||
|
|
||||||
// PebbLisp environment
|
// PebbLisp environment
|
||||||
static struct Environment env;
|
static struct Environment env;
|
||||||
|
|
||||||
|
@ -49,7 +48,7 @@ const char *tokens[] = {
|
||||||
"+ ", "- ", "* ", "/ ",
|
"+ ", "- ", "* ", "/ ",
|
||||||
"1","2","3",
|
"1","2","3",
|
||||||
"4","5","6",
|
"4","5","6",
|
||||||
"7","8","9", "0",
|
"7","8","9", "0", "x",
|
||||||
"a", "b", "c", "d", "e",
|
"a", "b", "c", "d", "e",
|
||||||
"= ", "< ", "> ",
|
"= ", "< ", "> ",
|
||||||
"\"",
|
"\"",
|
||||||
|
@ -63,7 +62,6 @@ const char *func_tokens[] = {
|
||||||
"spent ", "window ",
|
"spent ", "window ",
|
||||||
"max ", "min ",
|
"max ", "min ",
|
||||||
"sq ", "cube ", "exp "
|
"sq ", "cube ", "exp "
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool using_func_tokens = false;
|
bool using_func_tokens = false;
|
||||||
|
|
|
@ -62,6 +62,7 @@ struct Environment envForLambda(const Object *params, const Object *arg_forms,
|
||||||
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;
|
||||||
|
// Eval the `march` list
|
||||||
const Object newEnvObj = eval(march, outer);
|
const Object newEnvObj = eval(march, outer);
|
||||||
addToEnv(&env, newObjName, newEnvObj); // Could use eval_forms?
|
addToEnv(&env, newObjName, newEnvObj); // Could use eval_forms?
|
||||||
march = march->forward;
|
march = march->forward;
|
||||||
|
@ -172,6 +173,10 @@ const char *codes[] = {
|
||||||
) \
|
) \
|
||||||
))",
|
))",
|
||||||
|
|
||||||
|
"(def r (fn (L) \
|
||||||
|
(if (= L () \
|
||||||
|
",
|
||||||
|
|
||||||
NULL
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
#define RESULT_LENGTH 20
|
#define RESULT_LENGTH 30
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
#define printd(...) printf(__VA_ARGS__)
|
#define printd(...) printf(__VA_ARGS__)
|
||||||
|
@ -211,7 +211,7 @@ static const char *errorText[] = {
|
||||||
"BAD_TYPE",
|
"BAD_TYPE",
|
||||||
"UNEXPECTED_FORM",
|
"UNEXPECTED_FORM",
|
||||||
"LISTS_NOT_SAME_SIZE",
|
"LISTS_NOT_SAME_SIZE",
|
||||||
"SYMBOLS_CANT_START_WITH_DIGITS",
|
"BAD_NUMBER",
|
||||||
"UNSUPPORTED_NUMBER_TYPE",
|
"UNSUPPORTED_NUMBER_TYPE",
|
||||||
"NOT_A_SYMBOL",
|
"NOT_A_SYMBOL",
|
||||||
"ONLY_ONE_ARGUMENT",
|
"ONLY_ONE_ARGUMENT",
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#define MAX_ENV_ELM 50 // 50
|
#define MAX_ENV_ELM 50 // 50
|
||||||
|
|
||||||
#define FOR_POINTER_IN_LIST(_list) \
|
#define FOR_POINTER_IN_LIST(_list) \
|
||||||
|
if(_list && _list->type == TYPE_LIST) \
|
||||||
for(Object *_element = _list->list; \
|
for(Object *_element = _list->list; \
|
||||||
_element != NULL;\
|
_element != NULL;\
|
||||||
_element = _element->forward)
|
_element = _element->forward)
|
||||||
|
@ -32,7 +33,7 @@ enum errorCode {
|
||||||
BAD_TYPE,
|
BAD_TYPE,
|
||||||
UNEXPECTED_FORM,
|
UNEXPECTED_FORM,
|
||||||
LISTS_NOT_SAME_SIZE,
|
LISTS_NOT_SAME_SIZE,
|
||||||
SYMBOLS_CANT_START_WITH_DIGITS,
|
BAD_NUMBER,
|
||||||
UNSUPPORTED_NUMBER_TYPE,
|
UNSUPPORTED_NUMBER_TYPE,
|
||||||
NOT_A_SYMBOL,
|
NOT_A_SYMBOL,
|
||||||
ONLY_ONE_ARGUMENT,
|
ONLY_ONE_ARGUMENT,
|
||||||
|
@ -65,12 +66,11 @@ struct Object {
|
||||||
Object *list;
|
Object *list;
|
||||||
char *string;
|
char *string;
|
||||||
Object (*func)(Object, Object, struct Environment *);
|
Object (*func)(Object, Object, struct Environment *);
|
||||||
struct Lambda *lambda; // Maybe better as not a pointer?
|
struct Lambda *lambda;
|
||||||
enum errorCode err;
|
enum errorCode err;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
// Maybe better as pointers?
|
|
||||||
struct Lambda {
|
struct Lambda {
|
||||||
Object params;
|
Object params;
|
||||||
Object body;
|
Object body;
|
||||||
|
@ -81,7 +81,6 @@ void printList(const Object *list);
|
||||||
void printObj(const Object *obj);
|
void printObj(const Object *obj);
|
||||||
void debugObj(const Object *obj);
|
void debugObj(const Object *obj);
|
||||||
void printErr(const Object *obj);
|
void printErr(const Object *obj);
|
||||||
int getType(const Object *obj);
|
|
||||||
|
|
||||||
int isEmpty(const Object *obj);
|
int isEmpty(const Object *obj);
|
||||||
Object *tail(const Object *listObj);
|
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)
|
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, env) :
|
||||||
eval(argForms->forward->forward, env);
|
eval(argForms->forward->forward, env);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object evalLambdaArgs(const Object *argForms)
|
Object evalLambdaArgs(const Object *argForms)
|
||||||
{
|
{
|
||||||
// params // body
|
return constructLambda(
|
||||||
return constructLambda(argForms, argForms? argForms->forward : NULL);
|
argForms, // Params
|
||||||
|
argForms ? argForms->forward : NULL // Body
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object evalMapArgs(const Object *argForms, struct Environment *env)
|
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);
|
Object lambda = eval(argForms, env);
|
||||||
const Object *inputList = argForms->forward;
|
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);
|
return errorObject(BAD_TYPE);
|
||||||
|
|
||||||
Object list = listObject();
|
|
||||||
|
|
||||||
FOR_POINTER_IN_LIST(inputList) {
|
FOR_POINTER_IN_LIST(inputList) {
|
||||||
// Create a new list for each element,
|
// Create a new list for each element,
|
||||||
// since lambda evaluation looks for a list
|
// 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);
|
struct Environment newEnv = envForLambda(params, &tempList, env);
|
||||||
|
|
||||||
// Add the lambda evaluation to the list
|
// 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);
|
deleteEnv(&newEnv);
|
||||||
cleanObject(&tempList);
|
cleanObject(&tempList);
|
||||||
}
|
}
|
||||||
cleanObject(&lambda);
|
cleanObject(&lambda);
|
||||||
|
|
||||||
return list;
|
return outputList;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object evalBuiltIns(const Object *first, const Object *rest,
|
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) {
|
if(first->type != TYPE_SYMBOL) {
|
||||||
return errorObject(NOT_A_SYMBOL);
|
return errorObject(NOT_A_SYMBOL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(strcmp(first->string, "def") == 0) {
|
if(strcmp(first->string, "def") == 0) {
|
||||||
return evalDefArgs(rest, env);
|
return evalDefArgs(rest, env);
|
||||||
} else if(strcmp(first->string, "if") == 0) {
|
} 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);
|
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
|
int length = listLength(src) - 1; // Not counting first_form
|
||||||
for(int i = 0; i < length; i++) { // Evaluates all in list
|
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)
|
Object eval(const Object *obj, struct Environment *env)
|
||||||
{
|
{
|
||||||
switch(obj->type) {
|
switch(obj->type) {
|
||||||
|
case TYPE_ERROR:
|
||||||
|
case TYPE_FUNC:
|
||||||
|
return *obj;
|
||||||
|
|
||||||
case TYPE_NUMBER:
|
case TYPE_NUMBER:
|
||||||
case TYPE_BOOL:
|
case TYPE_BOOL:
|
||||||
case TYPE_STRING:
|
case TYPE_STRING:
|
||||||
|
@ -106,79 +181,13 @@ Object eval(const Object *obj, struct Environment *env)
|
||||||
return fetchFromEnvironment(obj->string, env);
|
return fetchFromEnvironment(obj->string, env);
|
||||||
|
|
||||||
case TYPE_LIST:
|
case TYPE_LIST:
|
||||||
{
|
return evalList(obj, env);
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case TYPE_LAMBDA:
|
case TYPE_LAMBDA:
|
||||||
return errorObject(UNEXPECTED_FORM);
|
return errorObject(UNEXPECTED_FORM);
|
||||||
|
|
||||||
default:
|
|
||||||
return *obj;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
Result result(Object obj, struct Slice *slices)
|
return errorObject(BAD_TYPE);
|
||||||
{
|
|
||||||
return (Result) {
|
|
||||||
.obj = obj,
|
|
||||||
.slices = slices
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Object catObjects(const Object obj1, const Object obj2, struct Environment *env)
|
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;
|
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,
|
Object _basicOp(const Object *obj1, const Object *obj2, const char op,
|
||||||
struct Environment *env)
|
struct Environment *env)
|
||||||
{
|
{
|
||||||
|
@ -225,7 +243,9 @@ Object _basicOp(const Object *obj1, const Object *obj2, const char op,
|
||||||
case '=':
|
case '=':
|
||||||
if(obj1->type == TYPE_STRING || obj2->type == TYPE_STRING)
|
if(obj1->type == TYPE_STRING || obj2->type == TYPE_STRING)
|
||||||
return boolObject(!strcmp(obj1->string, obj2->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 '>':
|
case '>':
|
||||||
return boolObject(n1 > n2);
|
return boolObject(n1 > n2);
|
||||||
case '<':
|
case '<':
|
||||||
|
@ -376,10 +396,10 @@ Result parse(struct Slice *slices)
|
||||||
// todo check for null rest
|
// todo check for null rest
|
||||||
return readSeq(rest);
|
return readSeq(rest);
|
||||||
} else { // todo error on missing close paren
|
} else { // todo error on missing close paren
|
||||||
return result(parseAtom(token), rest);
|
return (Result){parseAtom(token), rest};
|
||||||
}
|
}
|
||||||
} else {
|
} 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 *next = tokens;
|
||||||
struct Slice *rest = next->text? &next[1] : NULL;
|
struct Slice *rest = next->text? &next[1] : NULL;
|
||||||
if(next->text[0] == ')') {
|
if(next->text[0] == ')') {
|
||||||
return result(res, rest);
|
return (Result){res, rest};
|
||||||
}
|
}
|
||||||
Result r = parse(tokens);
|
Result r = parse(tokens);
|
||||||
if(r.obj.type == TYPE_ERROR)
|
if(r.obj.type == TYPE_ERROR)
|
||||||
|
@ -406,7 +426,7 @@ Object parseDecimal(struct Slice *s)
|
||||||
int num = 0;
|
int num = 0;
|
||||||
for(int i = 0; i < s->length; i++) {
|
for(int i = 0; i < s->length; i++) {
|
||||||
if(!isDigit(s->text[i])) {
|
if(!isDigit(s->text[i])) {
|
||||||
return errorObject(SYMBOLS_CANT_START_WITH_DIGITS);
|
return errorObject(BAD_NUMBER);
|
||||||
}
|
}
|
||||||
num *= 10;
|
num *= 10;
|
||||||
num += s->text[i] - '0';
|
num += s->text[i] - '0';
|
||||||
|
@ -414,22 +434,56 @@ Object parseDecimal(struct Slice *s)
|
||||||
return numberObject(num);
|
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)
|
Object parseAtom(struct Slice *s)
|
||||||
{
|
{
|
||||||
if(isDigit(s->text[0])) {
|
const char c = s->text[0];
|
||||||
if(s->text[0] != '0' || s->length == 1)
|
if(isDigit(c)) {
|
||||||
|
if(c != '0' || s->length == 1) {
|
||||||
return parseDecimal(s);
|
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);
|
return errorObject(UNSUPPORTED_NUMBER_TYPE);
|
||||||
}
|
}
|
||||||
|
} else if (s->length == 1 && (c == 'T' || c == 't')) {
|
||||||
} else if (s->length == 1 && (s->text[0] == 'T' || s->text[0] == 't')) {
|
|
||||||
return boolObject(1);
|
return boolObject(1);
|
||||||
|
} else if (s->length == 1 && (c == 'F' || c == 'f')) {
|
||||||
} else if (s->length == 1 && (s->text[0] == 'F' || s->text[0] == 'f')) {
|
|
||||||
return boolObject(0);
|
return boolObject(0);
|
||||||
|
} else if (c == '"' || c == '\'') {
|
||||||
} else if (s->text[0] == '"' || s->text[0] == '\'') {
|
|
||||||
return objFromSlice(s->text, s->length);
|
return objFromSlice(s->text, s->length);
|
||||||
} else {
|
} else {
|
||||||
return symFromSlice(s->text, s->length);
|
return symFromSlice(s->text, s->length);
|
||||||
|
@ -449,7 +503,7 @@ Object parseEval(const char *input, struct Environment *env)
|
||||||
printd("start slice\n");
|
printd("start slice\n");
|
||||||
if(debug) {
|
if(debug) {
|
||||||
while(debug->text) {
|
while(debug->text) {
|
||||||
char tok[MAX_TOK_LEN];
|
char tok[100];
|
||||||
copySlice(tok, debug);
|
copySlice(tok, debug);
|
||||||
printd("slice: '%s'\n", tok);
|
printd("slice: '%s'\n", tok);
|
||||||
debug++;
|
debug++;
|
||||||
|
@ -461,32 +515,27 @@ Object parseEval(const char *input, struct Environment *env)
|
||||||
int parens = 0;
|
int parens = 0;
|
||||||
Object obj = numberObject(0);
|
Object obj = numberObject(0);
|
||||||
struct Slice *tok = tokens;
|
struct Slice *tok = tokens;
|
||||||
if(tok[i].text[0] != '(') {
|
while(tok[i].text != NULL) {
|
||||||
Object parsed = parse(tok).obj;
|
if(tok[i].text[0] == '(') {
|
||||||
if(parsed.type == TYPE_ERROR)
|
parens++;
|
||||||
return parsed;
|
} else if(tok[i].text[0] == ')') {
|
||||||
obj = eval(&parsed, env);
|
parens--;
|
||||||
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++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
free(tokens);
|
||||||
return obj;
|
return obj;
|
||||||
|
|
|
@ -29,14 +29,12 @@ Object eval(const Object *obj, struct Environment *env);
|
||||||
Result parse(struct Slice *slices);
|
Result parse(struct Slice *slices);
|
||||||
Result readSeq(struct Slice *slices);
|
Result readSeq(struct Slice *slices);
|
||||||
Object parseAtom(struct Slice *slice);
|
Object parseAtom(struct Slice *slice);
|
||||||
void copySlice(char * dest, struct Slice *src);
|
|
||||||
Object parseEval(const char *input, struct Environment *env);
|
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);
|
Object evalLambdaArgs(const Object *arg_forms);
|
||||||
|
|
||||||
Result result(Object obj, struct Slice *slices);
|
|
||||||
|
|
||||||
// Slices
|
// Slices
|
||||||
void copySlice(char * dest, struct Slice *src);
|
void copySlice(char * dest, struct Slice *src);
|
||||||
void debugSlice(struct Slice *s);
|
void debugSlice(struct Slice *s);
|
||||||
|
@ -48,6 +46,7 @@ BASIC_OP(mul); BASIC_OP(dvi);
|
||||||
BASIC_OP(mod); BASIC_OP(equ);
|
BASIC_OP(mod); BASIC_OP(equ);
|
||||||
BASIC_OP(gth); BASIC_OP(lth);
|
BASIC_OP(gth); BASIC_OP(lth);
|
||||||
#undef BASIC_OP
|
#undef BASIC_OP
|
||||||
|
|
||||||
Object catObjects(const Object obj1, const Object obj2, struct Environment *env);
|
Object catObjects(const Object obj1, const Object obj2, struct Environment *env);
|
||||||
Object filter(Object obj1, Object obj2, struct Environment *env);
|
Object filter(Object obj1, Object obj2, struct Environment *env);
|
||||||
Object append(Object list, Object newElement, 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
|
#!/bin/bash
|
||||||
|
|
||||||
|
VALCOM="valgrind -q --leak-check=full --track-origins=yes"
|
||||||
|
|
||||||
TOTAL_PASSES=0
|
TOTAL_PASSES=0
|
||||||
TOTAL_FAILS=0
|
TOTAL_FAILS=0
|
||||||
PASSES=0
|
PASSES=0
|
||||||
|
@ -24,11 +26,9 @@ endBlock() {
|
||||||
|
|
||||||
title() {
|
title() {
|
||||||
echo "[1m$1[0m"
|
echo "[1m$1[0m"
|
||||||
# echo "$1" | sed 's/./-/g' | sed 's/$/-/g'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pass() {
|
pass() {
|
||||||
# echo "[0;32m$1 test PASSED[0m"
|
|
||||||
((PASSES++))
|
((PASSES++))
|
||||||
((TOTAL_PASSES++))
|
((TOTAL_PASSES++))
|
||||||
}
|
}
|
||||||
|
@ -42,7 +42,7 @@ fail() {
|
||||||
check() {
|
check() {
|
||||||
local output
|
local output
|
||||||
if (($VALGRIND == 1)); then
|
if (($VALGRIND == 1)); then
|
||||||
local output=$(valgrind -q --leak-check=full --track-origins=yes ./pebblisp "$2")
|
local output=$($VALCOM ./pebblisp "$2")
|
||||||
else
|
else
|
||||||
local output=$(./pebblisp "$2")
|
local output=$(./pebblisp "$2")
|
||||||
fi
|
fi
|
||||||
|
@ -62,6 +62,8 @@ title "Plain return tests"
|
||||||
check "PlainRet" "10" "10"
|
check "PlainRet" "10" "10"
|
||||||
check "StrRetrn" "\"hey\"" "hey"
|
check "StrRetrn" "\"hey\"" "hey"
|
||||||
check "SingleStrRetrn" "'hey'" "hey"
|
check "SingleStrRetrn" "'hey'" "hey"
|
||||||
|
check "HexReturn" "0x0f0f0" "61680"
|
||||||
|
check "BinaryReturn" "0b011010011010011" "13523"
|
||||||
endBlock
|
endBlock
|
||||||
|
|
||||||
title "Arithmetic"
|
title "Arithmetic"
|
||||||
|
@ -99,7 +101,7 @@ check "EmptLst2" "( )" "( )"
|
||||||
check "ListIndex" "(at (+ 1 1) (1 2 1000 4 5))" "1000"
|
check "ListIndex" "(at (+ 1 1) (1 2 1000 4 5))" "1000"
|
||||||
check "EvalElems" "((* 10 10) 7)" "( 100 7 )"
|
check "EvalElems" "((* 10 10) 7)" "( 100 7 )"
|
||||||
check "AppendToList" "(ap (1 20 300 4000 50000) 600000)" "( 1 20 300 4000 50000 600000 )"
|
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 "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"
|
check "ListLength" "(def l (1 20 3 'abc' 'banana' (+ 10 5))) (len l)" "6"
|
||||||
endBlock
|
endBlock
|
||||||
|
@ -158,7 +160,9 @@ title "ShouldError"
|
||||||
check "LenOfNotList" "(len 5)" "NOT_A_LIST"
|
check "LenOfNotList" "(len 5)" "NOT_A_LIST"
|
||||||
check "NoMapList" "(map sq)" "( )"
|
check "NoMapList" "(map sq)" "( )"
|
||||||
check "UnevenLists" "(+ (1 2) (1 2 3))" "LISTS_NOT_SAME_SIZE"
|
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 "BadParens" "(hey()" "MISMATCHED_PARENS"
|
||||||
check "BadParens2" "(hey)(" "MISMATCHED_PARENS"
|
check "BadParens2" "(hey)(" "MISMATCHED_PARENS"
|
||||||
check "BadParens3" "((hey(" "MISMATCHED_PARENS"
|
check "BadParens3" "((hey(" "MISMATCHED_PARENS"
|
||||||
|
@ -175,10 +179,13 @@ endBlock
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
if [ "$TOTAL_FAILS" -ne "0" ]; then
|
if [ "$TOTAL_FAILS" -ne "0" ]; then
|
||||||
echo "[1;31m$TOTAL_FAILS Tests Failed[0m"
|
echo -n "[1;31m"
|
||||||
else
|
|
||||||
echo "$TOTAL_FAILS Tests Failed[0m"
|
|
||||||
fi
|
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 ""
|
echo ""
|
||||||
|
|
|
@ -35,6 +35,10 @@ int isDigit(const char c) {
|
||||||
return c >= '0' && c <= '9';
|
return c >= '0' && c <= '9';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int isHex(const char c) {
|
||||||
|
return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f');
|
||||||
|
}
|
||||||
|
|
||||||
int isWhitespace(const char c) {
|
int isWhitespace(const char c) {
|
||||||
return c == ' ' || c == '\t' || c == '\n';
|
return c == ' ' || c == '\t' || c == '\n';
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
int isSingle(const char c);
|
int isSingle(const char c);
|
||||||
int isDigit(const char c);
|
int isDigit(const char c);
|
||||||
|
int isHex(const char c);
|
||||||
struct Slice *nf_tokenize(const char *input);
|
struct Slice *nf_tokenize(const char *input);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue