Files
assm-test/src/scanner.c
T

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, &reg)) {
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;
}