You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
166 lines
3.7 KiB
166 lines
3.7 KiB
/* |
|
* Copyright (C) 2023 Riyyi |
|
* |
|
* SPDX-License-Identifier: MIT |
|
*/ |
|
|
|
#include <cstdint> // int64_t |
|
#include <memory> // std::static_pointer_cast |
|
|
|
#include "ast.h" |
|
#include "env/macro.h" |
|
#include "util.h" |
|
|
|
namespace blaze { |
|
|
|
void Environment::loadOperators() |
|
{ |
|
#define APPLY_NUMBER_OR_DECIMAL(it, apply) \ |
|
IS_VALUE(Numeric, (*it)); \ |
|
if (is<Number>(it->get())) { \ |
|
auto it_numeric = std::static_pointer_cast<Number>(*it)->number(); \ |
|
do { \ |
|
apply \ |
|
} while (0); \ |
|
} \ |
|
else { \ |
|
return_decimal = true; \ |
|
auto it_numeric = std::static_pointer_cast<Decimal>(*it)->decimal(); \ |
|
do { \ |
|
apply \ |
|
} while (0); \ |
|
} |
|
|
|
#define RETURN_NUMBER_OR_DECIMAL() \ |
|
if (!return_decimal) { \ |
|
return makePtr<Number>(number); \ |
|
} \ |
|
return makePtr<Decimal>(decimal); |
|
|
|
ADD_FUNCTION( |
|
"+", |
|
"number...", |
|
"Return the sum of any amount of arguments, where NUMBER is of type number.", |
|
{ |
|
bool return_decimal = false; |
|
|
|
int64_t number = 0; |
|
double decimal = 0; |
|
|
|
for (auto it = begin; it != end; ++it) { |
|
APPLY_NUMBER_OR_DECIMAL(it, { |
|
number += it_numeric; |
|
decimal += it_numeric; |
|
}); |
|
} |
|
|
|
RETURN_NUMBER_OR_DECIMAL(); |
|
}); |
|
|
|
ADD_FUNCTION( |
|
"-", |
|
"[number] subtract...", |
|
R"(Negate NUMBER or SUBTRACT numbers and return the result. |
|
|
|
With one arg, negates it. With more than one arg, |
|
subtracts all but the first from the first.)", |
|
{ |
|
size_t length = SIZE(); |
|
if (length == 0) { |
|
return makePtr<Number>(0); |
|
} |
|
|
|
bool return_decimal = false; |
|
|
|
int64_t number = 0; |
|
double decimal = 0; |
|
|
|
// Start with the first number |
|
APPLY_NUMBER_OR_DECIMAL(begin, { |
|
number = it_numeric; |
|
decimal = it_numeric; |
|
}); |
|
|
|
// Return negative on single argument |
|
if (length == 1) { |
|
number = -number; |
|
decimal = -decimal; |
|
RETURN_NUMBER_OR_DECIMAL(); |
|
} |
|
|
|
// Skip the first node |
|
for (auto it = begin + 1; it != end; ++it) { |
|
APPLY_NUMBER_OR_DECIMAL(it, { |
|
number -= it_numeric; |
|
decimal -= it_numeric; |
|
}); |
|
} |
|
|
|
RETURN_NUMBER_OR_DECIMAL(); |
|
}); |
|
|
|
ADD_FUNCTION( |
|
"*", |
|
"", |
|
"", |
|
{ |
|
bool return_decimal = false; |
|
|
|
int64_t number = 1; |
|
double decimal = 1; |
|
|
|
for (auto it = begin; it != end; ++it) { |
|
APPLY_NUMBER_OR_DECIMAL(it, { |
|
number *= it_numeric; |
|
decimal *= it_numeric; |
|
}); |
|
} |
|
|
|
RETURN_NUMBER_OR_DECIMAL(); |
|
}); |
|
|
|
ADD_FUNCTION( |
|
"/", |
|
"", |
|
"", |
|
{ |
|
CHECK_ARG_COUNT_AT_LEAST("/", SIZE(), 1); |
|
|
|
bool return_decimal = false; |
|
|
|
int64_t number = 0; |
|
double decimal = 0; |
|
|
|
// Start with the first number |
|
APPLY_NUMBER_OR_DECIMAL(begin, { |
|
number = it_numeric; |
|
decimal = it_numeric; |
|
}); |
|
|
|
// Skip the first node |
|
for (auto it = begin + 1; it != end; ++it) { |
|
APPLY_NUMBER_OR_DECIMAL(it, { |
|
number /= it_numeric; |
|
decimal /= it_numeric; |
|
}); |
|
} |
|
|
|
RETURN_NUMBER_OR_DECIMAL(); |
|
}); |
|
|
|
// (% 5 2) -> 1 |
|
ADD_FUNCTION( |
|
"%", |
|
"", |
|
"", |
|
{ |
|
CHECK_ARG_COUNT_IS("/", SIZE(), 2); |
|
|
|
VALUE_CAST(divide, Number, (*begin)); |
|
VALUE_CAST(by, Number, (*(begin + 1))); |
|
|
|
return makePtr<Number>(divide->number() % by->number()); |
|
}); |
|
} |
|
|
|
} // namespace blaze
|
|
|