diff --git a/Makefile b/Makefile index 39802f9..6816f5a 100644 --- a/Makefile +++ b/Makefile @@ -13,7 +13,7 @@ LDFLAGS = -lm -lc CC = cc -SRC = cirilisp.c read.c print.c +SRC = cirilisp.c read.c eval.c print.c util.c OBJ = $(SRC:.c=.o) all: cirilisp @@ -21,7 +21,7 @@ all: cirilisp .c.o: $(CC) -c $(CFLAGS) $< -$(OBJ): util.h read.h print.h +$(OBJ): util.h read.h eval.h print.h cirilisp: $(OBJ) $(CC) -o $@ $(OBJ) $(LDFLAGS) @@ -31,7 +31,7 @@ clean: dist: clean mkdir -p cirilisp-$(VERSION) - cp -r Makefile readline.h $(SRC) cirilisp-$(VERSION) + cp -r Makefile util.h read.h eval.h print.h $(SRC) cirilisp-$(VERSION) tar -cf cirilisp-$(VERSION).tar cirilisp-$(VERSION) gzip cirilisp-$(VERSION).tar rm -rf cirilisp-$(VERSION) diff --git a/cirilisp.c b/cirilisp.c index 535b669..9c444fe 100644 --- a/cirilisp.c +++ b/cirilisp.c @@ -4,6 +4,7 @@ #include "util.h" #include "read.h" +#include "eval.h" #include "print.h" int main(int argc, char **argv) @@ -18,9 +19,7 @@ int main(int argc, char **argv) for (;;) { - print(read("ШКЉ> ")); - /* append(&tokenList, lexLine(readline())); - printTokenList(tokenList); */ + print(eval(read("ШКЉ> "))); } } diff --git a/eval.c b/eval.c new file mode 100644 index 0000000..8b886a9 --- /dev/null +++ b/eval.c @@ -0,0 +1,233 @@ +#include +#include +#include + +#include "util.h" + +char *improperListError = "Конс објекат мора бити правилна листа да би\ + могао бити евалуиран"; +char *typeError = "Неправилан тип аргумента прослеђен функцији"; +char *unrecognizedSymbolError = "Непознати симбол"; +char *notApplicableError = "Објекат није примењив"; +char *divisionByZeroError = "Дељење нулом"; +char *argumentNumberError = "Функцији није прослеђен правилан број аргумената"; + +object apply(object function, object parameters); + +object eval(object input) +{ + object result; + + if (TYPE(input) == nilObject || TYPE(input) == numberObject || + TYPE(input) == errorObject) + result = input; + else if (TYPE(input) == symbolObject) + { + if (!strcmp(SYM(input), "+") || !strcmp(SYM(input), "-") || + !strcmp(SYM(input), "*") || !strcmp(SYM(input), "/")) + { + result = input; + } + else + { + deleteObject(input); + TYPE(result) = errorObject; + ERRMSG(result) = unrecognizedSymbolError; + } + } + else if (TYPE(input) == consObject) + { + if (!properList(input)) + { + deleteObject(input); + TYPE(result) = errorObject; + ERRMSG(result) = improperListError; + } + + object *currentCell = &input; + int noErrors = 1; + while (TYPE(*currentCell) != nilObject) + { + CAR(*currentCell) = eval(CAR(*currentCell)); + + if (TYPE(CAR(*currentCell)) == errorObject) + { + noErrors = 0; + TYPE(result) = errorObject; + ERRMSG(result) = ERRMSG(CAR(*currentCell)); + break; + } + currentCell = &CDR(*currentCell); + } + + if (noErrors) + { + result = apply(CAR(input), CDR(input)); + } + deleteObject(input); + } + + return result; +} + +object add(object parameters); +object subtract(object parameters); +object multiply(object parameters); +object divide(object parameters); + +object apply(object function, object parameters) +{ + object result; + + if (TYPE(function) != symbolObject) + { + deleteObject(function); + TYPE(result) = errorObject; + ERRMSG(result) = notApplicableError; + } + /* TODO Ово налажење ће се касније извршавати кроз табелу симбола */ + else if (!strcmp(SYM(function), "+")) + { + result = add(parameters); + } + else if (!strcmp(SYM(function), "-")) + { + result = subtract(parameters); + } + else if (!strcmp(SYM(function), "*")) + { + result = multiply(parameters); + } + else if (!strcmp(SYM(function), "/")) + { + result = divide(parameters); + } + else + { + deleteObject(function); + TYPE(result) = errorObject; + ERRMSG(result) = improperListError; + } + + return result; +} + +int allNumbers(object list) +{ + object *currentCell = &list; + while (TYPE(*currentCell) != nilObject) + { + if (TYPE(CAR(*currentCell)) != numberObject) + { + return 0; + } + currentCell = &CDR(*currentCell); + } + return 1; +} + +object add(object parameters) +{ + object result; + TYPE(result) = numberObject; + + if (!allNumbers(parameters)) + { + TYPE(result) = errorObject; + ERRMSG(result) = typeError; + } + else if (listLength(parameters) == 0) + { + NUM(result) = 0LL; + } + else + { + object first, rest; + first = CAR(parameters); + rest = add(CDR(parameters)); + + NUM(result) = NUM(first) + NUM(rest); + } + + return result; +} + +object subtract(object parameters) +{ + object result; + TYPE(result) = numberObject; + + if (!allNumbers(parameters)) + { + TYPE(result) = errorObject; + ERRMSG(result) = typeError; + } + else if (listLength(parameters) == 0) + { + TYPE(result) = errorObject; + ERRMSG(result) = argumentNumberError; + } + else if (listLength(parameters) == 1) + { + NUM(result) = -NUM(CAR(parameters)); + } + else + { + NUM(result) = NUM(CAR(parameters)) - NUM(add(CDR(parameters))); + } + + return result; +} + +object multiply(object parameters) +{ + object result; + TYPE(result) = numberObject; + + if (!allNumbers(parameters)) + { + TYPE(result) = errorObject; + ERRMSG(result) = typeError; + } + else if (listLength(parameters) == 0) + { + NUM(result) = 1LL; + } + else + { + object first, rest; + first = CAR(parameters); + rest = multiply(CDR(parameters)); + + NUM(result) = NUM(first) * NUM(rest); + } + + return result; +} + +object divide(object parameters) +{ + object result; + TYPE(result) = numberObject; + + if (!allNumbers(parameters)) + { + TYPE(result) = errorObject; + ERRMSG(result) = typeError; + } + else if (listLength(parameters) == 0) + { + TYPE(result) = errorObject; + ERRMSG(result) = argumentNumberError; + } + else if (listLength(parameters) == 1) + { + NUM(result) = 1/NUM(CAR(parameters)); + } + else + { + NUM(result) = NUM(CAR(parameters))/NUM(add(CDR(parameters))); + } + + return result; +} diff --git a/eval.h b/eval.h new file mode 100644 index 0000000..13f8a37 --- /dev/null +++ b/eval.h @@ -0,0 +1,3 @@ +#pragma once + +object eval(object input); diff --git a/print.c b/print.c index 1ce44d0..0116963 100644 --- a/print.c +++ b/print.c @@ -6,31 +6,38 @@ void printValue(object input); void print(object input) { - printf("\n; Value: "); - printValue(input); - printf("\n\n"); + if (input.type == errorObject) + { + fprintf(stderr, "\nГРЕШКА: %s\n\n", ERRMSG(input)); + } + else + { + printf("\n; Value: "); + printValue(input); + printf("\n\n"); + } } void printValue(object input) { if (input.type == nilObject) { - printf("nil"); + printf("()"); } else if (input.type == numberObject) { - printf("%lld", *((long long *) input.address)); + printf("%lld", NUM(input)); } else if (input.type == symbolObject) { - printf("%s", (char *) input.address); + printf("%s", SYM(input)); } else if (input.type == consObject) { printf("("); - printValue(((cons *) input.address)->car); + printValue(CAR(input)); printf(" . "); - printValue(((cons *) input.address)->cdr); + printValue(CDR(input)); printf(")"); } } diff --git a/read.c b/read.c index 392fa08..2d2da66 100644 --- a/read.c +++ b/read.c @@ -24,36 +24,36 @@ typedef struct _Token struct _Token *next; } token; -int completeSExpr(token **tokenList); +int completeExpression(token **tokenQueue); char *readline(); void append(token **head, token *appendix); token *lexLine(char *input); -object parseObject(token **inputList); +object parseExpression(token **inputList); -token *tokenList = NULL; +token *tokenQueue = NULL; object read(char *prompt) { printf("%s", prompt); - while (!completeSExpr(&tokenList)) + while (!completeExpression(&tokenQueue)) { char *input = readline(); - if (input == NULL) /* унесен је EOF сигнал */ + if (input == NULL) /* унесен је EOF */ { printf("\nКрај улазног стрима.\n"); exit(0); } - append(&tokenList, lexLine(input)); + append(&tokenQueue, lexLine(input)); } - return parseObject(&tokenList); + return parseExpression(&tokenQueue); } -int completeSExpr(token **tokenList) +int completeExpression(token **tokenQueue) { int result = 0, indentLevel = 0; - token *current = *tokenList; + token *current = *tokenQueue; while (current != NULL) { @@ -65,7 +65,7 @@ int completeSExpr(token **tokenList) { if (indentLevel == 0) { - token **deleteParen = tokenList; + token **deleteParen = tokenQueue; while (*deleteParen != current) { deleteParen = &(*deleteParen)->next; @@ -115,7 +115,8 @@ char *readline() char *cpy = malloc(strlen(buffer)+1); strcpy(cpy, buffer); cpy[strlen(cpy)-1] = '\0'; - // Уклања завршни њу-лајн или ЕОФ у стрингу и копира га на ново место + /* Уклања завршни њу-лајн или ЕОФ у стрингу + * и копира стринг на ново место */ return cpy; } @@ -141,7 +142,7 @@ token *lex1Token(char *input, int *i) regcomp(®Space, "^[[:space:]]*", REG_EXTENDED); regcomp(®Number, "^[-+]?[[:digit:]]+", REG_EXTENDED); - regcomp(®Symbol, "^[-+/*]", REG_EXTENDED); + regcomp(®Symbol, "^[-+/*]+", REG_EXTENDED); /* за сада подржава само симболе -, +, * и / */ regcomp(®LParenthesis, "^\\(", REG_EXTENDED); regcomp(®RParenthesis, "^\\)", REG_EXTENDED); @@ -206,7 +207,7 @@ token *lexLine(char *input) if ((*new)->type == undefinedToken) { /* уколико се у реду нађе токен који је лексички погрешан, штампа се место тог -токена у реду и бришу се сви токени нађени у реду, функција враћа NULL*/ + * токена у реду и бришу се сви токени нађени у реду, функција враћа NULL*/ fprintf(stderr, "Невалидан токен на месту %d\n", i); new = &root; while (*new != NULL) @@ -223,27 +224,29 @@ token *lexLine(char *input) return root; } -object parseObject(token **inputList) +object parseExpression(token **inputList) { object result; token input = **inputList; free(*inputList); *inputList = input.next; + /* скида први преостали токен са листе унесених, да би се могао + * прерадити */ if (input.type == numberToken) { - result.type = numberObject; - result.address = malloc(sizeof(long long int)); - *((long long *) result.address) = atoll(input.lexeme); + TYPE(result) = numberObject; + NUM(result) = atoll(input.lexeme); return result; } else if (input.type == symbolToken) { - result.type = symbolObject; - result.address = malloc((strlen(input.lexeme) + 1) - * sizeof(char)); - strcpy((char *) result.address, input.lexeme); + TYPE(result) = symbolObject; + SYM(result) = malloc((strlen(input.lexeme) + 1) + * sizeof(char)); + /* Алокација стринга */ + strcpy(SYM(result), input.lexeme); return result; } else if (input.type == lParenthesisToken) @@ -252,17 +255,16 @@ object parseObject(token **inputList) while ((*inputList)->type != rParenthesisToken) { - listCurrent->type = consObject; - listCurrent->address = malloc(sizeof(cons)); + TYPE(*listCurrent) = consObject; + CONS(*listCurrent) = malloc(sizeof(cons)); + /* Алокација конс ћелије */ - ((cons *) listCurrent->address)->car = - parseObject(inputList); + CAR(*listCurrent) = parseExpression(inputList); - listCurrent = &(((cons *) listCurrent->address)->cdr); + listCurrent = &CDR(*listCurrent); } - (*listCurrent).type = nilObject; - (*listCurrent).address = NULL; + TYPE(*listCurrent) = nilObject; input = **inputList; free(*inputList); diff --git a/util.c b/util.c new file mode 100644 index 0000000..72395f2 --- /dev/null +++ b/util.c @@ -0,0 +1,72 @@ +#include +#include + +#include "util.h" + +int properList(object list) +{ + object *current = &list; + while (TYPE(*current) == consObject) + { + current = &CDR(*current); + } + return TYPE(*current) == nilObject; +} + +int listLength(object list) +{ + object *current = &list; + int i = 0; + while (TYPE(*current) != nilObject) + { + current = &CDR(*current); + ++i; + } + return i; +} + +void deleteObject(object input) +{ + if ((TYPE(input) == symbolObject) && SYM(input) != NULL) + { + free(SYM(input)); + SYM(input) = NULL; + } + else if (TYPE(input) == consObject) + { + deleteObject(CAR(input)); + deleteObject(CDR(input)); + free(CONS(input)); + CONS(input) = NULL; + } + + TYPE(input) = nilObject; +} + +object copyObject(object input) +{ + object result; + TYPE(result) = TYPE(input); + if (TYPE(input) == errorObject) + { + ERRMSG(result) = ERRMSG(input); + } + else if (TYPE(input) == numberObject) + { + NUM(result) = NUM(input); + } + else if (TYPE(input) == symbolObject) + { + SYM(result) = + malloc(sizeof(char) * (strlen(SYM(input)) + 1)); + strcpy(SYM(result), SYM(input)); + } + else if (TYPE(input) == consObject) + { + CONS(result) = malloc(sizeof(cons)); + CAR(result) = copyObject(CAR(input)); + CDR(result) = copyObject(CDR(input)); + } + + return result; +} diff --git a/util.h b/util.h index cadc0ad..ee2d640 100644 --- a/util.h +++ b/util.h @@ -1,22 +1,45 @@ #pragma once +#define TYPE(x) ((x).type) + +#define CONS(x) ((x).value.consCell) +#define CAR(x) (((x).value.consCell)->car) +#define CDR(x) (((x).value.consCell)->cdr) +#define NUM(x) ((x).value.number) +#define SYM(x) ((x).value.symbol) +#define ERRMSG(x) ((x).value.errmsg) + +typedef struct object object; +typedef struct cons cons; + typedef enum { nilObject, consObject, numberObject, - symbolObject + symbolObject, + errorObject } dataType; -typedef struct _Object +struct object { dataType type; - void *address; -} object; + union + { + char *errmsg; + char *symbol; + long long int number; + cons *consCell; + } value; +}; -typedef struct _Cons +struct cons { object car; object cdr; -} cons; +}; +int properList(object list); +int listLength(object list); +void deleteObject(object input); +object copyObject(object input);