From 0eebb90c2ca50bcfed4a6ab7dae36957caee610b Mon Sep 17 00:00:00 2001 From: Arian Wardak Date: Fri, 16 May 2025 23:13:41 +0200 Subject: [PATCH] Fix #36. Implemented support for interger arrays --- src/lexer.c | 10 +++- src/lexer.h | 5 +- src/parser.c | 138 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/vm.c | 111 ++++++++++++++++++++++++++++++++++++++++- src/vm.h | 9 +++- 5 files changed, 265 insertions(+), 8 deletions(-) diff --git a/src/lexer.c b/src/lexer.c index 05a6c66..27e87f6 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -28,7 +28,7 @@ static const KeywordEntry kKeywordMap[] = { {"endif", kTokenEndif}, {"for", kTokenFor}, {"endfor", kTokenEndfor}, {"local", kTokenLocal}, {"func", kTokenFunc}, {"endfunc", kTokenEndfunc}, {"ret", kTokenRet}, {"int", kTokenInt}, {"float", kTokenFloat}, - {"str", kTokenStr}, {"bool", kTokenBool}}; + {"str", kTokenStr}, {"bool", kTokenBool}, {"array", kTokenArray}}; #ifdef __CC65__ static const size_t kKeywordCount = sizeof(kKeywordMap) / sizeof(KeywordEntry); @@ -224,6 +224,14 @@ static bool IsCharacter() { case '.': token.type = kTokenDot; + break; + case '[': + token.type = kTokenLeftBracket; + + break; + case ']': + token.type = kTokenRightBracket; + break; default: return false; diff --git a/src/lexer.h b/src/lexer.h index e8e8890..7f62824 100644 --- a/src/lexer.h +++ b/src/lexer.h @@ -71,7 +71,10 @@ typedef enum TokenType { kTokenInt, kTokenFloat, kTokenStr, - kTokenBool + kTokenBool, + kTokenLeftBracket, + kTokenRightBracket, + kTokenArray } TokenType; typedef struct Token { diff --git a/src/parser.c b/src/parser.c index 07d914d..e2617da 100644 --- a/src/parser.c +++ b/src/parser.c @@ -83,6 +83,8 @@ static VariableType TokenTypeToVariableType(const TokenType token_type) { return kVariableTypeBool; case kTokenFloat: return kVariableTypeFloat; + case kTokenArray: + return kVariableTypeArray; default: puts("Error: Unknown variable type."); return kVariableTypeUnknown; @@ -258,6 +260,35 @@ static VariableType ParseIdentifier(const char* const identifier_name) { var_type = symbol_table[index].type; } + if (AcceptToken(1, kTokenLeftBracket)) { + VariableType index_type = ParseExpression(); + + if (index_type != kVariableTypeInt) { + puts("Type error: Array index must be integer."); + token.type = kTokenEof; + return kVariableTypeUnknown; + } + + if (!ExpectToken(1, kTokenRightBracket)) { + return kVariableTypeUnknown; + } + + if (AcceptToken(1, kTokenAssign)) { + VariableType value_type = ParseExpression(); + + if (value_type != kVariableTypeInt) { + puts("Type error: Only integer values can be assigned to arrays."); + token.type = kTokenEof; + return kVariableTypeUnknown; + } + + EmitByte(kOpStoreElement); + EmitByte((unsigned char)index); + return kVariableTypeInt; + } + EmitByte(kOpIndexArray); + return kVariableTypeInt; + } if (AcceptToken(1, kTokenLeftParenthesis)) { ParseFunctionCall(); @@ -509,6 +540,50 @@ static void DefineVariable(const char* const identifier_name, EmitByte(type); } +static VariableType ParseArrayLiteral() { + size_t element_count = 0; + int value = 0; + size_t index = 0; + if (!ExpectToken(1, kTokenLeftBracket)) { + return kVariableTypeUnknown; + } + + while (token.type != kTokenRightBracket && token.type != kTokenEof) { + if (token.type != kTokenNumber) { + puts("Error: Only integers are supported in arrays."); + token.type = kTokenEof; + return kVariableTypeUnknown; + } + + value = token.value.number; + ConsumeNextToken(); + + index = AddNumberConstant(value, kConstantTypeNumber); + EmitByte(kOpConstant); + EmitByte(index); + ++element_count; + + if (token.type == kTokenComma) { + ConsumeNextToken(); + } else if (token.type != kTokenRightBracket) { + puts("Error: Expected ',' or ']'"); + token.type = kTokenEof; + return kVariableTypeUnknown; + } + } + + if (!ExpectToken(1, kTokenRightBracket)) { + puts("Error: Missing closing bracket for array."); + token.type = kTokenEof; + return kVariableTypeUnknown; + } + + EmitByte(kOpMakeArray); + EmitByte(element_count); + + return kVariableTypeArray; +} + static void SetVariable(const char* const identifier_name) { size_t index = 0; @@ -547,7 +622,8 @@ static void ParseVariableDeclaration(const char* const identifier_name) { variable_type = TokenTypeToVariableType(token.type); - if (!ExpectToken(4, kTokenInt, kTokenStr, kTokenBool, kTokenFloat)) { + if (!ExpectToken(5, kTokenInt, kTokenStr, kTokenBool, kTokenFloat, + kTokenArray)) { return; } @@ -560,8 +636,11 @@ static void ParseVariableDeclaration(const char* const identifier_name) { return; } - - expr_type = ParseExpression(); + if (variable_type == kVariableTypeArray) { + expr_type = ParseArrayLiteral(); + } else { + expr_type = ParseExpression(); + } if (expr_type != variable_type) { printf("Type error: Cannot assign %s to variable of type %s.\n", @@ -716,12 +795,59 @@ static void ParseReturnStatement() { // NOLINTNEXTLINE(misc-no-recursion) static void ParseIdentifierStatement(const char* const identifier_name) { + VariableType value_type = kVariableTypeUnknown; if (AcceptToken(1, kTokenColon)) { ParseVariableDeclaration(identifier_name); return; } + if (AcceptToken(1, kTokenLeftBracket)) { + VariableType index_type = ParseExpression(); + size_t index = 0; + + if (index_type != kVariableTypeInt) { + puts("Type error: Array index must be integer."); + token.type = kTokenEof; + return; + } + + if (!ExpectToken(1, kTokenRightBracket)) { + return; + } + + if (!ExpectToken(1, kTokenAssign)) { + return; + } + + value_type = ParseExpression(); + + if (value_type != kVariableTypeInt) { + puts("Type error: Only integer values can be stored in arrays."); + token.type = kTokenEof; + return; + } + + index = FindLocalSymbol(identifier_name); + if (index != (size_t)-1) { + EmitByte(kOpLoadLocal); + EmitByte((unsigned char)index); + } else { + index = FindGlobalSymbol(identifier_name); + if (index == (size_t)-1) { + printf("Error: Undefined variable '%s'.\n", identifier_name); + token.type = kTokenEof; + return; + } + + EmitByte(kOpLoadGlobal); + EmitByte((unsigned char)index); + } + + EmitByte(kOpStoreElement); + return; + } + if (AcceptToken(1, kTokenAssign)) { ParseVariableAssignment(identifier_name); @@ -756,6 +882,12 @@ static void ParseStatement() { ExtractIdentifierName(identifier_name); if (AcceptToken(1, kTokenIdentifier)) { + if (token.type == kTokenLeftBracket) { + VariableType result_type = ParseIdentifier(identifier_name); + (void)result_type; + return; + } + if (kTokenColon == token.type || kTokenAssign == token.type) { ParseIdentifierStatement(identifier_name); diff --git a/src/vm.c b/src/vm.c index 22d9c60..97311d2 100644 --- a/src/vm.c +++ b/src/vm.c @@ -14,7 +14,9 @@ enum { kStringPoolSize = 512, kNumberPoolSize = 64, kStackSize = 16, - kFunctionPoolSize = 16 + kFunctionPoolSize = 16, + kArrayPoolSize = 16, + kArrayElementsMax = 16 }; #else static constexpr int kGlobalVariablesSize = 64; @@ -24,6 +26,9 @@ static constexpr int kStringPoolSize = 512; static constexpr int kNumberPoolSize = 64; static constexpr int kStackSize = 16; static constexpr int kFunctionPoolSize = 16; +static constexpr int kArrayPoolSize = 16; +static constexpr int kArrayElementsMax = 16; + #endif /// Pushes a value onto the stack. @@ -57,11 +62,17 @@ typedef struct Function { VariableType return_type; } Function; +typedef struct Array { + int elements[kArrayElementsMax]; + size_t count; +} Array; + typedef struct StackValue { union as { int number; char* string; Function* function; + Array* array; } as; VariableType type; #ifdef __CC65__ @@ -103,6 +114,10 @@ static size_t function_pool_index = 0; static StackValue stack[kStackSize]; static size_t stack_index = 0; + +static Array array_pool[kArrayPoolSize]; +static size_t array_pool_index = 0; + // NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables) void ResetInterpreterState() { @@ -615,6 +630,100 @@ void RunVm() { case kOpPopCallFrame: { break; } + case kOpMakeArray: { + const size_t kElementCount = instructions[instruction_address++]; + StackValue val; + size_t element = 0; + Array* array_instance = &array_pool[array_pool_index++]; + StackValue array_value = {0}; + array_value.type = kVariableTypeArray; + array_value.as.array = array_instance; + + if (kElementCount > kArrayElementsMax) { + puts("Error: Array too large."); + return; + } + + if (array_pool_index >= kArrayPoolSize) { + puts("Error: Too many arrays."); + return; + } + + for (element = 0; element < kElementCount; ++element) { + val = Pop(); + if (val.type != kVariableTypeInt) { + puts("Error: Only integer are supported in arrays."); + return; + } + array_instance->elements[kElementCount - element - 1] = val.as.number; + } + array_instance->count = kElementCount; + + Push(array_value); + break; + } + case kOpIndexArray: { + StackValue index; + StackValue array; + StackValue result; + + index = Pop(); + array = Pop(); + if (array.type != kVariableTypeArray) { + puts("Runtime error: Cannot index non-array."); + return; + } + + if (index.type != kVariableTypeInt) { + puts("Runtime error: Array index must be integer."); + return; + } + + if (index.as.number < 0 || + (size_t)index.as.number >= array.as.array->count) { + puts("Runtime error: Array index out of bounds."); + return; + } + result.type = kVariableTypeInt; + result.as.number = array.as.array->elements[index.as.number]; + + Push(result); + break; + } + case kOpStoreElement: { + const size_t kArrayIndex = instructions[instruction_address++]; + StackValue value; + StackValue index; + StackValue array; + + value = Pop(); + index = Pop(); + array = global_variables[kArrayIndex]; + + if (array.type != kVariableTypeArray || !array.as.array) { + puts("Runtime error: This is not an array."); + return; + } + + if (index.type != kVariableTypeInt) { + puts("Runtime error: Array index must be integer."); + return; + } + + if (value.type != kVariableTypeInt) { + puts("Runtime error: Only integers can be stored in arrays."); + return; + } + + if (index.as.number < 0 || + (size_t)index.as.number >= array.as.array->count) { + puts("Runtime error: Array index out of bounds."); + return; + } + + array.as.array->elements[index.as.number] = value.as.number; + break; + } case kOpHalt: { return; } diff --git a/src/vm.h b/src/vm.h index f63ae26..b075ef4 100644 --- a/src/vm.h +++ b/src/vm.h @@ -38,13 +38,17 @@ typedef enum Opcode { kOpReturn, kOpPushCallFrame, kOpPopCallFrame, + kOpMakeArray, + kOpIndexArray, + kOpStoreElement, } Opcode; typedef enum ConstantType { kConstantTypeNumber, kConstantTypeString, kConstantTypeBoolean, - kConstantTypeFunction + kConstantTypeFunction, + kConstantTypeArray } ConstantType; typedef enum VariableType { @@ -52,7 +56,8 @@ typedef enum VariableType { kVariableTypeStr, kVariableTypeBool, kVariableTypeFloat, - kVariableTypeUnknown + kVariableTypeUnknown, + kVariableTypeArray } VariableType; // NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables)