Я пишу лексер для языка JACK как часть создаваемого мной компилятора, и в моем списке лексем я постоянно получаю ошибку сегментации.У меня есть переменная, которая является указателем на указатель на токен, который хранит список лексем.Он передается двум различным функциям, которые выделяют ему память.Этот вопрос является обновлением моего предыдущего вопроса здесь со всем включенным кодом.
main.c
#include <stdio.h>
#include <stdlib.h>
#include "jlex.h"
int main(int argc, char * argv[])
{
FILE * sourceFile;
int lexerStatus;
token ** tokenList = NULL;
printf("Attempting to open file...\n");
if(argc > 1) {
if(!(sourceFile = fopen(argv[1], "r"))) {
fprintf(stderr, "Error: Could not open file \'%s\'!\n", argv[1]);
return FILE_ERROR;
}
} else {
fprintf(stderr, "Error: No input file given!\n");
return FILE_ERROR;
}
printf("Success!\nLexing input file...\n");
if((lexerStatus = lexer(&tokenList, sourceFile)) != EXEC_SUCCESS) {
fprintf(stderr, "Error: Failed to lex source file! (%d)\n", lexerStatus);
return lexerStatus;
}
fclose(sourceFile);
printf("Lexing complete!\n");
printf("Token Name\tToken Type\tLine Number\n");
for(token * currToken = tokenList[0]; currToken->type != terminator; currToken++) {
if(currToken->type == integer || currToken->type == keyword || currToken->type == identifier)
printf("%s", currToken->string);
else
putchar(currToken->character);
printf("\t\t%d\t\t%d\n", currToken->type, currToken->lineNum);
}
return EXEC_SUCCESS;
}
jlex.h
#ifndef JLEX_H
#define JLEX_H
#include <stdio.h> /* Required for FILE data type */
#define EXEC_SUCCESS 0
#define FILE_ERROR 1
#define MEM_ERROR 2
#define LEX_ERROR 3
#define DEFAULT_LIST_SIZE 1024
typedef enum tokenTypes { keyword, identifier, operator, string, integer, punctuator, terminator } tokenName;
typedef struct token {
union {
char * string;
int character;
};
tokenName type;
int lineNum;
} token;
extern const char * const keywords[];
extern const char * const operators;
extern const char * const punctuators;
int addTokenToList(token * nextToken, token *** tokenList);
int getNextToken(token * nextToken, FILE * sourceFile);
int lexer(token *** tokenList, FILE * sourceFile);
#endif
jlex.c
#include <ctype.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include "jlex.h"
const char * const tokenTypeNames[] = { "keyword", "identifier", "operator", "string", "integer", "punctuator", "terminator" };
const char * const keywords[] = { "boolean",
"char",
"class",
"constructor",
"do",
"else",
"false",
"field",
"function",
"if",
"int",
"method",
"null",
"return",
"static",
"true",
"this"
"var",
"void",
"while" };
const char * const operators = "+-*/&|~<>+=";
const char * const punctuators = "({[)}],.;";
static inline bool isoperator(int c)
{
for(unsigned int i = 0; i < strlen(operators); i++)
if(c == operators[i])
return true;
return false;
}
static inline bool ispunctuator(int c)
{
for(unsigned int i = 0; i < strlen(punctuators); i++)
if(c == punctuators[i])
return true;
return false;
}
static inline bool iskeyword(char * string)
{
for(unsigned int i = 0; i < sizeof(keywords) / sizeof(char*); i++)
if(!strcmp(keywords[i], string))
return true;
return false;
}
int getNextToken(token * nextToken, FILE * sourceFile)
{
/*
* Skip all whitespace and comments
* From first token try and determine token type (possible if an operator, punctuator, terminator, integer, or terminator)
* If token is determined then build it and return lexer status
* if not then keep reading until a full token can be contstructed
* Change chosen delimiters based on what kind of token we think we're reading
* Return lexer status (might fail if invalid lexeme is detected, i.e. a number followed by letters)
*/
int c;
static int lineNum = 1;
do {
c = fgetc(sourceFile);
if(c == '\n')
lineNum++;
} while((c == '\n') || (c == '\t') || (c == ' '));
nextToken->lineNum = lineNum;
nextToken->character = c;
if(c == EOF) {
nextToken->type = terminator;
return EXEC_SUCCESS;
}
if(isoperator(c)) {
nextToken->type = operator;
return EXEC_SUCCESS;
}
if(ispunctuator(c)) {
nextToken->type = punctuator;
return EXEC_SUCCESS;
}
/* If we get to this point then c is not a single character lexeme so we need to allocate some space for it in the token */
if(!(nextToken->string = malloc(1024 * sizeof(char))))
return MEM_ERROR;
int pos = 0;
if(isdigit(c)) {
do {
nextToken->string[pos++] = c;
c = fgetc(sourceFile);
} while(isdigit(c) && pos < 1023);
nextToken->string[pos] = '\0';
if(!isoperator(c) && !ispunctuator(c) && !isspace(c))
return LEX_ERROR;
nextToken->type = integer;
return EXEC_SUCCESS;
}
/* If we get to this point then we have to be reading an identifier or a keyword */
do {
nextToken->string[pos++] = c;
c = fgetc(sourceFile);
} while((isalpha(c) || isdigit(c) || c == '_') && pos < 1023);
nextToken->string[pos] = '\0';
if(iskeyword(nextToken->string))
nextToken->type = keyword;
else
nextToken->type = identifier;
return EXEC_SUCCESS;
}
int addTokenToList(token * nextToken, token *** tokenList)
{
static unsigned int listSize = DEFAULT_LIST_SIZE;
static unsigned int tokenNum = 0;
if(listSize <= tokenNum) {
listSize *= 2;
if(!(*tokenList = realloc(*tokenList, listSize * sizeof(token *)))) /* If the list isn't large enough then double its size */
return MEM_ERROR;
}
if(!(tokenList[tokenNum] = malloc(sizeof(token)))) /* Allocate memory for the data we are about to copy */
return MEM_ERROR;
memcpy(tokenList[tokenNum++], nextToken, sizeof(token)); /* Copy token into the array */
if(nextToken->type == terminator)
if(!(*tokenList = realloc(*tokenList, tokenNum * sizeof(token *)))) /* After EOF we know what the final size of the list is so resize it appropriately */
return MEM_ERROR;
return EXEC_SUCCESS;
}
int lexer(token *** tokenList, FILE * sourceFile)
{
int status;
token nextToken;
if(!(*tokenList = malloc(DEFAULT_LIST_SIZE * sizeof(token *))))
return MEM_ERROR;
do {
status = getNextToken(&nextToken, sourceFile);
if(addTokenToList(&nextToken, tokenList) != EXEC_SUCCESS)
status = MEM_ERROR;
} while(nextToken.type != terminator && status == EXEC_SUCCESS);
return status;
}
Тестовый файл:
jackExample.jack
class Main {
function void main () {
var Array a;
var int length;
var int i, sum;
let length = Keyboard.readInt();
let a = Array.new(length);
let i = 0;
while (i < length) {
let a[i] = Keyboard.readInt();
let sum = sum + a[i];
let i= i+1;
}
do Output.printString();
do Output.printInt(sum / length);
do Output.println();
return;
}
}
При запуске с этим исходным файлом программа выводит следующее:
Attempting to open file...
Success!
Lexing input file...
Lexing complete!
Token Name Token Type Line Number
class 0 1
1041 0
Segmentation fault
Выходные данные Valgrind идентифицируют множественные ошибки, связанные с вызовом malloc()
в функции addTokenToList()
.
Примечание. Приведенный выше исходный файл JACK не является допустимым JACK, а является версией, которую лексер используетв его текущем состоянии должен быть в состоянии обрабатывать.Пока не умеет работать со строковыми литералами и комментариями.