xo-interpreter2 stack: refactor + bugfix operator expr
This commit is contained in:
parent
c8bc13cb2e
commit
6c7216ed7d
7 changed files with 195 additions and 200 deletions
|
|
@ -6,8 +6,8 @@ include(CMakeFindDependencyMacro)
|
||||||
# must coordinate with xo_dependency() calls
|
# must coordinate with xo_dependency() calls
|
||||||
# in CMakeLists.txt
|
# in CMakeLists.txt
|
||||||
#
|
#
|
||||||
|
find_dependency(xo_type)
|
||||||
find_dependency(xo_object2)
|
find_dependency(xo_object2)
|
||||||
#find_dependency(xo_gc)
|
|
||||||
find_dependency(subsys)
|
find_dependency(subsys)
|
||||||
|
|
||||||
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
|
include("${CMAKE_CURRENT_LIST_DIR}/@PROJECT_NAME@Targets.cmake")
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,18 @@ namespace xo {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @brief Schematika primitive with fixed number of arguments
|
/** @brief Schematika primitive with fixed number of arguments
|
||||||
|
*
|
||||||
|
* Distinction between type-constructor (@ref type_) and
|
||||||
|
* type-description (@ref fn_td_): type-constructor is narrower;
|
||||||
|
* it may lift into schematika type system some information about function
|
||||||
|
* behavior. For example
|
||||||
|
* @pre
|
||||||
|
* cons :: (T x list<T>) -> list<T>
|
||||||
|
* @endpre
|
||||||
|
* but implementation has signature:
|
||||||
|
* @pre
|
||||||
|
* (obj<AGCobject> x obj<AGCObject,DList>) -> obj<AGCObject,DList>
|
||||||
|
* @endpre
|
||||||
**/
|
**/
|
||||||
template <typename Fn>
|
template <typename Fn>
|
||||||
class Primitive {
|
class Primitive {
|
||||||
|
|
@ -70,13 +82,28 @@ namespace xo {
|
||||||
using ppindentinfo = xo::print::ppindentinfo;
|
using ppindentinfo = xo::print::ppindentinfo;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Primitive(std::string_view name, Fn fn) : name_{name},
|
/** @defgroup scm-primitive-ctors constructors **/
|
||||||
fn_td_{Reflect::require<Fn>()},
|
///@{
|
||||||
fn_{fn} {}
|
|
||||||
|
Primitive(std::string_view name, Fn fn)
|
||||||
|
: name_{name},
|
||||||
|
fn_td_{Reflect::require<Fn>()},
|
||||||
|
fn_{fn} {}
|
||||||
|
|
||||||
|
static Primitive * _make(obj<AAllocator> mm, std::string_view name, Fn fn) {
|
||||||
|
void * mem = mm.alloc_for<Primitive>();
|
||||||
|
|
||||||
|
return new (mem) Primitive(name, fn);
|
||||||
|
}
|
||||||
|
|
||||||
|
///@}
|
||||||
|
/** @defgroup scm-primitive-methods general methods **/
|
||||||
|
///@{
|
||||||
|
|
||||||
TypeDescr fn_td() const noexcept { return fn_td_; }
|
TypeDescr fn_td() const noexcept { return fn_td_; }
|
||||||
|
|
||||||
std::string_view name() const noexcept { return name_; }
|
std::string_view name() const noexcept { return name_; }
|
||||||
|
|
||||||
static constexpr std::int32_t n_args() noexcept { return Traits::n_args; }
|
static constexpr std::int32_t n_args() noexcept { return Traits::n_args; }
|
||||||
|
|
||||||
bool is_nary() const noexcept { return false; }
|
bool is_nary() const noexcept { return false; }
|
||||||
|
|
@ -86,6 +113,7 @@ namespace xo {
|
||||||
std::make_index_sequence<Traits::n_args>{});
|
std::make_index_sequence<Traits::n_args>{});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///@}
|
||||||
/** @defgroup scm-primitive-printable-facet **/
|
/** @defgroup scm-primitive-printable-facet **/
|
||||||
///@{
|
///@{
|
||||||
|
|
||||||
|
|
@ -166,8 +194,12 @@ namespace xo {
|
||||||
|
|
||||||
template <typename Fn>
|
template <typename Fn>
|
||||||
std::size_t
|
std::size_t
|
||||||
Primitive<Fn>::forward_children(obj<ACollector>) noexcept {
|
Primitive<Fn>::forward_children(obj<ACollector> gc) noexcept {
|
||||||
// Primitive holds no GC refs (just string_view + function pointer)
|
{
|
||||||
|
auto e = type_.to_facet<AGCObject>(); // FacetRegistry dep
|
||||||
|
gc.forward_inplace(e.iface(), (void **)&(type_.data_));
|
||||||
|
}
|
||||||
|
|
||||||
return this->shallow_size();
|
return this->shallow_size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
89
include/xo/procedure2/PrimitiveRegistry.hpp
Normal file
89
include/xo/procedure2/PrimitiveRegistry.hpp
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
/** @file PrimitiveRegistry.hpp
|
||||||
|
*
|
||||||
|
* @author Roland Conybeare, Mar 2026
|
||||||
|
**/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Procedure.hpp"
|
||||||
|
#include <xo/alloc2/Allocator.hpp>
|
||||||
|
#include <xo/reflect/TypeDescr.hpp>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
namespace scm {
|
||||||
|
/** partition primitives based on scope.
|
||||||
|
* Each primitive associates with exactly one flag value.
|
||||||
|
* Flags also operate as bitmasks when calling
|
||||||
|
* @ref PrimitiveRegistry::install_primitives()
|
||||||
|
**/
|
||||||
|
enum class InstallFlags {
|
||||||
|
f_none = 0x0,
|
||||||
|
|
||||||
|
/** mandatory primitives, necessary for operator support,
|
||||||
|
* e.g. _add needed to implement infix +
|
||||||
|
**/
|
||||||
|
f_essential = 0x1,
|
||||||
|
f_generalpurpose = 0x2,
|
||||||
|
|
||||||
|
/** all primitives **/
|
||||||
|
f_all = f_essential | f_generalpurpose,
|
||||||
|
};
|
||||||
|
|
||||||
|
inline InstallFlags operator&(InstallFlags x, InstallFlags y) {
|
||||||
|
return InstallFlags(static_cast<uint64_t>(x) & static_cast<uint64_t>(y));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** provided by VSM to receive created primitives.
|
||||||
|
* InstallSink(pm) adopts a primitive
|
||||||
|
**/
|
||||||
|
using InstallSink = std::function<bool (std::string_view name,
|
||||||
|
xo::reflect::TypeDescr fn_td,
|
||||||
|
obj<AProcedure> pm,
|
||||||
|
InstallFlags flags)>;
|
||||||
|
|
||||||
|
/** @class PrimitiveRegistry
|
||||||
|
*
|
||||||
|
* @brief Runtime registry for primitives
|
||||||
|
*
|
||||||
|
* Singleton to remember setup code for known primitives.
|
||||||
|
* Use to gather primitives for installation in global
|
||||||
|
* environment for a virtual schematika machine (VSM)
|
||||||
|
**/
|
||||||
|
class PrimitiveRegistry {
|
||||||
|
public:
|
||||||
|
using AAllocator = xo::mm::AAllocator;
|
||||||
|
|
||||||
|
/** provided by a subsystem that provides primitives.
|
||||||
|
* Allocates primitives using memory from mm, delivering them
|
||||||
|
* to InstallSink sink.
|
||||||
|
**/
|
||||||
|
using InstallSource = std::function<bool (obj<AAllocator> mm,
|
||||||
|
InstallSink sink,
|
||||||
|
InstallFlags flags)>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** singleton instance **/
|
||||||
|
static PrimitiveRegistry & instance();
|
||||||
|
|
||||||
|
/** remember primitive-factory @p source_fn **/
|
||||||
|
void register_primitives(InstallSource source_fn);
|
||||||
|
|
||||||
|
/** create primitives using memory from @p mm,
|
||||||
|
* delivering each primitive to @p sink.
|
||||||
|
**/
|
||||||
|
bool install_primitives(obj<AAllocator> mm,
|
||||||
|
InstallSink sink,
|
||||||
|
InstallFlags flags);
|
||||||
|
|
||||||
|
private:
|
||||||
|
/** a set of factories that create primitives **/
|
||||||
|
std::vector<InstallSource> init_seq_v_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} /*namespace scm*/
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
||||||
|
/* end PrimitiveRegistry.hpp */
|
||||||
18
include/xo/procedure2/procedure2_register_primitives.hpp
Normal file
18
include/xo/procedure2/procedure2_register_primitives.hpp
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
/** @file procedure2_register_primitives.hpp
|
||||||
|
*
|
||||||
|
* @author Roland Conybeare, Mar 2026
|
||||||
|
**/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "PrimitiveRegistry.hpp"
|
||||||
|
#include <xo/alloc2/Collector.hpp>
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
namespace scm {
|
||||||
|
/** Register gc-aware (AGCObject,DRepr) combinations with garbage collector @p gc **/
|
||||||
|
bool procedure2_register_primitives(obj<xo::mm::AAllocator> gc, InstallSink sink);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* end procedure2_register_primitives.hpp */
|
||||||
|
|
@ -34,10 +34,9 @@ xo_install_include_tree3(include/xo/procedure2)
|
||||||
# NOTE: dependency set here must be kept consistent with
|
# NOTE: dependency set here must be kept consistent with
|
||||||
# xo-procedure2/cmake/xo_procedure2Config.cmake.in
|
# xo-procedure2/cmake/xo_procedure2Config.cmake.in
|
||||||
|
|
||||||
|
xo_dependency(${SELF_LIB} xo_type)
|
||||||
xo_dependency(${SELF_LIB} xo_object2)
|
xo_dependency(${SELF_LIB} xo_object2)
|
||||||
#xo_dependency(${SELF_LIB} xo_gc)
|
|
||||||
xo_dependency(${SELF_LIB} subsys)
|
xo_dependency(${SELF_LIB} subsys)
|
||||||
#xo_dependency(${SELF_LIB} xo_indentlog)
|
|
||||||
|
|
||||||
xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets)
|
xo_export_cmake_config(${PROJECT_NAME} ${PROJECT_VERSION} ${PROJECT_NAME}Targets)
|
||||||
|
|
||||||
|
|
|
||||||
49
src/procedure2/PrimitiveRegistry.cpp
Normal file
49
src/procedure2/PrimitiveRegistry.cpp
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
/** @file PrimitiveRegistry.cpp
|
||||||
|
*
|
||||||
|
* @author Roland Conybeare, Mar 2026
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include "PrimitiveRegistry.hpp"
|
||||||
|
#include <xo/indentlog/scope.hpp>
|
||||||
|
|
||||||
|
namespace xo {
|
||||||
|
namespace scm {
|
||||||
|
PrimitiveRegistry &
|
||||||
|
PrimitiveRegistry::instance()
|
||||||
|
{
|
||||||
|
static PrimitiveRegistry s_instance;
|
||||||
|
|
||||||
|
return s_instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
PrimitiveRegistry::register_primitives(InstallSource factory)
|
||||||
|
{
|
||||||
|
scope log(XO_DEBUG(true));
|
||||||
|
|
||||||
|
init_seq_v_.push_back(factory);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
PrimitiveRegistry::install_primitives(obj<AAllocator> mm,
|
||||||
|
InstallSink sink,
|
||||||
|
InstallFlags flags)
|
||||||
|
{
|
||||||
|
scope log(XO_DEBUG(true));
|
||||||
|
|
||||||
|
bool ok = true;
|
||||||
|
|
||||||
|
size_t i = 0;
|
||||||
|
size_t n = init_seq_v_.size();
|
||||||
|
log && log("run n init steps", xtag("n", n));
|
||||||
|
|
||||||
|
for (const auto & fn : init_seq_v_) {
|
||||||
|
log && log("do install fn (", i+1, "/", n, ")");
|
||||||
|
|
||||||
|
ok = ok & fn(mm, sink, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} /*namespace xo*/
|
||||||
|
|
@ -37,187 +37,6 @@ namespace xo {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef OBSOLETE // see xo-numeric/xo/numeric/NumericPrimitives.cpp
|
|
||||||
obj<AGCObject>
|
|
||||||
mul_gco_gco(obj<ARuntimeContext> rcx,
|
|
||||||
obj<AGCObject> x_gco,
|
|
||||||
obj<AGCObject> y_gco)
|
|
||||||
{
|
|
||||||
using xo::reflect::typeseq;
|
|
||||||
|
|
||||||
obj<AAllocator> mm = rcx.allocator();
|
|
||||||
|
|
||||||
// PLACEHOLDER
|
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// 1. move this to xo-numeric2/ when available
|
|
||||||
// 2. at that point will require polymorphic dispatch
|
|
||||||
// on argument representations, analogous to dispatch
|
|
||||||
// in FacetRegistry
|
|
||||||
|
|
||||||
typeseq x_tseq = x_gco._typeseq();
|
|
||||||
typeseq y_tseq = y_gco._typeseq();
|
|
||||||
|
|
||||||
// FOR NOW: just test runtime values
|
|
||||||
//
|
|
||||||
if (x_tseq == typeseq::id<DInteger>()) {
|
|
||||||
// i64 * ..
|
|
||||||
long x = GCObjectConversion<long>::from_gco(mm, x_gco);
|
|
||||||
|
|
||||||
if (y_tseq == typeseq::id<DInteger>()) {
|
|
||||||
// i64 * i64
|
|
||||||
long y = GCObjectConversion<long>::from_gco(mm, y_gco);
|
|
||||||
|
|
||||||
return DInteger::box<AGCObject>(mm, x * y);
|
|
||||||
} else if (y_tseq == typeseq::id<DFloat>()) {
|
|
||||||
// i64 * f64
|
|
||||||
double y = GCObjectConversion<double>::from_gco(mm, y_gco);
|
|
||||||
|
|
||||||
return DFloat::box<AGCObject>(mm, x * y);
|
|
||||||
}
|
|
||||||
} else if (x_tseq == typeseq::id<DFloat>()) {
|
|
||||||
if (y_tseq == typeseq::id<DInteger>()) {
|
|
||||||
// f64 * i64.
|
|
||||||
double x = GCObjectConversion<double>::from_gco(mm, x_gco);
|
|
||||||
long y = GCObjectConversion<long>::from_gco(mm, y_gco);
|
|
||||||
|
|
||||||
return DFloat::box<AGCObject>(mm, x * y);
|
|
||||||
} else if (y_tseq == typeseq::id<DFloat>()) {
|
|
||||||
// f64 * f64.
|
|
||||||
double x = GCObjectConversion<double>::from_gco(mm, x_gco);
|
|
||||||
double y = GCObjectConversion<double>::from_gco(mm, y_gco);
|
|
||||||
|
|
||||||
return DFloat::box<AGCObject>(mm, x * y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// here: error
|
|
||||||
throw std::runtime_error(tostr("mul_gco_gco: unexpected argument types xt,yt",
|
|
||||||
xtag("x.tseq", x_tseq),
|
|
||||||
xtag("y.tseq", y_tseq)));
|
|
||||||
return obj<AGCObject>();
|
|
||||||
}
|
|
||||||
|
|
||||||
obj<AGCObject>
|
|
||||||
sub_gco_gco(obj<ARuntimeContext> rcx,
|
|
||||||
obj<AGCObject> x_gco,
|
|
||||||
obj<AGCObject> y_gco)
|
|
||||||
{
|
|
||||||
using xo::reflect::typeseq;
|
|
||||||
|
|
||||||
obj<AAllocator> mm = rcx.allocator();
|
|
||||||
|
|
||||||
// PLACEHOLDER
|
|
||||||
|
|
||||||
// TODO:
|
|
||||||
// 1. move this to xo-numeric2/ when available
|
|
||||||
// 2. at that point will require polymorphic dispatch
|
|
||||||
// on argument representations, analogous to dispatch
|
|
||||||
// in FacetRegistry
|
|
||||||
|
|
||||||
typeseq x_tseq = x_gco._typeseq();
|
|
||||||
typeseq y_tseq = y_gco._typeseq();
|
|
||||||
|
|
||||||
// FOR NOW: just test runtime values
|
|
||||||
//
|
|
||||||
if (x_tseq == typeseq::id<DInteger>()) {
|
|
||||||
// i64 * ..
|
|
||||||
long x = GCObjectConversion<long>::from_gco(mm, x_gco);
|
|
||||||
|
|
||||||
if (y_tseq == typeseq::id<DInteger>()) {
|
|
||||||
// i64 * i64
|
|
||||||
long y = GCObjectConversion<long>::from_gco(mm, y_gco);
|
|
||||||
|
|
||||||
return DInteger::box<AGCObject>(mm, x - y);
|
|
||||||
} else if (y_tseq == typeseq::id<DFloat>()) {
|
|
||||||
// i64 * f64
|
|
||||||
double y = GCObjectConversion<double>::from_gco(mm, y_gco);
|
|
||||||
|
|
||||||
return DFloat::box<AGCObject>(mm, x - y);
|
|
||||||
}
|
|
||||||
} else if (x_tseq == typeseq::id<DFloat>()) {
|
|
||||||
if (y_tseq == typeseq::id<DInteger>()) {
|
|
||||||
// f64 * i64.
|
|
||||||
double x = GCObjectConversion<double>::from_gco(mm, x_gco);
|
|
||||||
long y = GCObjectConversion<long>::from_gco(mm, y_gco);
|
|
||||||
|
|
||||||
return DFloat::box<AGCObject>(mm, x - y);
|
|
||||||
} else if (y_tseq == typeseq::id<DFloat>()) {
|
|
||||||
// f64 * f64.
|
|
||||||
double x = GCObjectConversion<double>::from_gco(mm, x_gco);
|
|
||||||
double y = GCObjectConversion<double>::from_gco(mm, y_gco);
|
|
||||||
|
|
||||||
return DFloat::box<AGCObject>(mm, x - y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// here: error
|
|
||||||
throw std::runtime_error(tostr("sub_gco_gco: unexpected argument types xt,yt",
|
|
||||||
xtag("x.tseq", x_tseq),
|
|
||||||
xtag("y.tseq", y_tseq)));
|
|
||||||
return obj<AGCObject>();
|
|
||||||
}
|
|
||||||
|
|
||||||
obj<AGCObject>
|
|
||||||
equal_gco_gco(obj<ARuntimeContext> rcx,
|
|
||||||
obj<AGCObject> x_gco,
|
|
||||||
obj<AGCObject> y_gco)
|
|
||||||
{
|
|
||||||
using xo::reflect::typeseq;
|
|
||||||
|
|
||||||
obj<AAllocator> mm = rcx.allocator();
|
|
||||||
|
|
||||||
// PLACEHOLDER
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
// 1. move this to xo-numeric2/ when available
|
|
||||||
// 2. at that point will require polymorphic dispatch on argument representations.
|
|
||||||
//
|
|
||||||
|
|
||||||
typeseq x_tseq = x_gco._typeseq();
|
|
||||||
typeseq y_tseq = y_gco._typeseq();
|
|
||||||
|
|
||||||
// FOR NOW: just test runtime values
|
|
||||||
//
|
|
||||||
if (x_tseq == typeseq::id<DInteger>()) {
|
|
||||||
// i64 * ..
|
|
||||||
long x = GCObjectConversion<long>::from_gco(mm, x_gco);
|
|
||||||
|
|
||||||
if (y_tseq == typeseq::id<DInteger>()) {
|
|
||||||
// i64 == i64
|
|
||||||
long y = GCObjectConversion<long>::from_gco(mm, y_gco);
|
|
||||||
|
|
||||||
return DBoolean::box<AGCObject>(mm, x == y);
|
|
||||||
} else if (y_tseq == typeseq::id<DFloat>()) {
|
|
||||||
// i64 == f64
|
|
||||||
double y = GCObjectConversion<double>::from_gco(mm, y_gco);
|
|
||||||
|
|
||||||
return DFloat::box<AGCObject>(mm, static_cast<double>(x) == y);
|
|
||||||
}
|
|
||||||
} else if (x_tseq == typeseq::id<DFloat>()) {
|
|
||||||
if (y_tseq == typeseq::id<DInteger>()) {
|
|
||||||
// f64 == i64.
|
|
||||||
double x = GCObjectConversion<double>::from_gco(mm, x_gco);
|
|
||||||
long y = GCObjectConversion<long>::from_gco(mm, y_gco);
|
|
||||||
|
|
||||||
return DFloat::box<AGCObject>(mm, x == static_cast<double>(y));
|
|
||||||
} else if (y_tseq == typeseq::id<DFloat>()) {
|
|
||||||
// f64 * f64.
|
|
||||||
double x = GCObjectConversion<double>::from_gco(mm, x_gco);
|
|
||||||
double y = GCObjectConversion<double>::from_gco(mm, y_gco);
|
|
||||||
|
|
||||||
return DFloat::box<AGCObject>(mm, x == y);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// here: error
|
|
||||||
throw std::runtime_error(tostr("mul_gco_gco: unexpected argument types xt,yt",
|
|
||||||
xtag("x.tseq", x_tseq),
|
|
||||||
xtag("y.tseq", y_tseq)));
|
|
||||||
return obj<AGCObject>();
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef NOT_YET
|
#ifdef NOT_YET
|
||||||
double
|
double
|
||||||
mul_f64_f64(double x, double y) {
|
mul_f64_f64(double x, double y) {
|
||||||
|
|
@ -255,17 +74,6 @@ namespace xo {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef OSOLETE
|
|
||||||
DPrimitive_gco_2_gco_gco
|
|
||||||
Primitives::s_mul_gco_gco_pm("_mul", &mul_gco_gco);
|
|
||||||
|
|
||||||
DPrimitive_gco_2_gco_gco
|
|
||||||
Primitives::s_sub_gco_gco_pm("_sub", &sub_gco_gco);
|
|
||||||
|
|
||||||
DPrimitive_gco_2_gco_gco
|
|
||||||
Primitives::s_equal_gco_gco_pm("_equal", &equal_gco_gco);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef NOT_YET
|
#ifdef NOT_YET
|
||||||
Primitive_f64_1_f64
|
Primitive_f64_1_f64
|
||||||
Primitives::s_neg_f64_pm("_neg_d",
|
Primitives::s_neg_f64_pm("_neg_d",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue