cirilisp/read.c
2019-01-29 23:54:32 +01:00

378 lines
8.7 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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,
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(&regSpace, "^[[:space:]]*", REG_EXTENDED);
regcomp(&regTokenGeneral,"^(\\(|\\)|'|[-,.+/*_\\\\=<>!&?[: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(&regSpace, input + i, nmatches, a, 0);
i += a[0].rm_eo;
/* помера индекс да би се игнорисали почетни "вајт-спејс" карактери */
while (i < n)
{
*new = malloc(sizeof(token));
(*new)->next = NULL;
if (!regexec(&regTokenGeneral, 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(&regSpace, input + i, nmatches, a, 0);
i += a[0].rm_eo;
new = &((*new)->next);
}
/* у овој петљи су нађени сви токени у реду и њихови лексеми су копирани у
* листу, међутим још није одређен њихов тип, нити чак то да су валидни */
regcomp(&regNumberFrac, "^[-+]?[[:digit:]]+(/[[:digit:]]+)?$",
REG_EXTENDED);
regcomp(&regNumberReal, "^[-+]?[[:digit:]]*,[[:digit:]]+$",
REG_EXTENDED);
regcomp(&regSymbol, "^([-+/*_\\\\=<>!&?[:alnum:]]+|\
\\|[[:print:]]+\\|)$", REG_EXTENDED);
regcomp(&regQuote, "^'$", REG_EXTENDED);
regcomp(&regLParenthesis, "^\\($", REG_EXTENDED);
regcomp(&regRParenthesis, "^\\)$", REG_EXTENDED);
regcomp(&regDot, "^\\.$", REG_EXTENDED);
regcomp(&regBool, "^(истинито|лажно)$", REG_EXTENDED);
new = &root;
while ((*new) != NULL)
{
if (!regexec(&regNumberFrac, (*new)->lexeme, nmatches, a, 0))
{
(*new)->type = numberFracToken;
}
else if (!regexec(&regNumberReal, (*new)->lexeme, nmatches, a,
0))
{
(*new)->type = numberRealToken;
}
else if (!regexec(&regBool, (*new)->lexeme, nmatches, a, 0))
{
(*new)->type = boolToken;
}
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);
}
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;
}