-
Notifications
You must be signed in to change notification settings - Fork 2
[refactor] Lexer tokens #32
Changes from all commits
7f61eab
96a0bc7
a4abf87
1815aba
0e7dfc5
98f319f
c739d88
de89e09
64839fa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,26 +3,14 @@ | |||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| #include <stdio.h> | ||||||||||||||||||||||||||
| #include <stdlib.h> | ||||||||||||||||||||||||||
| #include <string.h> | ||||||||||||||||||||||||||
| #include <ctype.h> | ||||||||||||||||||||||||||
| #include "./tokens.h" | ||||||||||||||||||||||||||
| #include "../utils/hashes.h" | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||
| * A token that was parsed by the Lexer. | ||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||
| struct Token { | ||||||||||||||||||||||||||
| int type; | ||||||||||||||||||||||||||
| char value[longestKeywordSize]; // Increased size to handle longer values like numbers | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
| #include "./lexer.h" | ||||||||||||||||||||||||||
| #include "./tokens.h" | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||
| * The result of the lexer execution. | ||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||
| struct LexerResult { | ||||||||||||||||||||||||||
| int size; | ||||||||||||||||||||||||||
| struct Token tokens[1024]; | ||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||
| #include "../utils/hashes.h" | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||
| * Sets the token type of the currently selected token in the LexerResult with the provided token type. | ||||||||||||||||||||||||||
|
|
@@ -32,105 +20,103 @@ void pushToken(struct LexerResult* result, enum TokenType type) { | |||||||||||||||||||||||||
| result->size++; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||
| * Runs the lexer on the provided string and returns the parsed tokens. | ||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||
| struct LexerResult runLexer(char string[]) { | ||||||||||||||||||||||||||
| struct LexerResult result; | ||||||||||||||||||||||||||
| result.size = 0; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| const int len = strlen(string); | ||||||||||||||||||||||||||
| for(int i = 0; i < len; ++i) { | ||||||||||||||||||||||||||
| const char c = string[i]; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (c == ' ' || c == '\t' || c == '\n') { | ||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||
| } else if (isdigit(c)) { | ||||||||||||||||||||||||||
| int numLen = 0; | ||||||||||||||||||||||||||
| char numStr[32] = {0}; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| while (i < len && (isdigit(string[i]) || string[i] == '.')) { | ||||||||||||||||||||||||||
| numStr[numLen++] = string[i++]; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| i--; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| struct Token token; | ||||||||||||||||||||||||||
| token.type = NUMBER; | ||||||||||||||||||||||||||
| strncpy(token.value, numStr, sizeof(token.value) - 1); | ||||||||||||||||||||||||||
| result.tokens[result.size++] = token; | ||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||
| } else if (c == '"') { | ||||||||||||||||||||||||||
| int numLen = 0; | ||||||||||||||||||||||||||
| char strValue[longestKeywordSize] = {0}; | ||||||||||||||||||||||||||
| i++; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| while (i < len && string[i] != '"') { | ||||||||||||||||||||||||||
| strValue[numLen++] = string[i++]; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| struct Token token; | ||||||||||||||||||||||||||
| token.type = STRING; | ||||||||||||||||||||||||||
| strncpy(token.value, strValue, sizeof(token.value) - 1); | ||||||||||||||||||||||||||
| result.tokens[result.size++] = token; | ||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||
| } else if (isalpha(c)) { | ||||||||||||||||||||||||||
| int wordLen = 0; | ||||||||||||||||||||||||||
| char word[longestKeywordSize] = {0}; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| while (i < len && (isalnum(string[i]) || string[i] == '_')) { | ||||||||||||||||||||||||||
| word[wordLen++] = string[i++]; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| i--; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| struct Token token; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (strcmp(word, "func") == 0) { | ||||||||||||||||||||||||||
| token.type = FUNCTION; | ||||||||||||||||||||||||||
| } else if (strcmp(word, "true") == 0 || strcmp(word, "false") == 0) { | ||||||||||||||||||||||||||
| token.type = BOOLEAN_VALUE; | ||||||||||||||||||||||||||
| } else if (strcmp(word, "null") == 0) { | ||||||||||||||||||||||||||
| token.type = NU; | ||||||||||||||||||||||||||
| } else if(strcmp(word, "use") == 0) { | ||||||||||||||||||||||||||
| token.type = USE; | ||||||||||||||||||||||||||
| } else if(strcmp(word, "var") == 0) { | ||||||||||||||||||||||||||
| token.type = VAR; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| else { | ||||||||||||||||||||||||||
| token.type = KEYWORD; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| strncpy(token.value, word, sizeof(token.value) - 1); | ||||||||||||||||||||||||||
| result.tokens[result.size++] = token; | ||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| switch(c) { | ||||||||||||||||||||||||||
| case '{': pushToken(&result, BRACKETS_OPEN); break; | ||||||||||||||||||||||||||
| case '}': pushToken(&result, BRACKETS_CLOSE); break; | ||||||||||||||||||||||||||
| case '(': pushToken(&result, PAREN_OPEN); break; | ||||||||||||||||||||||||||
| case ')': pushToken(&result, PAREN_CLOSE); break; | ||||||||||||||||||||||||||
| case '[': pushToken(&result, ARRAY_OPEN); break; | ||||||||||||||||||||||||||
| case ']': pushToken(&result, ARRAY_CLOSE); break; | ||||||||||||||||||||||||||
| case ';': pushToken(&result, SEMICOLON); break; | ||||||||||||||||||||||||||
| case ',': pushToken(&result, COMMA); break; | ||||||||||||||||||||||||||
| case '=': pushToken(&result, DECLARE); break; | ||||||||||||||||||||||||||
| case '?': | ||||||||||||||||||||||||||
| pushToken(&result, NONE); | ||||||||||||||||||||||||||
| result.tokens[result.size - 1].value[0] = '?'; | ||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||
| case '+': | ||||||||||||||||||||||||||
| case '-': | ||||||||||||||||||||||||||
| case '/': | ||||||||||||||||||||||||||
| case '*': | ||||||||||||||||||||||||||
| pushToken(&result, MATH_OP); | ||||||||||||||||||||||||||
| result.tokens[result.size - 1].value[0] = c; | ||||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if (result.size > 0 && strlen(result.tokens[result.size - 1].value) == 0) { | ||||||||||||||||||||||||||
| result.size--; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| return result; | ||||||||||||||||||||||||||
| struct LexerResult runLexer(char* string) { | ||||||||||||||||||||||||||
| struct LexerResult result; | ||||||||||||||||||||||||||
| result.size = 0; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| result.tokens = malloc(sizeof(struct Token) * 1024); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| char c; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| while(c = *string++) { | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| int buffLen = 32; | ||||||||||||||||||||||||||
| char* buff = malloc(buffLen); | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
Comment on lines
+33
to
+35
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Memory leak: The buffer Ensure that + // After using buff
+ if (buff_needed) {
+ result.tokens[result.size - 1].value = buff;
+ } else {
+ free(buff);
+ }Also applies to: 52-52, 64-64 |
||||||||||||||||||||||||||
| if(c == ' ' || c == '\t' || c == '\n') { | ||||||||||||||||||||||||||
| continue; | ||||||||||||||||||||||||||
| } else if (isdigit(c)) { | ||||||||||||||||||||||||||
| int numLen = 0; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| while(isdigit(c)) { | ||||||||||||||||||||||||||
| buff[numLen] = c; | ||||||||||||||||||||||||||
| numLen++; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| c = *string++; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
Comment on lines
+33
to
+46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Potential buffer overflow due to fixed buffer size The buffers Apply this diff to implement dynamic resizing of 33 int buffLen = 32;
34 char* buff = malloc(buffLen);
+ int buffIndex = 0;
...
42 buff[numLen] = c;
43 numLen++;
+ buffIndex++;
+ if (buffIndex >= buffLen - 1) { // Resize if needed
+ buffLen *= 2;
+ buff = realloc(buff, buffLen);
+ if (buff == NULL) {
+ // Handle allocation failure
+ }
+ }
45 c = *string++;Repeat similar changes for string parsing (lines 54-59) and keyword parsing (lines 67-72). Also applies to: 54-59, 67-72
Comment on lines
+41
to
+46
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Missing null terminators in token values When collecting characters for numbers and keywords, the buffers are not null-terminated, which can lead to undefined behavior when used as strings. Add a null terminator at the end of the buffers after the loops. 46 }
+ buff[numLen] = '\0';Apply similar changes for keyword parsing. Also applies to: 67-72 |
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| pushToken(&result, NUMBER); | ||||||||||||||||||||||||||
| result.tokens[result.size - 1].value = buff; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| } else if (c == '\"') { | ||||||||||||||||||||||||||
| int strLen = 0; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| while(c != '\"') { | ||||||||||||||||||||||||||
| buff[strLen] = c; | ||||||||||||||||||||||||||
| strLen++; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| c = *string++; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
Comment on lines
+54
to
+59
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Infinite loop risk when parsing strings The loop Modify the loop condition to check for the null terminator to prevent infinite loops. 54 while(c != '\"' && c != '\0') {📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| pushToken(&result, STRING); | ||||||||||||||||||||||||||
| result.tokens[result.size - 1].value = buff; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| } else if(isalpha(c)) { | ||||||||||||||||||||||||||
| int keywordLen = 0; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| while(isalpha(c)) { | ||||||||||||||||||||||||||
| buff[keywordLen] = c; | ||||||||||||||||||||||||||
| keywordLen++; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| c = *string++; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| if(strcmp(buff, "func") == 0) { | ||||||||||||||||||||||||||
| pushToken(&result, FUNCTION); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| else if(strcmp(buff, "true") == 0 || strcmp(buff, "false") == 0) { | ||||||||||||||||||||||||||
| pushToken(&result, BOOLEAN_VALUE); | ||||||||||||||||||||||||||
| result.tokens[result.size - 1].value = buff; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| else if(strcmp(buff, "null") == 0) { | ||||||||||||||||||||||||||
| pushToken(&result, NU); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| else if(strcmp(buff, "use") == 0) { | ||||||||||||||||||||||||||
| pushToken(&result, USE); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| else if(strcmp(buff, "var") == 0) { | ||||||||||||||||||||||||||
| pushToken(&result, VAR); | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| else { | ||||||||||||||||||||||||||
| pushToken(&result, KEYWORD); | ||||||||||||||||||||||||||
| result.tokens[result.size - 1].value = buff; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| else { | ||||||||||||||||||||||||||
| switch(c) { | ||||||||||||||||||||||||||
| case '{': pushToken(&result, BRACKETS_OPEN); break; | ||||||||||||||||||||||||||
| case '}': pushToken(&result, BRACKETS_CLOSE); break; | ||||||||||||||||||||||||||
| case '(': pushToken(&result, PAREN_OPEN); break; | ||||||||||||||||||||||||||
| case ')': pushToken(&result, PAREN_CLOSE); break; | ||||||||||||||||||||||||||
| case '[': pushToken(&result, ARRAY_OPEN); break; | ||||||||||||||||||||||||||
| case ']': pushToken(&result, ARRAY_CLOSE); break; | ||||||||||||||||||||||||||
| case ';': pushToken(&result, SEMICOLON); break; | ||||||||||||||||||||||||||
| case ',': pushToken(&result, COMMA); break; | ||||||||||||||||||||||||||
| case '=': pushToken(&result, DECLARE); break; | ||||||||||||||||||||||||||
| case '?': pushToken(&result, NONE); break; | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| case '+': | ||||||||||||||||||||||||||
| case '-': | ||||||||||||||||||||||||||
| case '*': | ||||||||||||||||||||||||||
| case '/': | ||||||||||||||||||||||||||
| case '^': | ||||||||||||||||||||||||||
| pushToken(&result, MATH_OP); | ||||||||||||||||||||||||||
| result.tokens[result.size - 1].value[0] = c; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
Comment on lines
+114
to
+115
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Uninitialized For math operator tokens, you're assigning Allocate memory for 114 pushToken(&result, MATH_OP);
+ result.tokens[result.size - 1].value = malloc(2);
115 result.tokens[result.size - 1].value[0] = c;
+ result.tokens[result.size - 1].value[1] = '\0';📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| return result; | ||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -4,27 +4,19 @@ | |
| #include <stdio.h> | ||
| #include "./tokens.h" | ||
|
|
||
| /** | ||
| * Represents a single token from lexical analysis | ||
| */ | ||
| struct Token { | ||
| enum TokenType type; | ||
| char value[32]; // Using 32 as longestKeywordSize | ||
| }; | ||
|
|
||
| /** | ||
| * Contains the results of lexical analysis | ||
| */ | ||
| struct LexerResult { | ||
| int size; | ||
| struct Token tokens[1024]; | ||
| struct Token* tokens; | ||
| }; | ||
|
|
||
| /** | ||
| * Performs lexical analysis on an input string | ||
| * Returns a LexerResult containing the tokens | ||
| */ | ||
| struct LexerResult runLexer(const char* input); | ||
| struct LexerResult runLexer(char* input); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Changing input parameter to The If the function does not need to modify the input string, change the parameter back to |
||
|
|
||
| /** | ||
| * Adds a token to the LexerResult | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,39 +1,36 @@ | ||
| #ifndef TOKENS_H | ||
| #define TOKENS_H | ||
|
|
||
| #define longestKeywordSize 32 | ||
| #define smallestKeywordSize 4 | ||
|
|
||
| enum TokenType { | ||
| FUNCTION = 1, | ||
| RETURN = 2, | ||
| VAR = 3, | ||
| BRACKETS_OPEN = 4, | ||
| BRACKETS_CLOSE = 5, | ||
| PAREN_OPEN = 6, | ||
| PAREN_CLOSE = 7, | ||
| ARRAY_OPEN = 8, | ||
| ARRAY_CLOSE = 9, | ||
| NUMBER = 10, | ||
| STRING = 11, | ||
| BOOLEAN_VALUE = 12, | ||
| NU = 13, | ||
| KEYWORD = 14, | ||
| SEMICOLON = 15, | ||
| COMMA = 16, | ||
| DECLARE = 17, | ||
| USE = 18, | ||
| NONE = 19, | ||
| MATH_OP = 20 | ||
| FUNCTION, | ||
| RETURN, | ||
| VAR, | ||
| BRACKETS_OPEN, | ||
| BRACKETS_CLOSE, | ||
| PAREN_OPEN, | ||
| PAREN_CLOSE, | ||
| ARRAY_OPEN, | ||
| ARRAY_CLOSE, | ||
| NUMBER, | ||
| STRING, | ||
| BOOLEAN_VALUE, | ||
| NU, | ||
| KEYWORD, | ||
| SEMICOLON, | ||
| COMMA, | ||
| DECLARE, | ||
| USE, | ||
| NONE, | ||
| MATH_OP | ||
| }; | ||
|
|
||
| struct KeywordResult { | ||
| int count; | ||
| char* keywords[10]; | ||
| enum TokenType types[10]; | ||
| /** | ||
| * An lexer token generated by the Lexer. | ||
| */ | ||
| struct Token { | ||
| enum TokenType type; | ||
| char* value; | ||
| }; | ||
|
|
||
| void initKeywords(); | ||
| struct KeywordResult getKeywords(char start); | ||
|
|
||
| #endif |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Fixed-size token array may cause overflow
Allocating
result.tokenswith a fixed size of 1024 tokens could lead to overflow if processing inputs with more tokens.Implement dynamic resizing for
result.tokensto handle inputs with more than 1024 tokens.27 int tokensCapacity = 1024; 27 result.tokens = malloc(sizeof(struct Token) * tokensCapacity); ... + // Before pushing a new token + if (result.size >= tokensCapacity) { + tokensCapacity *= 2; + result.tokens = realloc(result.tokens, sizeof(struct Token) * tokensCapacity); + if (result.tokens == NULL) { + // Handle allocation failure + } + }