351 lines
9.0 KiB
C
351 lines
9.0 KiB
C
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "plfunc.h"
|
|
|
|
/**
|
|
* (reduce (list, initial) (fn (prev total) (+ prev total)))
|
|
*/
|
|
Object reduce(const Object listInitial, const Object func, struct Environment* env)
|
|
{
|
|
Object* list = itemAt(&listInitial, 0);
|
|
Object total = cloneObject(*list->forward); // From given initial value
|
|
|
|
if (list->type != TYPE_LIST) {
|
|
return simpleFuncEval(func, total, *list, env);
|
|
}
|
|
|
|
FOR_POINTER_IN_LIST(list) {
|
|
total = simpleFuncEval(func, total, *POINTER, env);
|
|
}
|
|
|
|
return total;
|
|
}
|
|
|
|
Object charAt(Object string, Object at, struct Environment* ignore)
|
|
{
|
|
char* c = malloc(sizeof(char) * 2);
|
|
c[0] = string.string[at.number];
|
|
c[1] = '\0';
|
|
string.string = c;
|
|
return string;
|
|
}
|
|
|
|
Object filter(Object condition, Object list, struct Environment* env)
|
|
{
|
|
Object filteredList = listObject();
|
|
FOR_POINTER_IN_LIST(&list) {
|
|
Object conditional = cloneObject(condition);
|
|
nf_addToList(&conditional, *POINTER);
|
|
Object result = eval(&conditional, env);
|
|
cleanObject(&conditional);
|
|
if (result.number == 1) {
|
|
nf_addToList(&filteredList, *POINTER);
|
|
}
|
|
}
|
|
return filteredList;
|
|
}
|
|
|
|
Object append(Object list, Object newElement, struct Environment* env)
|
|
{
|
|
Object newList = cloneObject(list);
|
|
nf_addToList(&newList, cloneObject(newElement));
|
|
return newList;
|
|
}
|
|
|
|
Object prepend(Object list, Object newElement, struct Environment* env)
|
|
{
|
|
Object newList = listObject();
|
|
nf_addToList(&newList, cloneObject(newElement));
|
|
appendList(&newList, &list);
|
|
return newList;
|
|
}
|
|
|
|
Object at(Object index, Object list, struct Environment* env)
|
|
{
|
|
const Object* found = itemAt(&list, index.number);
|
|
if (found) {
|
|
return cloneObject(*found);
|
|
} else {
|
|
return errorObject(INDEX_PAST_END);
|
|
}
|
|
}
|
|
|
|
Object rest(Object list, Object ignore, struct Environment* env)
|
|
{
|
|
Object ret = listObject();
|
|
Object* l = &list;
|
|
FOR_POINTER_IN_LIST(l) {
|
|
if (POINTER == l->list) {
|
|
continue;
|
|
}
|
|
nf_addToList(&ret, cloneObject(*POINTER));
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
Object reverse(Object _list, Object ignore, struct Environment* ignore2)
|
|
{
|
|
const Object* list = &_list;
|
|
Object rev = listObject();
|
|
|
|
Object* tail = NULL;
|
|
FOR_POINTER_IN_LIST(list) {
|
|
Object* oldTail = tail;
|
|
allocObject(&tail, cloneObject(*POINTER));
|
|
if (oldTail) {
|
|
tail->forward = oldTail;
|
|
}
|
|
}
|
|
|
|
rev.list = tail;
|
|
return rev;
|
|
}
|
|
|
|
Object isNum(Object test, Object ignore, struct Environment* ignore2)
|
|
{
|
|
return test.type == TYPE_NUMBER ? boolObject(1) : boolObject(0);
|
|
}
|
|
|
|
Object isList(Object test, Object ignore, struct Environment* ignore2)
|
|
{
|
|
return test.type == TYPE_LIST ? boolObject(1) : boolObject(0);
|
|
}
|
|
|
|
Object isString(Object test, Object ignore, struct Environment* ignore2)
|
|
{
|
|
return test.type == TYPE_STRING ? boolObject(1) : boolObject(0);
|
|
}
|
|
|
|
// Get the int value of a string's first character
|
|
Object charVal(Object test, Object ignore, struct Environment* ignore2)
|
|
{
|
|
return numberObject(test.string[0]);
|
|
// return test.type == TYPE_STRING && test.string[0] == '\0' ?
|
|
// boolObject(1) : boolObject(0);
|
|
}
|
|
|
|
Object isErr(Object test, Object ignore, struct Environment* ignore2)
|
|
{
|
|
return test.type == TYPE_ERROR ? boolObject(1) : boolObject(0);
|
|
}
|
|
|
|
Object parseEvalO(Object text, Object ignore, struct Environment* env)
|
|
{
|
|
if (text.type == TYPE_SYMBOL) {
|
|
Object string = eval(&text, env);
|
|
Object parsed = parseEval(string.string, env);
|
|
cleanObject(&string);
|
|
return parsed;
|
|
} else if (text.type != TYPE_STRING) {
|
|
return errorObject(CAN_ONLY_EVAL_STRINGS);
|
|
}
|
|
return parseEval(text.string, env);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
#define CAT_MAX 1024
|
|
|
|
Object catObjects(const Object obj1, const Object obj2, struct Environment* env)
|
|
{
|
|
Object evalObj1 = eval(&obj1, env);
|
|
Object evalObj2 = eval(&obj2, env);
|
|
if (isError(evalObj2, ONLY_ONE_ARGUMENT)) {
|
|
return evalObj1;
|
|
}
|
|
|
|
char str1[CAT_MAX] = "";
|
|
char str2[CAT_MAX] = "";
|
|
stringObj(str1, &evalObj1);
|
|
stringObj(str2, &evalObj2);
|
|
cleanObject(&evalObj1);
|
|
cleanObject(&evalObj2);
|
|
size_t length = strlen(str1) + strlen(str2) + 1;
|
|
|
|
Object o = newObject(TYPE_STRING);
|
|
o.string = calloc(sizeof(char), length);
|
|
strcat(o.string, str1);
|
|
strcat(o.string, str2);
|
|
|
|
return o;
|
|
}
|
|
|
|
Object _basicOp(const Object* obj1, const Object* obj2, const char op,
|
|
struct Environment* env)
|
|
{
|
|
const int n1 = obj1->number;
|
|
const int n2 = obj2->number;
|
|
|
|
switch (op) {
|
|
case '+':
|
|
if (eitherIs(TYPE_STRING, obj1, obj2)) {
|
|
return catObjects(*obj1, *obj2, env);
|
|
}
|
|
return numberObject(n1 + n2);
|
|
case '-':
|
|
return numberObject(n1 - n2);
|
|
case '*':
|
|
return numberObject(n1 * n2);
|
|
case '/':
|
|
return numberObject(n1 / n2);
|
|
case '%':
|
|
return numberObject(n1 % n2);
|
|
|
|
case '&':
|
|
return boolObject(n1 != 0 && n2 != 0);
|
|
case '|':
|
|
return boolObject(n1 != 0 || n2 != 0);
|
|
|
|
case '=':
|
|
if (bothAre(TYPE_STRING, obj1, obj2)) {
|
|
return boolObject(!strcmp(obj1->string, obj2->string));
|
|
}
|
|
if (bothAre(TYPE_LIST, obj1, obj2)) {
|
|
return listEquality(obj1, obj2);
|
|
}
|
|
return boolObject(n1 == n2 && areSameType(obj1, obj2));
|
|
case '>':
|
|
return boolObject(n1 > n2);
|
|
case '<':
|
|
return boolObject(n1 < n2);
|
|
default:
|
|
return *obj1;
|
|
}
|
|
}
|
|
|
|
Object basicOp(const Object* obj1, const Object* obj2, const char op,
|
|
struct Environment* env)
|
|
{
|
|
if (isError(*obj2, ONLY_ONE_ARGUMENT)) {
|
|
return *obj2;
|
|
}
|
|
|
|
int lists = (obj1->type == TYPE_LIST) + (obj2->type == TYPE_LIST);
|
|
if (lists == 0) {
|
|
return _basicOp(obj1, obj2, op, env);
|
|
|
|
} else if (lists == 1) { // Single operand is applied to each element in list
|
|
const Object* listObj = (obj1->type == TYPE_LIST) ? obj1 : obj2;
|
|
const Object* singleObj = (obj1->type == TYPE_LIST) ? obj2 : obj1;
|
|
|
|
Object newList = listObject();
|
|
FOR_POINTER_IN_LIST(listObj) {
|
|
Object adding = eval(POINTER, env);
|
|
nf_addToList(&newList, _basicOp(&adding, singleObj, op, env));
|
|
}
|
|
return newList;
|
|
|
|
} else { // 2 lists with the op applied to matching indices of both lists
|
|
if (listLength(obj1) == listLength(obj2)) {
|
|
Object newList = listObject();
|
|
FOR_POINTERS_IN_LISTS(obj1, obj2) {
|
|
const Object ev1 = eval(P1, env);
|
|
const Object ev2 = eval(P2, env);
|
|
nf_addToList(&newList, _basicOp(&ev1, &ev2, op, env));
|
|
}
|
|
return newList;
|
|
} else {
|
|
return errorObject(LISTS_NOT_SAME_SIZE);
|
|
}
|
|
}
|
|
}
|
|
|
|
Object len(Object obj1, Object o_ignore, struct Environment* e_ignore)
|
|
{
|
|
Object o = numberObject(listLength(&obj1));
|
|
if (o.number < 0) {
|
|
return errorObject(NOT_A_LIST);
|
|
}
|
|
return o;
|
|
}
|
|
|
|
#define BASIC_OP(_name, _char) \
|
|
Object _name(Object obj1, Object obj2, struct Environment *env) \
|
|
{ \
|
|
return basicOp(&obj1, &obj2, _char, env); \
|
|
}
|
|
|
|
BASIC_OP(add, '+')
|
|
|
|
BASIC_OP(sub, '-')
|
|
|
|
BASIC_OP(mul, '*')
|
|
|
|
BASIC_OP(dvi, '/')
|
|
|
|
BASIC_OP(mod, '%')
|
|
|
|
BASIC_OP(equ, '=')
|
|
|
|
BASIC_OP(gth, '>')
|
|
|
|
BASIC_OP(lth, '<')
|
|
|
|
BASIC_OP(and, '&')
|
|
|
|
BASIC_OP(or, '|')
|
|
|
|
#undef BASIC_OP
|
|
|
|
#ifdef STANDALONE
|
|
|
|
Object pChar(Object c, Object i1, struct Environment* i2)
|
|
{
|
|
if (c.type != TYPE_NUMBER) {
|
|
return errorObject(BAD_NUMBER);
|
|
}
|
|
printf("%c", c.number % 256);
|
|
return numberObject(0);
|
|
}
|
|
|
|
Object printEnvO(Object i1, Object i2, struct Environment* env)
|
|
{
|
|
printEnv(global());
|
|
return numberObject(0);
|
|
}
|
|
|
|
Object takeInput(Object prompt, Object i2, struct Environment* i3)
|
|
{
|
|
if (prompt.type == TYPE_STRING) {
|
|
printf("%s", prompt.string);
|
|
}
|
|
char input[256] = "";
|
|
fgets(input, 256, stdin);
|
|
return stringFromSlice(input, strlen(input) - 1);
|
|
}
|
|
|
|
Object loadFile(Object filename, Object _, struct Environment* env)
|
|
{
|
|
if (isStringy(filename)) {
|
|
readFile(filename.string, env);
|
|
return numberObject(0);
|
|
}
|
|
return numberObject(1);
|
|
}
|
|
|
|
Object systemCall(Object process, Object _, struct Environment* env)
|
|
{
|
|
if (isStringy(process)) {
|
|
return numberObject(system(process.string));
|
|
}
|
|
return numberObject(255);
|
|
}
|
|
|
|
Object help(Object symbol, Object ignore, struct Environment* ignore2)
|
|
{
|
|
char* help = getHelp(symbol.string);
|
|
Object helpText = newObject(TYPE_STRING);
|
|
helpText.string = help;
|
|
return helpText;
|
|
}
|
|
|
|
#endif // STANDALONE
|