Skip to content

Commit

Permalink
(compiler) Initial support for ** operator in compiled mode (#420)
Browse files Browse the repository at this point in the history
Note that `BigInteger`-based values are not yet supported. This also
means that simple things like `int ** int` won't work, because the type
of such expressions is `BigInteger`.
  • Loading branch information
perlun authored Jan 13, 2024
1 parent 46e3112 commit a0e3aa0
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 24 deletions.
2 changes: 2 additions & 0 deletions release-notes/v0.4.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- Add first steps towards experimental compiler [[#409][409]]
- Preserve exact floating point representation [[#412][412]]
- Add `BigInt` support for compiled mode [[#418][418]]
- Initial support for `**` operator in compiled mode [[#420][420]]
- Use correct error message for unsupported operand types [[#421][421]]

### Changed
Expand Down Expand Up @@ -48,4 +49,5 @@
[413]: https://github.com/perlang-org/perlang/pull/413
[414]: https://github.com/perlang-org/perlang/pull/414
[418]: https://github.com/perlang-org/perlang/pull/418
[420]: https://github.com/perlang-org/perlang/pull/420
[421]: https://github.com/perlang-org/perlang/pull/421
44 changes: 39 additions & 5 deletions src/Perlang.Interpreter/Compiler/PerlangCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -828,7 +828,41 @@ private void AddGlobalClass(string name, PerlangClass perlangClass)
break;

case STAR_STAR:
throw new NotImplementedInCompiledModeException("** operator is not yet supported in compiled mode");
if (new[] { typeof(int), typeof(long), typeof(uint), typeof(ulong), typeof(BigInteger) }.Contains(expr.Left.TypeReference.ClrType) &&
expr.Right.TypeReference.ClrType == typeof(int))
{
// TODO: We need something lke BigInteger.Pow() in .NET to be able to accomplish this. Perhaps do
// TODO: like the .NET folks and implement something using this approach?
// TODO: https://en.wikipedia.org/wiki/Exponentiation_by_squaring
throw new NotImplementedInCompiledModeException("** operator for BigInteger is not yet supported in compiled mode");
}
else if (new[] { typeof(int), typeof(long), typeof(uint), typeof(ulong), typeof(float), typeof(double) }.Contains(expr.Left.TypeReference.ClrType) &&
new[] { typeof(float), typeof(double) }.Contains(expr.Right.TypeReference.ClrType))
{
// Normal math.h-based pow(), returning a double
currentMethod.Append($"{leftCast}pow({expr.Left.Accept(this)}, {rightCast}{expr.Right.Accept(this)})");
}
else if (new[] { typeof(float), typeof(double) }.Contains(expr.Left.TypeReference.ClrType) &&
new[] { typeof(int), typeof(long), typeof(uint), typeof(ulong), typeof(float), typeof(double) }.Contains(expr.Right.TypeReference.ClrType))
{
// Normal math.h-based pow(), returning a double
currentMethod.Append($"{leftCast}pow({expr.Left.Accept(this)}, {rightCast}{expr.Right.Accept(this)})");
}
else if (expr.Left.TypeReference.ClrType == typeof(BigInteger) ||
expr.Right.TypeReference.ClrType == typeof(BigInteger))
{
// TODO: We need something lke BigInteger.Pow() in .NET to be able to accomplish this. Perhaps do
// TODO: like the .NET folks and implement something using this approach?
// TODO: https://en.wikipedia.org/wiki/Exponentiation_by_squaring
throw new NotImplementedInCompiledModeException("** operator for BigInteger is not yet supported in compiled mode");
}
else
{
string message = CompilerMessages.UnsupportedOperandsInBinaryExpression(expr.Operator.Type, expr.Left.TypeReference, expr.Right.TypeReference);
throw new RuntimeError(expr.Operator, message);
}

break;

case PERCENT:
if (expr.Left.TypeReference.IsValidNumberType && expr.Right.TypeReference.IsValidNumberType)
Expand All @@ -838,14 +872,14 @@ private void AddGlobalClass(string name, PerlangClass perlangClass)
{
throw new NotImplementedInCompiledModeException("% operator for BigInteger is not yet supported in compiled mode");
}
else if (typeof(Double) == expr.Left.TypeReference.ClrType ||
typeof(Double) == expr.Right.TypeReference.ClrType)
else if (expr.Left.TypeReference.ClrType == typeof(double) ||
expr.Right.TypeReference.ClrType == typeof(double))
{
// C and C++ does not support the % operator for double; we must use `fmod()` instead
currentMethod.Append($"fmod({expr.Left.Accept(this)}, {expr.Right.Accept(this)})");
}
else if (typeof(Single) == expr.Left.TypeReference.ClrType ||
typeof(Single) == expr.Right.TypeReference.ClrType)
else if (expr.Left.TypeReference.ClrType == typeof(float) ||
expr.Right.TypeReference.ClrType == typeof(float))
{
// Likewise, but with `float` instead of `double` as arguments and return type
currentMethod.Append($"fmodf({expr.Left.Accept(this)}, {expr.Right.Accept(this)})");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Perlang.Tests.Integration.Operator.Binary
{
public class AdditionTests
{
[SkippableTheory]
[Theory]
[MemberData(nameof(BinaryOperatorData.Addition_result), MemberType = typeof(BinaryOperatorData))]
void performs_addition(string i, string j, string expectedResult)
{
Expand Down
24 changes: 12 additions & 12 deletions src/Perlang.Tests.Integration/Operator/Binary/Comparison.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public class Comparison
// Tests for the < (less than) operator
//

[SkippableTheory]
[Theory]
[MemberData(nameof(ComparisonTypes))]
public void less_than_greater_is_true(string left, string right)
{
Expand All @@ -54,7 +54,7 @@ public void less_than_greater_is_true(string left, string right)
.Be("true");
}

[SkippableTheory]
[Theory]
[MemberData(nameof(ComparisonTypes))]
public void less_than_same_is_false(string left, string right)
{
Expand All @@ -73,7 +73,7 @@ public void less_than_same_is_false(string left, string right)
.Be("false");
}

[SkippableTheory]
[Theory]
[MemberData(nameof(ComparisonTypes))]
public void less_than_smaller_is_false(string left, string right)
{
Expand All @@ -96,7 +96,7 @@ public void less_than_smaller_is_false(string left, string right)
// Tests for the <= (less than or equals) operator
//

[SkippableTheory]
[Theory]
[MemberData(nameof(ComparisonTypes))]
public void less_than_or_equals_greater_is_true(string left, string right)
{
Expand All @@ -115,7 +115,7 @@ public void less_than_or_equals_greater_is_true(string left, string right)
.Be("true");
}

[SkippableTheory]
[Theory]
[MemberData(nameof(ComparisonTypes))]
public void less_than_or_equals_same_is_true(string left, string right)
{
Expand All @@ -134,7 +134,7 @@ public void less_than_or_equals_same_is_true(string left, string right)
.Be("true");
}

[SkippableTheory]
[Theory]
[MemberData(nameof(ComparisonTypes))]
public void less_than_or_equals_smaller_is_false(string left, string right)
{
Expand All @@ -157,7 +157,7 @@ public void less_than_or_equals_smaller_is_false(string left, string right)
// Tests for the > (greater than) operator
//

[SkippableTheory]
[Theory]
[MemberData(nameof(ComparisonTypes))]
public void greater_than_smaller_is_false(string left, string right)
{
Expand All @@ -176,7 +176,7 @@ public void greater_than_smaller_is_false(string left, string right)
.Be("false");
}

[SkippableTheory]
[Theory]
[MemberData(nameof(ComparisonTypes))]
public void greater_than_same_is_false(string left, string right)
{
Expand All @@ -195,7 +195,7 @@ public void greater_than_same_is_false(string left, string right)
.Be("false");
}

[SkippableTheory]
[Theory]
[MemberData(nameof(ComparisonTypes))]
public void greater_than_smaller_is_true(string left, string right)
{
Expand All @@ -217,7 +217,7 @@ public void greater_than_smaller_is_true(string left, string right)
//
// Tests for the >= (greater than or equals) operator
//
[SkippableTheory]
[Theory]
[MemberData(nameof(ComparisonTypes))]
public void greater_than_or_equals_larger_is_false(string left, string right)
{
Expand All @@ -236,7 +236,7 @@ public void greater_than_or_equals_larger_is_false(string left, string right)
.Be("false");
}

[SkippableTheory]
[Theory]
[MemberData(nameof(ComparisonTypes))]
public void greater_than_or_equals_same_is_true(string left, string right)
{
Expand All @@ -255,7 +255,7 @@ public void greater_than_or_equals_same_is_true(string left, string right)
.Be("true");
}

[SkippableTheory]
[Theory]
[MemberData(nameof(ComparisonTypes))]
public void greater_than_or_equals_smaller_is_true(string left, string right)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ public void performs_exponential_calculation(string i, string j, string expected
[MemberData(nameof(BinaryOperatorData.Exponential_type), MemberType = typeof(BinaryOperatorData))]
public void with_supported_types_returns_expected_type(string i, string j, string expectedResult)
{
Skip.If(PerlangMode.ExperimentalCompilation, "Not supported in compiled mode");

string source = $@"
print ({i} ** {j}).get_type();
";
Expand Down Expand Up @@ -278,7 +280,7 @@ public void exponential_function_return_values()
Assert.Equal("256", result);
}

[SkippableFact]
[Fact]
public void exponential_bigint_and_negative_int_throws_expected_runtime_error()
{
string source = @"
Expand Down
10 changes: 10 additions & 0 deletions src/stdlib/src/bigint.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,16 @@ BigInt::BigInt(const unsigned long long& num) {
sign = '+';
}

BigInt::BigInt(const double& num) {
if (ceil(num) != num) {
// num has a fractional part and can inherently never be equal to a BigInt.
throw std::invalid_argument("Expected a value without any fractional part, got \'" + std::to_string(num) + "\'");
}

// Without this cast, values like "4096" will be converted to "4096.000000"
value = std::to_string((long)num);
sign = '+';
}

/*
String to BigInt
Expand Down
1 change: 1 addition & 0 deletions src/stdlib/src/bigint.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class BigInt {
BigInt(const unsigned long&);
BigInt(const long long&);
BigInt(const unsigned long long& num);
BigInt(const double& num);
BigInt(const std::string&);

// Assignment operators:
Expand Down
5 changes: 0 additions & 5 deletions src/stdlib/test/print.cc
Original file line number Diff line number Diff line change
Expand Up @@ -91,11 +91,6 @@ TEST(PrintDouble_9223372036854775807)
fwrite_mocked = false;

CHECK_EQ("9.22337203685478E+18\n", captured_output);

// TODO: temp code
double i1 = -12.0;
BigInt i2 = BigInt("18446744073709551616");
perlang::print(i1 != i2);
}

// TODO: Make this test work. We need to (linker-)wrap puts to make it happen.
Expand Down

0 comments on commit a0e3aa0

Please sign in to comment.