#define _POSIX_C_SOURCE 200809L #include #include #include #include #include "util.h" #include "read.h" #include "print.h" typedef enum { undefinedToken, numberFracToken, numberRealToken, symbolToken, 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"); 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; } regex_t regNumberFrac, regNumberReal, regLParenthesis, regRParenthesis, regSpace, regSymbol, regQuote; token *lex1Token(char *input, int *i) { token *result = malloc(sizeof(token)); result->next = NULL; regcomp(®Space, "^[[:space:]]*", REG_EXTENDED); regcomp(®NumberFrac, "^[-+]?[[:digit:]]+(/[[:digit:]]+)?", REG_EXTENDED); regcomp(®NumberReal, "^[-+]?[[:digit:]]*,[[:digit:]]+", REG_EXTENDED); regcomp(®Symbol, "^[-+/*_\\\\=<>!&?[:alnum:]]+", REG_EXTENDED); regcomp(®Quote, "^'", REG_EXTENDED); regcomp(®LParenthesis, "^\\(", REG_EXTENDED); regcomp(®RParenthesis, "^\\)", REG_EXTENDED); const int nmatches = 1; regmatch_t a[nmatches]; regexec(®Space, input + *i, nmatches, a, 0); *i += a[0].rm_eo; /* помера индекс да би се игнорисали почетни "вајт-спејс" карактери */ if (!regexec(®NumberReal, input + *i, nmatches, a, 0)) { result->type = numberRealToken; } else if (!regexec(®Symbol, input + *i, nmatches, a, 0)) { int tmp = a[0].rm_eo; if (!regexec(®NumberFrac, input + *i, nmatches, a, 0) && tmp == a[0].rm_eo) /* симбол може садржати цифре на било којој позицији али не може сам бити број * не постоји погодан начина да се ово путем regex-a запише стога овај if * исказ */ { result->type = numberFracToken; } else { regexec(®Symbol, input + *i, nmatches, a, 0); /* претходни regexec позив је променио вредност a[0].rm_eo, овиме се она враћа * на дужину нађеног симбола */ result->type = symbolToken; } } else if (!regexec(®NumberFrac, input + *i, nmatches, a, 0)) { result->type = numberFracToken; } else if (!regexec(®Quote, input + *i, nmatches, a, 0)) { result->type = quoteToken; } else if (!regexec(®LParenthesis, input + *i, nmatches, a, 0)) { result->type = lParenthesisToken; } else if (!regexec(®RParenthesis, input + *i, nmatches, a, 0)) { result->type = rParenthesisToken; } else { result->type = undefinedToken; result->lexeme = NULL; goto skipStringCopy; } result->lexeme = malloc((a[0].rm_eo + 1) * sizeof(char)); strncpy(result->lexeme, input + *i, a[0].rm_eo); result->lexeme[a[0].rm_eo] = '\0'; *i += a[0].rm_eo; regexec(®Space, input + *i, nmatches, a, 0); *i += a[0].rm_eo; /* игнорисање крајњих вајт-спејс карактера */ skipStringCopy: regfree(®Space); regfree(®NumberFrac); regfree(®NumberReal); regfree(®Symbol); regfree(®LParenthesis); regfree(®RParenthesis); return result; } token *lexLine(char *input) { int i = 0, n; n = strlen(input); token *root = NULL, **new; new = &root; while (i < n) { *new = lex1Token(input, &i); if ((*new)->type == undefinedToken) { /* уколико се у реду нађе токен који је лексички погрешан, штампа се место тог * токена у реду и бришу се сви токени нађени у реду, функција враћа NULL*/ fprintf(stderr, "Невалидан токен на месту %d\n", i); new = &root; while (*new != NULL) { free(root->lexeme); new = &((*new)->next); free(root); root = *new; } 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 == 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; }