Skip to content

Commit

Permalink
Add to_real method to bytes.
Browse files Browse the repository at this point in the history
This interprets the data as representing an ASCII-encoded floating
point number and converts that into a ``real``. The data can be in
either decimal or hexadecimal format. If it cannot be parsed as
either, throws an `InvalidValue` exception.

Closes #1750.
  • Loading branch information
rsmmr committed Jun 6, 2024
1 parent 09ee9b5 commit 70cc0a7
Show file tree
Hide file tree
Showing 11 changed files with 86 additions and 6 deletions.
7 changes: 7 additions & 0 deletions doc/autogen/types/bytes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,13 @@
Interprets the ``bytes`` as representing an binary number encoded with
the given byte order, and converts it into signed integer.

.. spicy:method:: bytes::to_real bytes to_real False real ()
Interprets the ``bytes`` as representing an ASCII-encoded floating
point number and converts that into a ``real``. The data can be in
either decimal or hexadecimal format. If it cannot be parsed as
either, throws an `InvalidValue` exception.

.. spicy:method:: bytes::to_time bytes to_time False time ([ base: uint<64> ])
Interprets the ``bytes`` as representing a number of seconds since the
Expand Down
8 changes: 8 additions & 0 deletions hilti/runtime/include/types/bytes.h
Original file line number Diff line number Diff line change
Expand Up @@ -488,6 +488,14 @@ class Bytes : protected std::string {
*/
uint64_t toUInt(hilti::rt::ByteOrder byte_order) const;

/**
* Interprets the data as an ASCII representation of a floating point value
* and extracts that. The data must be in a format that `strtod` can handle.
*
* @return converted real value
*/
double toReal() const;

/**
* Interprets the data as an ASCII representation of a integer value
* representing seconds since the UNIX epoch, and extracts that.
Expand Down
18 changes: 18 additions & 0 deletions hilti/runtime/src/types/bytes.cc
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,24 @@ uint64_t Bytes::toUInt(ByteOrder byte_order) const {
return i;
}

double Bytes::toReal() const {
// Ensure there are no null bytes inside our data, so that we can call strtod().
if ( Base::find('\0') != Base::npos )
throw InvalidValue("cannot parse real value: null byte in data");

const char* cstr = Base::c_str();
char* endp = nullptr;

errno = 0;
auto d = strtod(cstr, &endp);
if ( endp == cstr || *endp != '\0' || (d == HUGE_VAL && errno == ERANGE) ) {
errno = 0;
throw InvalidValue(fmt("cannot parse real value: %s", cstr));
}

return d;
}

Result<Bytes> Bytes::match(const RegExp& re, unsigned int group) const {
auto groups = re.matchGroups(*this);

Expand Down
1 change: 1 addition & 0 deletions hilti/toolchain/include/ast/forward.h
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ class ToIntAscii;
class ToUIntAscii;
class ToIntBinary;
class ToUIntBinary;
class ToRealAscii;
class ToTimeAscii;
class ToTimeBinary;
class Decode;
Expand Down
13 changes: 7 additions & 6 deletions hilti/toolchain/include/ast/node-tag.h
Original file line number Diff line number Diff line change
Expand Up @@ -193,12 +193,13 @@ constexpr Tag SumAssignStreamView = 922;
constexpr Tag SumAssignUInt8 = 923;
constexpr Tag ToIntAscii = 924;
constexpr Tag ToIntBinary = 925;
constexpr Tag ToTimeAscii = 926;
constexpr Tag ToTimeBinary = 927;
constexpr Tag ToUIntAscii = 928;
constexpr Tag ToUIntBinary = 929;
constexpr Tag Unequal = 930;
constexpr Tag UpperCase = 931;
constexpr Tag ToRealAscii = 926;
constexpr Tag ToTimeAscii = 927;
constexpr Tag ToTimeBinary = 928;
constexpr Tag ToUIntAscii = 929;
constexpr Tag ToUIntBinary = 930;
constexpr Tag Unequal = 931;
constexpr Tag UpperCase = 932;

namespace iterator {
constexpr Tag Deref = 1000;
Expand Down
1 change: 1 addition & 0 deletions hilti/toolchain/include/ast/operators/bytes.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ HILTI_NODE_OPERATOR(bytes, ToIntAscii)
HILTI_NODE_OPERATOR(bytes, ToUIntAscii)
HILTI_NODE_OPERATOR(bytes, ToIntBinary)
HILTI_NODE_OPERATOR(bytes, ToUIntBinary)
HILTI_NODE_OPERATOR(bytes, ToRealAscii)
HILTI_NODE_OPERATOR(bytes, ToTimeAscii)
HILTI_NODE_OPERATOR(bytes, ToTimeBinary)
HILTI_NODE_OPERATOR(bytes, Decode)
Expand Down
1 change: 1 addition & 0 deletions hilti/toolchain/include/ast/visitor-dispatcher.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ class Dispatcher {
virtual void operator()(hilti::operator_::bytes::ToUIntAscii* n) {}
virtual void operator()(hilti::operator_::bytes::ToIntBinary* n) {}
virtual void operator()(hilti::operator_::bytes::ToUIntBinary* n) {}
virtual void operator()(hilti::operator_::bytes::ToRealAscii* n) {}
virtual void operator()(hilti::operator_::bytes::ToTimeAscii* n) {}
virtual void operator()(hilti::operator_::bytes::ToTimeBinary* n) {}
virtual void operator()(hilti::operator_::bytes::Decode* n) {}
Expand Down
23 changes: 23 additions & 0 deletions hilti/toolchain/src/ast/operators/bytes.cc
Original file line number Diff line number Diff line change
Expand Up @@ -876,6 +876,29 @@ byte order, and converts it into an unsigned integer.
};
HILTI_OPERATOR_IMPLEMENTATION(ToUIntBinary);

class ToRealAscii : public BuiltInMemberCall {
public:
Signature signature(Builder* builder) const final {
return Signature{
.kind = Kind::MemberCall,
.self = {parameter::Kind::In, builder->typeBytes()},
.member = "to_real",
.result = {Constness::Const, builder->typeReal()},
.ns = "bytes",
.doc =
R"(
Interprets the ``bytes`` as representing an ASCII-encoded floating point number
and converts that into a ``real``. The data can be in either decimal or
hexadecimal format. If it cannot be parsed as either, throws an `InvalidValue`
exception.
)",
};
}

HILTI_OPERATOR(hilti, bytes::ToRealAscii);
};
HILTI_OPERATOR_IMPLEMENTATION(ToRealAscii);

class ToTimeAscii : public BuiltInMemberCall {
public:
Signature signature(Builder* builder) const final {
Expand Down
5 changes: 5 additions & 0 deletions hilti/toolchain/src/compiler/codegen/operators.cc
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,11 @@ struct Visitor : hilti::visitor::PreOrder {
result = fmt("%s.toUInt(%s)", self, optionalArgument(args, 0));
}

void operator()(operator_::bytes::ToRealAscii* n) final {
auto [self, args] = methodArguments(n);
result = fmt("%s.toReal()", self);
}

void operator()(operator_::bytes::ToTimeAscii* n) final {
auto [self, args] = methodArguments(n);
result = fmt("%s.toTime(%s)", self, optionalArgument(args, 0));
Expand Down
1 change: 1 addition & 0 deletions tests/Baseline/hilti.types.bytes.to-real/output
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
### BTest baseline data generated by btest-diff. Do not edit. Use "btest -U/-u" to update. Requires BTest >= 0.63.
14 changes: 14 additions & 0 deletions tests/hilti/types/bytes/to-real.hlt
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# @TEST-EXEC: hiltic -j %INPUT >output
# @TEST-EXEC: btest-diff output

module Test {

assert(b"3.14".to_real() == 3.14);
assert(b"314e5".to_real() == 314e5);
assert(b"0X1.BC70A3D70A3D7P+6".to_real() == 111.11);

assert-exception(b"3.14XYZ".to_real());
assert-exception(b"XXX".to_real());
assert-exception(b"\03.14".to_real());

}

0 comments on commit 70cc0a7

Please sign in to comment.