#include "../includes/scanner.h" #include #include #include #include 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); List* GenerateTokenList(const char* source) { List* tokens = CreateList(); 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; AddListItem(token, sizeof(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 ".org" or ".db" token = ParseDirective(); if (token) AddListItem(token, sizeof(Token), tokens); break; case '"': token = ParseString(); if (token) AddListItem(token, sizeof(Token), tokens); break; default: if (isdigit(c)) { AddListItem(ParseNumber(), sizeof(Token), tokens); break; } if (IsPunctuation(c)) { AdvanceScanner(); AddListItem(ParsePunctuation(c), sizeof(Token), tokens); break; } token = ParseIdentifier(); if (token) AddListItem(token, sizeof(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; AddListItem(token, sizeof(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())) { 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, ".org") == 0) { fprintf(stderr, "[Warning] Org is not a supported directive.\n"); free(directive); free(token); IgnoreLine(); return NULL; } fprintf(stderr, "[Error] Unknown assembler directive '%s'\n", directive); free(directive); 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') { 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; } 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; }