NickelEval.jl

Julia FFI bindings for Nickel configuration language
Log | Files | Refs | README | LICENSE

nickel_lang.h (20408B)


      1 // SPDX-License-Identifier: MIT
      2 
      3 #ifndef NICKEL_LANG_H
      4 #define NICKEL_LANG_H
      5 
      6 #include <stdint.h>
      7 
      8 /**
      9  * For functions that can fail, these are the interpretations of the return value.
     10  */
     11 typedef enum {
     12     /**
     13      * A successful result.
     14      */
     15     NICKEL_RESULT_OK = 0,
     16     /**
     17      * A bad result.
     18      */
     19     NICKEL_RESULT_ERR = 1,
     20 } nickel_result;
     21 
     22 /**
     23  * For functions that can fail, these are the interpretations of the return value.
     24  */
     25 typedef enum {
     26     /**
     27      * Format an error as human-readable text.
     28      */
     29     NICKEL_ERROR_FORMAT_TEXT = 0,
     30     /**
     31      * Format an error as human-readable text, with ANSI color codes.
     32      */
     33     NICKEL_ERROR_FORMAT_ANSI_TEXT = 1,
     34     /**
     35      * Format an error as JSON.
     36      */
     37     NICKEL_ERROR_FORMAT_JSON = 2,
     38     /**
     39      * Format an error as YAML.
     40      */
     41     NICKEL_ERROR_FORMAT_YAML = 3,
     42     /**
     43      * Format an error as TOML.
     44      */
     45     NICKEL_ERROR_FORMAT_TOML = 4,
     46 } nickel_error_format;
     47 
     48 /**
     49  * A Nickel array.
     50  *
     51  * See [`nickel_expr_is_array`] and [`nickel_expr_as_array`].
     52  */
     53 typedef struct nickel_array nickel_array;
     54 
     55 /**
     56  * The main entry point.
     57  */
     58 typedef struct nickel_context nickel_context;
     59 
     60 /**
     61  * A Nickel error.
     62  *
     63  * If you want to collect an error message from a fallible function
     64  * (like `nickel_context_eval_deep`), first allocate an error using
     65  * `nickel_error_alloc`, and then pass the resulting pointer to your fallible
     66  * function. If that function fails, it will save the error data in your
     67  * `nickel_error`.
     68  */
     69 typedef struct nickel_error nickel_error;
     70 
     71 /**
     72  * A Nickel expression.
     73  *
     74  * This might be fully evaluated (for example, if you got it from [`nickel_context_eval_deep`])
     75  * or might have unevaluated sub-expressions (if you got it from [`nickel_context_eval_shallow`]).
     76  */
     77 typedef struct nickel_expr nickel_expr;
     78 
     79 /**
     80  * A Nickel number.
     81  *
     82  * See [`nickel_expr_is_number`] and [`nickel_expr_as_number`].
     83  */
     84 typedef struct nickel_number nickel_number;
     85 
     86 /**
     87  * A Nickel record.
     88  *
     89  * See [`nickel_expr_is_record`] and [`nickel_expr_as_record`].
     90  */
     91 typedef struct nickel_record nickel_record;
     92 
     93 /**
     94  * A Nickel string.
     95  */
     96 typedef struct nickel_string nickel_string;
     97 
     98 /**
     99  * A callback function for writing data.
    100  *
    101  * This function will be called with a buffer (`buf`) of data, having length
    102  * `len`. It need not consume the entire buffer, and should return the number
    103  * of bytes consumed.
    104  */
    105 typedef uintptr_t (*nickel_write_callback)(void *context, const uint8_t *buf, uintptr_t len);
    106 
    107 /**
    108  * A callback function for flushing data that was written by a write callback.
    109  */
    110 typedef void (*nickel_flush_callback)(const void *context);
    111 
    112 #ifdef __cplusplus
    113 extern "C" {
    114 #endif // __cplusplus
    115 
    116 /**
    117  * Allocate a new [`nickel_context`], which can be used to evaluate Nickel expressions.
    118  *
    119  * Returns a newly-allocated [`nickel_context`] that can be freed with [`nickel_context_free`].
    120  */
    121 nickel_context *nickel_context_alloc(void);
    122 
    123 /**
    124  * Free a [`nickel_context`] that was created with [`nickel_context_alloc`].
    125  */
    126 void nickel_context_free(nickel_context *ctx);
    127 
    128 /**
    129  * Provide a callback that will be called when evaluating Nickel
    130  * code that uses `std.trace`.
    131  */
    132 void nickel_context_set_trace_callback(nickel_context *ctx,
    133                                        nickel_write_callback write,
    134                                        nickel_flush_callback flush,
    135                                        void *user_data);
    136 
    137 /**
    138  * Provide a name for the main input program.
    139  *
    140  * This is used to format error messages. If you read the main input
    141  * program from a file, its path is a good choice.
    142  *
    143  * `name` should be a UTF-8-encoded, null-terminated string. It is only
    144  * borrowed temporarily; the pointer need not remain valid.
    145  */
    146 void nickel_context_set_source_name(nickel_context *ctx, const char *name);
    147 
    148 /**
    149  * Evaluate a Nickel program deeply.
    150  *
    151  * "Deeply" means that we recursively evaluate records and arrays. For
    152  * an alternative, see [`nickel_context_eval_shallow`].
    153  *
    154  * - `src` is a null-terminated string containing UTF-8-encoded Nickel source.
    155  * - `out_expr` either NULL or something that was created with [`nickel_expr_alloc`]
    156  * - `out_error` can be NULL if you aren't interested in getting detailed
    157  *   error messages
    158  *
    159  * If evaluation is successful, returns `NICKEL_RESULT_OK` and replaces
    160  * the value at `out_expr` (if non-NULL) with the newly-evaluated Nickel expression.
    161  *
    162  * If evaluation fails, returns `NICKEL_RESULT_ERR` and replaces the
    163  * value at `out_error` (if non-NULL) by a pointer to a newly-allocated Nickel error.
    164  * That error should be freed with `nickel_error_free` when you are
    165  * done with it.
    166  */
    167 nickel_result nickel_context_eval_deep(nickel_context *ctx,
    168                                        const char *src,
    169                                        nickel_expr *out_expr,
    170                                        nickel_error *out_error);
    171 
    172 /**
    173  * Evaluate a Nickel program deeply.
    174  *
    175  * This differs from [`nickel_context_eval_deep`] in that it ignores
    176  * fields marked as `not_exported`.
    177  *
    178  * - `src` is a null-terminated string containing UTF-8-encoded Nickel source.
    179  * - `out_expr` either NULL or something that was created with [`nickel_expr_alloc`]
    180  * - `out_error` can be NULL if you aren't interested in getting detailed
    181  *   error messages
    182  *
    183  * If evaluation is successful, returns `NICKEL_RESULT_OK` and replaces
    184  * the value at `out_expr` (if non-NULL) with the newly-evaluated Nickel expression.
    185  *
    186  * If evaluation fails, returns `NICKEL_RESULT_ERR` and replaces the
    187  * value at `out_error` (if non-NULL) by a pointer to a newly-allocated Nickel error.
    188  * That error should be freed with `nickel_error_free` when you are
    189  * done with it.
    190  */
    191 nickel_result nickel_context_eval_deep_for_export(nickel_context *ctx,
    192                                                   const char *src,
    193                                                   nickel_expr *out_expr,
    194                                                   nickel_error *out_error);
    195 
    196 /**
    197  * Evaluate a Nickel program to weak head normal form (WHNF).
    198  *
    199  * The result of this evaluation is a null, bool, number, string,
    200  * enum, record, or array. In case it's a record, array, or enum
    201  * variant, the payload (record values, array elements, or enum
    202  * payloads) will be left unevaluated.
    203  *
    204  * Sub-expressions of the result can be evaluated further by [nickel_context_eval_expr_shallow].
    205  *
    206  * - `src` is a null-terminated string containing UTF-8-encoded Nickel source.
    207  * - `out_expr` is either NULL or something that was created with [`nickel_expr_alloc`]
    208  * - `out_error` can be NULL if you aren't interested in getting detailed
    209  *   error messages
    210  *
    211  * If evaluation is successful, returns `NICKEL_RESULT_OK` and replaces the value at `out_expr`
    212  * (if non-NULL) with the newly-evaluated Nickel expression.
    213  *
    214  * If evaluation fails, returns `NICKEL_RESULT_ERR` and replaces the value at `out_error` (if
    215  * non-NULL) by a pointer to a newly-allocated Nickel error. That error should be freed with
    216  * `nickel_error_free` when you are done with it.
    217  */
    218 nickel_result nickel_context_eval_shallow(nickel_context *ctx,
    219                                           const char *src,
    220                                           nickel_expr *out_expr,
    221                                           nickel_error *out_error);
    222 
    223 /**
    224  * Allocate a new Nickel expression.
    225  *
    226  * The returned expression pointer can be used to store the results of
    227  * evaluation, for example by passing it as the `out_expr` location of
    228  * `nickel_context_eval_deep`.
    229  *
    230  * Each call to `nickel_expr_alloc` should be paired with a call to
    231  * `nickel_expr_free`. The various functions (like `nickel_context_eval_deep`)
    232  * that take an `out_expr` parameter overwrite the existing expression
    233  * contents, and do not affect the pairing of `nickel_expr_alloc` and
    234  * `nickel_expr_free`.
    235  *
    236  * For example:
    237  *
    238  * ```c
    239  * nickel_context *ctx = nickel_context_alloc();
    240  * nickel_context *expr = nickel_expr_alloc();
    241  *
    242  * nickel_context_eval_deep(ctx, "{ foo = 1 }", expr, NULL);
    243  *
    244  * // now expr is a record
    245  * printf("record: %d\n", nickel_expr_is_record(expr));
    246  *
    247  * nickel_context_eval_deep(ctx, "[1, 2, 3]", expr, NULL);
    248  *
    249  * // now expr is an array
    250  * printf("array: %d\n", nickel_expr_is_array(expr));
    251  *
    252  * // the calls to nickel_context_eval_deep haven't created any new exprs:
    253  * // we only need to free it once
    254  * nickel_expr_free(expr);
    255  * nickel_context_free(ctx);
    256  * ```
    257  *
    258  * An `Expr` owns its data. There are various ways to get a reference to
    259  * data owned by an expression, which are then invalidated when the expression
    260  * is freed (by `nickel_expr_free`) or overwritten (for example, by
    261  * `nickel_context_deep_eval`).
    262  *
    263  * ```c
    264  * nickel_context *ctx = nickel_context_alloc();
    265  * nickel_expr *expr = nickel_expr_alloc();
    266  *
    267  * nickel_context_eval_deep(ctx, "{ foo = 1 }", expr, NULL);
    268  *
    269  * nickel_record *rec = nickel_expr_as_record(expr);
    270  * nickel_expr *field = nickel_expr_alloc();
    271  * nickel_record_value_by_name(rec, "foo", field);
    272  *
    273  * // Now `rec` points to data owned by `expr`, but `field`
    274  * // owns its own data. The following deallocation invalidates
    275  * // `rec`, but not `field`.
    276  * nickel_expr_free(expr);
    277  * printf("number: %d\n", nickel_expr_is_number(field));
    278  * ```
    279  */
    280 nickel_expr *nickel_expr_alloc(void);
    281 
    282 /**
    283  * Free a Nickel expression.
    284  *
    285  * See [`nickel_expr_alloc`].
    286  */
    287 void nickel_expr_free(nickel_expr *expr);
    288 
    289 /**
    290  * Is this expression a boolean?
    291  */
    292 int nickel_expr_is_bool(const nickel_expr *expr);
    293 
    294 /**
    295  * Is this expression a number?
    296  */
    297 int nickel_expr_is_number(const nickel_expr *expr);
    298 
    299 /**
    300  * Is this expression a string?
    301  */
    302 int nickel_expr_is_str(const nickel_expr *expr);
    303 
    304 /**
    305  * Is this expression an enum tag?
    306  */
    307 int nickel_expr_is_enum_tag(const nickel_expr *expr);
    308 
    309 /**
    310  * Is this expression an enum variant?
    311  */
    312 int nickel_expr_is_enum_variant(const nickel_expr *expr);
    313 
    314 /**
    315  * Is this expression a record?
    316  */
    317 int nickel_expr_is_record(const nickel_expr *expr);
    318 
    319 /**
    320  * Is this expression an array?
    321  */
    322 int nickel_expr_is_array(const nickel_expr *expr);
    323 
    324 /**
    325  * Has this expression been evaluated?
    326  *
    327  * An evaluated expression is either null, or it's a number, bool, string, record, array, or enum.
    328  * If this expression is not a value, you probably got it from looking inside the result of
    329  * [`nickel_context_eval_shallow`], and you can use the [`nickel_context_eval_expr_shallow`] to
    330  * evaluate this expression further.
    331  */
    332 int nickel_expr_is_value(const nickel_expr *expr);
    333 
    334 /**
    335  * Is this expression null?
    336  */
    337 int nickel_expr_is_null(const nickel_expr *expr);
    338 
    339 /**
    340  * If this expression is a boolean, returns that boolean.
    341  *
    342  * # Panics
    343  *
    344  * Panics if `expr` is not a boolean.
    345  */
    346 int nickel_expr_as_bool(const nickel_expr *expr);
    347 
    348 /**
    349  * If this expression is a string, returns that string.
    350  *
    351  * A pointer to the string contents, which are UTF-8 encoded, is returned in
    352  * `out_str`. These contents are *not* null-terminated. The return value of this
    353  * function is the length of these contents.
    354  *
    355  * The returned string contents are owned by this `Expr`, and will be invalidated
    356  * when the `Expr` is freed with [`nickel_expr_free`].
    357  *
    358  * # Panics
    359  *
    360  * Panics if `expr` is not a string.
    361  */
    362 uintptr_t nickel_expr_as_str(const nickel_expr *expr, const char **out_str);
    363 
    364 /**
    365  * If this expression is a number, returns the number.
    366  *
    367  * The returned number pointer borrows from `expr`, and will be invalidated
    368  * when `expr` is overwritten or freed.
    369  *
    370  * # Panics
    371  *
    372  * Panics if `expr` is not an number.
    373  */
    374 const nickel_number *nickel_expr_as_number(const nickel_expr *expr);
    375 
    376 /**
    377  * If this expression is an enum tag, returns its string value.
    378  *
    379  * A pointer to the string contents, which are UTF-8 encoded, is returned in
    380  * `out_str`. These contents are *not* null-terminated. The return value of this
    381  * function is the length of these contents.
    382  *
    383  * The returned string contents point to an interned string and will never be
    384  * invalidated.
    385  *
    386  * # Panics
    387  *
    388  * Panics if `expr` is null or is not an enum tag.
    389  */
    390 uintptr_t nickel_expr_as_enum_tag(const nickel_expr *expr, const char **out_str);
    391 
    392 /**
    393  * If this expression is an enum variant, returns its string value and its payload.
    394  *
    395  * A pointer to the string contents, which are UTF-8 encoded, is returned in
    396  * `out_str`. These contents are *not* null-terminated. The return value of this
    397  * function is the length of these contents.
    398  *
    399  * The returned string contents point to an interned string and will never be
    400  * invalidated.
    401  *
    402  * # Panics
    403  *
    404  * Panics if `expr` is not an enum tag.
    405  */
    406 uintptr_t nickel_expr_as_enum_variant(const nickel_expr *expr,
    407                                       const char **out_str,
    408                                       nickel_expr *out_expr);
    409 
    410 /**
    411  * If this expression is a record, returns the record.
    412  *
    413  * The returned record pointer borrows from `expr`, and will be invalidated
    414  * when `expr` is overwritten or freed.
    415  *
    416  * # Panics
    417  *
    418  * Panics if `expr` is not an record.
    419  */
    420 const nickel_record *nickel_expr_as_record(const nickel_expr *expr);
    421 
    422 /**
    423  * If this expression is an array, returns the array.
    424  *
    425  * The returned array pointer borrows from `expr`, and will be invalidated
    426  * when `expr` is overwritten or freed.
    427  *
    428  * # Panics
    429  *
    430  * Panics if `expr` is not an array.
    431  */
    432 const nickel_array *nickel_expr_as_array(const nickel_expr *expr);
    433 
    434 /**
    435  * Converts an expression to JSON.
    436  *
    437  * This is fallible because enum variants have no canonical conversion to
    438  * JSON: if the expression contains any enum variants, this will fail.
    439  * This also fails if the expression contains any unevaluated sub-expressions.
    440  */
    441 nickel_result nickel_context_expr_to_json(nickel_context *ctx,
    442                                           const nickel_expr *expr,
    443                                           nickel_string *out_string,
    444                                           nickel_error *out_err);
    445 
    446 /**
    447  * Converts an expression to YAML.
    448  *
    449  * This is fallible because enum variants have no canonical conversion to
    450  * YAML: if the expression contains any enum variants, this will fail.
    451  * This also fails if the expression contains any unevaluated sub-expressions.
    452  */
    453 nickel_result nickel_context_expr_to_yaml(nickel_context *ctx,
    454                                           const nickel_expr *expr,
    455                                           nickel_string *out_string,
    456                                           nickel_error *out_err);
    457 
    458 /**
    459  * Converts an expression to TOML.
    460  *
    461  * This is fallible because enum variants have no canonical conversion to
    462  * TOML: if the expression contains any enum variants, this will fail.
    463  * This also fails if the expression contains any unevaluated sub-expressions.
    464  */
    465 nickel_result nickel_context_expr_to_toml(nickel_context *ctx,
    466                                           const nickel_expr *expr,
    467                                           nickel_string *out_string,
    468                                           nickel_error *out_err);
    469 
    470 /**
    471  * Is this number an integer within the range of an `int64_t`?
    472  */
    473 int nickel_number_is_i64(const nickel_number *num);
    474 
    475 /**
    476  * If this number is an integer within the range of an `int64_t`, returns it.
    477  *
    478  * # Panics
    479  *
    480  * Panics if this number is not an integer in the appropriate range (you should
    481  * check with [`nickel_number_is_i64`] first).
    482  */
    483 int64_t nickel_number_as_i64(const nickel_number *num);
    484 
    485 /**
    486  * The value of this number, rounded to the nearest `double`.
    487  */
    488 double nickel_number_as_f64(const nickel_number *num);
    489 
    490 /**
    491  * The value of this number, as an exact rational number.
    492  *
    493  * - `out_numerator` must have been allocated with [`nickel_string_alloc`]. It
    494  *   will be overwritten with the numerator, as a decimal string.
    495  * - `out_denominator` must have been allocated with [`nickel_string_alloc`].
    496  *   It will be overwritten with the denominator, as a decimal string.
    497  */
    498 void nickel_number_as_rational(const nickel_number *num,
    499                                nickel_string *out_numerator,
    500                                nickel_string *out_denominator);
    501 
    502 /**
    503  * The number of elements of this Nickel array.
    504  */
    505 uintptr_t nickel_array_len(const nickel_array *arr);
    506 
    507 /**
    508  * Retrieve the element at the given array index.
    509  *
    510  * The retrieved element will be written to `out_expr`, which must have been allocated with
    511  * [`nickel_expr_alloc`].
    512  *
    513  * # Panics
    514  *
    515  * Panics if the given index is out of bounds.
    516  */
    517 void nickel_array_get(const nickel_array *arr, uintptr_t idx, nickel_expr *out_expr);
    518 
    519 /**
    520  * The number of keys in this Nickel record.
    521  */
    522 uintptr_t nickel_record_len(const nickel_record *rec);
    523 
    524 /**
    525  * Retrieve the key and value at the given index.
    526  *
    527  * If this record was deeply evaluated, every key will come with a value.
    528  * However, shallowly evaluated records may have fields with no value.
    529  *
    530  * Returns 1 if the key came with a value, and 0 if it didn't. The value
    531  * will be written to `out_expr` if it is non-NULL.
    532  *
    533  * # Panics
    534  *
    535  * Panics if `idx` is out of range.
    536  */
    537 int nickel_record_key_value_by_index(const nickel_record *rec,
    538                                      uintptr_t idx,
    539                                      const char **out_key,
    540                                      uintptr_t *out_key_len,
    541                                      nickel_expr *out_expr);
    542 
    543 /**
    544  * Look up a key in this record and return its value, if there is one.
    545  *
    546  * Returns 1 if the key has a value, and 0 if it didn't. The value is
    547  * written to `out_expr` if it is non-NULL.
    548  */
    549 int nickel_record_value_by_name(const nickel_record *rec, const char *key, nickel_expr *out_expr);
    550 
    551 /**
    552  * Allocates a new string.
    553  *
    554  * The lifecycle management of a string is much like that of an expression
    555  * (see `nickel_expr_alloc`). It gets allocated here, modified by various other
    556  * functions, and finally is freed by a call to `nickel_string_free`.
    557  */
    558 nickel_string *nickel_string_alloc(void);
    559 
    560 /**
    561  * Frees a string.
    562  */
    563 void nickel_string_free(nickel_string *s);
    564 
    565 /**
    566  * Retrieve the data inside a string.
    567  *
    568  * A pointer to the string contents, which are UTF-8 encoded, is written to
    569  * `data`. These contents are *not* null-terminated, but their length (in bytes)
    570  * is written to `len`. The string contents will be invalidated when `s` is
    571  * freed or overwritten.
    572  */
    573 void nickel_string_data(const nickel_string *s, const char **data, uintptr_t *len);
    574 
    575 /**
    576  * Evaluate an expression to weak head normal form (WHNF).
    577  *
    578  * This has no effect if the expression is already evaluated (see
    579  * [`nickel_expr_is_value`]).
    580  *
    581  * The result of this evaluation is a null, bool, number, string,
    582  * enum, record, or array. In case it's a record, array, or enum
    583  * variant, the payload (record values, array elements, or enum
    584  * payloads) will be left unevaluated.
    585  */
    586 nickel_result nickel_context_eval_expr_shallow(nickel_context *ctx,
    587                                                const nickel_expr *expr,
    588                                                nickel_expr *out_expr,
    589                                                nickel_error *out_error);
    590 
    591 /**
    592  * Allocate a new `nickel_error`.
    593  */
    594 nickel_error *nickel_error_alloc(void);
    595 
    596 /**
    597  * Frees a `nickel_error`.
    598  */
    599 void nickel_error_free(nickel_error *err);
    600 
    601 /**
    602  * Write out an error as a user- or machine-readable diagnostic.
    603  *
    604  * - `err` must have been allocated by `nickel_error_alloc` and initialized by some failing
    605  *   function (like `nickel_context_eval_deep`).
    606  * - `write` is a callback function that will be invoked with UTF-8 encoded data.
    607  * - `write_payload` is optional extra data to pass to `write`
    608  * - `format` selects the error-rendering format.
    609  */
    610 nickel_result nickel_error_display(const nickel_error *err,
    611                                    nickel_write_callback write,
    612                                    void *write_payload,
    613                                    nickel_error_format format);
    614 
    615 /**
    616  * Write out an error as a user- or machine-readable diagnostic.
    617  *
    618  * This is like `nickel_error_format`, but writes the error to a string instead
    619  * of via a callback function.
    620  */
    621 nickel_result nickel_error_format_as_string(const nickel_error *err,
    622                                             nickel_string *out_string,
    623                                             nickel_error_format format);
    624 
    625 #ifdef __cplusplus
    626 }  // extern "C"
    627 #endif  // __cplusplus
    628 
    629 #endif  /* NICKEL_LANG_H */