Added TODO. Text size adjusts with code length

Can handle hex and binary. Other cleanup

All tests pass
This commit is contained in:
= 2020-08-02 21:16:26 +01:00
parent 796a4cdc91
commit a5ecb1b3aa
11 changed files with 232 additions and 140 deletions

View File

@ -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.

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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
};

View File

@ -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",

View File

@ -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);

View File

@ -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;

View File

@ -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);

View File

@ -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 "$1"
# echo "$1" | sed 's/./-/g' | sed 's/$/-/g'
}
pass() {
# echo "$1 test PASSED"
((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 "$TOTAL_FAILS Tests Failed"
else
echo "$TOTAL_FAILS Tests Failed"
echo -n ""
fi
echo "$TOTAL_FAILS Tests Failed"
if [ "$TOTAL_PASSES" -ne "0" ]; then
echo -n ""
fi
echo "$TOTAL_PASSES Tests Passed"
echo "$TOTAL_PASSES Tests Passed"
echo ""

View File

@ -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';
}

View File

@ -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