#include #include #include #include #include "util.h" #include "read.h" #include "print.h" typedef enum { undefinedToken, numberFracToken, numberRealToken, symbolToken, boolToken, quoteToken, lParenthesisToken, rParenthesisToken } tokenType; typedef struct _Token { tokenType type; char *lexeme; struct _Token *next; } token; int completeExpression(token **tokenQueue); char *readline(); void append(token **head, token *appendix); token *lexLine(char *input); object parseExpression(token **inputList); token *tokenQueue = NULL; object read(char *prompt) { printf("%s", prompt); while (!completeExpression(&tokenQueue)) { char *input = readline(); if (input == NULL) /* унесен је EOF */ { printf("\nКрај улазног стрима.\nВоЗдра и дођите нам\ опет!\n"); exit(0); } append(&tokenQueue, lexLine(input)); } return parseExpression(&tokenQueue); } int completeExpression(token **tokenQueue) { int result = 0, indentLevel = 0; token *current = *tokenQueue; while (current != NULL) { if (current->type == lParenthesisToken) { ++indentLevel; } else if (current->type == rParenthesisToken) { if (indentLevel == 0) { token **deleteParen = tokenQueue; while (*deleteParen != current) { deleteParen = &(*deleteParen)->next; } *deleteParen = current->next; free(current); current = *deleteParen; continue; } else { if (indentLevel == 1) { result = 1; } --indentLevel; } } else { if (indentLevel == 0 && current->type != quoteToken) { result = 1; } } current = current->next; } return result; } ssize_t bytesRead; size_t nbytes = 2048; char *buffer = NULL; char *readline() { if (buffer == NULL) { buffer = (char *) malloc(nbytes + 1); } bytesRead = getline(&buffer, &nbytes, stdin); if (bytesRead == -1) { return NULL; } buffer[strlen(buffer)-1] = '\0'; /* Уклања завршни њу-лајн или ЕОФ у стрингу * и копира стринг на ново место */ return buffer; } void append(token **head, token *tail) { token **current = head; while (*current != NULL) { current = &(*current)->next; } *current = tail; } void deleteTokenList(token *root) { if (root->lexeme != NULL) { free(root->lexeme); } if (root->next != NULL) { deleteTokenList(root->next); } free(root); } token *lexLine(char *input) { regex_t regSpace, regTokenGeneral, regNumberFrac, regNumberReal, regLParenthesis, regRParenthesis, regSymbol, regQuote, regDot, regBool; regcomp(®Space, "^[[:space:]]*", REG_EXTENDED); regcomp(®TokenGeneral,"^(\\(|\\)|'|[-,.+/*_\\\\=<>!&?[:alnum:]]+|\ \\|[[:print:]]+\\||\"[[:print:]]+\")", REG_EXTENDED); const int nmatches = 1; regmatch_t a[nmatches]; token *root = NULL, **new; new = &root; int i = 0, n; n = strlen(input); regexec(®Space, input + i, nmatches, a, 0); i += a[0].rm_eo; /* помера индекс да би се игнорисали почетни "вајт-спејс" карактери */ while (i < n) { *new = malloc(sizeof(token)); (*new)->next = NULL; if (!regexec(®TokenGeneral, input + i, nmatches, a, 0)) { (*new)->lexeme = malloc((a[0].rm_eo + 1) * sizeof(char)); strncpy((*new)->lexeme, input + i, a[0].rm_eo); (*new)->lexeme[a[0].rm_eo] = '\0'; i += a[0].rm_eo; } else { /* уколико се у реду нађе карактер који се не може прихватити, штампа се место * тог каракетера у реду и бришу се сви токени тог реда, функција враћа NULL*/ fprintf(stderr, "Невалидан карактер на месту %d\n", i); (*new)->lexeme = NULL; deleteTokenList(root); return NULL; } regexec(®Space, input + i, nmatches, a, 0); i += a[0].rm_eo; new = &((*new)->next); } /* у овој петљи су нађени сви токени у реду и њихови лексеми су копирани у * листу, међутим још није одређен њихов тип, нити чак то да су валидни */ regcomp(®NumberFrac, "^[-+]?[[:digit:]]+(/[[:digit:]]+)?$", REG_EXTENDED); regcomp(®NumberReal, "^[-+]?[[:digit:]]*,[[:digit:]]+$", REG_EXTENDED); regcomp(®Symbol, "^([-+/*_\\\\=<>!&?[:alnum:]]+|\ \\|[[:print:]]+\\|)$", REG_EXTENDED); regcomp(®Quote, "^'$", REG_EXTENDED); regcomp(®LParenthesis, "^\\($", REG_EXTENDED); regcomp(®RParenthesis, "^\\)$", REG_EXTENDED); regcomp(®Dot, "^\\.$", REG_EXTENDED); regcomp(®Bool, "^(истинито|лажно)$", REG_EXTENDED); new = &root; while ((*new) != NULL) { if (!regexec(®NumberFrac, (*new)->lexeme, nmatches, a, 0)) { (*new)->type = numberFracToken; } else if (!regexec(®NumberReal, (*new)->lexeme, nmatches, a, 0)) { (*new)->type = numberRealToken; } else if (!regexec(®Bool, (*new)->lexeme, nmatches, a, 0)) { (*new)->type = boolToken; } else if (!regexec(®Symbol, (*new)->lexeme, nmatches, a, 0)) { (*new)->type = symbolToken; } else if (!regexec(®Quote, (*new)->lexeme, nmatches, a, 0)) { (*new)->type = quoteToken; } else if (!regexec(®LParenthesis, (*new)->lexeme, nmatches, a, 0)) { (*new)->type = lParenthesisToken; } else if (!regexec(®RParenthesis, (*new)->lexeme, nmatches, a, 0)) { (*new)->type = rParenthesisToken; } else { /* уколико се неки токен не може класификовати, штампа се лексем тог токена, * бришу се сви нађени токени у реду, и враћа се NULL */ fprintf(stderr, "Невалидан токен:\"%s\"\n", (*new)->lexeme); deleteTokenList(root); return NULL; } new = &((*new)->next); } return root; } object parseExpression(token **inputList) { object result; token input = **inputList; free(*inputList); *inputList = input.next; /* скида први преостали токен са листе унесених, да би се могао * прерадити */ if (input.type == numberFracToken) { TYPE(result) = numberObject; NUM_TYPE(result) = fractionNum; NUM_NUMER(result) = atoll(input.lexeme); char *tmp; NUM_DENOM(result) = (tmp = strchr(input.lexeme, '/')) == NULL ? 1 : atoll(tmp + 1); result = shortenFractionNum(result); } else if (input.type == boolToken) { TYPE(result) = boolObject; BOOL(result) = !strcmp("истинито", input.lexeme) ? 1 : 0; } else if (input.type == numberRealToken) { TYPE(result) = numberObject; NUM_TYPE(result) = realNum; NUM_REAL(result) = strtold(input.lexeme, NULL); } else if (input.type == symbolToken) { TYPE(result) = symbolObject; SYM(result) = malloc((strlen(input.lexeme) + 1) * sizeof(char)); /* Алокација стринга */ strcpy(SYM(result), input.lexeme); } else if (input.type == quoteToken) { if ((*inputList)->type != rParenthesisToken) { TYPE(result) = consObject; CONS(result) = malloc(sizeof(cons)); TYPE(CAR(result)) = symbolObject; SYM(CAR(result)) = malloc((strlen("навод") + 1) * sizeof(char)); strcpy(SYM(CAR(result)), "навод"); TYPE(CDR(result)) = consObject; CONS(CDR(result)) = malloc(sizeof(cons)); CAR(CDR(result)) = parseExpression(inputList); TYPE(CDR(CDR(result))) = nilObject; } else { TYPE(result) = errorObject; ERR(result) = syntaxError; } /* уколико "'" оператер директно претходи затвореној загради није могуће * правилно га претворити у навод оператер стога се он изоставља из било којег * израза */ } else if (input.type == lParenthesisToken) { object *listCurrent = &result; while ((*inputList)->type != rParenthesisToken) { TYPE(*listCurrent) = consObject; CONS(*listCurrent) = malloc(sizeof(cons)); /* Алокација конс ћелије */ CAR(*listCurrent) = parseExpression(inputList); listCurrent = &CDR(*listCurrent); } TYPE(*listCurrent) = nilObject; input = **inputList; free(*inputList); *inputList = input.next; int noErrors = 1; listCurrent = &result; while (TYPE(*listCurrent) != nilObject) { if (TYPE(CAR(*listCurrent)) == errorObject) { noErrors = 0; } listCurrent = &CDR(*listCurrent); } if (!noErrors) { deleteObject(result); TYPE(result) = errorObject; ERR(result) = syntaxError; } } else if (input.type == rParenthesisToken) { TYPE(result) = errorObject; ERR(result) = syntaxError; } free(input.lexeme); return result; }