364 lines
8.3 KiB
C
364 lines
8.3 KiB
C
#define _POSIX_C_SOURCE 200809L
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <regex.h>
|
||
|
||
#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;
|
||
}
|