Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion src/lexer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
5 changes: 4 additions & 1 deletion src/lexer.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ typedef enum TokenType {
kTokenInt,
kTokenFloat,
kTokenStr,
kTokenBool
kTokenBool,
kTokenLeftBracket,
kTokenRightBracket,
kTokenArray
} TokenType;

typedef struct Token {
Expand Down
138 changes: 135 additions & 3 deletions src/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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;
}

Expand All @@ -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",
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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);

Expand Down
111 changes: 110 additions & 1 deletion src/vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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.
Expand Down Expand Up @@ -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__
Expand Down Expand Up @@ -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() {
Expand Down Expand Up @@ -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;
}
Expand Down
Loading