AST+Env+Printer+Reader: Implement floating point numbers
This commit is contained in:
+8
-1
@@ -193,7 +193,14 @@ Keyword::Keyword(int64_t number)
|
||||
// -----------------------------------------
|
||||
|
||||
Number::Number(int64_t number)
|
||||
: m_number(number)
|
||||
: Numeric()
|
||||
, m_number(number)
|
||||
{
|
||||
}
|
||||
|
||||
Decimal::Decimal(double decimal)
|
||||
: Numeric()
|
||||
, m_decimal(decimal)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,9 @@ public:
|
||||
virtual bool isHashMap() const { return false; }
|
||||
virtual bool isString() const { return false; }
|
||||
virtual bool isKeyword() const { return false; }
|
||||
virtual bool isNumeric() const { return false; }
|
||||
virtual bool isNumber() const { return false; }
|
||||
virtual bool isDecimal() const { return false; }
|
||||
virtual bool isConstant() const { return false; }
|
||||
virtual bool isSymbol() const { return false; }
|
||||
virtual bool isCallable() const { return false; }
|
||||
@@ -252,8 +254,19 @@ private:
|
||||
};
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
class Numeric : public Value {
|
||||
public:
|
||||
virtual ~Numeric() = default;
|
||||
|
||||
protected:
|
||||
Numeric() = default;
|
||||
|
||||
virtual bool isNumeric() const override { return true; }
|
||||
};
|
||||
|
||||
// 123
|
||||
class Number final : public Value {
|
||||
class Number final : public Numeric {
|
||||
public:
|
||||
Number(int64_t number);
|
||||
virtual ~Number() = default;
|
||||
@@ -268,6 +281,22 @@ private:
|
||||
const int64_t m_number { 0 };
|
||||
};
|
||||
|
||||
// 123.456
|
||||
class Decimal final : public Numeric {
|
||||
public:
|
||||
Decimal(double decimal);
|
||||
virtual ~Decimal() = default;
|
||||
|
||||
double decimal() const { return m_decimal; }
|
||||
|
||||
WITH_NO_META();
|
||||
|
||||
private:
|
||||
virtual bool isDecimal() const override { return true; }
|
||||
|
||||
const double m_decimal { 0 };
|
||||
};
|
||||
|
||||
// -----------------------------------------
|
||||
|
||||
// true, false, nil
|
||||
@@ -428,9 +457,15 @@ inline bool Value::fastIs<String>() const { return isString(); }
|
||||
template<>
|
||||
inline bool Value::fastIs<Keyword>() const { return isKeyword(); }
|
||||
|
||||
template<>
|
||||
inline bool Value::fastIs<Numeric>() const { return isNumeric(); }
|
||||
|
||||
template<>
|
||||
inline bool Value::fastIs<Number>() const { return isNumber(); }
|
||||
|
||||
template<>
|
||||
inline bool Value::fastIs<Decimal>() const { return isDecimal(); }
|
||||
|
||||
template<>
|
||||
inline bool Value::fastIs<Constant>() const { return isConstant(); }
|
||||
|
||||
|
||||
Vendored
+55
-26
@@ -16,28 +16,51 @@ namespace blaze {
|
||||
|
||||
void Environment::loadCompare()
|
||||
{
|
||||
#define NUMBER_COMPARE(operator) \
|
||||
{ \
|
||||
bool result = true; \
|
||||
\
|
||||
CHECK_ARG_COUNT_AT_LEAST(#operator, SIZE(), 2); \
|
||||
\
|
||||
/* Start with the first number */ \
|
||||
VALUE_CAST(number_node, Number, (*begin)); \
|
||||
int64_t number = number_node->number(); \
|
||||
\
|
||||
/* Skip the first node */ \
|
||||
for (auto it = begin + 1; it != end; ++it) { \
|
||||
VALUE_CAST(current_number_node, Number, (*it)); \
|
||||
int64_t current_number = current_number_node->number(); \
|
||||
if (!(number operator current_number)) { \
|
||||
result = false; \
|
||||
break; \
|
||||
} \
|
||||
number = current_number; \
|
||||
} \
|
||||
\
|
||||
return makePtr<Constant>((result) ? Constant::True : Constant::False); \
|
||||
#define NUMBER_COMPARE(operator) \
|
||||
{ \
|
||||
CHECK_ARG_COUNT_AT_LEAST(#operator, SIZE(), 2); \
|
||||
\
|
||||
bool result = true; \
|
||||
\
|
||||
int64_t number = 0; \
|
||||
double decimal = 0; \
|
||||
bool current_numeric_is_number = false; \
|
||||
\
|
||||
/* Start with the first number */ \
|
||||
IS_VALUE(Numeric, (*begin)); \
|
||||
if (is<Number>(begin->get())) { \
|
||||
number = std::static_pointer_cast<Number>(*begin)->number(); \
|
||||
current_numeric_is_number = true; \
|
||||
} \
|
||||
else { \
|
||||
decimal = std::static_pointer_cast<Decimal>(*begin)->decimal(); \
|
||||
current_numeric_is_number = false; \
|
||||
} \
|
||||
\
|
||||
/* Skip the first node */ \
|
||||
for (auto it = begin + 1; it != end; ++it) { \
|
||||
IS_VALUE(Numeric, (*it)); \
|
||||
if (is<Number>(*it->get())) { \
|
||||
int64_t it_number = std::static_pointer_cast<Number>(*it)->number(); \
|
||||
if (!((current_numeric_is_number ? number : decimal) operator it_number)) { \
|
||||
result = false; \
|
||||
break; \
|
||||
} \
|
||||
number = it_number; \
|
||||
current_numeric_is_number = true; \
|
||||
} \
|
||||
else { \
|
||||
double it_decimal = std::static_pointer_cast<Decimal>(*it)->decimal(); \
|
||||
if (!((current_numeric_is_number ? number : decimal) operator it_decimal)) { \
|
||||
result = false; \
|
||||
break; \
|
||||
} \
|
||||
decimal = it_decimal; \
|
||||
current_numeric_is_number = false; \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
return makePtr<Constant>((result) ? Constant::True : Constant::False); \
|
||||
}
|
||||
|
||||
ADD_FUNCTION("<", "", "", NUMBER_COMPARE(<));
|
||||
@@ -58,8 +81,7 @@ void Environment::loadCompare()
|
||||
|
||||
std::function<bool(ValuePtr, ValuePtr)> equal =
|
||||
[&equal](ValuePtr lhs, ValuePtr rhs) -> bool {
|
||||
if ((is<List>(lhs.get()) || is<Vector>(lhs.get()))
|
||||
&& (is<List>(rhs.get()) || is<Vector>(rhs.get()))) {
|
||||
if (is<Collection>(lhs.get()) && is<Collection>(rhs.get())) {
|
||||
auto lhs_collection = std::static_pointer_cast<Collection>(lhs);
|
||||
auto rhs_collection = std::static_pointer_cast<Collection>(rhs);
|
||||
|
||||
@@ -104,10 +126,17 @@ void Environment::loadCompare()
|
||||
&& std::static_pointer_cast<Keyword>(lhs)->keyword() == std::static_pointer_cast<Keyword>(rhs)->keyword()) {
|
||||
return true;
|
||||
}
|
||||
if (is<Number>(lhs.get()) && is<Number>(rhs.get())
|
||||
&& std::static_pointer_cast<Number>(lhs)->number() == std::static_pointer_cast<Number>(rhs)->number()) {
|
||||
// clang-format off
|
||||
if (is<Numeric>(lhs.get()) && is<Numeric>(rhs.get())
|
||||
&& (is<Number>(lhs.get())
|
||||
? std::static_pointer_cast<Number>(lhs)->number()
|
||||
: std::static_pointer_cast<Decimal>(lhs)->decimal())
|
||||
== (is<Number>(rhs.get())
|
||||
? std::static_pointer_cast<Number>(rhs)->number()
|
||||
: std::static_pointer_cast<Decimal>(rhs)->decimal())) {
|
||||
return true;
|
||||
}
|
||||
// clang-format on
|
||||
if (is<Constant>(lhs.get()) && is<Constant>(rhs.get())
|
||||
&& std::static_pointer_cast<Constant>(lhs)->state() == std::static_pointer_cast<Constant>(rhs)->state()) {
|
||||
return true;
|
||||
|
||||
Vendored
+20
-9
@@ -4,7 +4,8 @@
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <charconv> // std::from_chars, std::to_chars
|
||||
#include <charconv> // std::from_chars, std::to_chars
|
||||
#include <memory>
|
||||
#include <system_error> // std::errc
|
||||
|
||||
#include "ast.h"
|
||||
@@ -23,12 +24,14 @@ void Environment::loadConvert()
|
||||
{
|
||||
CHECK_ARG_COUNT_IS("number-to-string", SIZE(), 1);
|
||||
|
||||
VALUE_CAST(number, Number, (*begin));
|
||||
IS_VALUE(Numeric, (*begin));
|
||||
|
||||
char result[32];
|
||||
auto conversion = std::to_chars(result,
|
||||
result + sizeof(result),
|
||||
number->number());
|
||||
is<Number>(begin->get())
|
||||
? std::static_pointer_cast<Number>(*begin)->number()
|
||||
: std::static_pointer_cast<Decimal>(*begin)->decimal());
|
||||
if (conversion.ec != std::errc()) {
|
||||
return makePtr<Constant>(Constant::Nil);
|
||||
}
|
||||
@@ -61,15 +64,23 @@ void Environment::loadConvert()
|
||||
VALUE_CAST(string_value, String, (*begin));
|
||||
std::string data = string_value->data();
|
||||
|
||||
int64_t result;
|
||||
auto conversion = std::from_chars(data.c_str(),
|
||||
data.c_str() + data.size(),
|
||||
result);
|
||||
if (conversion.ec != std::errc()) {
|
||||
if (data.find('.') == std::string::npos) {
|
||||
int64_t number;
|
||||
auto conversion_number = std::from_chars(data.c_str(), data.c_str() + data.size(), number);
|
||||
if (conversion_number.ec != std::errc()) {
|
||||
return makePtr<Constant>(Constant::Nil);
|
||||
}
|
||||
|
||||
return makePtr<Number>(number);
|
||||
}
|
||||
|
||||
double decimal;
|
||||
auto conversion_decimal = std::from_chars(data.c_str(), data.c_str() + data.size(), decimal);
|
||||
if (conversion_decimal.ec != std::errc()) {
|
||||
return makePtr<Constant>(Constant::Nil);
|
||||
}
|
||||
|
||||
return makePtr<Number>(result);
|
||||
return makePtr<Decimal>(decimal);
|
||||
});
|
||||
|
||||
#define STRING_TO_COLLECTION(name, type) \
|
||||
|
||||
Vendored
+74
-20
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
#include <cstdint> // int64_t
|
||||
#include <memory> // std::static_pointer_cast
|
||||
|
||||
#include "ast.h"
|
||||
#include "env/macro.h"
|
||||
@@ -14,19 +15,46 @@ 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.",
|
||||
{
|
||||
int64_t result = 0;
|
||||
bool return_decimal = false;
|
||||
|
||||
int64_t number = 0;
|
||||
double decimal = 0;
|
||||
|
||||
for (auto it = begin; it != end; ++it) {
|
||||
VALUE_CAST(number, Number, (*it));
|
||||
result += number->number();
|
||||
APPLY_NUMBER_OR_DECIMAL(it, {
|
||||
number += it_numeric;
|
||||
decimal += it_numeric;
|
||||
});
|
||||
}
|
||||
|
||||
return makePtr<Number>(result);
|
||||
RETURN_NUMBER_OR_DECIMAL();
|
||||
});
|
||||
|
||||
ADD_FUNCTION(
|
||||
@@ -42,21 +70,33 @@ subtracts all but the first from the first.)",
|
||||
return makePtr<Number>(0);
|
||||
}
|
||||
|
||||
// Start with the first number
|
||||
VALUE_CAST(number, Number, (*begin));
|
||||
int64_t result = number->number();
|
||||
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) {
|
||||
return makePtr<Number>(-result);
|
||||
number = -number;
|
||||
decimal = -decimal;
|
||||
RETURN_NUMBER_OR_DECIMAL();
|
||||
}
|
||||
|
||||
// Skip the first node
|
||||
for (auto it = begin + 1; it != end; ++it) {
|
||||
VALUE_CAST(number, Number, (*it));
|
||||
result -= number->number();
|
||||
APPLY_NUMBER_OR_DECIMAL(it, {
|
||||
number -= it_numeric;
|
||||
decimal -= it_numeric;
|
||||
});
|
||||
}
|
||||
|
||||
return makePtr<Number>(result);
|
||||
RETURN_NUMBER_OR_DECIMAL();
|
||||
});
|
||||
|
||||
ADD_FUNCTION(
|
||||
@@ -64,14 +104,19 @@ subtracts all but the first from the first.)",
|
||||
"",
|
||||
"",
|
||||
{
|
||||
int64_t result = 1;
|
||||
bool return_decimal = false;
|
||||
|
||||
int64_t number = 1;
|
||||
double decimal = 1;
|
||||
|
||||
for (auto it = begin; it != end; ++it) {
|
||||
VALUE_CAST(number, Number, (*it));
|
||||
result *= number->number();
|
||||
APPLY_NUMBER_OR_DECIMAL(it, {
|
||||
number *= it_numeric;
|
||||
decimal *= it_numeric;
|
||||
});
|
||||
}
|
||||
|
||||
return makePtr<Number>(result);
|
||||
RETURN_NUMBER_OR_DECIMAL();
|
||||
});
|
||||
|
||||
ADD_FUNCTION(
|
||||
@@ -81,17 +126,26 @@ subtracts all but the first from the first.)",
|
||||
{
|
||||
CHECK_ARG_COUNT_AT_LEAST("/", SIZE(), 1);
|
||||
|
||||
bool return_decimal = false;
|
||||
|
||||
int64_t number = 0;
|
||||
double decimal = 0;
|
||||
|
||||
// Start with the first number
|
||||
VALUE_CAST(number, Number, (*begin));
|
||||
double result = number->number();
|
||||
APPLY_NUMBER_OR_DECIMAL(begin, {
|
||||
number = it_numeric;
|
||||
decimal = it_numeric;
|
||||
});
|
||||
|
||||
// Skip the first node
|
||||
for (auto it = begin + 1; it != end; ++it) {
|
||||
VALUE_CAST(number, Number, (*it));
|
||||
result /= number->number();
|
||||
APPLY_NUMBER_OR_DECIMAL(it, {
|
||||
number /= it_numeric;
|
||||
decimal /= it_numeric;
|
||||
});
|
||||
}
|
||||
|
||||
return makePtr<Number>((int64_t)result);
|
||||
RETURN_NUMBER_OR_DECIMAL();
|
||||
});
|
||||
|
||||
// (% 5 2) -> 1
|
||||
|
||||
@@ -128,6 +128,10 @@ void Printer::printImpl(ValuePtr value, bool print_readably)
|
||||
printSpacing();
|
||||
m_print += ::format("{}", std::static_pointer_cast<Number>(value)->number());
|
||||
}
|
||||
else if (is<Decimal>(value_raw_ptr)) {
|
||||
printSpacing();
|
||||
m_print += ::format("{:.15}", std::static_pointer_cast<Decimal>(value)->decimal());
|
||||
}
|
||||
else if (is<Constant>(value_raw_ptr)) {
|
||||
printSpacing();
|
||||
std::string constant;
|
||||
|
||||
+29
-14
@@ -4,11 +4,13 @@
|
||||
* SPDX-License-Identifier: MIT
|
||||
*/
|
||||
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // uint64_t
|
||||
#include <cstdlib> // std::strtoll
|
||||
#include <memory> // std::static_pointer_cast
|
||||
#include <utility> // std::move
|
||||
#include <charconv> // std::from_chars
|
||||
#include <cstddef> // size_t
|
||||
#include <cstdint> // uint64_t
|
||||
#include <cstdlib> // std::strtoll
|
||||
#include <memory> // std::static_pointer_cast
|
||||
#include <system_error> // std::errc
|
||||
#include <utility> // std::move
|
||||
|
||||
#include "error.h"
|
||||
#include "ruc/format/color.h"
|
||||
@@ -306,24 +308,33 @@ ValuePtr Reader::readKeyword()
|
||||
|
||||
ValuePtr Reader::readValue()
|
||||
{
|
||||
Token token = consume();
|
||||
char* end_ptr = nullptr;
|
||||
int64_t result = std::strtoll(token.symbol.c_str(), &end_ptr, 10);
|
||||
if (end_ptr == token.symbol.c_str() + token.symbol.size()) {
|
||||
return makePtr<Number>(result);
|
||||
auto symbol = consume().symbol;
|
||||
|
||||
int64_t number;
|
||||
auto [_, error] = std::from_chars(symbol.data(), symbol.data() + symbol.size(), number);
|
||||
if (error == std::errc() && symbol.find('.') == std::string::npos) {
|
||||
return makePtr<Number>(number);
|
||||
}
|
||||
|
||||
if (token.symbol == "nil") {
|
||||
double decimal;
|
||||
{
|
||||
auto [_, error] = std::from_chars(symbol.data(), symbol.data() + symbol.size(), decimal);
|
||||
if (error == std::errc()) {
|
||||
return makePtr<Decimal>(decimal);
|
||||
}
|
||||
}
|
||||
|
||||
if (symbol == "nil") {
|
||||
return makePtr<Constant>(Constant::Nil);
|
||||
}
|
||||
else if (token.symbol == "true") {
|
||||
else if (symbol == "true") {
|
||||
return makePtr<Constant>(Constant::True);
|
||||
}
|
||||
else if (token.symbol == "false") {
|
||||
else if (symbol == "false") {
|
||||
return makePtr<Constant>(Constant::False);
|
||||
}
|
||||
|
||||
return makePtr<Symbol>(token.symbol);
|
||||
return makePtr<Symbol>(symbol);
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
@@ -431,6 +442,10 @@ void Reader::dumpImpl(ValuePtr node)
|
||||
pretty_print ? print(yellow, "NumberNode") : print("NumberNode");
|
||||
print(" <{}>", node);
|
||||
}
|
||||
else if (is<Decimal>(node_raw_ptr)) {
|
||||
pretty_print ? print(yellow, "DecimalNode") : print("DecimalNode");
|
||||
print(" <{}>", node);
|
||||
}
|
||||
else if (is<Constant>(node_raw_ptr)) {
|
||||
pretty_print ? print(yellow, "ValueNode") : print("ValueNode");
|
||||
print(" <{}>", node);
|
||||
|
||||
Vendored
+1
-1
Submodule vendor/ruc updated: 07c9f9959d...c8e4ae884e
Reference in New Issue
Block a user