libexpr: add C bindings

This commit is contained in:
Yorick van Pelt 2023-07-14 15:53:30 +02:00 committed by José Luis Lafuente
parent 1d41600498
commit e76652a5d3
No known key found for this signature in database
GPG Key ID: 8A3455EBE455489A
10 changed files with 1527 additions and 1 deletions

View File

@ -897,6 +897,10 @@ void Value::mkStringMove(const char * s, const NixStringContext & context)
copyContextToValue(*this, context);
}
void Value::mkPath(std::string_view path)
{
mkPath(makeImmutableString(path));
}
void Value::mkPath(const SourcePath & path)
{

141
src/libexpr/nix_api_expr.cc Normal file
View File

@ -0,0 +1,141 @@
#include <cstring>
#include <iostream>
#include <stdexcept>
#include <string>
#include "config.hh"
#include "eval.hh"
#include "globals.hh"
#include "util.hh"
#include "nix_api_expr.h"
#include "nix_api_expr_internal.h"
#include "nix_api_store.h"
#include "nix_api_store_internal.h"
#include "nix_api_util.h"
#include "nix_api_util_internal.h"
#ifdef HAVE_BOEHMGC
#define GC_INCLUDE_NEW 1
#include "gc_cpp.h"
#endif
nix_err nix_libexpr_init(nix_c_context *context) {
if (context)
context->last_err_code = NIX_OK;
{
auto ret = nix_libutil_init(context);
if (ret != NIX_OK)
return ret;
}
{
auto ret = nix_libstore_init(context);
if (ret != NIX_OK)
return ret;
}
try {
nix::initGC();
}
NIXC_CATCH_ERRS
}
Expr *nix_parse_expr_from_string(nix_c_context *context, State *state,
const char *expr, const char *path,
GCRef *ref) {
if (context)
context->last_err_code = NIX_OK;
try {
Expr *result = state->state.parseExprFromString(
expr, state->state.rootPath(nix::CanonPath(path)));
if (ref)
ref->ptr = result;
return result;
}
NIXC_CATCH_ERRS_NULL
}
nix_err nix_expr_eval(nix_c_context *context, State *state, Expr *expr,
Value *value) {
if (context)
context->last_err_code = NIX_OK;
try {
state->state.eval((nix::Expr *)expr, *(nix::Value *)value);
}
NIXC_CATCH_ERRS
}
nix_err nix_value_call(nix_c_context *context, State *state, Value *fn,
Value *arg, Value *value) {
if (context)
context->last_err_code = NIX_OK;
try {
state->state.callFunction(*(nix::Value *)fn, *(nix::Value *)arg,
*(nix::Value *)value, nix::noPos);
}
NIXC_CATCH_ERRS
}
nix_err nix_value_force(nix_c_context *context, State *state, Value *value) {
if (context)
context->last_err_code = NIX_OK;
try {
state->state.forceValue(*(nix::Value *)value, nix::noPos);
}
NIXC_CATCH_ERRS
}
nix_err nix_value_force_deep(nix_c_context *context, State *state,
Value *value) {
if (context)
context->last_err_code = NIX_OK;
try {
state->state.forceValueDeep(*(nix::Value *)value);
}
NIXC_CATCH_ERRS
}
State *nix_state_create(nix_c_context *context, const char **searchPath_c,
Store *store) {
if (context)
context->last_err_code = NIX_OK;
try {
nix::Strings searchPath;
if (searchPath_c != nullptr)
for (size_t i = 0; searchPath_c[i] != nullptr; i++)
searchPath.push_back(searchPath_c[i]);
return new State{
nix::EvalState(nix::SearchPath::parse(searchPath), store->ptr)};
}
NIXC_CATCH_ERRS_NULL
}
void nix_state_free(State *state) { delete state; }
GCRef *nix_gc_ref(nix_c_context *context, void *obj) {
if (context)
context->last_err_code = NIX_OK;
try {
#if HAVE_BOEHMGC
return new (NoGC) GCRef{obj};
#else
return new GCRef{obj};
#endif
}
NIXC_CATCH_ERRS_NULL
}
void nix_gc_free(GCRef *ref) {
#if HAVE_BOEHMGC
GC_FREE(ref);
#else
delete ref;
#endif
}
void nix_gc_register_finalizer(void *obj, void *cd,
void (*finalizer)(void *obj, void *cd)) {
#ifdef HAVE_BOEHMGC
GC_REGISTER_FINALIZER(obj, finalizer, cd, 0, 0);
#endif
}

176
src/libexpr/nix_api_expr.h Normal file
View File

@ -0,0 +1,176 @@
#ifndef NIX_API_EXPR_H
#define NIX_API_EXPR_H
/** @file
* @brief Main entry for the libexpr C bindings
*/
#include "nix_api_store.h"
#include "nix_api_util.h"
#ifdef __cplusplus
extern "C" {
#endif
// cffi start
// Type definitions
/**
* @brief Represents a parsed nix Expression, can be evaluated into a Value.
*
* Owned by the GC.
*/
typedef void Expr; // nix::Expr
/**
* @brief Represents a nix evaluator state.
*
* Multiple can be created for multi-threaded
* operation.
*/
typedef struct State State; // nix::EvalState
/**
* @brief Represents a nix value.
*
* Owned by the GC.
*/
typedef void Value; // nix::Value
/**
* @brief Reference for the GC
*
* Nix uses a garbage collector that may not be able to see into
* your stack and heap. Keep GCRef objects around for every
* garbage-collected object that you want to keep alive.
*/
typedef struct GCRef GCRef; // void*
// Function propotypes
/**
* @brief Initializes the Nix expression evaluator.
*
* This function should be called before creating a State.
* This function can be called multiple times.
*
* @param[out] context Optional, stores error information
* @return NIX_OK if the initialization was successful, an error code otherwise.
*/
nix_err nix_libexpr_init(nix_c_context *context);
/**
* @brief Parses a Nix expression from a string.
*
* The returned expression is owned by the garbage collector.
* Pass a gcref to keep a reference.
*
* @param[out] context Optional, stores error information
* @param[in] state Evaluator state.
* @param[in] expr The Nix expression to parse.
* @param[in] path The file path to associate with the expression.
* @param[out] ref Optional, will store a reference to the returned value.
* @return A parsed expression or NULL on failure.
*/
Expr *nix_parse_expr_from_string(nix_c_context *context, State *state,
const char *expr, const char *path,
GCRef *ref);
/**
* @brief Evaluates a parsed Nix expression.
*
* @param[out] context Optional, stores error information
* @param[in] state The state of the evaluation.
* @param[in] expr The Nix expression to evaluate.
* @param[in] value The result of the evaluation.
* @return NIX_OK if the evaluation was successful, an error code otherwise.
*/
nix_err nix_expr_eval(nix_c_context *context, State *state, Expr *expr,
Value *value);
/**
* @brief Calls a Nix function with an argument.
*
* @param[out] context Optional, stores error information
* @param[in] state The state of the evaluation.
* @param[in] fn The Nix function to call.
* @param[in] arg The argument to pass to the function.
* @param[out] value The result of the function call.
* @return NIX_OK if the function call was successful, an error code otherwise.
*/
nix_err nix_value_call(nix_c_context *context, State *state, Value *fn,
Value *arg, Value *value);
/**
* @brief Forces the evaluation of a Nix value.
*
* @param[out] context Optional, stores error information
* @param[in] state The state of the evaluation.
* @param[in,out] value The Nix value to force.
* @return NIX_OK if the force operation was successful, an error code
* otherwise.
*/
nix_err nix_value_force(nix_c_context *context, State *state, Value *value);
/**
* @brief Forces the deep evaluation of a Nix value.
*
* @param[out] context Optional, stores error information
* @param[in] state The state of the evaluation.
* @param[in,out] value The Nix value to force.
* @return NIX_OK if the deep force operation was successful, an error code
* otherwise.
*/
nix_err nix_value_force_deep(nix_c_context *context, State *state,
Value *value);
/**
* @brief Creates a new Nix state.
*
* @param[out] context Optional, stores error information
* @param[in] searchPath The NIX_PATH.
* @param[in] store The Nix store to use.
* @return A new Nix state or NULL on failure.
*/
State *nix_state_create(nix_c_context *context, const char **searchPath,
Store *store);
/**
* @brief Frees a Nix state.
*
* Does not fail.
*
* @param[in] state The state to free.
*/
void nix_state_free(State *state);
/**
* @brief Creates a new garbage collector reference.
*
* @param[out] context Optional, stores error information
* @param[in] obj The object to create a reference for.
* @return A new garbage collector reference or NULL on failure.
*/
GCRef *nix_gc_ref(nix_c_context *context, void *obj);
/**
* @brief Frees a garbage collector reference.
*
* Does not fail.
*
* @param[in] ref The reference to free.
*/
void nix_gc_free(GCRef *ref);
/**
* @brief Register a callback that gets called when the object is garbage
* collected.
* @note objects can only have a single finalizer. This function overwrites
* silently.
* @param[in] obj the object to watch
* @param[in] cd the data to pass to the finalizer
* @param[in] finalizer the callback function, called with obj and cd
*/
void nix_gc_register_finalizer(void *obj, void *cd,
void (*finalizer)(void *obj, void *cd));
// cffi end
#ifdef __cplusplus
}
#endif
#endif // NIX_API_EXPR_H

View File

@ -0,0 +1,17 @@
#ifndef NIX_API_EXPR_INTERNAL_H
#define NIX_API_EXPR_INTERNAL_H
// forward declaration
namespace nix {
class EvalState;
};
struct State {
nix::EvalState state;
};
struct GCRef {
void *ptr;
};
#endif // NIX_API_EXPR_INTERNAL_H

View File

@ -0,0 +1,198 @@
#include "attr-set.hh"
#include "config.hh"
#include "eval.hh"
#include "gc/gc.h"
#include "globals.hh"
#include "value.hh"
#include "nix_api_expr.h"
#include "nix_api_expr_internal.h"
#include "nix_api_external.h"
#include "nix_api_util.h"
#include "nix_api_util_internal.h"
#include "nix_api_value.h"
#include "value/context.hh"
#include <nlohmann/json.hpp>
#ifdef HAVE_BOEHMGC
#define GC_INCLUDE_NEW 1
#include "gc_cpp.h"
#endif
struct nix_returned_string {
std::string str;
};
struct nix_printer {
std::ostream &s;
};
struct nix_string_context {
nix::NixStringContext &ctx;
};
nix_returned_string *nix_external_alloc_string(const char *c) {
return new nix_returned_string{c};
}
void nix_external_dealloc_string(nix_returned_string *str) { delete str; }
nix_err nix_external_print(nix_c_context *context, nix_printer *printer,
const char *c) {
if (context)
context->last_err_code = NIX_OK;
try {
printer->s << c;
}
NIXC_CATCH_ERRS
}
nix_err nix_external_add_string_context(nix_c_context *context,
nix_string_context *ctx,
const char *c) {
if (context)
context->last_err_code = NIX_OK;
try {
auto r = nix::NixStringContextElem::parse(c);
ctx->ctx.insert(r);
}
NIXC_CATCH_ERRS
}
class NixCExternalValue : public nix::ExternalValueBase {
NixCExternalValueDesc &desc;
void *v;
public:
NixCExternalValue(NixCExternalValueDesc &desc, void *v) : desc(desc), v(v){};
void *get_ptr() { return v; }
/**
* Print out the value
*/
virtual std::ostream &print(std::ostream &str) const override {
nix_printer p{str};
desc.print(v, &p);
return str;
}
/**
* Return a simple string describing the type
*/
virtual std::string showType() const override {
std::unique_ptr<nix_returned_string> r(desc.showType(v));
return std::move(r->str);
}
/**
* Return a string to be used in builtins.typeOf
*/
virtual std::string typeOf() const override {
std::unique_ptr<nix_returned_string> r(desc.typeOf(v));
return std::move(r->str);
}
/**
* Coerce the value to a string.
*/
virtual std::string coerceToString(const nix::Pos &pos,
nix::NixStringContext &context,
bool copyMore,
bool copyToStore) const override {
if (!desc.coerceToString) {
return nix::ExternalValueBase::coerceToString(pos, context, copyMore,
copyToStore);
}
nix_string_context ctx{context};
// todo: pos, errors
std::unique_ptr<nix_returned_string> r(
desc.coerceToString(v, &ctx, copyMore, copyToStore));
if (!r) {
return nix::ExternalValueBase::coerceToString(pos, context, copyMore,
copyToStore);
}
return std::move(r->str);
}
/**
* Compare to another value of the same type.
*/
virtual bool operator==(const ExternalValueBase &b) const override {
if (!desc.equal) {
return false;
}
auto r = dynamic_cast<const NixCExternalValue *>(&b);
if (!r)
return false;
return desc.equal(v, r->v);
}
/**
* Print the value as JSON.
*/
virtual nlohmann::json
printValueAsJSON(nix::EvalState &state, bool strict,
nix::NixStringContext &context,
bool copyToStore = true) const override {
if (!desc.printValueAsJSON) {
return nix::ExternalValueBase::printValueAsJSON(state, strict, context,
copyToStore);
}
nix_string_context ctx{context};
std::unique_ptr<nix_returned_string> r(
desc.printValueAsJSON((State *)&state, strict, &ctx, copyToStore));
if (!r) {
return nix::ExternalValueBase::printValueAsJSON(state, strict, context,
copyToStore);
}
return nlohmann::json::parse(r->str);
}
/**
* Print the value as XML.
*/
virtual void printValueAsXML(nix::EvalState &state, bool strict,
bool location, nix::XMLWriter &doc,
nix::NixStringContext &context,
nix::PathSet &drvsSeen,
const nix::PosIdx pos) const override {
if (!desc.printValueAsXML) {
return nix::ExternalValueBase::printValueAsXML(
state, strict, location, doc, context, drvsSeen, pos);
}
nix_string_context ctx{context};
desc.printValueAsXML((State *)&state, strict, location, &doc, &ctx,
&drvsSeen, *reinterpret_cast<const uint32_t *>(&pos));
}
virtual ~NixCExternalValue() override{};
};
ExternalValue *nix_create_external_value(nix_c_context *context,
NixCExternalValueDesc *desc, void *v,
GCRef *gc) {
if (context)
context->last_err_code = NIX_OK;
try {
auto ret = new
#ifdef HAVE_BOEHMGC
(GC)
#endif
NixCExternalValue(*desc, v);
if (gc)
gc->ptr = ret;
return (ExternalValue *)ret;
}
NIXC_CATCH_ERRS_NULL
}
void *nix_get_external_value_content(nix_c_context *context, ExternalValue *b) {
if (context)
context->last_err_code = NIX_OK;
try {
auto r = dynamic_cast<NixCExternalValue *>((nix::ExternalValueBase *)b);
if (r)
return r->get_ptr();
return nullptr;
}
NIXC_CATCH_ERRS_NULL
}

View File

@ -0,0 +1,195 @@
#ifndef NIX_API_EXTERNAL_H
#define NIX_API_EXTERNAL_H
/** @file
* @brief libexpr C bindings dealing with external values
*/
#include "nix_api_expr.h"
#include "nix_api_util.h"
#include "nix_api_value.h"
#include "stdbool.h"
#include "stddef.h"
#include "stdint.h"
#ifdef __cplusplus
extern "C" {
#endif
// cffi start
/**
* @brief Represents a string meant for consumption by nix.
*/
typedef struct nix_returned_string nix_returned_string;
/**
* @brief Wraps a stream that can output multiple string pieces.
*/
typedef struct nix_printer nix_printer;
/**
* @brief A list of string context items
*/
typedef struct nix_string_context nix_string_context;
/**
* @brief Allocate a nix_returned_string from a const char*.
*
* Copies the passed string.
* @param[in] c The string to copy
* @returns A nix_returned_string*
*/
nix_returned_string *nix_external_alloc_string(const char *c);
/**
* @brief Deallocate a nix_returned_string
*
* There's generally no need to call this, since
* returning the string will pass ownership to nix,
* but you can use it in case of errors.
* @param[in] str The string to deallocate
*/
void nix_external_dealloc_string(nix_returned_string *str);
/**
* Print to the nix_printer
*
* @param[out] context Optional, stores error information
* @param printer The nix_printer to print to
* @param[in] str The string to print
* @returns NIX_OK if everything worked
*/
nix_err nix_external_print(nix_c_context *context, nix_printer *printer,
const char *str);
/**
* Add string context to the nix_string_context object
* @param[out] context Optional, stores error information
* @param[out] string_context The nix_string_context to add to
* @param[in] c The context string to add
* @returns NIX_OK if everything worked
*/
nix_err nix_external_add_string_context(nix_c_context *context,
nix_string_context *string_context,
const char *c);
/**
* @brief Definition for a class of external values
*
* Create and implement one of these, then pass it to nix_create_external_value
* Make sure to keep it alive while the external value lives.
*
* Optional functions can be set to NULL
*
* @see nix_create_external_value
*/
typedef struct NixCExternalValueDesc {
/**
* @brief Called when printing the external value
*
* @param[in] self the void* passed to nix_create_external_value
* @param[out] printer The printer to print to, pass to nix_external_print
*/
void (*print)(void *self, nix_printer *printer);
/**
* @brief Called on :t
* @param[in] self the void* passed to nix_create_external_value
* @returns a nix_returned_string, ownership passed to nix
*/
nix_returned_string *(*showType)(void *self); // std::string
/**
* @brief Called on `builtins.typeOf`
* @param self the void* passed to nix_create_external_value
* @returns a nix_returned_string, ownership passed to nix
*/
nix_returned_string *(*typeOf)(void *self); // std::string
/**
* @brief Called on "${str}" and builtins.toString.
*
* The latter with coerceMore=true
* Optional, the default is to throw an error.
* @param[in] self the void* passed to nix_create_external_value
* @param[out] c writable string context for the resulting string
* @param[in] coerceMore boolean, try to coerce to strings in more cases
* instead of throwing an error
* @param[in] copyToStore boolean, whether to copy referenced paths to store
* or keep them as-is
* @returns a nix_returned_string, ownership passed to nix. Optional,
* returning NULL will make the conversion throw an error.
*/
nix_returned_string *(*coerceToString)(void *self, nix_string_context *c,
int coerceMore, int copyToStore);
/**
* @brief Try to compare two external values
*
* Optional, the default is always false.
* If the other object was not a Nix C external value, this comparison will
* also return false
* @param[in] self the void* passed to nix_create_external_value
* @param[in] other the void* passed to the other object's
* nix_create_external_value
* @returns true if the objects are deemed to be equal
*/
int (*equal)(void *self, void *other);
/**
* @brief Convert the external value to json
*
* Optional, the default is to throw an error
* @param[in] state The evaluator state
* @param[in] strict boolean Whether to force the value before printing
* @param[out] c writable string context for the resulting string
* @param[in] copyToStore whether to copy referenced paths to store or keep
* them as-is
* @returns string that gets parsed as json. Optional, returning NULL will
* make the conversion throw an error.
*/
nix_returned_string *(*printValueAsJSON)(State *, int strict,
nix_string_context *c,
bool copyToStore);
/**
* @brief Convert the external value to XML
*
* Optional, the default is to throw an error
* @todo The mechanisms for this call are incomplete. There are no C
* bindings to work with XML, pathsets and positions.
* @param[in] state The evaluator state
* @param[in] strict boolean Whether to force the value before printing
* @param[in] location boolean Whether to include position information in the
* xml
* @param[out] doc XML document to output to
* @param[out] c writable string context for the resulting string
* @param[in,out] drvsSeen a path set to avoid duplicating derivations
* @param[in] pos The position of the call.
*/
void (*printValueAsXML)(State *, int strict, int location, void *doc,
nix_string_context *c, void *drvsSeen, int pos);
} NixCExternalValueDesc;
/**
* @brief Create an external value, that can be given to nix_set_external
*
* Pass a gcref to keep a reference.
* @param[out] context Optional, stores error information
* @param[in] desc a NixCExternalValueDesc, you should keep this alive as long
* as the ExternalValue lives
* @param[in] v the value to store
* @param[out] ref Optional, will store a reference to the returned value.
* @returns external value, owned by the garbage collector
* @see nix_set_external
*/
ExternalValue *nix_create_external_value(nix_c_context *context,
NixCExternalValueDesc *desc, void *v,
GCRef *ref);
/**
* @brief Extract the pointer from a nix c external value.
* @param[out] context Optional, stores error information
* @param[in] b The external value
* @returns The pointer, or null if the external value was not from nix c.
* @see nix_get_external
*/
void *nix_get_external_value_content(nix_c_context *context, ExternalValue *b);
// cffi end
#ifdef __cplusplus
}
#endif
#endif // NIX_API_EXTERNAL_H

View File

@ -0,0 +1,439 @@
#include "attr-set.hh"
#include "config.hh"
#include "eval.hh"
#include "gc/gc.h"
#include "globals.hh"
#include "value.hh"
#include "nix_api_expr.h"
#include "nix_api_expr_internal.h"
#include "nix_api_util.h"
#include "nix_api_util_internal.h"
#include "nix_api_value.h"
#ifdef HAVE_BOEHMGC
#define GC_INCLUDE_NEW 1
#include "gc_cpp.h"
#endif
// Helper function to throw an exception if value is null
static const nix::Value &check_value_not_null(const Value *value) {
if (!value) {
throw std::runtime_error("Value is null");
}
return *((const nix::Value *)value);
}
static nix::Value &check_value_not_null(Value *value) {
if (!value) {
throw std::runtime_error("Value is null");
}
return *((nix::Value *)value);
}
PrimOp *nix_alloc_primop(nix_c_context *context, PrimOpFun fun, int arity,
const char *name, const char **args, const char *doc,
GCRef *ref) {
if (context)
context->last_err_code = NIX_OK;
try {
auto fun2 = (nix::PrimOpFun)fun;
auto p = new
#ifdef HAVE_BOEHMGC
(GC)
#endif
nix::PrimOp{.name = name, .args = {}, .doc = doc, .fun = fun2};
if (args)
for (size_t i = 0; args[i]; i++)
p->args.emplace_back(*args);
if (ref)
ref->ptr = p;
return (PrimOp *)p;
}
NIXC_CATCH_ERRS_NULL
}
Value *nix_alloc_value(nix_c_context *context, State *state, GCRef *ref) {
if (context)
context->last_err_code = NIX_OK;
try {
Value *res = state->state.allocValue();
if (ref)
ref->ptr = res;
return res;
}
NIXC_CATCH_ERRS_NULL
}
ValueType nix_get_type(nix_c_context *context, const Value *value) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
using namespace nix;
switch (v.type()) {
case nThunk:
return NIX_TYPE_THUNK;
case nInt:
return NIX_TYPE_INT;
case nFloat:
return NIX_TYPE_FLOAT;
case nBool:
return NIX_TYPE_BOOL;
case nString:
return NIX_TYPE_STRING;
case nPath:
return NIX_TYPE_PATH;
case nNull:
return NIX_TYPE_NULL;
case nAttrs:
return NIX_TYPE_ATTRS;
case nList:
return NIX_TYPE_LIST;
case nFunction:
return NIX_TYPE_FUNCTION;
case nExternal:
return NIX_TYPE_EXTERNAL;
}
return NIX_TYPE_NULL;
}
NIXC_CATCH_ERRS_RES(NIX_TYPE_NULL);
}
const char *nix_get_typename(nix_c_context *context, const Value *value) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
auto s = nix::showType(v);
return strdup(s.c_str());
}
NIXC_CATCH_ERRS_NULL
}
bool nix_get_bool(nix_c_context *context, const Value *value) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
assert(v.type() == nix::nBool);
return v.boolean;
}
NIXC_CATCH_ERRS_RES(false);
}
const char *nix_get_string(nix_c_context *context, const Value *value) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
assert(v.type() == nix::nString);
return v.string.s;
}
NIXC_CATCH_ERRS_NULL
}
const char *nix_get_path_string(nix_c_context *context, const Value *value) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
assert(v.type() == nix::nPath);
return v._path;
}
NIXC_CATCH_ERRS_NULL
}
unsigned int nix_get_list_size(nix_c_context *context, const Value *value) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
assert(v.type() == nix::nList);
return v.listSize();
}
NIXC_CATCH_ERRS_RES(0);
}
unsigned int nix_get_attrs_size(nix_c_context *context, const Value *value) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
assert(v.type() == nix::nAttrs);
return v.attrs->size();
}
NIXC_CATCH_ERRS_RES(0);
}
double nix_get_double(nix_c_context *context, const Value *value) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
assert(v.type() == nix::nFloat);
return v.fpoint;
}
NIXC_CATCH_ERRS_RES(NAN);
}
int64_t nix_get_int(nix_c_context *context, const Value *value) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
assert(v.type() == nix::nInt);
return v.integer;
}
NIXC_CATCH_ERRS_RES(0);
}
ExternalValue *nix_get_external(nix_c_context *context, Value *value) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
assert(v.type() == nix::nExternal);
return (ExternalValue *)v.external;
}
NIXC_CATCH_ERRS_NULL;
}
Value *nix_get_list_byidx(nix_c_context *context, const Value *value,
unsigned int ix, GCRef *ref) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
assert(v.type() == nix::nList);
return (Value *)v.listElems()[ix];
}
NIXC_CATCH_ERRS_NULL
}
Value *nix_get_attr_byname(nix_c_context *context, const Value *value,
State *state, const char *name, GCRef *ref) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
assert(v.type() == nix::nAttrs);
nix::Symbol s = state->state.symbols.create(name);
auto attr = v.attrs->get(s);
if (attr) {
if (ref)
ref->ptr = attr->value;
return attr->value;
}
nix_set_err_msg(context, NIX_ERR_KEY, "missing attribute");
return nullptr;
}
NIXC_CATCH_ERRS_NULL
}
bool nix_has_attr_byname(nix_c_context *context, const Value *value,
State *state, const char *name) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
assert(v.type() == nix::nAttrs);
nix::Symbol s = state->state.symbols.create(name);
auto attr = v.attrs->get(s);
if (attr)
return true;
return false;
}
NIXC_CATCH_ERRS_RES(false);
}
Value *nix_get_attr_byidx(nix_c_context *context, const Value *value,
State *state, unsigned int i, const char **name,
GCRef *ref) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
const nix::Attr &a = (*v.attrs)[i];
*name = ((const std::string &)(state->state.symbols[a.name])).c_str();
if (ref)
ref->ptr = a.value;
return a.value;
}
NIXC_CATCH_ERRS_NULL
}
nix_err nix_set_bool(nix_c_context *context, Value *value, bool b) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
v.mkBool(b);
}
NIXC_CATCH_ERRS
}
// todo string context
nix_err nix_set_string(nix_c_context *context, Value *value, const char *str) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
v.mkString(std::string_view(str));
}
NIXC_CATCH_ERRS
}
nix_err nix_set_path_string(nix_c_context *context, Value *value,
const char *str) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
v.mkPath(std::string_view(str));
}
NIXC_CATCH_ERRS
}
nix_err nix_set_double(nix_c_context *context, Value *value, double d) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
v.mkFloat(d);
}
NIXC_CATCH_ERRS
}
nix_err nix_set_int(nix_c_context *context, Value *value, int64_t i) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
v.mkInt(i);
}
NIXC_CATCH_ERRS
}
nix_err nix_set_null(nix_c_context *context, Value *value) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
v.mkNull();
}
NIXC_CATCH_ERRS
}
nix_err nix_set_external(nix_c_context *context, Value *value,
ExternalValue *val) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
auto r = (nix::ExternalValueBase *)val;
v.mkExternal(r);
}
NIXC_CATCH_ERRS
}
nix_err nix_make_list(nix_c_context *context, State *s, Value *value,
unsigned int size) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
s->state.mkList(v, size);
}
NIXC_CATCH_ERRS
}
nix_err nix_set_list_byidx(nix_c_context *context, Value *value,
unsigned int ix, Value *elem) {
if (context)
context->last_err_code = NIX_OK;
try {
// todo: assert that this is a list
auto &v = check_value_not_null(value);
auto &e = check_value_not_null(elem);
v.listElems()[ix] = &e;
}
NIXC_CATCH_ERRS
}
nix_err nix_set_primop(nix_c_context *context, Value *value, PrimOp *p) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
v.mkPrimOp((nix::PrimOp *)p);
}
NIXC_CATCH_ERRS
}
nix_err nix_copy_value(nix_c_context *context, Value *value, Value *source) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
auto &s = check_value_not_null(source);
v = s;
}
NIXC_CATCH_ERRS
}
nix_err nix_set_thunk(nix_c_context *context, State *s, Value *value,
Expr *expr) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
s->state.mkThunk_(v, (nix::Expr *)expr);
}
NIXC_CATCH_ERRS
}
typedef std::shared_ptr<nix::BindingsBuilder> BindingsBuilder_Inner;
nix_err nix_make_attrs(nix_c_context *context, Value *value,
BindingsBuilder *b) {
if (context)
context->last_err_code = NIX_OK;
try {
auto &v = check_value_not_null(value);
nix::BindingsBuilder &builder = **(BindingsBuilder_Inner *)b;
v.mkAttrs(builder);
}
NIXC_CATCH_ERRS
}
BindingsBuilder *nix_make_bindings_builder(nix_c_context *context, State *state,
size_t capacity) {
if (context)
context->last_err_code = NIX_OK;
try {
auto bb = state->state.buildBindings(capacity);
auto res = new BindingsBuilder_Inner();
*res = std::allocate_shared<nix::BindingsBuilder>(
traceable_allocator<nix::BindingsBuilder>(), bb);
return res;
}
NIXC_CATCH_ERRS_NULL
}
nix_err nix_bindings_builder_insert(nix_c_context *context, BindingsBuilder *b,
const char *name, Value *value) {
if (context)
context->last_err_code = NIX_OK;
try {
nix::BindingsBuilder &builder = **(BindingsBuilder_Inner *)b;
auto &v = check_value_not_null(value);
nix::Symbol s = builder.state.symbols.create(name);
builder.insert(s, &v);
}
NIXC_CATCH_ERRS
}
void nix_bindings_builder_unref(BindingsBuilder *bb) {
delete (BindingsBuilder_Inner *)bb;
}

