cirilisp/read.c

363 lines
8.2 KiB
C
Raw Normal View History

#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;
}
2019-01-22 16:22:00 +01:00
void deleteTokenList(token *root)
{
2019-01-22 16:22:00 +01:00
if (root->lexeme != NULL)
{
free(root->lexeme);
}
2019-01-22 16:22:00 +01:00
if (root->next != NULL)
{
deleteTokenList(root->next);
}
2019-01-22 16:22:00 +01:00
free(root);
}
token *lexLine(char *input)
{
regex_t regSpace, regTokenGeneral, regNumberFrac, regNumberReal,
regLParenthesis, regRParenthesis, regSymbol, regQuote;
regcomp(&regSpace, "^[[:space:]]*", REG_EXTENDED);
regcomp(&regTokenGeneral,"^(\\(|\\)|'|[-,.+/*_\\\\=<>!&?[:alnum:]]+)",
REG_EXTENDED);
const int nmatches = 1;
regmatch_t a[nmatches];
2019-01-22 16:22:00 +01:00
token *root = NULL, **new;
new = &root;
2019-01-22 16:22:00 +01:00
int i = 0, n;
n = strlen(input);
regexec(&regSpace, input + i, nmatches, a, 0);
i += a[0].rm_eo;
/* помера индекс да би се игнорисали почетни "вајт-спејс" карактери */
while (i < n)
{
2019-01-22 16:22:00 +01:00
*new = malloc(sizeof(token));
(*new)->next = NULL;
if (!regexec(&regTokenGeneral, input + i, nmatches, a, 0))
{
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;
}
else
{
2019-01-22 16:22:00 +01:00
/* уколико се у реду нађе карактер који се не може прихватити, штампа се место
* тог каракетера у реду и бришу се сви токени тог реда, функција враћа NULL*/
fprintf(stderr, "Невалидан карактер на месту %d\n", i);
(*new)->lexeme = NULL;
deleteTokenList(root);
return NULL;
}
2019-01-22 16:22:00 +01:00
regexec(&regSpace, input + i, nmatches, a, 0);
i += a[0].rm_eo;
new = &((*new)->next);
}
2019-01-22 16:22:00 +01:00
/* у овој петљи су нађени сви токени у реду и њихови лексеми су копирани у
* листу, међутим још није одређен њихов тип, нити чак то да су валидни */
2019-01-22 16:22:00 +01:00
regcomp(&regNumberFrac, "^[-+]?[[:digit:]]+(/[[:digit:]]+)?$",
REG_EXTENDED);
regcomp(&regNumberReal, "^[-+]?[[:digit:]]*,[[:digit:]]+$",
REG_EXTENDED);
regcomp(&regSymbol, "^[-+/*_\\\\=<>!&?[:alnum:]]+$", REG_EXTENDED);
regcomp(&regQuote, "^'$", REG_EXTENDED);
regcomp(&regLParenthesis, "^\\($", REG_EXTENDED);
regcomp(&regRParenthesis, "^\\)$", REG_EXTENDED);
new = &root;
2019-01-22 16:22:00 +01:00
while ((*new) != NULL)
{
2019-01-22 16:22:00 +01:00
if (!regexec(&regNumberFrac, (*new)->lexeme, nmatches, a, 0))
{
2019-01-22 16:22:00 +01:00
(*new)->type = numberFracToken;
}
else if (!regexec(&regNumberReal, (*new)->lexeme, nmatches, a,
0))
{
(*new)->type = numberRealToken;
}
else if (!regexec(&regSymbol, (*new)->lexeme, nmatches, a, 0))
{
(*new)->type = symbolToken;
}
else if (!regexec(&regQuote, (*new)->lexeme, nmatches, a, 0))
{
(*new)->type = quoteToken;
}
else if (!regexec(&regLParenthesis, (*new)->lexeme, nmatches,
a, 0))
{
(*new)->type = lParenthesisToken;
}
else if (!regexec(&regRParenthesis, (*new)->lexeme, nmatches,
a, 0))
{
(*new)->type = rParenthesisToken;
}
else
{
/* уколико се неки токен не може класификовати, штампа се лексем тог токена,
* бришу се сви нађени токени у реду, и враћа се NULL */
fprintf(stderr, "Невалидан токен:\"%s\"\n",
(*new)->lexeme);
deleteTokenList(root);
return NULL;
}
new = &((*new)->next);
}
2019-01-22 16:22:00 +01:00
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;
}