Browse Source

AST+Env+Printer+Reader: Implement floating point numbers

master
Riyyi 1 year ago
parent
commit
11f0553b5a
  1. 9
      src/ast.cpp
  2. 37
      src/ast.h
  3. 51
      src/env/functions/compare.cpp
  4. 27
      src/env/functions/convert.cpp
  5. 92
      src/env/functions/operators.cpp
  6. 4
      src/printer.cpp
  7. 33
      src/reader.cpp
  8. 2
      vendor/ruc

9
src/ast.cpp

@ -193,7 +193,14 @@ Keyword::Keyword(int64_t number)
// ----------------------------------------- // -----------------------------------------
Number::Number(int64_t number) Number::Number(int64_t number)
: m_number(number) : Numeric()
, m_number(number)
{
}
Decimal::Decimal(double decimal)
: Numeric()
, m_decimal(decimal)
{ {
} }

37
src/ast.h

@ -52,7 +52,9 @@ public:
virtual bool isHashMap() const { return false; } virtual bool isHashMap() const { return false; }
virtual bool isString() const { return false; } virtual bool isString() const { return false; }
virtual bool isKeyword() const { return false; } virtual bool isKeyword() const { return false; }
virtual bool isNumeric() const { return false; }
virtual bool isNumber() const { return false; } virtual bool isNumber() const { return false; }
virtual bool isDecimal() const { return false; }
virtual bool isConstant() const { return false; } virtual bool isConstant() const { return false; }
virtual bool isSymbol() const { return false; } virtual bool isSymbol() const { return false; }
virtual bool isCallable() 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 // 123
class Number final : public Value { class Number final : public Numeric {
public: public:
Number(int64_t number); Number(int64_t number);
virtual ~Number() = default; virtual ~Number() = default;
@ -268,6 +281,22 @@ private:
const int64_t m_number { 0 }; 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 // true, false, nil
@ -428,9 +457,15 @@ inline bool Value::fastIs<String>() const { return isString(); }
template<> template<>
inline bool Value::fastIs<Keyword>() const { return isKeyword(); } inline bool Value::fastIs<Keyword>() const { return isKeyword(); }
template<>
inline bool Value::fastIs<Numeric>() const { return isNumeric(); }
template<> template<>
inline bool Value::fastIs<Number>() const { return isNumber(); } inline bool Value::fastIs<Number>() const { return isNumber(); }
template<>
inline bool Value::fastIs<Decimal>() const { return isDecimal(); }
template<> template<>
inline bool Value::fastIs<Constant>() const { return isConstant(); } inline bool Value::fastIs<Constant>() const { return isConstant(); }

51
src/env/functions/compare.cpp vendored

@ -18,23 +18,46 @@ void Environment::loadCompare()
{ {
#define NUMBER_COMPARE(operator) \ #define NUMBER_COMPARE(operator) \
{ \ { \
CHECK_ARG_COUNT_AT_LEAST(#operator, SIZE(), 2); \
\
bool result = true; \ bool result = true; \
\ \
CHECK_ARG_COUNT_AT_LEAST(#operator, SIZE(), 2); \ int64_t number = 0; \
double decimal = 0; \
bool current_numeric_is_number = false; \
\ \
/* Start with the first number */ \ /* Start with the first number */ \
VALUE_CAST(number_node, Number, (*begin)); \ IS_VALUE(Numeric, (*begin)); \
int64_t number = number_node->number(); \ 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 */ \ /* Skip the first node */ \
for (auto it = begin + 1; it != end; ++it) { \ for (auto it = begin + 1; it != end; ++it) { \
VALUE_CAST(current_number_node, Number, (*it)); \ IS_VALUE(Numeric, (*it)); \
int64_t current_number = current_number_node->number(); \ if (is<Number>(*it->get())) { \
if (!(number operator current_number)) { \ int64_t it_number = std::static_pointer_cast<Number>(*it)->number(); \
if (!((current_numeric_is_number ? number : decimal) operator it_number)) { \
result = false; \ result = false; \
break; \ break; \
} \ } \
number = current_number; \ 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); \ return makePtr<Constant>((result) ? Constant::True : Constant::False); \
@ -58,8 +81,7 @@ void Environment::loadCompare()
std::function<bool(ValuePtr, ValuePtr)> equal = std::function<bool(ValuePtr, ValuePtr)> equal =
[&equal](ValuePtr lhs, ValuePtr rhs) -> bool { [&equal](ValuePtr lhs, ValuePtr rhs) -> bool {
if ((is<List>(lhs.get()) || is<Vector>(lhs.get())) if (is<Collection>(lhs.get()) && is<Collection>(rhs.get())) {
&& (is<List>(rhs.get()) || is<Vector>(rhs.get()))) {
auto lhs_collection = std::static_pointer_cast<Collection>(lhs); auto lhs_collection = std::static_pointer_cast<Collection>(lhs);
auto rhs_collection = std::static_pointer_cast<Collection>(rhs); 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()) { && std::static_pointer_cast<Keyword>(lhs)->keyword() == std::static_pointer_cast<Keyword>(rhs)->keyword()) {
return true; return true;
} }
if (is<Number>(lhs.get()) && is<Number>(rhs.get()) // clang-format off
&& std::static_pointer_cast<Number>(lhs)->number() == std::static_pointer_cast<Number>(rhs)->number()) { 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; return true;
} }
// clang-format on
if (is<Constant>(lhs.get()) && is<Constant>(rhs.get()) if (is<Constant>(lhs.get()) && is<Constant>(rhs.get())
&& std::static_pointer_cast<Constant>(lhs)->state() == std::static_pointer_cast<Constant>(rhs)->state()) { && std::static_pointer_cast<Constant>(lhs)->state() == std::static_pointer_cast<Constant>(rhs)->state()) {
return true; return true;

27
src/env/functions/convert.cpp vendored

@ -5,6 +5,7 @@
*/ */
#include <charconv> // std::from_chars, std::to_chars #include <charconv> // std::from_chars, std::to_chars
#include <memory>
#include <system_error> // std::errc #include <system_error> // std::errc
#include "ast.h" #include "ast.h"
@ -23,12 +24,14 @@ void Environment::loadConvert()
{ {
CHECK_ARG_COUNT_IS("number-to-string", SIZE(), 1); CHECK_ARG_COUNT_IS("number-to-string", SIZE(), 1);
VALUE_CAST(number, Number, (*begin)); IS_VALUE(Numeric, (*begin));
char result[32]; char result[32];
auto conversion = std::to_chars(result, auto conversion = std::to_chars(result,
result + sizeof(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()) { if (conversion.ec != std::errc()) {
return makePtr<Constant>(Constant::Nil); return makePtr<Constant>(Constant::Nil);
} }
@ -61,15 +64,23 @@ void Environment::loadConvert()
VALUE_CAST(string_value, String, (*begin)); VALUE_CAST(string_value, String, (*begin));
std::string data = string_value->data(); std::string data = string_value->data();
int64_t result; if (data.find('.') == std::string::npos) {
auto conversion = std::from_chars(data.c_str(), int64_t number;
data.c_str() + data.size(), auto conversion_number = std::from_chars(data.c_str(), data.c_str() + data.size(), number);
result); if (conversion_number.ec != std::errc()) {
if (conversion.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<Constant>(Constant::Nil);
} }
return makePtr<Number>(result); return makePtr<Decimal>(decimal);
}); });
#define STRING_TO_COLLECTION(name, type) \ #define STRING_TO_COLLECTION(name, type) \

92
src/env/functions/operators.cpp vendored

@ -5,6 +5,7 @@
*/ */
#include <cstdint> // int64_t #include <cstdint> // int64_t
#include <memory> // std::static_pointer_cast
#include "ast.h" #include "ast.h"
#include "env/macro.h" #include "env/macro.h"
@ -14,19 +15,46 @@ namespace blaze {
void Environment::loadOperators() 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( ADD_FUNCTION(
"+", "+",
"number...", "number...",
"Return the sum of any amount of arguments, where NUMBER is of type 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) { for (auto it = begin; it != end; ++it) {
VALUE_CAST(number, Number, (*it)); APPLY_NUMBER_OR_DECIMAL(it, {
result += number->number(); number += it_numeric;
decimal += it_numeric;
});
} }
return makePtr<Number>(result); RETURN_NUMBER_OR_DECIMAL();
}); });
ADD_FUNCTION( ADD_FUNCTION(
@ -42,21 +70,33 @@ subtracts all but the first from the first.)",
return makePtr<Number>(0); return makePtr<Number>(0);
} }
bool return_decimal = false;
int64_t number = 0;
double decimal = 0;
// Start with the first number // Start with the first number
VALUE_CAST(number, Number, (*begin)); APPLY_NUMBER_OR_DECIMAL(begin, {
int64_t result = number->number(); number = it_numeric;
decimal = it_numeric;
});
// Return negative on single argument
if (length == 1) { if (length == 1) {
return makePtr<Number>(-result); number = -number;
decimal = -decimal;
RETURN_NUMBER_OR_DECIMAL();
} }
// Skip the first node // Skip the first node
for (auto it = begin + 1; it != end; ++it) { for (auto it = begin + 1; it != end; ++it) {
VALUE_CAST(number, Number, (*it)); APPLY_NUMBER_OR_DECIMAL(it, {
result -= number->number(); number -= it_numeric;
decimal -= it_numeric;
});
} }
return makePtr<Number>(result); RETURN_NUMBER_OR_DECIMAL();
}); });
ADD_FUNCTION( 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) { for (auto it = begin; it != end; ++it) {
VALUE_CAST(number, Number, (*it)); APPLY_NUMBER_OR_DECIMAL(it, {
result *= number->number(); number *= it_numeric;
decimal *= it_numeric;
});
} }
return makePtr<Number>(result); RETURN_NUMBER_OR_DECIMAL();
}); });
ADD_FUNCTION( ADD_FUNCTION(
@ -81,17 +126,26 @@ subtracts all but the first from the first.)",
{ {
CHECK_ARG_COUNT_AT_LEAST("/", SIZE(), 1); CHECK_ARG_COUNT_AT_LEAST("/", SIZE(), 1);
bool return_decimal = false;
int64_t number = 0;
double decimal = 0;
// Start with the first number // Start with the first number
VALUE_CAST(number, Number, (*begin)); APPLY_NUMBER_OR_DECIMAL(begin, {
double result = number->number(); number = it_numeric;
decimal = it_numeric;
});
// Skip the first node // Skip the first node
for (auto it = begin + 1; it != end; ++it) { for (auto it = begin + 1; it != end; ++it) {
VALUE_CAST(number, Number, (*it)); APPLY_NUMBER_OR_DECIMAL(it, {
result /= number->number(); number /= it_numeric;
decimal /= it_numeric;
});
} }
return makePtr<Number>((int64_t)result); RETURN_NUMBER_OR_DECIMAL();
}); });
// (% 5 2) -> 1 // (% 5 2) -> 1

4
src/printer.cpp

@ -128,6 +128,10 @@ void Printer::printImpl(ValuePtr value, bool print_readably)
printSpacing(); printSpacing();
m_print += ::format("{}", std::static_pointer_cast<Number>(value)->number()); 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)) { else if (is<Constant>(value_raw_ptr)) {
printSpacing(); printSpacing();
std::string constant; std::string constant;

33
src/reader.cpp

@ -4,10 +4,12 @@
* SPDX-License-Identifier: MIT * SPDX-License-Identifier: MIT
*/ */
#include <charconv> // std::from_chars
#include <cstddef> // size_t #include <cstddef> // size_t
#include <cstdint> // uint64_t #include <cstdint> // uint64_t
#include <cstdlib> // std::strtoll #include <cstdlib> // std::strtoll
#include <memory> // std::static_pointer_cast #include <memory> // std::static_pointer_cast
#include <system_error> // std::errc
#include <utility> // std::move #include <utility> // std::move
#include "error.h" #include "error.h"
@ -306,24 +308,33 @@ ValuePtr Reader::readKeyword()
ValuePtr Reader::readValue() ValuePtr Reader::readValue()
{ {
Token token = consume(); auto symbol = consume().symbol;
char* end_ptr = nullptr;
int64_t result = std::strtoll(token.symbol.c_str(), &end_ptr, 10); int64_t number;
if (end_ptr == token.symbol.c_str() + token.symbol.size()) { auto [_, error] = std::from_chars(symbol.data(), symbol.data() + symbol.size(), number);
return makePtr<Number>(result); 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); return makePtr<Constant>(Constant::Nil);
} }
else if (token.symbol == "true") { else if (symbol == "true") {
return makePtr<Constant>(Constant::True); return makePtr<Constant>(Constant::True);
} }
else if (token.symbol == "false") { else if (symbol == "false") {
return makePtr<Constant>(Constant::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"); pretty_print ? print(yellow, "NumberNode") : print("NumberNode");
print(" <{}>", node); 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)) { else if (is<Constant>(node_raw_ptr)) {
pretty_print ? print(yellow, "ValueNode") : print("ValueNode"); pretty_print ? print(yellow, "ValueNode") : print("ValueNode");
print(" <{}>", node); print(" <{}>", node);

2
vendor/ruc vendored

@ -1 +1 @@
Subproject commit 07c9f9959d3ce46da8bc7b0777d803524f9d1ec0 Subproject commit c8e4ae884eacc963dc0fe6a5254679aad050cb74
Loading…
Cancel
Save