Add useful error handler
This commit is contained in:
parent
09af190aa9
commit
70ae06af44
|
@ -68,6 +68,7 @@ enum TokenTypes {
|
||||||
LI_INT, // Integer literal
|
LI_INT, // Integer literal
|
||||||
LI_STR, // String literal
|
LI_STR, // String literal
|
||||||
LI_SEMIC, // ;
|
LI_SEMIC, // ;
|
||||||
|
LI_COLON, // :
|
||||||
|
|
||||||
LI_LBRAC, // {
|
LI_LBRAC, // {
|
||||||
LI_RBRAC, // }
|
LI_RBRAC, // }
|
||||||
|
@ -93,6 +94,10 @@ enum TokenTypes {
|
||||||
KW_BREAK, // "break" keyword
|
KW_BREAK, // "break" keyword
|
||||||
KW_CONTINUE, // "continue" keyword
|
KW_CONTINUE, // "continue" keyword
|
||||||
|
|
||||||
|
KW_SWITCH, // "switch" keyword
|
||||||
|
KW_DEFAULT, // "default" keyword
|
||||||
|
KW_CASE, // "case" keyword
|
||||||
|
|
||||||
KW_PRINT,
|
KW_PRINT,
|
||||||
KW_IF,
|
KW_IF,
|
||||||
KW_ELSE,
|
KW_ELSE,
|
||||||
|
@ -180,6 +185,10 @@ enum SyntaxOps {
|
||||||
OP_FUNC = 40, // Define a function
|
OP_FUNC = 40, // Define a function
|
||||||
OP_BREAK, // Break out of the loop
|
OP_BREAK, // Break out of the loop
|
||||||
OP_CONTINUE, // Continue the loop
|
OP_CONTINUE, // Continue the loop
|
||||||
|
|
||||||
|
OP_SWITCH, // Switch statement
|
||||||
|
OP_DEFAULT, // Default case
|
||||||
|
OP_CASE = 45 // Case
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -269,14 +278,14 @@ struct FileData {
|
||||||
// The filename of the assembled object code
|
// The filename of the assembled object code
|
||||||
char* ObjectName;
|
char* ObjectName;
|
||||||
|
|
||||||
// The full contents of the source file
|
|
||||||
char* Content;
|
|
||||||
// The line of the file we are currently working on, -1 if it is finished
|
// The line of the file we are currently working on, -1 if it is finished
|
||||||
long CurrentLine;
|
long CurrentLine;
|
||||||
// The column of the file we are currently working on, -1 if it is finished
|
// The column of the file we are currently working on, -1 if it is finished
|
||||||
long CurrentColumn;
|
long CurrentColumn;
|
||||||
// The depth of the loop currently being parsed.
|
// The depth of the loop currently being parsed.
|
||||||
long CurrentLoopDepth;
|
long CurrentLoopDepth;
|
||||||
|
// The column that was last marked as "valid", the start of the error block if something goes wrong.
|
||||||
|
long CurrentSafeColumn;
|
||||||
|
|
||||||
// The symbol currently being lexed - TokenTypes index and integer value.
|
// The symbol currently being lexed - TokenTypes index and integer value.
|
||||||
struct Token CurrentSymbol;
|
struct Token CurrentSymbol;
|
||||||
|
@ -508,6 +517,10 @@ void DieChar(char* Error, int Char);
|
||||||
|
|
||||||
void DieBinary(char* Error, int Number);
|
void DieBinary(char* Error, int Number);
|
||||||
|
|
||||||
|
void ErrorReport(char* message, ...);
|
||||||
|
|
||||||
|
void Safe();
|
||||||
|
|
||||||
|
|
||||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
/* * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||||
* * * * C O D E G E N E R A T I O N * * * *
|
* * * * C O D E G E N E R A T I O N * * * *
|
||||||
|
@ -571,6 +584,8 @@ struct AssemblerVtable {
|
||||||
// Loops and jumps
|
// Loops and jumps
|
||||||
int (*AsIf)(struct ASTNode*, int, int);
|
int (*AsIf)(struct ASTNode*, int, int);
|
||||||
int (*AsWhile)(struct ASTNode*);
|
int (*AsWhile)(struct ASTNode*);
|
||||||
|
int (*AsSwitch)(struct ASTNode*);
|
||||||
|
int (*AsSwitchTable)(int, int, int, int*, int*, int);
|
||||||
int (*NewLabel)();
|
int (*NewLabel)();
|
||||||
void (*AsJmp)(int);
|
void (*AsJmp)(int);
|
||||||
void (*AsLabel)(int);
|
void (*AsLabel)(int);
|
||||||
|
@ -619,5 +634,7 @@ struct ASTNode* WhileStatement();
|
||||||
|
|
||||||
struct ASTNode* ForStatement();
|
struct ASTNode* ForStatement();
|
||||||
|
|
||||||
|
struct ASTNode* SwitchStatement();
|
||||||
|
|
||||||
|
|
||||||
void DumpTree(struct ASTNode* node, int level);
|
void DumpTree(struct ASTNode* node, int level);
|
84
src/Errors.c
Normal file
84
src/Errors.c
Normal file
|
@ -0,0 +1,84 @@
|
||||||
|
/*************/
|
||||||
|
/*GEMWIRE */
|
||||||
|
/* ERYTHRO*/
|
||||||
|
/*************/
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <Defs.h>
|
||||||
|
#include <Data.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
|
||||||
|
void Safe() {
|
||||||
|
CurrentFile->CurrentSafeColumn = CurrentFile->CurrentColumn;
|
||||||
|
}
|
||||||
|
|
||||||
|
void printLine(FILE* file, int ln) {
|
||||||
|
char buffer[256];
|
||||||
|
fgets(buffer, 256, file);
|
||||||
|
// Line number
|
||||||
|
printf("%03d|%s", ln + 1, buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printErrorLine(FILE* file, int ln) {
|
||||||
|
char firstBuffer[256], problemBuffer[256], tailBuffer[256];
|
||||||
|
if (CurrentFile->CurrentSafeColumn != 0)
|
||||||
|
fgets(firstBuffer, CurrentFile->CurrentSafeColumn, file);
|
||||||
|
fgets(problemBuffer, (CurrentFile->CurrentColumn > CurrentFile->CurrentSafeColumn ? CurrentFile->CurrentColumn - CurrentFile->CurrentSafeColumn : CurrentFile->CurrentSafeColumn), file);
|
||||||
|
if (CurrentFile->CurrentColumn > CurrentFile->CurrentSafeColumn)
|
||||||
|
fgets(tailBuffer, 256, file);
|
||||||
|
// Line number
|
||||||
|
printf("%03d|%s\033[0;31m%s\033[0m%s", ln + 1, firstBuffer, problemBuffer, tailBuffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
void printHelpLine(int line, char* message) {
|
||||||
|
printf(" | %s", message);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ErrorReport(char* message, ...) {
|
||||||
|
char strbuf[256];
|
||||||
|
|
||||||
|
// Resolve varargs to a string
|
||||||
|
va_list args;
|
||||||
|
va_start(args, message);
|
||||||
|
vsprintf(strbuf, message, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
int line = CurrentFile->CurrentLine - 1;
|
||||||
|
FILE* file = fopen(CurrentFile->SourceName, "r");
|
||||||
|
|
||||||
|
int errorOnLastLine = CurrentFile->CurrentColumn == 0;
|
||||||
|
|
||||||
|
// Print context for the error - up to 2 lines above the error
|
||||||
|
if (line < 3) {
|
||||||
|
// If we're at 0 or 1, we need to be careful to not try to print -1 for example.
|
||||||
|
for (int i = 0; i < line; i++)
|
||||||
|
printLine(file, i);
|
||||||
|
} else {
|
||||||
|
// Skip to line - 2
|
||||||
|
char buffer[256];
|
||||||
|
int count = 0;
|
||||||
|
while (count < line - (errorOnLastLine ? 3 : 2)) {
|
||||||
|
fgets(buffer, 256, file);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorOnLastLine)
|
||||||
|
printLine(file, line - 3);
|
||||||
|
|
||||||
|
printLine(file, line - 2);
|
||||||
|
if (!errorOnLastLine) printLine(file, line - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errorOnLastLine) {
|
||||||
|
printErrorLine(file, line - 1);
|
||||||
|
printHelpLine(line, strbuf);
|
||||||
|
printLine(file, line);
|
||||||
|
printLine(file, line + 1);
|
||||||
|
} else {
|
||||||
|
printErrorLine(file, line);
|
||||||
|
printHelpLine(line, strbuf);
|
||||||
|
printLine(file, line + 1);
|
||||||
|
printLine(file, line + 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
16
src/Lexer.c
16
src/Lexer.c
|
@ -45,8 +45,12 @@ static int NextChar(void) {
|
||||||
|
|
||||||
Char = fgetc(CurrentFile->Stream);
|
Char = fgetc(CurrentFile->Stream);
|
||||||
|
|
||||||
if (Char == '\n')
|
CurrentFile->CurrentColumn++;
|
||||||
|
|
||||||
|
if (Char == '\n') {
|
||||||
CurrentFile->CurrentLine++;
|
CurrentFile->CurrentLine++;
|
||||||
|
CurrentFile->CurrentColumn = 0;
|
||||||
|
}
|
||||||
|
|
||||||
return Char;
|
return Char;
|
||||||
}
|
}
|
||||||
|
@ -94,7 +98,7 @@ void VerifyToken(int Type, char* TokenExpected) {
|
||||||
if (CurrentFile->CurrentSymbol.type == Type)
|
if (CurrentFile->CurrentSymbol.type == Type)
|
||||||
Tokenise();
|
Tokenise();
|
||||||
else {
|
else {
|
||||||
printf("Expected %s on line %ld\n", TokenExpected, CurrentFile->CurrentLine);
|
ErrorReport("Expected %s, but got %s instead.\n", TokenExpected, TokenNames[CurrentFile->CurrentSymbol.type]);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -298,9 +302,15 @@ static int ReadKeyword(char* Str) {
|
||||||
return TY_CHAR;
|
return TY_CHAR;
|
||||||
if (!strcmp(Str, "continue"))
|
if (!strcmp(Str, "continue"))
|
||||||
return KW_CONTINUE;
|
return KW_CONTINUE;
|
||||||
|
if (!strcmp(Str, "case"))
|
||||||
|
return KW_CASE;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'd':
|
||||||
|
if (!strcmp(Str, "default"))
|
||||||
|
return KW_DEFAULT;
|
||||||
|
|
||||||
case 'e':
|
case 'e':
|
||||||
if (!strcmp(Str, "else"))
|
if (!strcmp(Str, "else"))
|
||||||
return KW_ELSE;
|
return KW_ELSE;
|
||||||
|
@ -354,6 +364,8 @@ static int ReadKeyword(char* Str) {
|
||||||
case 's':
|
case 's':
|
||||||
if (!strcmp(Str, "struct"))
|
if (!strcmp(Str, "struct"))
|
||||||
return KW_STRUCT;
|
return KW_STRUCT;
|
||||||
|
if (!strcmp(Str, "switch"))
|
||||||
|
return KW_SWITCH;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'u':
|
case 'u':
|
||||||
|
|
Loading…
Reference in New Issue
Block a user