diff --git a/CSharp/Interpreter/ArcscriptExpression.cs b/CSharp/Interpreter/ArcscriptExpression.cs
index bb9b67c..eb450ce 100644
--- a/CSharp/Interpreter/ArcscriptExpression.cs
+++ b/CSharp/Interpreter/ArcscriptExpression.cs
@@ -93,6 +93,28 @@ public class Expression : ArcscriptExpressionBase, IComparable
}
return new Expression(result);
}
+
+ public static Expression operator %(Expression first, Expression second)
+ {
+ if (first.Type() == typeof(string) || second.Type() == typeof(string))
+ {
+ throw new InvalidOperationException("Modulo is not supported for string types.");
+ }
+ var doubleValues = GetDoubleValues(first.Value, second.Value);
+ if (doubleValues.Value2 == 0)
+ {
+ throw new DivideByZeroException("Modulo by zero.");
+ }
+ if (!doubleValues.HasDouble)
+ {
+ var intValue = (int)(doubleValues.Value1 % doubleValues.Value2);
+ return new Expression(intValue);
+ }
+ else
+ {
+ return new Expression(doubleValues.Value1 % doubleValues.Value2);
+ }
+ }
public static bool operator ==(Expression first, Expression second)
{
@@ -113,8 +135,12 @@ public class Expression : ArcscriptExpressionBase, IComparable
return false;
}
- public int CompareTo(object other)
+ public int CompareTo(object? other)
{
+ if (other == null || !(other is Expression))
+ {
+ throw new ArgumentException("Object is not an Expression");
+ }
Expression o = (Expression)other;
DoubleValues fValues = GetDoubleValues(this.Value, o.Value);
double result = fValues.Value1 - fValues.Value2;
@@ -128,7 +154,7 @@ public int CompareTo(object other)
return 0;
}
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
if (obj == null || !(obj is Expression))
{
diff --git a/CSharp/Interpreter/ArcscriptVisitor.cs b/CSharp/Interpreter/ArcscriptVisitor.cs
index 741050a..785cd95 100644
--- a/CSharp/Interpreter/ArcscriptVisitor.cs
+++ b/CSharp/Interpreter/ArcscriptVisitor.cs
@@ -167,6 +167,8 @@ public override object VisitStatement_assignment([NotNull] ArcscriptParser.State
variableValue *= compound_condition_or;
} else if ( context.ASSIGNDIV() != null ) {
variableValue /= compound_condition_or;
+ } else if (context.ASSIGNMOD() != null) {
+ variableValue %= compound_condition_or;
}
this.state.SetVarValue(variableName, variableValue.Value);
@@ -281,8 +283,13 @@ public override object VisitMultiplicative_numeric_expression([NotNull] Arcscrip
if ( context.MUL() != null ) {
return result * signed_unary_num_expr;
}
- // Else DIV
- return result / signed_unary_num_expr;
+
+ if (context.DIV() != null)
+ {
+ return result / signed_unary_num_expr;
+ }
+ // Else MOD
+ return result % signed_unary_num_expr;
}
return (Expression)this.VisitSigned_unary_numeric_expression(context.signed_unary_numeric_expression());
diff --git a/CSharp/__tests__/runtimeErrors.json b/CSharp/__tests__/runtimeErrors.json
index 2f4924a..9adb85e 100644
--- a/CSharp/__tests__/runtimeErrors.json
+++ b/CSharp/__tests__/runtimeErrors.json
@@ -67,6 +67,18 @@
{
"code": "
w -= _a
",
"error": "runtime"
+ },
+ {
+ "code": "z = 10 % 0
",
+ "error": "runtime"
+ },
+ {
+ "code": "x %= 0
",
+ "error": "runtime"
+ },
+ {
+ "code": "y = 5 % \"test\"
",
+ "error": "runtime"
}
]
}
\ No newline at end of file
diff --git a/CSharp/__tests__/valid.json b/CSharp/__tests__/valid.json
index 6df3bb8..ea3214d 100644
--- a/CSharp/__tests__/valid.json
+++ b/CSharp/__tests__/valid.json
@@ -99,6 +99,42 @@
"var1": 8
}
},
+ {
+ "code": "x = 10 % 3
",
+ "changes": {
+ "var1": 1
+ }
+ },
+ {
+ "code": "x = 10 % 3 * 2
",
+ "changes": {
+ "var1": 2
+ }
+ },
+ {
+ "code": "x = 10 % (3 * 2)
",
+ "changes": {
+ "var1": 4
+ }
+ },
+ {
+ "code": "x = 5 + 6 % 3
",
+ "changes": {
+ "var1": 5
+ }
+ },
+ {
+ "code": "x %= 6
",
+ "changes": {
+ "var1": 2
+ }
+ },
+ {
+ "code": "x = 10 % 7 % 2
",
+ "changes": {
+ "var1": 1
+ }
+ },
{
"code": "x = 3 / 4 * 5
",
"changes": {
diff --git a/Cpp/demo/tests/runtimeErrors.json b/Cpp/demo/tests/runtimeErrors.json
index 2f4924a..9adb85e 100644
--- a/Cpp/demo/tests/runtimeErrors.json
+++ b/Cpp/demo/tests/runtimeErrors.json
@@ -67,6 +67,18 @@
{
"code": "w -= _a
",
"error": "runtime"
+ },
+ {
+ "code": "z = 10 % 0
",
+ "error": "runtime"
+ },
+ {
+ "code": "x %= 0
",
+ "error": "runtime"
+ },
+ {
+ "code": "y = 5 % \"test\"
",
+ "error": "runtime"
}
]
}
\ No newline at end of file
diff --git a/Cpp/demo/tests/valid.json b/Cpp/demo/tests/valid.json
index 6df3bb8..ea3214d 100644
--- a/Cpp/demo/tests/valid.json
+++ b/Cpp/demo/tests/valid.json
@@ -99,6 +99,42 @@
"var1": 8
}
},
+ {
+ "code": "x = 10 % 3
",
+ "changes": {
+ "var1": 1
+ }
+ },
+ {
+ "code": "x = 10 % 3 * 2
",
+ "changes": {
+ "var1": 2
+ }
+ },
+ {
+ "code": "x = 10 % (3 * 2)
",
+ "changes": {
+ "var1": 4
+ }
+ },
+ {
+ "code": "x = 5 + 6 % 3
",
+ "changes": {
+ "var1": 5
+ }
+ },
+ {
+ "code": "x %= 6
",
+ "changes": {
+ "var1": 2
+ }
+ },
+ {
+ "code": "x = 10 % 7 % 2
",
+ "changes": {
+ "var1": 1
+ }
+ },
{
"code": "x = 3 / 4 * 5
",
"changes": {
diff --git a/Cpp/src/ArcscriptExpression.cpp b/Cpp/src/ArcscriptExpression.cpp
index ca13bfb..4b75888 100755
--- a/Cpp/src/ArcscriptExpression.cpp
+++ b/Cpp/src/ArcscriptExpression.cpp
@@ -1,5 +1,6 @@
#include "ArcscriptExpression.h"
#include
+#include
#include "ArcscriptErrorExceptions.h"
@@ -31,11 +32,10 @@ std::string Expression::valueToString(std::any value)
Expression::NumberValues Expression::doubleValues(std::any value1, std::any value2) {
- int intValue1, intValue2;
- double dblValue1, dblValue2;
+ double dblValue1 = 0, dblValue2 = 0;
bool isDouble = false;
if (value1.type() == typeid(int)) {
- intValue1 = std::any_cast(value1);
+ const int intValue1 = std::any_cast(value1);
dblValue1 = intValue1;
} else if (value1.type() == typeid(double)){ // type double;
isDouble = true;
@@ -49,7 +49,7 @@ Expression::NumberValues Expression::doubleValues(std::any value1, std::any valu
}
}
if (value2.type() == typeid(int)) {
- intValue2 = std::any_cast(value2);
+ const int intValue2 = std::any_cast(value2);
dblValue2 = intValue2;
} else if (value2.type() == typeid(double)) {
isDouble = true;
@@ -77,15 +77,15 @@ bool Expression::valueToBool(std::any value) {
return (std::any_cast(value) > 0);
}
if (value.type() == typeid(std::string)) {
- return (std::any_cast(value) != "");
+ return (!std::any_cast(value).empty());
}
return (std::any_cast(value));
}
-Expression Expression::operator+ (const Expression &other) {
+Expression Expression::operator+ (const Expression &other) const {
if (value.type() == typeid(std::string) || other.value.type() == typeid(std::string))
{
- return Expression(valueToString(value) + valueToString(other.value));
+ return {valueToString(value) + valueToString(other.value)};
}
NumberValues values = doubleValues(value, other.value);
Expression* result;
@@ -98,7 +98,7 @@ Expression Expression::operator+ (const Expression &other) {
return *result;
}
-Expression Expression::operator- (const Expression &other) {
+Expression Expression::operator- (const Expression &other) const {
if (value.type() == typeid(std::string) || other.value.type() == typeid(std::string)) {
throw RuntimeErrorException("Cannot subtract strings");
}
@@ -113,7 +113,7 @@ Expression Expression::operator- (const Expression &other) {
return *result;
}
-Expression Expression::operator* (const Expression &other) {
+Expression Expression::operator* (const Expression &other) const {
NumberValues values = doubleValues(value, other.value);
Expression* result;
if (!values.hasDoubles) {
@@ -125,7 +125,7 @@ Expression Expression::operator* (const Expression &other) {
return *result;
}
-Expression Expression::operator* (const int other) {
+Expression Expression::operator* (const int other) const {
NumberValues values = doubleValues(value, other);
Expression* result;
if (!values.hasDoubles) {
@@ -137,7 +137,7 @@ Expression Expression::operator* (const int other) {
return *result;
}
-Expression Expression::operator/ (const Expression &other) {
+Expression Expression::operator/ (const Expression &other) const {
NumberValues values = doubleValues(value, other.value);
Expression* result;
@@ -150,6 +150,24 @@ Expression Expression::operator/ (const Expression &other) {
return *result;
}
+Expression Expression::operator% (const Expression &other) const {
+ NumberValues values = doubleValues(value, other.value);
+ Expression* result;
+
+ if (values.value2 == 0) {
+ throw RuntimeErrorException("Modulo by zero is not allowed.");
+ }
+
+ if (!values.hasDoubles) {
+ int intValue = static_cast(static_cast(values.value1) % static_cast(values.value2));
+ result = new Expression(intValue);
+ } else {
+ double modValue = std::fmod(values.value1, values.value2);
+ result = new Expression(modValue);
+ }
+ return *result;
+}
+
Expression Expression::operator+= (const Expression &other) {
if (value.type() == typeid(std::string) || other.value.type() == typeid(std::string)) {
auto val1 = valueToString(value);
@@ -216,7 +234,24 @@ Expression Expression::operator/= (const Expression &other) {
return *this;
}
-bool Expression::operator== (const Expression &other) {
+Expression Expression::operator%= (const Expression &other) {
+ NumberValues values = doubleValues(value, other.value);
+
+ if (values.value2 == 0) {
+ throw RuntimeErrorException("Modulo by zero is not allowed.");
+ }
+
+ if (!values.hasDoubles) {
+ int intValue = static_cast(static_cast(values.value1) % static_cast(values.value2));
+ value = new Expression(intValue);
+ } else {
+ double modValue = std::fmod(values.value1, values.value2);
+ value = new Expression(modValue);
+ }
+ return *this;
+}
+
+bool Expression::operator== (const Expression &other) const {
if (value.type() == typeid(int) || value.type() == typeid(double)) {
NumberValues values = doubleValues(value, other.value);
return values.value1 == values.value2;
@@ -227,28 +262,28 @@ bool Expression::operator== (const Expression &other) {
return std::any_cast(value) == std::any_cast(other.value);
}
-bool Expression::operator== (double other) {
+bool Expression::operator== (double other) const {
NumberValues values = doubleValues(value, other);
return values.value1 == values.value2;
}
-bool Expression::operator== (int other) {
+bool Expression::operator== (int other) const {
NumberValues values = doubleValues(value, other);
return values.value1 == values.value2;
}
-bool Expression::operator== (std::string other) {
+bool Expression::operator== (const std::string& other) const {
return std::any_cast(value) == other;
}
-bool Expression::operator== (bool other) {
+bool Expression::operator== (const bool other) const {
return valueToBool(value) == other;
}
-bool Expression::operator!= (const Expression &other) {
+bool Expression::operator!= (const Expression &other) const {
if (value.type() == typeid(std::string) || other.value.type() == typeid(std::string)) {
if (value.type() != other.value.type()) {
- return true; // Different types, cannot be equal
+ return true; // Different types cannot be equal
} else {
return std::any_cast(value) != std::any_cast(other.value);
}
@@ -263,63 +298,63 @@ bool Expression::operator!= (const Expression &other) {
return std::any_cast(value) != std::any_cast(other.value);
}
-bool Expression::operator!= (double other) {
+bool Expression::operator!= (double other) const {
NumberValues values = doubleValues(value, other);
return values.value1 != values.value2;
}
-bool Expression::operator!= (int other) {
+bool Expression::operator!= (int other) const {
NumberValues values = doubleValues(value, other);
return values.value1 != values.value2;
}
-bool Expression::operator!= (std::string other) {
+bool Expression::operator!= (const std::string& other) const {
return std::any_cast(value) != other;
}
-bool Expression::operator!= (const char other[]) {
+bool Expression::operator!= (const char other[]) const {
return strcmp(std::any_cast(value).c_str(), other) == 0;
}
-bool Expression::operator> (const Expression &other) {
+bool Expression::operator> (const Expression &other) const {
NumberValues values = doubleValues(value, other.value);
return values.value1 > values.value2;
}
-bool Expression::operator> (int other) {
+bool Expression::operator> (int other) const {
NumberValues values = doubleValues(value, other);
return values.value1 > values.value2;
}
-bool Expression::operator> (double other) {
+bool Expression::operator> (double other) const {
NumberValues values = doubleValues(value, other);
return values.value1 > values.value2;
}
-bool Expression::operator>= (const Expression &other) {
+bool Expression::operator>= (const Expression &other) const {
NumberValues values = doubleValues(value, other.value);
return values.value1 >= values.value2;
}
-bool Expression::operator< (const Expression &other) {
+bool Expression::operator< (const Expression &other) const {
NumberValues values = doubleValues(value, other.value);
return values.value1 < values.value2;
}
-bool Expression::operator<= (const Expression &other) {
+bool Expression::operator<= (const Expression &other) const {
NumberValues values = doubleValues(value, other.value);
return values.value1 <= values.value2;
}
-bool Expression::operator! () {
+bool Expression::operator! () const {
return !(valueToBool(value));
}
-bool Expression::operator&& (const Expression &other) {
+bool Expression::operator&& (const Expression &other) const {
return valueToBool(value) && valueToBool(other.value);
}
-bool Expression::operator|| (const Expression &other) {
+bool Expression::operator|| (const Expression &other) const {
return valueToBool(value) || valueToBool(other.value);
}
}
diff --git a/Cpp/src/ArcscriptExpression.h b/Cpp/src/ArcscriptExpression.h
index 09d5c00..0bcf11e 100755
--- a/Cpp/src/ArcscriptExpression.h
+++ b/Cpp/src/ArcscriptExpression.h
@@ -1,7 +1,8 @@
+#pragma once
+
#include
#include
#include
-#include
namespace Arcweave {
@@ -46,35 +47,37 @@ class Expression {
return value.type();
}
- Expression operator+ (const Expression &other);
- Expression operator- (const Expression &other);
- Expression operator* (const Expression &other);
- Expression operator* (const int other);
- Expression operator/ (const Expression &other);
+ Expression operator+ (const Expression &other) const;
+ Expression operator- (const Expression &other) const;
+ Expression operator* (const Expression &other) const;
+ Expression operator* (const int other) const;
+ Expression operator/ (const Expression &other) const;
+ Expression operator% (const Expression &other) const;
Expression operator+= (const Expression &other);
Expression operator-= (const Expression &other);
Expression operator*= (const Expression &other);
Expression operator/= (const Expression &other);
+ Expression operator%= (const Expression &other);
- bool operator== (const Expression &other);
- bool operator== (double other);
- bool operator== (int other);
- bool operator== (std::string other);
- bool operator== (bool other);
- bool operator!= (const Expression &other);
- bool operator!= (double other);
- bool operator!= (int other);
- bool operator!= (std::string other);
- bool operator!= (const char other[]);
- bool operator> (const Expression &other);
- bool operator> (int other);
- bool operator> (double other);
- bool operator>= (const Expression &other);
- bool operator< (const Expression &other);
- bool operator<= (const Expression &other);
- bool operator! ();
- bool operator&& (const Expression &other);
- bool operator|| (const Expression &other);
+ bool operator== (const Expression &other) const;
+ bool operator== (double other) const;
+ bool operator== (int other) const;
+ bool operator== (const std::string& other) const;
+ bool operator== (bool other) const;
+ bool operator!= (const Expression &other) const;
+ bool operator!= (double other) const;
+ bool operator!= (int other) const;
+ bool operator!= (const std::string& other) const;
+ bool operator!= (const char other[]) const;
+ bool operator> (const Expression &other) const;
+ bool operator> (int other) const;
+ bool operator> (double other) const;
+ bool operator>= (const Expression &other) const;
+ bool operator< (const Expression &other) const;
+ bool operator<= (const Expression &other) const;
+ bool operator! () const;
+ bool operator&& (const Expression &other) const;
+ bool operator|| (const Expression &other) const;
};
}
diff --git a/Cpp/src/ArcscriptVisitor.cpp b/Cpp/src/ArcscriptVisitor.cpp
index f849517..da05015 100755
--- a/Cpp/src/ArcscriptVisitor.cpp
+++ b/Cpp/src/ArcscriptVisitor.cpp
@@ -154,6 +154,9 @@ std::any ArcscriptVisitor::visitStatement_assignment(ArcscriptParser::Statement_
if (ctx->ASSIGNDIV() != NULL) {
varValue /= compound_condition_or;
}
+ if (ctx->ASSIGNMOD() != NULL) {
+ varValue %= compound_condition_or;
+ }
state->setVarValue(variableName, varValue.value);
return std::any();
@@ -277,6 +280,8 @@ std::any ArcscriptVisitor::visitMultiplicative_numeric_expression(ArcscriptParse
}
else if (ctx->DIV() != NULL) {
signed_unary_num_expr = result / signed_unary_num_expr;
+ } else if (ctx->MOD() != NULL) {
+ signed_unary_num_expr = result % signed_unary_num_expr;
}
return signed_unary_num_expr;
}
diff --git a/Cpp/src/ArcscriptVisitor.h b/Cpp/src/ArcscriptVisitor.h
index 2b07166..41ec406 100755
--- a/Cpp/src/ArcscriptVisitor.h
+++ b/Cpp/src/ArcscriptVisitor.h
@@ -15,7 +15,7 @@ class ArcscriptVisitor : public ArcscriptParserBaseVisitor {
ArcscriptState *state;
ArcscriptFunctions *functions;
- ArcscriptVisitor(ArcscriptState* _state) : state(_state) {
+ explicit ArcscriptVisitor(ArcscriptState* _state) : state(_state) {
functions = new ArcscriptFunctions(state);
}
diff --git a/JavaScript/__tests__/runtimeErrors.json b/JavaScript/__tests__/runtimeErrors.json
index 2f4924a..9adb85e 100644
--- a/JavaScript/__tests__/runtimeErrors.json
+++ b/JavaScript/__tests__/runtimeErrors.json
@@ -67,6 +67,18 @@
{
"code": "w -= _a
",
"error": "runtime"
+ },
+ {
+ "code": "z = 10 % 0
",
+ "error": "runtime"
+ },
+ {
+ "code": "x %= 0
",
+ "error": "runtime"
+ },
+ {
+ "code": "y = 5 % \"test\"
",
+ "error": "runtime"
}
]
}
\ No newline at end of file
diff --git a/JavaScript/__tests__/valid.json b/JavaScript/__tests__/valid.json
index 86ea21a..2459060 100644
--- a/JavaScript/__tests__/valid.json
+++ b/JavaScript/__tests__/valid.json
@@ -99,6 +99,42 @@
"var1": 8
}
},
+ {
+ "code": "x = 10 % 3
",
+ "changes": {
+ "var1": 1
+ }
+ },
+ {
+ "code": "x = 10 % 3 * 2
",
+ "changes": {
+ "var1": 2
+ }
+ },
+ {
+ "code": "x = 10 % (3 * 2)
",
+ "changes": {
+ "var1": 4
+ }
+ },
+ {
+ "code": "x = 5 + 6 % 3
",
+ "changes": {
+ "var1": 5
+ }
+ },
+ {
+ "code": "x %= 6
",
+ "changes": {
+ "var1": 2
+ }
+ },
+ {
+ "code": "x = 10 % 7 % 2
",
+ "changes": {
+ "var1": 1
+ }
+ },
{
"code": "x = 3 / 4 * 5
",
"changes": {
diff --git a/JavaScript/src/ArcscriptVisitor.js b/JavaScript/src/ArcscriptVisitor.js
index 68742a1..53d7a2c 100644
--- a/JavaScript/src/ArcscriptVisitor.js
+++ b/JavaScript/src/ArcscriptVisitor.js
@@ -163,6 +163,7 @@ export default class ArcscriptVisitor extends ArcscriptParserVisitor {
ctx.ASSIGNADD() ||
ctx.ASSIGNSUB() ||
ctx.ASSIGNMUL() ||
+ ctx.ASSIGNMOD() ||
ctx.ASSIGNDIV()
) {
if (typeof variableValue === 'boolean') {
@@ -191,6 +192,13 @@ export default class ArcscriptVisitor extends ArcscriptParserVisitor {
if (!Number.isFinite(result)) {
throw new RuntimeError(`Invalid division by zero`);
}
+ } else if (ctx.ASSIGNMOD()) {
+ result = new BigNumber(variableValue)
+ .modulo(new BigNumber(compound_condition_or))
+ .toNumber();
+ if (!Number.isFinite(result)) {
+ throw new RuntimeError(`Invalid division by zero`);
+ }
} else if (ctx.ASSIGN()) {
result = compound_condition_or;
}
@@ -396,9 +404,18 @@ export default class ArcscriptVisitor extends ArcscriptParserVisitor {
.multipliedBy(new BigNumber(signed_unary_numeric_expression))
.toNumber();
}
- // else DIV
+ if (ctx.DIV()) {
+ const result = new BigNumber(multiplicative_numeric_expression)
+ .dividedBy(new BigNumber(signed_unary_numeric_expression))
+ .toNumber();
+ if (!Number.isFinite(result)) {
+ throw new RuntimeError(`Invalid division by zero`);
+ }
+ return result;
+ }
+ // else MOD
const result = new BigNumber(multiplicative_numeric_expression)
- .dividedBy(new BigNumber(signed_unary_numeric_expression))
+ .modulo(new BigNumber(signed_unary_numeric_expression))
.toNumber();
if (!Number.isFinite(result)) {
throw new RuntimeError(`Invalid division by zero`);
diff --git a/grammar/ArcscriptLexer.g4 b/grammar/ArcscriptLexer.g4
index 3b97474..b4d1ac8 100644
--- a/grammar/ArcscriptLexer.g4
+++ b/grammar/ArcscriptLexer.g4
@@ -37,11 +37,13 @@ ASSIGNMUL: '*=';
ASSIGNDIV: '/=';
ASSIGNADD: '+=';
ASSIGNSUB: '-=';
+ASSIGNMOD: '%=';
MUL: '*';
DIV: '/';
ADD: '+';
SUB: '-';
+MOD: '%';
GE: GT '=';
GT: '>' | '>';
diff --git a/grammar/ArcscriptParser.g4 b/grammar/ArcscriptParser.g4
index 957e352..6b8a569 100644
--- a/grammar/ArcscriptParser.g4
+++ b/grammar/ArcscriptParser.g4
@@ -63,6 +63,7 @@ statement_assignment:
| ASSIGNSUB
| ASSIGNMUL
| ASSIGNDIV
+ | ASSIGNMOD
| ASSIGN
) compound_condition_or {this.assertVariable($VARIABLE);};
@@ -84,7 +85,7 @@ additive_numeric_expression:
multiplicative_numeric_expression:
signed_unary_numeric_expression
- | multiplicative_numeric_expression (MUL | DIV) signed_unary_numeric_expression;
+ | multiplicative_numeric_expression (MUL | DIV | MOD) signed_unary_numeric_expression;
signed_unary_numeric_expression:
sign unary_numeric_expression