2019-01-08 22:19:29 +01:00
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <regex.h>
|
|
|
|
|
|
|
|
|
|
#include "util.h"
|
|
|
|
|
#include "read.h"
|
|
|
|
|
#include "print.h"
|
|
|
|
|
|
|
|
|
|
typedef enum
|
|
|
|
|
{
|
|
|
|
|
undefinedToken,
|
2019-01-20 23:48:12 +01:00
|
|
|
|
numberFracToken,
|
|
|
|
|
numberRealToken,
|
2019-01-08 22:19:29 +01:00
|
|
|
|
symbolToken,
|
2019-01-29 23:54:32 +01:00
|
|
|
|
boolToken,
|
2019-01-20 14:12:47 +01:00
|
|
|
|
quoteToken,
|
2019-01-08 22:19:29 +01:00
|
|
|
|
lParenthesisToken,
|
|
|
|
|
rParenthesisToken
|
|
|
|
|
} tokenType;
|
|
|
|
|
|
|
|
|
|
typedef struct _Token
|
|
|
|
|
{
|
|
|
|
|
tokenType type;
|
|
|
|
|
char *lexeme;
|
|
|
|
|
struct _Token *next;
|
|
|
|
|
} token;
|
|
|
|
|
|
2019-01-14 03:16:25 +01:00
|
|
|
|
int completeExpression(token **tokenQueue);
|
2019-01-08 22:19:29 +01:00
|
|
|
|
char *readline();
|
|
|
|
|
void append(token **head, token *appendix);
|
|
|
|
|
token *lexLine(char *input);
|
2019-01-14 03:16:25 +01:00
|
|
|
|
object parseExpression(token **inputList);
|
2019-01-08 22:19:29 +01:00
|
|
|
|
|
2019-01-14 03:16:25 +01:00
|
|
|
|
token *tokenQueue = NULL;
|
2019-01-08 22:19:29 +01:00
|
|
|
|
|
|
|
|
|
object read(char *prompt)
|
|
|
|
|
{
|
|
|
|
|
printf("%s", prompt);
|
|
|
|
|
|
2019-01-14 03:16:25 +01:00
|
|
|
|
while (!completeExpression(&tokenQueue))
|
2019-01-08 22:19:29 +01:00
|
|
|
|
{
|
|
|
|
|
char *input = readline();
|
2019-01-14 03:16:25 +01:00
|
|
|
|
if (input == NULL) /* унесен је EOF */
|
2019-01-08 22:19:29 +01:00
|
|
|
|
{
|
2019-01-29 23:54:32 +01:00
|
|
|
|
printf("\nКрај улазног стрима.\nВоЗдра и дођите нам\
|
|
|
|
|
опет!\n");
|
2019-01-08 22:19:29 +01:00
|
|
|
|
exit(0);
|
|
|
|
|
}
|
2019-01-14 03:16:25 +01:00
|
|
|
|
append(&tokenQueue, lexLine(input));
|
2019-01-08 22:19:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-01-14 03:16:25 +01:00
|
|
|
|
return parseExpression(&tokenQueue);
|
2019-01-08 22:19:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-01-14 03:16:25 +01:00
|
|
|
|
int completeExpression(token **tokenQueue)
|
2019-01-08 22:19:29 +01:00
|
|
|
|
{
|
|
|
|
|
int result = 0, indentLevel = 0;
|
2019-01-14 03:16:25 +01:00
|
|
|
|
token *current = *tokenQueue;
|
2019-01-08 22:19:29 +01:00
|
|
|
|
|
|
|
|
|
while (current != NULL)
|
|
|
|
|
{
|
|
|
|
|
if (current->type == lParenthesisToken)
|
|
|
|
|
{
|
|
|
|
|
++indentLevel;
|
|
|
|
|
}
|
|
|
|
|
else if (current->type == rParenthesisToken)
|
|
|
|
|
{
|
|
|
|
|
if (indentLevel == 0)
|
|
|
|
|
{
|
2019-01-14 03:16:25 +01:00
|
|
|
|
token **deleteParen = tokenQueue;
|
2019-01-08 22:19:29 +01:00
|
|
|
|
while (*deleteParen != current)
|
|
|
|
|
{
|
|
|
|
|
deleteParen = &(*deleteParen)->next;
|
|
|
|
|
}
|
|
|
|
|
*deleteParen = current->next;
|
|
|
|
|
free(current);
|
|
|
|
|
current = *deleteParen;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
if (indentLevel == 1)
|
|
|
|
|
{
|
|
|
|
|
result = 1;
|
|
|
|
|
}
|
|
|
|
|
--indentLevel;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-01-20 14:12:47 +01:00
|
|
|
|
if (indentLevel == 0 && current->type != quoteToken)
|
2019-01-08 22:19:29 +01:00
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-15 00:33:54 +01:00
|
|
|
|
buffer[strlen(buffer)-1] = '\0';
|
2019-01-14 03:16:25 +01:00
|
|
|
|
/* Уклања завршни њу-лајн или ЕОФ у стрингу
|
|
|
|
|
* и копира стринг на ново место */
|
2019-01-08 22:19:29 +01:00
|
|
|
|
|
2019-01-15 00:33:54 +01:00
|
|
|
|
return buffer;
|
2019-01-08 22:19:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void append(token **head, token *tail)
|
|
|
|
|
{
|
|
|
|
|
token **current = head;
|
|
|
|
|
|
|
|
|
|
while (*current != NULL)
|
|
|
|
|
{
|
|
|
|
|
current = &(*current)->next;
|
|
|
|
|
}
|
|
|
|
|
*current = tail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-01-22 16:22:00 +01:00
|
|
|
|
|
|
|
|
|
void deleteTokenList(token *root)
|
2019-01-08 22:19:29 +01:00
|
|
|
|
{
|
2019-01-22 16:22:00 +01:00
|
|
|
|
if (root->lexeme != NULL)
|
|
|
|
|
{
|
|
|
|
|
free(root->lexeme);
|
|
|
|
|
}
|
2019-01-08 22:19:29 +01:00
|
|
|
|
|
2019-01-22 16:22:00 +01:00
|
|
|
|
if (root->next != NULL)
|
|
|
|
|
{
|
|
|
|
|
deleteTokenList(root->next);
|
|
|
|
|
}
|
2019-01-08 22:19:29 +01:00
|
|
|
|
|
2019-01-22 16:22:00 +01:00
|
|
|
|
free(root);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
token *lexLine(char *input)
|
|
|
|
|
{
|
|
|
|
|
regex_t regSpace, regTokenGeneral, regNumberFrac, regNumberReal,
|
2019-01-29 23:54:32 +01:00
|
|
|
|
regLParenthesis, regRParenthesis, regSymbol, regQuote, regDot,
|
|
|
|
|
regBool;
|
2019-01-22 16:22:00 +01:00
|
|
|
|
|
|
|
|
|
regcomp(®Space, "^[[:space:]]*", REG_EXTENDED);
|
2019-01-29 23:54:32 +01:00
|
|
|
|
regcomp(®TokenGeneral,"^(\\(|\\)|'|[-,.+/*_\\\\=<>!&?[:alnum:]]+|\
|
|
|
|
|
\\|[[:print:]]+\\||\"[[:print:]]+\")", REG_EXTENDED);
|
2019-01-08 22:19:29 +01:00
|
|
|
|
|
|
|
|
|
const int nmatches = 1;
|
|
|
|
|
regmatch_t a[nmatches];
|
|
|
|
|
|
2019-01-22 16:22:00 +01:00
|
|
|
|
token *root = NULL, **new;
|
|
|
|
|
new = &root;
|
2019-01-08 22:19:29 +01:00
|
|
|
|
|
2019-01-22 16:22:00 +01:00
|
|
|
|
int i = 0, n;
|
|
|
|
|
n = strlen(input);
|
|
|
|
|
regexec(®Space, input + i, nmatches, a, 0);
|
|
|
|
|
i += a[0].rm_eo;
|
|
|
|
|
/* помера индекс да би се игнорисали почетни "вајт-спејс" карактери */
|
|
|
|
|
while (i < n)
|
2019-01-08 22:19:29 +01:00
|
|
|
|
{
|
2019-01-22 16:22:00 +01:00
|
|
|
|
*new = malloc(sizeof(token));
|
|
|
|
|
(*new)->next = NULL;
|
|
|
|
|
|
|
|
|
|
if (!regexec(®TokenGeneral, input + i, nmatches, a, 0))
|
2019-01-20 01:24:29 +01:00
|
|
|
|
{
|
2019-01-22 16:22:00 +01:00
|
|
|
|
(*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;
|
2019-01-20 01:24:29 +01:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-01-22 16:22:00 +01:00
|
|
|
|
/* уколико се у реду нађе карактер који се не може прихватити, штампа се место
|
|
|
|
|
* тог каракетера у реду и бришу се сви токени тог реда, функција враћа NULL*/
|
|
|
|
|
fprintf(stderr, "Невалидан карактер на месту %d\n", i);
|
|
|
|
|
(*new)->lexeme = NULL;
|
|
|
|
|
deleteTokenList(root);
|
|
|
|
|
return NULL;
|
2019-01-20 01:24:29 +01:00
|
|
|
|
}
|
2019-01-22 16:22:00 +01:00
|
|
|
|
regexec(®Space, input + i, nmatches, a, 0);
|
|
|
|
|
i += a[0].rm_eo;
|
|
|
|
|
new = &((*new)->next);
|
2019-01-08 22:19:29 +01:00
|
|
|
|
}
|
2019-01-22 16:22:00 +01:00
|
|
|
|
/* у овој петљи су нађени сви токени у реду и њихови лексеми су копирани у
|
|
|
|
|
* листу, међутим још није одређен њихов тип, нити чак то да су валидни */
|
2019-01-08 22:19:29 +01:00
|
|
|
|
|
2019-01-22 16:22:00 +01:00
|
|
|
|
regcomp(®NumberFrac, "^[-+]?[[:digit:]]+(/[[:digit:]]+)?$",
|
|
|
|
|
REG_EXTENDED);
|
|
|
|
|
regcomp(®NumberReal, "^[-+]?[[:digit:]]*,[[:digit:]]+$",
|
|
|
|
|
REG_EXTENDED);
|
2019-01-29 23:54:32 +01:00
|
|
|
|
regcomp(®Symbol, "^([-+/*_\\\\=<>!&?[:alnum:]]+|\
|
|
|
|
|
\\|[[:print:]]+\\|)$", REG_EXTENDED);
|
2019-01-22 16:22:00 +01:00
|
|
|
|
regcomp(®Quote, "^'$", REG_EXTENDED);
|
|
|
|
|
regcomp(®LParenthesis, "^\\($", REG_EXTENDED);
|
|
|
|
|
regcomp(®RParenthesis, "^\\)$", REG_EXTENDED);
|
2019-01-29 23:54:32 +01:00
|
|
|
|
regcomp(®Dot, "^\\.$", REG_EXTENDED);
|
|
|
|
|
regcomp(®Bool, "^(истинито|лажно)$", REG_EXTENDED);
|
2019-01-08 22:19:29 +01:00
|
|
|
|
|
|
|
|
|
new = &root;
|
2019-01-22 16:22:00 +01:00
|
|
|
|
while ((*new) != NULL)
|
2019-01-08 22:19:29 +01:00
|
|
|
|
{
|
2019-01-22 16:22:00 +01:00
|
|
|
|
if (!regexec(®NumberFrac, (*new)->lexeme, nmatches, a, 0))
|
2019-01-08 22:19:29 +01:00
|
|
|
|
{
|
2019-01-22 16:22:00 +01:00
|
|
|
|
(*new)->type = numberFracToken;
|
|
|
|
|
}
|
|
|
|
|
else if (!regexec(®NumberReal, (*new)->lexeme, nmatches, a,
|
|
|
|
|
0))
|
|
|
|
|
{
|
|
|
|
|
(*new)->type = numberRealToken;
|
|
|
|
|
}
|
2019-01-29 23:54:32 +01:00
|
|
|
|
else if (!regexec(®Bool, (*new)->lexeme, nmatches, a, 0))
|
|
|
|
|
{
|
|
|
|
|
(*new)->type = boolToken;
|
|
|
|
|
}
|
2019-01-22 16:22:00 +01:00
|
|
|
|
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);
|
2019-01-08 22:19:29 +01:00
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
new = &((*new)->next);
|
|
|
|
|
}
|
2019-01-22 16:22:00 +01:00
|
|
|
|
|
2019-01-08 22:19:29 +01:00
|
|
|
|
return root;
|
|
|
|
|
}
|
|
|
|
|
|
2019-01-14 03:16:25 +01:00
|
|
|
|
object parseExpression(token **inputList)
|
2019-01-08 22:19:29 +01:00
|
|
|
|
{
|
|
|
|
|
object result;
|
|
|
|
|
|
|
|
|
|
token input = **inputList;
|
|
|
|
|
free(*inputList);
|
|
|
|
|
*inputList = input.next;
|
2019-01-14 03:16:25 +01:00
|
|
|
|
/* скида први преостали токен са листе унесених, да би се могао
|
|
|
|
|
* прерадити */
|
2019-01-08 22:19:29 +01:00
|
|
|
|
|
2019-01-20 23:48:12 +01:00
|
|
|
|
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);
|
|
|
|
|
}
|
2019-01-29 23:54:32 +01:00
|
|
|
|
else if (input.type == boolToken)
|
|
|
|
|
{
|
|
|
|
|
TYPE(result) = boolObject;
|
|
|
|
|
BOOL(result) = !strcmp("истинито", input.lexeme) ? 1 : 0;
|
|
|
|
|
}
|
2019-01-20 23:48:12 +01:00
|
|
|
|
else if (input.type == numberRealToken)
|
2019-01-08 22:19:29 +01:00
|
|
|
|
{
|
2019-01-14 03:16:25 +01:00
|
|
|
|
TYPE(result) = numberObject;
|
2019-01-20 23:48:12 +01:00
|
|
|
|
NUM_TYPE(result) = realNum;
|
|
|
|
|
NUM_REAL(result) = strtold(input.lexeme, NULL);
|
2019-01-08 22:19:29 +01:00
|
|
|
|
}
|
|
|
|
|
else if (input.type == symbolToken)
|
|
|
|
|
{
|
2019-01-14 03:16:25 +01:00
|
|
|
|
TYPE(result) = symbolObject;
|
|
|
|
|
SYM(result) = malloc((strlen(input.lexeme) + 1)
|
|
|
|
|
* sizeof(char));
|
|
|
|
|
/* Алокација стринга */
|
|
|
|
|
strcpy(SYM(result), input.lexeme);
|
2019-01-08 22:19:29 +01:00
|
|
|
|
}
|
2019-01-20 14:12:47 +01:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* уколико "'" оператер директно претходи затвореној загради није могуће
|
|
|
|
|
* правилно га претворити у навод оператер стога се он изоставља из било којег
|
|
|
|
|
* израза */
|
|
|
|
|
}
|
2019-01-08 22:19:29 +01:00
|
|
|
|
else if (input.type == lParenthesisToken)
|
|
|
|
|
{
|
|
|
|
|
object *listCurrent = &result;
|
|
|
|
|
|
|
|
|
|
while ((*inputList)->type != rParenthesisToken)
|
|
|
|
|
{
|
2019-01-14 03:16:25 +01:00
|
|
|
|
TYPE(*listCurrent) = consObject;
|
|
|
|
|
CONS(*listCurrent) = malloc(sizeof(cons));
|
|
|
|
|
/* Алокација конс ћелије */
|
2019-01-08 22:19:29 +01:00
|
|
|
|
|
2019-01-14 03:16:25 +01:00
|
|
|
|
CAR(*listCurrent) = parseExpression(inputList);
|
2019-01-08 22:19:29 +01:00
|
|
|
|
|
2019-01-14 03:16:25 +01:00
|
|
|
|
listCurrent = &CDR(*listCurrent);
|
2019-01-08 22:19:29 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-01-14 03:16:25 +01:00
|
|
|
|
TYPE(*listCurrent) = nilObject;
|
2019-01-08 22:19:29 +01:00
|
|
|
|
|
|
|
|
|
input = **inputList;
|
|
|
|
|
free(*inputList);
|
|
|
|
|
*inputList = input.next;
|
|
|
|
|
|
2019-01-20 14:12:47 +01:00
|
|
|
|
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);
|
2019-01-08 22:19:29 +01:00
|
|
|
|
return result;
|
|
|
|
|
}
|