355
src/libexpr/nix_api_value.h Normal file
View File

@ -0,0 +1,355 @@
#ifndef NIX_API_VALUE_H
#define NIX_API_VALUE_H
/** @file
* @brief libexpr C bindings dealing with values
*/
#include "nix_api_util.h"
#include "stdbool.h"
#include "stddef.h"
#include "stdint.h"
#ifdef __cplusplus
extern "C" {
#endif
// cffi start
// Type definitions
typedef enum {
NIX_TYPE_THUNK,
NIX_TYPE_INT,
NIX_TYPE_FLOAT,
NIX_TYPE_BOOL,
NIX_TYPE_STRING,
NIX_TYPE_PATH,
NIX_TYPE_NULL,
NIX_TYPE_ATTRS,
NIX_TYPE_LIST,
NIX_TYPE_FUNCTION,
NIX_TYPE_EXTERNAL
} ValueType;
// forward declarations
typedef void Value;
typedef void Expr;
typedef struct State State;
typedef struct GCRef GCRef;
// type defs
/** @brief Stores an under-construction set of bindings
* Reference-counted
* @see nix_make_bindings_builder, nix_bindings_builder_unref, nix_make_attrs
* @see nix_bindings_builder_insert
*/
typedef void BindingsBuilder;
/** @brief PrimOp function
*
* Owned by the GC
* @see nix_alloc_primop, nix_set_primop
*/
typedef struct PrimOp PrimOp;
/** @brief External Value
*
* Owned by the GC
* @see nix_api_external.h
*/
typedef struct ExternalValue ExternalValue;
/** @brief Function pointer for primops
* @param[in] state Evaluator state
* @param[in] pos position of function call
* @param[in] args list of arguments
* @param[out] v return value
* @see nix_alloc_primop, nix_set_primop
*/
typedef void (*PrimOpFun)(State *state, int pos, Value **args, Value *v);
/** @brief Allocate a primop
*
* Owned by the GC
* Pass a gcref to keep a reference.
*
* @param[out] context Optional, stores error information
* @param[in] fun callback
* @param[in] arity expected amount of function arguments
* @param[in] name function name
* @param[in] args array of argument names
* @param[in] doc optional, documentation for this primop
* @param[out] ref Optional, will store a reference to the returned value.
* @return primop, or null in case of errors
* @see nix_set_primop
*/
PrimOp *nix_alloc_primop(nix_c_context *context, PrimOpFun fun, int arity,
const char *name, const char **args, const char *doc,
GCRef *ref);
// Function prototypes
/** @brief Allocate a Nix value
*
* Owned by the GC
* Pass a gcref to keep a reference.
* @param[out] context Optional, stores error information
* @param[in] state nix evaluator state
* @param[out] ref Optional, will store a reference to the returned value.
* @return value, or null in case of errors
*
*/
Value *nix_alloc_value(nix_c_context *context, State *state, GCRef *ref);
/** @name Getters
*/
/**@{*/
/** @brief Get value type
* @param[out] context Optional, stores error information
* @param[in] value Nix value to inspect
* @return type of nix value
*/
ValueType nix_get_type(nix_c_context *context, const Value *value);
/** @brief Get type name of value
* @param[out] context Optional, stores error information
* @param[in] value Nix value to inspect
* @return type name, owned string
* @todo way to free the result
*/
const char *nix_get_typename(nix_c_context *context, const Value *value);
/** @brief Get boolean value
* @param[out] context Optional, stores error information
* @param[in] value Nix value to inspect
* @return true or false, error info via context
*/
bool nix_get_bool(nix_c_context *context, const Value *value);
/** @brief Get string
* @param[out] context Optional, stores error information
* @param[in] value Nix value to inspect
* @return string
* @return NULL in case of error.
*/
const char *nix_get_string(nix_c_context *context, const Value *value);
/** @brief Get path as string
* @param[out] context Optional, stores error information
* @param[in] value Nix value to inspect
* @return string
* @return NULL in case of error.
*/
const char *nix_get_path_string(nix_c_context *context, const Value *value);
/** @brief Get the length of a list
* @param[out] context Optional, stores error information
* @param[in] value Nix value to inspect
* @return length of list, error info via context
*/
unsigned int nix_get_list_size(nix_c_context *context, const Value *value);
/** @brief Get the element count of an attrset
* @param[out] context Optional, stores error information
* @param[in] value Nix value to inspect
* @return attrset element count, error info via context
*/
unsigned int nix_get_attrs_size(nix_c_context *context, const Value *value);
/** @brief Get float value in 64 bits
* @param[out] context Optional, stores error information
* @param[in] value Nix value to inspect
* @return float contents, error info via context
*/
double nix_get_double(nix_c_context *context, const Value *value);
/** @brief Get int value
* @param[out] context Optional, stores error information
* @param[in] value Nix value to inspect
* @return int contents, error info via context
*/
int64_t nix_get_int(nix_c_context *context, const Value *value);
/** @brief Get external reference
* @param[out] context Optional, stores error information
* @param[in] value Nix value to inspect
* @return reference to external, NULL in case of error
*/
ExternalValue *nix_get_external(nix_c_context *context, Value *);
/** @brief Get the ix'th element of a list
*
* Pass a gcref to keep a reference.
* @param[out] context Optional, stores error information
* @param[in] value Nix value to inspect
* @param[in] ix list element to get
* @param[out] ref Optional, will store a reference to the returned value.
* @return value, NULL in case of errors
*/
Value *nix_get_list_byidx(nix_c_context *context, const Value *value,
unsigned int ix, GCRef *ref);
/** @brief Get an attr by name
*
* Pass a gcref to keep a reference.
* @param[out] context Optional, stores error information
* @param[in] value Nix value to inspect
* @param[in] state nix evaluator state
* @param[in] name attribute name
* @param[out] ref Optional, will store a reference to the returned value.
* @return value, NULL in case of errors
*/
Value *nix_get_attr_byname(nix_c_context *context, const Value *value,
State *state, const char *name, GCRef *ref);
/** @brief Check if an attribute name exists on a value
* @param[out] context Optional, stores error information
* @param[in] value Nix value to inspect
* @param[in] state nix evaluator state
* @param[in] name attribute name
* @return value, NULL in case of errors
*/
bool nix_has_attr_byname(nix_c_context *context, const Value *value,
State *state, const char *name);
/** @brief Get an attribute by index in the sorted bindings
* @param[out] context Optional, stores error information
* @param[in] value Nix value to inspect
* @param[in] state nix evaluator state
* @param[in] i attribute index
* @param[out] name will store a pointer to the attribute name
* @return value, NULL in case of errors
*/
Value *nix_get_attr_byidx(nix_c_context *context, const Value *value,
State *state, unsigned int i, const char **name,
GCRef *ref);
/**@}*/
/** @name Setters
*/
/**@{*/
/** @brief Set boolean value
* @param[out] context Optional, stores error information
* @param[out] value Nix value to modify
* @param[in] b the boolean value
* @return error code, NIX_OK on success.
*/
nix_err nix_set_bool(nix_c_context *context, Value *value, bool b);
/** @brief Set a string
* @param[out] context Optional, stores error information
* @param[out] value Nix value to modify
* @param[in] str the string, copied
* @return error code, NIX_OK on success.
*/
nix_err nix_set_string(nix_c_context *context, Value *value, const char *str);
/** @brief Set a path
* @param[out] context Optional, stores error information
* @param[out] value Nix value to modify
* @param[in] str the path string, copied
* @return error code, NIX_OK on success.
*/
nix_err nix_set_path_string(nix_c_context *context, Value *value,
const char *str);
/** @brief Set a double
* @param[out] context Optional, stores error information
* @param[out] value Nix value to modify
* @param[in] d the double
* @return error code, NIX_OK on success.
*/
nix_err nix_set_double(nix_c_context *context, Value *value, double d);
/** @brief Set an int
* @param[out] context Optional, stores error information
* @param[out] value Nix value to modify
* @param[in] i the int
* @return error code, NIX_OK on success.
*/
nix_err nix_set_int(nix_c_context *context, Value *value, int64_t i);
/** @brief Set null
* @param[out] context Optional, stores error information
* @param[out] value Nix value to modify
* @return error code, NIX_OK on success.
*/
nix_err nix_set_null(nix_c_context *context, Value *value);
/** @brief Set an external value
* @param[out] context Optional, stores error information
* @param[out] value Nix value to modify
* @param[in] val the external value to set. Will be GC-referenced by the value.
* @return error code, NIX_OK on success.
*/
nix_err nix_set_external(nix_c_context *context, Value *value,
ExternalValue *val);
/** @brief Allocate a list
* @param[out] context Optional, stores error information
* @param[out] value Nix value to modify
* @param[in] size size of list
* @return error code, NIX_OK on success.
*/
nix_err nix_make_list(nix_c_context *context, State *s, Value *value,
unsigned int size);
/** @brief Manipulate a list by index
*
* Don't do this mid-computation.
* @pre your list should be at least 'ix+1' items long
* @param[out] context Optional, stores error information
* @param[out] value Nix value to modify
* @param[in] ix index to manipulate
* @param[in] elem the value to set, will be gc-referenced by the value
* @return error code, NIX_OK on success.
*/
nix_err nix_set_list_byidx(nix_c_context *context, Value *value,
unsigned int ix, Value *elem);
/** @brief Create an attribute set from a bindings builder
* @param[out] context Optional, stores error information
* @param[out] value Nix value to modify
* @param[in] b bindings builder to use. Make sure to unref this afterwards.
* @return error code, NIX_OK on success.
*/
nix_err nix_make_attrs(nix_c_context *context, Value *value,
BindingsBuilder *b);
/** @brief Set primop
* @param[out] context Optional, stores error information
* @param[out] value Nix value to modify
* @param[in] op primop, will be gc-referenced by the value
* @see nix_alloc_primop
* @return error code, NIX_OK on success.
*/
nix_err nix_set_primop(nix_c_context *context, Value *value, PrimOp *op);
/** @brief Copy from another value
* @param[out] context Optional, stores error information
* @param[out] value Nix value to modify
* @param[in] source value to copy from
* @return error code, NIX_OK on success.
*/
nix_err nix_copy_value(nix_c_context *context, Value *value, Value *source);
/** @brief Make a thunk from an expr.
*
* Expr will be evaluated when the value is forced
* @param[out] context Optional, stores error information
* @param[out] value Nix value to modify
* @param[in] expr the expr to thunk
* @return error code, NIX_OK on success.
*/
nix_err nix_set_thunk(nix_c_context *context, State *s, Value *value,
Expr *expr);
/**@}*/
/** @brief Create a bindings builder
* @param[out] context Optional, stores error information
* @param[in] state nix evaluator state
* @param[in] capacity how many bindings you'll add. Don't exceed.
* @return owned reference to a bindings builder. Make sure to unref when you're
done.
*/
BindingsBuilder *nix_make_bindings_builder(nix_c_context *context, State *state,
size_t capacity);
/** @brief Insert bindings into a builder
* @param[out] context Optional, stores error information
* @param[in] builder BindingsBuilder to insert into
* @param[in] name attribute name, copied into the symbol store
* @param[in] value value to give the binding
* @return error code, NIX_OK on success.
*/
nix_err nix_bindings_builder_insert(nix_c_context *context,
BindingsBuilder *builder, const char *name,
Value *value);
/** @brief Unref a bindings builder
*
* Does not fail.
* It'll be deallocated when all references are gone.
* @param[in] builder the builder to unref
*/
void nix_bindings_builder_unref(BindingsBuilder *builder);
// cffi end
#ifdef __cplusplus
}
#endif
#endif // NIX_API_VALUE_H

View File

@ -44,7 +44,7 @@ SearchPath::Elem SearchPath::Elem::parse(std::string_view rawElem)
}
SearchPath parseSearchPath(const Strings & rawElems)
SearchPath SearchPath::parse(const Strings & rawElems)
{
SearchPath res;
for (auto & rawElem : rawElems)

View File

@ -328,6 +328,7 @@ public:
}
void mkPath(const SourcePath & path);
void mkPath(std::string_view path);
inline void mkPath(InputAccessor * accessor, const char * path)
{