diff --git a/src/lexer.c b/src/lexer.c index 27e87f6..6db4781 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -180,6 +180,10 @@ static bool IsCharacter() { case '*': token.type = kTokenStar; + break; + case '%': + token.type = kTokenPercent; + break; case '/': token.type = kTokenSlash; diff --git a/src/lexer.h b/src/lexer.h index 7f62824..cb650a3 100644 --- a/src/lexer.h +++ b/src/lexer.h @@ -43,6 +43,7 @@ typedef enum TokenType { kTokenMinus, kTokenStar, kTokenSlash, + kTokenPercent, kTokenDot, kTokenComma, kTokenColon, diff --git a/src/parser.c b/src/parser.c index e2617da..720659a 100644 --- a/src/parser.c +++ b/src/parser.c @@ -157,6 +157,10 @@ static void ParseOperator(const TokenType operation) { case kTokenStar: EmitByte(kOpMultiply); + break; + case kTokenPercent: + EmitByte(kOpModulo); + break; case kTokenSlash: EmitByte(kOpDivide); @@ -343,7 +347,8 @@ static VariableType ParseTerm() { VariableType left_type = ParseFactor(); VariableType right_type = kVariableTypeUnknown; - while (kTokenStar == token.type || kTokenSlash == token.type) { + while (kTokenStar == token.type || kTokenSlash == token.type || + kTokenPercent == token.type) { const TokenType kOperator = token.type; ConsumeNextToken(); diff --git a/src/vm.c b/src/vm.c index 97311d2..4223c08 100644 --- a/src/vm.c +++ b/src/vm.c @@ -358,6 +358,30 @@ void RunVm() { break; } + case kOpModulo: { + StackValue stack_value_one = {}; + StackValue stack_value_two = {}; + StackValue result = {}; + + // cppcheck-suppress-begin redundantInitialization + stack_value_one = Pop(); + stack_value_two = Pop(); + // cppcheck-suppress-end redundantInitialization + + if (0 == stack_value_one.as.number) { + puts("Error: Division by zero."); + + return; + } + + result.as.number = + stack_value_two.as.number % stack_value_one.as.number; + result.type = kConstantTypeNumber; + + Push(result); + + break; + } case kOpEquals: { StackValue stack_value_one = {}; StackValue stack_value_two = {}; diff --git a/src/vm.h b/src/vm.h index b075ef4..7a23131 100644 --- a/src/vm.h +++ b/src/vm.h @@ -19,6 +19,7 @@ typedef enum Opcode { kOpSubtract, kOpMultiply, kOpDivide, + kOpModulo, kOpEquals, kOpNotEquals, kOpGreaterThan, diff --git a/tests/lexer_test.c b/tests/lexer_test.c index 5cc7e23..a74972d 100644 --- a/tests/lexer_test.c +++ b/tests/lexer_test.c @@ -94,6 +94,14 @@ void TestSlash() { TEST_ASSERT_EQUAL_INT(kTokenSlash, token.type); } +void TestPercent() { + FillProgramBuffer("60%3"); + + ConsumeNextToken(); // 60 + ConsumeNextToken(); // % + TEST_ASSERT_EQUAL_INT(kTokenPercent, token.type); +} + void TestString() { FillProgramBuffer("\"Hello, world!\""); diff --git a/tests/lexer_test.h b/tests/lexer_test.h index b39fe4f..0deaebd 100644 --- a/tests/lexer_test.h +++ b/tests/lexer_test.h @@ -7,6 +7,7 @@ void TestPrintArithmetic(); void TestMinus(); void TestStar(); void TestSlash(); +void TestPercent(); void TestString(); void TestPrintString(); void TestIdentifier(); diff --git a/tests/main.c b/tests/main.c index cdedc67..91824d5 100644 --- a/tests/main.c +++ b/tests/main.c @@ -21,6 +21,7 @@ int main() { RUN_TEST(TestMinus); RUN_TEST(TestStar); RUN_TEST(TestSlash); + RUN_TEST(TestPercent); RUN_TEST(TestString); RUN_TEST(TestPrintString); RUN_TEST(TestIdentifier); @@ -60,6 +61,7 @@ int main() { RUN_TEST(TestSubtractArithmetic); RUN_TEST(TestMultiplyArithmetic); RUN_TEST(TestDivideArithmetic); + RUN_TEST(TestModuloArithmetic); RUN_TEST(TestTrueBoolean); RUN_TEST(TestFalseBoolean); RUN_TEST(TestStringParse); diff --git a/tests/parser_test.c b/tests/parser_test.c index 8be8ca9..587f05d 100644 --- a/tests/parser_test.c +++ b/tests/parser_test.c @@ -58,6 +58,8 @@ void TestMultiplyArithmetic() { void TestDivideArithmetic() { TestBinaryOperator("print(10 / 5)", kOpDivide); } +void TestModuloArithmetic() { TestBinaryOperator("print(10 % 5)", kOpModulo); } + void TestLessThanCondition() { TestBinaryOperator("print(1 < 10)", kOpLessThan); } diff --git a/tests/parser_test.h b/tests/parser_test.h index 3807c0f..4a4fccb 100644 --- a/tests/parser_test.h +++ b/tests/parser_test.h @@ -13,6 +13,7 @@ void TestAddArithmetic(); void TestSubtractArithmetic(); void TestMultiplyArithmetic(); void TestDivideArithmetic(); +void TestModuloArithmetic(); void TestTrueBoolean(); void TestFalseBoolean(); void TestStringParse();