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
22 changes: 14 additions & 8 deletions src/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -422,10 +422,13 @@ static void ParsePrintStatement() {
EmitByte(kOpPrint);
}

// clang-format off
#pragma static-locals(push, off)
// clang-format on
// NOLINTNEXTLINE(misc-no-recursion)
static void ParseIfStatement() {
size_t if_jump_address = 0;
size_t else_jump_address = 0;
size_t condition_patch_slot = 0;
size_t exit_patch_slot = 0;

if (!ExpectToken(1, kTokenLeftParenthesis)) {
return;
Expand All @@ -439,7 +442,7 @@ static void ParseIfStatement() {

EmitByte(kOpJumpIfFalse);

if_jump_address = instruction_address;
condition_patch_slot = instruction_address;

EmitByte(0);

Expand All @@ -449,7 +452,7 @@ static void ParseIfStatement() {
}

if (kTokenElse != token.type) {
instructions[if_jump_address] = instruction_address;
instructions[condition_patch_slot] = instruction_address;

ExpectToken(1, kTokenEndif);

Expand All @@ -458,22 +461,25 @@ static void ParseIfStatement() {

EmitByte(kOpJump);

else_jump_address = instruction_address;
exit_patch_slot = instruction_address;

EmitByte(0);

instructions[if_jump_address] = instruction_address;
instructions[condition_patch_slot] = instruction_address;

ConsumeNextToken();

while (kTokenEndif != token.type && kTokenEof != token.type) {
ParseStatement();
}

instructions[else_jump_address] = instruction_address;
instructions[exit_patch_slot] = instruction_address;

ExpectToken(1, kTokenEndif);
}
// clang-format off
#pragma static-locals(pop)
// clang-format on

static void DefineVariable(const char* const identifier_name,
const VariableType type, const bool is_local) {
Expand Down Expand Up @@ -727,7 +733,7 @@ static void ParseIdentifierStatement(const char* const identifier_name) {

// NOLINTNEXTLINE(misc-no-recursion)
static void ParseStatement() {
static char identifier_name[kIdentifierNameLength];
char identifier_name[kIdentifierNameLength];

if (AcceptToken(1, kTokenPrint)) {
ParsePrintStatement();
Expand Down
30 changes: 23 additions & 7 deletions src/vm.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,23 @@ static constexpr int kFunctionPoolSize = 16;
#endif

/// Pushes a value onto the stack.
/// Is defined as a macro since cc65 doesn't support passing structs to
/// functions by value for regular functions.
#define Push(value) (stack[stack_index++] = (value))
/// Defined as a macro since cc65 doesn't support passing structs to functions
/// by value for regular functions.
#define Push(value) \
do { \
if (kStackSize <= stack_index) { \
puts("Error: Stack overflow."); \
} else { \
stack[stack_index++] = (value); \
} \
} while (0)

/// Pops a value from the stack.
/// Is defined as a macro since cc65 doesn't support passing structs to
/// functions by value for regular functions.
#define Pop() (stack[--stack_index])
/// Defined as a macro since cc65 doesn't support passing structs to functions
/// by value for regular functions.
#define Pop() \
(0 == stack_index ? (puts("Error: Stack underflow."), kEmptyStackValue) \
: stack[--stack_index])

typedef struct Constants {
const void* pointer[kConstantsSize];
Expand All @@ -49,14 +58,21 @@ typedef struct Function {
} Function;

typedef struct StackValue {
VariableType type;
union as {
int number;
char* string;
Function* function;
} as;
VariableType type;
#ifdef __CC65__
unsigned char padding; ///< Used to add 1 byte padding to the struct, so that
///< the whole struct has a size of exactly 4 bytes.
///< See https://cc65.github.io/doc/cc65.html#s4
#endif
} StackValue;

static const StackValue kEmptyStackValue = {};

typedef struct CallFrame {
size_t return_address[kCallFrameTableSize];
size_t stack_offset[kCallFrameTableSize];
Expand Down
70 changes: 70 additions & 0 deletions tests/lexer_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,76 @@ void TestIf() {
TEST_ASSERT_EQUAL_INT(kTokenEndif, token.type);
}

void TestNestedIf() {
FillProgramBuffer(
"if(true)\nif(false)\nprint(\"foo\")\nelse\nprint(\"bar\")"
"\nendif\nendif");

ConsumeNextToken(); // if
TEST_ASSERT_EQUAL_INT(kTokenIf, token.type);

ConsumeNextToken(); // (
TEST_ASSERT_EQUAL_INT(kTokenLeftParenthesis, token.type);

ConsumeNextToken(); // true
TEST_ASSERT_EQUAL_INT(kTokenBoolean, token.type);
TEST_ASSERT_EQUAL_INT(1, token.value.number);

ConsumeNextToken(); // )
TEST_ASSERT_EQUAL_INT(kTokenRightParenthesis, token.type);

ConsumeNextToken(); // if
TEST_ASSERT_EQUAL_INT(kTokenIf, token.type);

ConsumeNextToken(); // (
TEST_ASSERT_EQUAL_INT(kTokenLeftParenthesis, token.type);

ConsumeNextToken(); // false
TEST_ASSERT_EQUAL_INT(kTokenBoolean, token.type);
TEST_ASSERT_EQUAL_INT(0, token.value.number);

ConsumeNextToken(); // )
TEST_ASSERT_EQUAL_INT(kTokenRightParenthesis, token.type);

ConsumeNextToken(); // print
TEST_ASSERT_EQUAL_INT(kTokenPrint, token.type);

ConsumeNextToken(); // (
TEST_ASSERT_EQUAL_INT(kTokenLeftParenthesis, token.type);

ConsumeNextToken(); // foo
TEST_ASSERT_EQUAL_INT(kTokenString, token.type);
TEST_ASSERT_EQUAL_STRING("foo", token.value.text);

ConsumeNextToken(); // )
TEST_ASSERT_EQUAL_INT(kTokenRightParenthesis, token.type);

ConsumeNextToken(); // else
TEST_ASSERT_EQUAL_INT(kTokenElse, token.type);

ConsumeNextToken(); // print
TEST_ASSERT_EQUAL_INT(kTokenPrint, token.type);

ConsumeNextToken(); // (
TEST_ASSERT_EQUAL_INT(kTokenLeftParenthesis, token.type);

ConsumeNextToken(); // bar
TEST_ASSERT_EQUAL_INT(kTokenString, token.type);
TEST_ASSERT_EQUAL_STRING("bar", token.value.text);

ConsumeNextToken(); // )
TEST_ASSERT_EQUAL_INT(kTokenRightParenthesis, token.type);

ConsumeNextToken(); // endif
TEST_ASSERT_EQUAL_INT(kTokenEndif, token.type);

ConsumeNextToken(); // endif
TEST_ASSERT_EQUAL_INT(kTokenEndif, token.type);

ConsumeNextToken(); // EOF
TEST_ASSERT_EQUAL_INT(kTokenEof, token.type);
}

void TestFor() {
FillProgramBuffer("for(true)print(\"Hello, world!\")endfor");

Expand Down
1 change: 1 addition & 0 deletions tests/lexer_test.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ void TestGreaterThanOrEqualTo();
void TestLessThanOrEqualTo();
void TestBooleanLiteral();
void TestIf();
void TestNestedIf();
void TestFor();
void TestNot();
void TestIntVariableDeclaration();
Expand Down
1 change: 1 addition & 0 deletions tests/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ int main() {
RUN_TEST(TestLessThanOrEqualTo);
RUN_TEST(TestBooleanLiteral);
RUN_TEST(TestIf);
RUN_TEST(TestNestedIf);
RUN_TEST(TestFor);
RUN_TEST(TestNot);
RUN_TEST(TestIntVariableDeclaration);
Expand Down