377 lines
8.9 KiB
C
377 lines
8.9 KiB
C
#include "../includes/scanner.h"
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <limits.h>
|
|
|
|
const char* SourceCode;
|
|
int Line = 1;
|
|
int Position = 0;
|
|
int SourceLength = 0;
|
|
int ScannerAtEnd(void);
|
|
int IsPunctuation(char);
|
|
int IsWhiteSpace(char);
|
|
char PeekScanner(void);
|
|
char PeekAheadScanner(void);
|
|
void AdvanceScanner(void);
|
|
Token* ParseString(void);
|
|
Token* ParseDirective(void);
|
|
Token* ParseIdentifier(void);
|
|
Token* ParseNumber(void);
|
|
Token* ParsePunctuation(char);
|
|
void IgnoreLine(void);
|
|
|
|
TokenList* GenerateTokenList(const char* source) {
|
|
TokenList* tokens = CreateTokenList();
|
|
Token* token = NULL;
|
|
|
|
if (!tokens) return NULL;
|
|
|
|
SourceCode = source;
|
|
SourceLength = strlen(source);
|
|
|
|
for(int i = 0; i < SourceLength; i++) {
|
|
char c = PeekScanner();
|
|
|
|
switch(c) {
|
|
case ' ':
|
|
case '\r':
|
|
case '\t':
|
|
case '\v':
|
|
case '\f':
|
|
AdvanceScanner();
|
|
break; //Ignore whitespace
|
|
case '\n':
|
|
if (tokens->size > 0) {
|
|
token = tokens->content[tokens->size - 1];
|
|
|
|
if (token->Class == PunctuationClass && token->Value.Punctuation == NewLine) {
|
|
AdvanceScanner();
|
|
Line++;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
token = CreateToken(Line, PunctuationClass);
|
|
token->Value.Punctuation = NewLine;
|
|
AddToken(token, tokens);
|
|
AdvanceScanner();
|
|
Line++;
|
|
break;
|
|
case ';':
|
|
IgnoreLine();
|
|
|
|
if (tokens->size == 0) {
|
|
//There's nothing here so that means this is some comments block at the start of the file.
|
|
AdvanceScanner(); //Consume the actual new line char.
|
|
Line++;
|
|
}
|
|
break;
|
|
case '.': //directive like ".include" or ".db"
|
|
token = ParseDirective();
|
|
if (token) AddToken(token, tokens);
|
|
break;
|
|
case '"':
|
|
token = ParseString();
|
|
if (token) AddToken(token, tokens);
|
|
break;
|
|
default:
|
|
if (isdigit(c)) {
|
|
AddToken(ParseNumber(), tokens);
|
|
|
|
break;
|
|
}
|
|
|
|
if (IsPunctuation(c)) {
|
|
AdvanceScanner();
|
|
AddToken(ParsePunctuation(c), tokens);
|
|
break;
|
|
}
|
|
|
|
token = ParseIdentifier();
|
|
|
|
if (token) AddToken(token, tokens);
|
|
|
|
break;
|
|
}
|
|
|
|
token = NULL;
|
|
}
|
|
|
|
if (tokens->size > 0) {
|
|
token = tokens->content[tokens->size - 1];
|
|
|
|
if (token->Class == PunctuationClass && token->Value.Punctuation == NewLine) {
|
|
//If the last token is a line break, remove it as its not too meaningful.
|
|
tokens->size--;
|
|
}
|
|
}
|
|
|
|
token = CreateToken(Line, PunctuationClass);
|
|
|
|
token->EndOfFile = 1;
|
|
|
|
AddToken(token, tokens);
|
|
|
|
return tokens;
|
|
}
|
|
|
|
Token* ParseNumber(void) {
|
|
int start = Position;
|
|
|
|
while(!ScannerAtEnd() && isdigit(PeekScanner()))
|
|
AdvanceScanner();
|
|
|
|
if (tolower(PeekScanner()) == 'x') {
|
|
char ahead = tolower(PeekAheadScanner());
|
|
|
|
if ((ahead >= 'a' && ahead <= 'f') || isdigit(ahead)) {
|
|
AdvanceScanner(); //Consume the 'x'
|
|
|
|
while(!ScannerAtEnd() && PeekScanner() != '\n' && !IsWhiteSpace(PeekScanner()) && PeekScanner() != ';') {
|
|
if (isdigit(PeekScanner())) {
|
|
AdvanceScanner();
|
|
continue;
|
|
}
|
|
|
|
if (tolower(PeekScanner()) >= 'a' && tolower(PeekScanner()) <= 'f') AdvanceScanner();
|
|
}
|
|
}
|
|
}
|
|
|
|
int length = Position - start;
|
|
|
|
if (length == 0) return NULL;
|
|
|
|
char* lexeme = calloc(sizeof(char), length + 1);
|
|
|
|
if (!lexeme) {
|
|
fprintf(stderr, "Failed to calloc space for number. %s.\n", strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
memcpy(lexeme, &SourceCode[start], length);
|
|
|
|
Token* token = CreateToken(Line, NumberClass);
|
|
|
|
token->Value.Number = strtol(lexeme, NULL, 0);
|
|
|
|
if (errno != 0) {
|
|
fprintf(stderr, "[Error] Line %d: Invalid number detected. %s.\n", Line, strerror(errno));
|
|
exit(1);
|
|
}
|
|
|
|
token->Lemexe = lexeme;
|
|
|
|
return token;
|
|
}
|
|
|
|
Token* ParseDirective(void) {
|
|
int start = Position;
|
|
|
|
while(!ScannerAtEnd() && PeekScanner() != ' ' && PeekScanner() != '\n') AdvanceScanner();
|
|
|
|
int length = Position - start;
|
|
|
|
if (length == 0) return NULL;
|
|
|
|
char* directive = calloc(sizeof(char), length + 1);
|
|
|
|
if (!directive) {
|
|
fprintf(stderr, "Failed to calloc for the assmebler directive. %s.\n", strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
memcpy(directive, &SourceCode[start], length);
|
|
|
|
Token* token = CreateToken(Line, DirectiveClass);
|
|
|
|
if (strcmp(directive, ".db") == 0) {
|
|
token->Value.Directive = DB;
|
|
|
|
return token;
|
|
}
|
|
else if (strcmp(directive, ".include") == 0) {
|
|
token->Value.Directive = Include;
|
|
|
|
return token;
|
|
}
|
|
|
|
fprintf(stderr, "[Error] Unknown assembler directive '%s'\n", directive);
|
|
|
|
free(directive);
|
|
free(token);
|
|
|
|
IgnoreLine();
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void IgnoreLine(void) {
|
|
while(!ScannerAtEnd() && PeekScanner() != '\n') AdvanceScanner();
|
|
}
|
|
|
|
Token* ParseString(void) {
|
|
AdvanceScanner(); //Consume the first double quote.
|
|
|
|
int start = Position;
|
|
|
|
while(!ScannerAtEnd() && PeekScanner() != '"') AdvanceScanner();
|
|
|
|
int length = Position - start;
|
|
|
|
if (length == 0) return NULL;
|
|
|
|
char* lexeme = calloc(sizeof(char), length + 1);
|
|
|
|
if (!lexeme) {
|
|
fprintf(stderr, "Failed to calloc memory for string. %s.\n", strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
memcpy(lexeme, &SourceCode[start], length);
|
|
|
|
AdvanceScanner(); //Consume the trailing double quote.
|
|
|
|
Token* token = CreateToken(Line, CharacterClass);
|
|
|
|
token->Lemexe = lexeme;
|
|
|
|
return token;
|
|
}
|
|
|
|
Token* ParseIdentifier(void) {
|
|
int start = Position;
|
|
|
|
while(!ScannerAtEnd() && !IsPunctuation(PeekScanner()) && PeekScanner() != ' ' && PeekScanner() != '\n' && PeekScanner() != ';') {
|
|
AdvanceScanner();
|
|
}
|
|
|
|
int length = Position - start;
|
|
|
|
if (length == 0) return NULL;
|
|
|
|
Mnemonic mnemonics;
|
|
Registers reg;
|
|
char* lexeme = calloc(sizeof(char), length + 1);
|
|
|
|
if (!lexeme) {
|
|
fprintf(stderr, "Failed to calloc memory for identifier. %s.\n", strerror(errno));
|
|
return NULL;
|
|
}
|
|
|
|
memcpy(lexeme, &SourceCode[start], length);
|
|
|
|
if (IsOpcode(lexeme, &mnemonics)) {
|
|
Token* token = CreateToken(Line, MnemonicClass);
|
|
|
|
token->Value.Mnemonic = mnemonics;
|
|
|
|
return token;
|
|
}
|
|
if (IsRegister(lexeme, ®)) {
|
|
Token* token = CreateToken(Line, RegisterClass);
|
|
token->Value.Register = reg;
|
|
|
|
return token;
|
|
}
|
|
if (lexeme[length - 1] == ':') {
|
|
lexeme[length - 1] = '\0'; //Bit hacky, but this makes the parser's job a bit easier.
|
|
Token* token = CreateToken(Line, LabelClass);
|
|
|
|
token->Lemexe = lexeme;
|
|
|
|
return token;
|
|
}
|
|
if (strcmp(lexeme, "byte") == 0) {
|
|
Token* token = CreateToken(Line, DirectiveClass);
|
|
|
|
token->Lemexe = lexeme;
|
|
token->Value.Directive = Byte;
|
|
|
|
return token;
|
|
}
|
|
|
|
Token* token = CreateToken(Line, IdentifierClass);
|
|
|
|
token->Lemexe = lexeme;
|
|
|
|
return token;
|
|
}
|
|
|
|
char PeekScanner(void) {
|
|
if (Position >= SourceLength) return '\0';
|
|
|
|
return SourceCode[Position];
|
|
}
|
|
|
|
char PeekAheadScanner(void) {
|
|
if (Position + 1 >= SourceLength) return '\0';
|
|
|
|
return SourceCode[Position + 1];
|
|
}
|
|
|
|
void AdvanceScanner(void) {
|
|
if (ScannerAtEnd()) return;
|
|
|
|
Position++;
|
|
}
|
|
|
|
int ScannerAtEnd(void) {
|
|
return Position >= SourceLength;
|
|
}
|
|
|
|
int IsPunctuation(char c) {
|
|
switch(c) {
|
|
case '[':
|
|
case ']':
|
|
case '(':
|
|
case ')':
|
|
case ',':
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int IsWhiteSpace(char c) {
|
|
switch(c) {
|
|
case ' ':
|
|
case '\t':
|
|
case '\v':
|
|
case '\f':
|
|
case '\r':
|
|
return 1;
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
Token* ParsePunctuation(char c) {
|
|
TokenPunctuation punctuation;
|
|
|
|
switch(c) {
|
|
case '[':
|
|
punctuation = LBracket;
|
|
break;
|
|
case ']':
|
|
punctuation = RBracket;
|
|
break;
|
|
case '(':
|
|
punctuation = LParan;
|
|
break;
|
|
case ')':
|
|
punctuation = RParan;
|
|
break;
|
|
case ',':
|
|
punctuation = Comma;
|
|
break;
|
|
default:
|
|
return NULL;
|
|
}
|
|
|
|
Token* token = CreateToken(Line, PunctuationClass);
|
|
token->Value.Punctuation = punctuation;
|
|
|
|
return token;
|
|
} |