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)
: 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 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(); }

51
src/env/functions/compare.cpp vendored

@ -18,23 +18,46 @@ void Environment::loadCompare()
{
#define NUMBER_COMPARE(operator) \
{ \
CHECK_ARG_COUNT_AT_LEAST(#operator, SIZE(), 2); \
\
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 */ \
VALUE_CAST(number_node, Number, (*begin)); \
int64_t number = number_node->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) { \
VALUE_CAST(current_number_node, Number, (*it)); \
int64_t current_number = current_number_node->number(); \
if (!(number operator current_number)) { \
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 = 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); \
@ -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;

27
src/env/functions/convert.cpp vendored

@ -5,6 +5,7 @@
*/
#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) \

92
src/env/functions/operators.cpp vendored

@ -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);
}
bool return_decimal = false;
int64_t number = 0;
double decimal = 0;
// Start with the first number
VALUE_CAST(number, Number, (*begin));
int64_t result = number->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

4
src/printer.cpp

@ -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;

33
src/reader.cpp

@ -4,10 +4,12 @@
* SPDX-License-Identifier: MIT
*/
#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"
@ -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);

2
vendor/ruc vendored

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