Template
feat(kdl): add typed KDL value model
This commit is contained in:
+5
-1
@@ -56,14 +56,18 @@ struct st_kdl_token_ref {
|
|||||||
/* A positional argument, in source order (spec mandates order). */
|
/* A positional argument, in source order (spec mandates order). */
|
||||||
struct st_kdl_arg {
|
struct st_kdl_arg {
|
||||||
struct st_kdl_token_ref value;
|
struct st_kdl_token_ref value;
|
||||||
|
struct st_kdl_token_ref *annotation; /* NULL when not annotated */
|
||||||
struct st_kdl_arg *next;
|
struct st_kdl_arg *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* A `key = value` property, in source order. The key is a string-ish
|
/* A `key = value` property, in source order. The key is a string-ish
|
||||||
* token (identifier or any string form); the value is any value token. */
|
* token (identifier or any string form); the value is any value token.
|
||||||
|
* `annotation` is the value's type annotation (ST_TOK_TYPE), e.g. the
|
||||||
|
* `u8` in `key=(u8)42`; NULL when the value is not annotated. */
|
||||||
struct st_kdl_prop {
|
struct st_kdl_prop {
|
||||||
struct st_kdl_token_ref key;
|
struct st_kdl_token_ref key;
|
||||||
struct st_kdl_token_ref value;
|
struct st_kdl_token_ref value;
|
||||||
|
struct st_kdl_token_ref *annotation; /* NULL when not annotated */
|
||||||
struct st_kdl_prop *next;
|
struct st_kdl_prop *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
+92
-15
@@ -218,7 +218,7 @@ ref_from_token(struct st_kdl_token_ref *ref, const struct st_token *t)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static struct st_kdl_arg *
|
static struct st_kdl_arg *
|
||||||
arg_new(const struct st_token *t)
|
arg_new(const struct st_token *t, const struct st_token *ann, bool has_ann)
|
||||||
{
|
{
|
||||||
struct st_kdl_arg *a = calloc(1, sizeof(*a));
|
struct st_kdl_arg *a = calloc(1, sizeof(*a));
|
||||||
|
|
||||||
@@ -226,11 +226,20 @@ arg_new(const struct st_token *t)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
ref_from_token(&a->value, t);
|
ref_from_token(&a->value, t);
|
||||||
|
if (has_ann) {
|
||||||
|
a->annotation = malloc(sizeof(*a->annotation));
|
||||||
|
if (a->annotation == NULL) {
|
||||||
|
free(a);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ref_from_token(a->annotation, ann);
|
||||||
|
}
|
||||||
return a;
|
return a;
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct st_kdl_prop *
|
static struct st_kdl_prop *
|
||||||
prop_new(const struct st_token *key, const struct st_token *value)
|
prop_new(const struct st_token *key, const struct st_token *value,
|
||||||
|
const struct st_token *ann, bool has_ann)
|
||||||
{
|
{
|
||||||
struct st_kdl_prop *pr = calloc(1, sizeof(*pr));
|
struct st_kdl_prop *pr = calloc(1, sizeof(*pr));
|
||||||
|
|
||||||
@@ -239,6 +248,14 @@ prop_new(const struct st_token *key, const struct st_token *value)
|
|||||||
}
|
}
|
||||||
ref_from_token(&pr->key, key);
|
ref_from_token(&pr->key, key);
|
||||||
ref_from_token(&pr->value, value);
|
ref_from_token(&pr->value, value);
|
||||||
|
if (has_ann) {
|
||||||
|
pr->annotation = malloc(sizeof(*pr->annotation));
|
||||||
|
if (pr->annotation == NULL) {
|
||||||
|
free(pr);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
ref_from_token(pr->annotation, ann);
|
||||||
|
}
|
||||||
return pr;
|
return pr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -278,11 +295,13 @@ node_free(struct st_kdl_node *n)
|
|||||||
free(n->type);
|
free(n->type);
|
||||||
for (a = n->args; a != NULL;) {
|
for (a = n->args; a != NULL;) {
|
||||||
struct st_kdl_arg *nx = a->next;
|
struct st_kdl_arg *nx = a->next;
|
||||||
|
free(a->annotation);
|
||||||
free(a);
|
free(a);
|
||||||
a = nx;
|
a = nx;
|
||||||
}
|
}
|
||||||
for (pr = n->props; pr != NULL;) {
|
for (pr = n->props; pr != NULL;) {
|
||||||
struct st_kdl_prop *px = pr->next;
|
struct st_kdl_prop *px = pr->next;
|
||||||
|
free(pr->annotation);
|
||||||
free(pr);
|
free(pr);
|
||||||
pr = px;
|
pr = px;
|
||||||
}
|
}
|
||||||
@@ -444,12 +463,45 @@ parse_node(struct st_parser *p, struct st_error **err)
|
|||||||
*err = err_at_owned(p->tok.span, "unexpected '='");
|
*err = err_at_owned(p->tok.span, "unexpected '='");
|
||||||
node_free(n);
|
node_free(n);
|
||||||
return NULL;
|
return NULL;
|
||||||
case ST_TOK_TYPE:
|
case ST_TOK_TYPE: {
|
||||||
*err = err_at_owned(p->tok.span,
|
/* Value annotation: capture it, then require an argument value.
|
||||||
"unexpected type annotation (value annotations are "
|
* (A type annotation in entry position prefixes a VALUE, so the
|
||||||
"not supported yet)");
|
* annotated token is always an argument, never a property key.) */
|
||||||
node_free(n);
|
struct st_token ann_tok = p->tok;
|
||||||
return NULL;
|
struct st_kdl_arg *a;
|
||||||
|
|
||||||
|
p_consume(p);
|
||||||
|
if (skip_comments(p, err) < 0) {
|
||||||
|
node_free(n);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
k = p_peek(p, err);
|
||||||
|
if (k == ST_TOK_ERROR) {
|
||||||
|
node_free(n);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!tok_is_value(k)) {
|
||||||
|
*err = err_at_owned(p->tok.span,
|
||||||
|
"expected a value after type annotation");
|
||||||
|
node_free(n);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
a = arg_new(&p->tok, &ann_tok, true);
|
||||||
|
if (a == NULL) {
|
||||||
|
*err = err_at_owned(p->tok.span, "out of memory");
|
||||||
|
node_free(n);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
last_line = p->tok.span.line;
|
||||||
|
p_consume(p);
|
||||||
|
if (args_tail == NULL) {
|
||||||
|
n->args = args_tail = a;
|
||||||
|
} else {
|
||||||
|
args_tail->next = a;
|
||||||
|
args_tail = a;
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
if (!tok_is_value(k)) {
|
if (!tok_is_value(k)) {
|
||||||
*err = err_at_owned(p->tok.span,
|
*err = err_at_owned(p->tok.span,
|
||||||
@@ -491,13 +543,38 @@ parse_node(struct st_parser *p, struct st_error **err)
|
|||||||
node_free(n);
|
node_free(n);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (!tok_is_value(k)) {
|
{
|
||||||
*err = err_at_owned(p->tok.span,
|
struct st_token vann_tok = { 0 };
|
||||||
"expected a value after '='");
|
bool has_vann = false;
|
||||||
node_free(n);
|
|
||||||
return NULL;
|
if (k == ST_TOK_TYPE) {
|
||||||
|
vann_tok = p->tok;
|
||||||
|
has_vann = true;
|
||||||
|
p_consume(p);
|
||||||
|
if (skip_comments(p, err) < 0) {
|
||||||
|
node_free(n);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
k = p_peek(p, err);
|
||||||
|
if (k == ST_TOK_ERROR) {
|
||||||
|
node_free(n);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!tok_is_value(k)) {
|
||||||
|
*err = err_at_owned(p->tok.span,
|
||||||
|
"expected a value after type annotation");
|
||||||
|
node_free(n);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!tok_is_value(k)) {
|
||||||
|
*err = err_at_owned(p->tok.span,
|
||||||
|
"expected a value after '='");
|
||||||
|
node_free(n);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
pr = prop_new(&val_tok, &p->tok, &vann_tok, has_vann);
|
||||||
}
|
}
|
||||||
pr = prop_new(&val_tok, &p->tok);
|
|
||||||
if (pr == NULL) {
|
if (pr == NULL) {
|
||||||
*err = err_at_owned(val_tok.span, "out of memory");
|
*err = err_at_owned(val_tok.span, "out of memory");
|
||||||
node_free(n);
|
node_free(n);
|
||||||
@@ -516,7 +593,7 @@ parse_node(struct st_parser *p, struct st_error **err)
|
|||||||
}
|
}
|
||||||
/* Argument. */
|
/* Argument. */
|
||||||
{
|
{
|
||||||
struct st_kdl_arg *a = arg_new(&val_tok);
|
struct st_kdl_arg *a = arg_new(&val_tok, NULL, false);
|
||||||
|
|
||||||
if (a == NULL) {
|
if (a == NULL) {
|
||||||
*err = err_at_owned(val_tok.span, "out of memory");
|
*err = err_at_owned(val_tok.span, "out of memory");
|
||||||
|
|||||||
+1053
File diff suppressed because it is too large
Load Diff
+126
@@ -0,0 +1,126 @@
|
|||||||
|
/*
|
||||||
|
* value.h - typed KDL value model for stupidtools (todo 8).
|
||||||
|
*
|
||||||
|
* The parser (todo 7) produces RAW token references (src/kdl/ast.h); this
|
||||||
|
* module INTERPRETS them into typed values. A KDL value is one of:
|
||||||
|
*
|
||||||
|
* - string (identifier, quoted, raw, or multi-line; escapes resolved)
|
||||||
|
* - integer (i64) and unsigned (u64) — numbers without a decimal point
|
||||||
|
* - float (f64) — numbers with a fraction/exponent, plus #inf/#-inf/#nan
|
||||||
|
* - bool (#true / #false)
|
||||||
|
* - null (#null)
|
||||||
|
*
|
||||||
|
* plus an optional type-annotation string (KDL §3.8), e.g. the `u8` in
|
||||||
|
* `(u8)42`, captured into `annotation` (an owned NUL-terminated copy, or
|
||||||
|
* NULL when the value was not annotated).
|
||||||
|
*
|
||||||
|
* SUBSET (documented deviation from KDL 2.0.0)
|
||||||
|
* --------------------------------------------
|
||||||
|
* KDL reserves the integer-width annotations i8/i16/i32/i64/isize and
|
||||||
|
* u8/u16/u32/u64/usize (plus i128/u128) and the float annotations f32/f64.
|
||||||
|
* This tool's value model supports at most 64-bit integers: an `i128` or
|
||||||
|
* `u128` annotation yields a clear "unsupported width" error
|
||||||
|
* (ST_ERR_KDL_PARSE) instead of a value. The `u*` annotations select the
|
||||||
|
* unsigned (u64) interpretation; `i*` the signed (i64) one; `f32`/`f64`
|
||||||
|
* the float interpretation. Any other annotation is captured verbatim and
|
||||||
|
* leaves the literal's natural interpretation unchanged (the DSL schema,
|
||||||
|
* todo 9, is responsible for rejecting application-invalid annotations).
|
||||||
|
*
|
||||||
|
* NUMBER INTERPRETATION (KDL §3.14)
|
||||||
|
* ---------------------------------
|
||||||
|
* Radix prefixes 0x/0o/0b (case-insensitive) and decimal; underscores
|
||||||
|
* allowed between/after digits; optional leading +/-. A literal with a
|
||||||
|
* decimal point or exponent is a float; otherwise an integer. Integers
|
||||||
|
* overflow-check to i64 (default) or u64 (u*-annotated) and produce a
|
||||||
|
* spanned error on overflow. #inf/#-inf/#nan map to INFINITY/-INFINITY/NAN
|
||||||
|
* (math.h macros; no libm linkage is required).
|
||||||
|
*
|
||||||
|
* STRING UNESCAPING (KDL §3.9/3.11/3.12)
|
||||||
|
* --------------------------------------
|
||||||
|
* Quoted and multi-line strings resolve the KDL escapes (\n \r \t \\ \" \b
|
||||||
|
* \f \s \u{...}) plus the lenient extras \' \a \v \xHH \U{...} (harmless
|
||||||
|
* superset; the lexer already rejects non-KDL escapes, so these only matter
|
||||||
|
* for directly-constructed token refs). A backslash followed by literal
|
||||||
|
* whitespace/newline is a whitespace escape and vanishes. Unknown escapes,
|
||||||
|
* a lone trailing backslash, and non-scalar-value unicode escapes are
|
||||||
|
* spanned errors. Multi-line strings additionally remove the leading
|
||||||
|
* newline and dedent by the closing line's whitespace (see value.c for the
|
||||||
|
* exact rule and its documented simplifications). `\0` is REJECTED: KDL
|
||||||
|
* forbids U+0000 and our value is a NUL-terminated C string, so a NUL byte
|
||||||
|
* cannot be represented.
|
||||||
|
*
|
||||||
|
* OWNERSHIP
|
||||||
|
* ---------
|
||||||
|
* st_kdl_value_from_token() writes into a caller-provided `struct
|
||||||
|
* st_kdl_value`; the string payload and the annotation are heap-allocated
|
||||||
|
* by this module and released by st_kdl_value_free() (NULL is a safe no-op;
|
||||||
|
* it also frees nothing for non-string kinds). The token ref's text slice
|
||||||
|
* remains borrowed.
|
||||||
|
*
|
||||||
|
* ERRORS
|
||||||
|
* ------
|
||||||
|
* All failures return an owned st_error of category ST_ERR_KDL_PARSE whose
|
||||||
|
* span is heap-allocated in the same block as the error (the parser.c
|
||||||
|
* pattern), so err->span stays valid until st_error_free(err). On error the
|
||||||
|
* output value is left zeroed (kind ST_KDL_VAL_NULL) and nothing leaks.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2026 huntedbytheirs
|
||||||
|
* SPDX-License-Identifier: BSD-3-Clause
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ST_KDL_VALUE_H
|
||||||
|
#define ST_KDL_VALUE_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#include "ast.h" /* struct st_kdl_token_ref */
|
||||||
|
|
||||||
|
struct st_error;
|
||||||
|
|
||||||
|
/* The tagged kinds a KDL value can take. */
|
||||||
|
enum st_kdl_value_kind {
|
||||||
|
ST_KDL_VAL_STRING = 0, /* as.str (owned, NUL-terminated) */
|
||||||
|
ST_KDL_VAL_INT, /* as.i (i64) */
|
||||||
|
ST_KDL_VAL_UINT, /* as.u (u64) */
|
||||||
|
ST_KDL_VAL_FLOAT, /* as.f (f64) */
|
||||||
|
ST_KDL_VAL_BOOL, /* as.b */
|
||||||
|
ST_KDL_VAL_NULL, /* no payload */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* A typed KDL value. `annotation` is an owned NUL-terminated copy of the
|
||||||
|
* type-annotation identifier, or NULL when the value was not annotated. */
|
||||||
|
struct st_kdl_value {
|
||||||
|
enum st_kdl_value_kind kind;
|
||||||
|
union {
|
||||||
|
char *str; /* STRING: owned, NUL-terminated */
|
||||||
|
long long i; /* INT */
|
||||||
|
unsigned long long u; /* UINT */
|
||||||
|
double f; /* FLOAT */
|
||||||
|
bool b; /* BOOL */
|
||||||
|
} as;
|
||||||
|
char *annotation; /* owned; may be NULL */
|
||||||
|
};
|
||||||
|
|
||||||
|
/* Interpret a raw value token ref into `out` (no type annotation). On
|
||||||
|
* success returns NULL and `out` is fully populated; on failure returns an
|
||||||
|
* owned error and `out` is left zeroed. */
|
||||||
|
struct st_error *st_kdl_value_from_token(const struct st_kdl_token_ref *tok,
|
||||||
|
struct st_kdl_value *out);
|
||||||
|
|
||||||
|
/* Same, but with an explicit type-annotation ref (ST_TOK_TYPE) captured
|
||||||
|
* into `out->annotation`. `annotation` may be NULL. */
|
||||||
|
struct st_error *st_kdl_value_from_token_annotated(
|
||||||
|
const struct st_kdl_token_ref *tok,
|
||||||
|
const struct st_kdl_token_ref *annotation,
|
||||||
|
struct st_kdl_value *out);
|
||||||
|
|
||||||
|
/* Release the payloads owned by `v` (string + annotation). NULL is a safe
|
||||||
|
* no-op. */
|
||||||
|
void st_kdl_value_free(struct st_kdl_value *v);
|
||||||
|
|
||||||
|
/* Stable display name for a value kind, e.g. "string". Unknown kinds yield
|
||||||
|
* "?" rather than indexing out of range. */
|
||||||
|
const char *st_kdl_value_kind_name(enum st_kdl_value_kind kind);
|
||||||
|
|
||||||
|
#endif /* ST_KDL_VALUE_H */
|
||||||
@@ -0,0 +1,632 @@
|
|||||||
|
/* LINK: ../../src/kdl/value.c ../../src/kdl/parser.c ../../src/kdl/lexer.c ../../src/error.c ../../src/span.c */
|
||||||
|
/* tests/unit/test_value.c
|
||||||
|
*
|
||||||
|
* Unit tests for the typed KDL value model (todo 8).
|
||||||
|
*
|
||||||
|
* The magic LINK comment on line 1 is REQUIRED by tests/run.sh: it lists the
|
||||||
|
* extra .c sources to compile into this test binary (paths relative to
|
||||||
|
* tests/unit/, space-separated). value.c depends on error.c (typed errors) and
|
||||||
|
* span.c (caret rendering); parser.c + lexer.c are linked so the value-level
|
||||||
|
* type-annotation integration can be tested END-TO-END through the parser.
|
||||||
|
*
|
||||||
|
* Most value-model cases are exercised by constructing a token ref DIRECTLY
|
||||||
|
* (mkref), which bypasses the lexer's already-strict escape/radix validation
|
||||||
|
* and lets us probe the value model's own boundary handling (unknown escapes,
|
||||||
|
* `0xZZ`, overflow, i128 width) that the lexer would otherwise reject first.
|
||||||
|
* Assertions check CONCRETE parsed values (kind + payload + annotation), never
|
||||||
|
* just "does not crash".
|
||||||
|
*/
|
||||||
|
#include "munit.h"
|
||||||
|
|
||||||
|
#include "error.h"
|
||||||
|
#include "kdl/ast.h"
|
||||||
|
#include "kdl/lexer.h"
|
||||||
|
#include "kdl/value.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
/* ---- token-ref construction ------------------------------------------- */
|
||||||
|
|
||||||
|
/* Build a borrowed token ref over a NUL-terminated C string (the string
|
||||||
|
* literal outlives the value call, so the borrow is safe). */
|
||||||
|
static struct st_kdl_token_ref
|
||||||
|
mkref(enum st_token_kind kind, const char *text)
|
||||||
|
{
|
||||||
|
struct st_kdl_token_ref r;
|
||||||
|
|
||||||
|
r.kind = kind;
|
||||||
|
r.span.file = "test.kdl";
|
||||||
|
r.span.line = 1;
|
||||||
|
r.span.col = 1;
|
||||||
|
r.text = text;
|
||||||
|
r.len = strlen(text);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse a value token directly (no annotation); assert success and return
|
||||||
|
* the value. munit's longjmp aborts the test on assertion failure. */
|
||||||
|
static struct st_kdl_value
|
||||||
|
ok_value(enum st_token_kind kind, const char *text)
|
||||||
|
{
|
||||||
|
struct st_kdl_token_ref r = mkref(kind, text);
|
||||||
|
struct st_kdl_value v;
|
||||||
|
struct st_error *err = st_kdl_value_from_token(&r, &v);
|
||||||
|
|
||||||
|
munit_assert_null(err);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse a value token with an explicit type-annotation ref; assert success. */
|
||||||
|
static struct st_kdl_value
|
||||||
|
ok_annotated(enum st_token_kind kind, const char *text, const char *ann)
|
||||||
|
{
|
||||||
|
struct st_kdl_token_ref r = mkref(kind, text);
|
||||||
|
struct st_kdl_token_ref a = mkref(ST_TOK_TYPE, ann);
|
||||||
|
struct st_kdl_value v;
|
||||||
|
struct st_error *err = st_kdl_value_from_token_annotated(&r, &a, &v);
|
||||||
|
|
||||||
|
munit_assert_null(err);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Assert that parsing `text` (kind) fails with category KDL_PARSE and a
|
||||||
|
* message containing `needle`. */
|
||||||
|
static void
|
||||||
|
expect_error(enum st_token_kind kind, const char *text, const char *needle)
|
||||||
|
{
|
||||||
|
struct st_kdl_token_ref r = mkref(kind, text);
|
||||||
|
struct st_kdl_value v;
|
||||||
|
struct st_error *err = st_kdl_value_from_token(&r, &v);
|
||||||
|
|
||||||
|
munit_assert_not_null(err);
|
||||||
|
munit_assert_int(st_error_category_of(err), ==, ST_ERR_KDL_PARSE);
|
||||||
|
munit_assert_not_null(err->span);
|
||||||
|
munit_assert_true(strstr(st_error_message(err), needle) != NULL);
|
||||||
|
st_error_free(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- number tests ------------------------------------------------------ */
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_radix_ints(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
struct st_kdl_value v;
|
||||||
|
|
||||||
|
v = ok_value(ST_TOK_NUMBER, "0xff");
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_INT);
|
||||||
|
munit_assert_llong(v.as.i, ==, 255);
|
||||||
|
|
||||||
|
v = ok_value(ST_TOK_NUMBER, "0o17");
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_INT);
|
||||||
|
munit_assert_llong(v.as.i, ==, 15);
|
||||||
|
|
||||||
|
v = ok_value(ST_TOK_NUMBER, "0b101");
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_INT);
|
||||||
|
munit_assert_llong(v.as.i, ==, 5);
|
||||||
|
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_decimal_and_sign(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
struct st_kdl_value v;
|
||||||
|
|
||||||
|
v = ok_value(ST_TOK_NUMBER, "42");
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_INT);
|
||||||
|
munit_assert_llong(v.as.i, ==, 42);
|
||||||
|
|
||||||
|
v = ok_value(ST_TOK_NUMBER, "-42");
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_INT);
|
||||||
|
munit_assert_llong(v.as.i, ==, -42);
|
||||||
|
|
||||||
|
v = ok_value(ST_TOK_NUMBER, "+7");
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_INT);
|
||||||
|
munit_assert_llong(v.as.i, ==, 7);
|
||||||
|
|
||||||
|
v = ok_value(ST_TOK_NUMBER, "1_000");
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_INT);
|
||||||
|
munit_assert_llong(v.as.i, ==, 1000);
|
||||||
|
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_floats(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
struct st_kdl_value v;
|
||||||
|
|
||||||
|
v = ok_value(ST_TOK_NUMBER, "1.5");
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_FLOAT);
|
||||||
|
munit_assert_double_equal(v.as.f, 1.5, 12);
|
||||||
|
|
||||||
|
/* trailing dot, no fraction digits (direct ref: the lexer splits this) */
|
||||||
|
v = ok_value(ST_TOK_NUMBER, "3.");
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_FLOAT);
|
||||||
|
munit_assert_double_equal(v.as.f, 3.0, 12);
|
||||||
|
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_exponents(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
struct st_kdl_value v;
|
||||||
|
|
||||||
|
v = ok_value(ST_TOK_NUMBER, "1e10");
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_FLOAT);
|
||||||
|
munit_assert_double_equal(v.as.f, 1e10, 12);
|
||||||
|
|
||||||
|
v = ok_value(ST_TOK_NUMBER, "2.5e-3");
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_FLOAT);
|
||||||
|
munit_assert_double_equal(v.as.f, 2.5e-3, 12);
|
||||||
|
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_keyword_numbers(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
struct st_kdl_value v;
|
||||||
|
|
||||||
|
v = ok_value(ST_TOK_INF, "#inf");
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_FLOAT);
|
||||||
|
munit_assert_true(isinf(v.as.f));
|
||||||
|
munit_assert_true(v.as.f > 0);
|
||||||
|
|
||||||
|
v = ok_value(ST_TOK_NEG_INF, "#-inf");
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_FLOAT);
|
||||||
|
munit_assert_true(isinf(v.as.f));
|
||||||
|
munit_assert_true(v.as.f < 0);
|
||||||
|
|
||||||
|
v = ok_value(ST_TOK_NAN, "#nan");
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_FLOAT);
|
||||||
|
munit_assert_true(isnan(v.as.f));
|
||||||
|
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_bool_null(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
struct st_kdl_value v;
|
||||||
|
|
||||||
|
v = ok_value(ST_TOK_TRUE, "#true");
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_BOOL);
|
||||||
|
munit_assert_true(v.as.b);
|
||||||
|
|
||||||
|
v = ok_value(ST_TOK_FALSE, "#false");
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_BOOL);
|
||||||
|
munit_assert_false(v.as.b);
|
||||||
|
|
||||||
|
v = ok_value(ST_TOK_NULL, "#null");
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_NULL);
|
||||||
|
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- string tests ------------------------------------------------------ */
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_ident_and_quoted(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
struct st_kdl_value v;
|
||||||
|
|
||||||
|
v = ok_value(ST_TOK_IDENT, "foo");
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_STRING);
|
||||||
|
munit_assert_string_equal(v.as.str, "foo");
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
|
||||||
|
v = ok_value(ST_TOK_STRING, "\"hello\"");
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_STRING);
|
||||||
|
munit_assert_string_equal(v.as.str, "hello");
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
|
||||||
|
/* empty quoted string */
|
||||||
|
v = ok_value(ST_TOK_STRING, "\"\"");
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_STRING);
|
||||||
|
munit_assert_string_equal(v.as.str, "");
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_escapes(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
struct st_kdl_value v;
|
||||||
|
|
||||||
|
/* "a\nb" -> a, newline, b */
|
||||||
|
v = ok_value(ST_TOK_STRING, "\"a\\nb\"");
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_STRING);
|
||||||
|
munit_assert_string_equal(v.as.str, "a\nb");
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
|
||||||
|
/* backslash + quote + tab + carriage return */
|
||||||
|
v = ok_value(ST_TOK_STRING, "\"\\\\\\\"\\t\\r\"");
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_STRING);
|
||||||
|
munit_assert_string_equal(v.as.str, "\\\"\t\r");
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
|
||||||
|
/* space escape \s */
|
||||||
|
v = ok_value(ST_TOK_STRING, "\"a\\sb\"");
|
||||||
|
munit_assert_string_equal(v.as.str, "a b");
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
|
||||||
|
/* backspace + form feed + vertical tab + bell + apostrophe (lenient) */
|
||||||
|
v = ok_value(ST_TOK_STRING, "\"\\b\\f\\v\\a\\'\"");
|
||||||
|
munit_assert_string_equal(v.as.str, "\b\f\v\a'");
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
|
||||||
|
/* hex byte \x41 -> 'A' */
|
||||||
|
v = ok_value(ST_TOK_STRING, "\"\\x41\"");
|
||||||
|
munit_assert_string_equal(v.as.str, "A");
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
|
||||||
|
/* unicode \u{1F600} -> U+1F600 (UTF-8 F0 9F 98 80) */
|
||||||
|
v = ok_value(ST_TOK_STRING, "\"\\u{1F600}\"");
|
||||||
|
munit_assert_string_equal(v.as.str, "\xF0\x9F\x98\x80");
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
|
||||||
|
/* \u{10FFFF} is the maximum Unicode scalar value (UTF-8 F4 8F BF BF) */
|
||||||
|
v = ok_value(ST_TOK_STRING, "\"\\u{10FFFF}\"");
|
||||||
|
munit_assert_string_equal(v.as.str, "\xF4\x8F\xBF\xBF");
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
|
||||||
|
/* whitespace escape: backslash + spaces + newline -> nothing */
|
||||||
|
v = ok_value(ST_TOK_STRING, "\"Hello \\ World\"");
|
||||||
|
munit_assert_string_equal(v.as.str, "Hello World");
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_raw_string(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
struct st_kdl_value v;
|
||||||
|
|
||||||
|
/* #"raw\n"# -> literal backslash-n (no escape processing) */
|
||||||
|
v = ok_value(ST_TOK_RAW_STRING, "#\"raw\\n\"#");
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_STRING);
|
||||||
|
munit_assert_string_equal(v.as.str, "raw\\n");
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_multiline_dedent(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
struct st_kdl_value v;
|
||||||
|
const char *ml = "\"\"\"\n foo\n bar\n \"\"\"";
|
||||||
|
|
||||||
|
v = ok_value(ST_TOK_MULTILINE_STRING, ml);
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_STRING);
|
||||||
|
munit_assert_string_equal(v.as.str, "foo\nbar");
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
|
||||||
|
/* no trailing newline issues: an empty multi-line string */
|
||||||
|
v = ok_value(ST_TOK_MULTILINE_STRING, "\"\"\"\n \"\"\"");
|
||||||
|
munit_assert_string_equal(v.as.str, "");
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_multiline_escaped_newline(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
struct st_kdl_value v;
|
||||||
|
/* interior \n escape stays a newline (not dedented away) */
|
||||||
|
const char *ml = "\"\"\"\n a\\nb\n \"\"\"";
|
||||||
|
|
||||||
|
v = ok_value(ST_TOK_MULTILINE_STRING, ml);
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_STRING);
|
||||||
|
munit_assert_string_equal(v.as.str, "a\nb");
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- error tests ------------------------------------------------------- */
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_unknown_escape(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
/* '\q' is not a KDL escape (direct ref; the lexer would reject it) */
|
||||||
|
expect_error(ST_TOK_STRING, "\"a\\qb\"", "unknown escape");
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_unterminated_escape(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
/* lone trailing backslash inside the quotes ("abc\" -> body "abc\") */
|
||||||
|
expect_error(ST_TOK_STRING, "\"abc\\\"", "unterminated");
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_bad_radix(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
expect_error(ST_TOK_NUMBER, "0xZZ", "invalid digit");
|
||||||
|
expect_error(ST_TOK_NUMBER, "0o8", "invalid digit");
|
||||||
|
expect_error(ST_TOK_NUMBER, "0b2", "invalid digit");
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_integer_overflow(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
/* 24 digits, far beyond u64 (and i64) */
|
||||||
|
expect_error(ST_TOK_NUMBER, "999999999999999999999999", "overflow");
|
||||||
|
|
||||||
|
/* 2^63 as an unannotated (signed) literal is one past i64 max */
|
||||||
|
expect_error(ST_TOK_NUMBER, "9223372036854775808", "overflow");
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- annotation tests -------------------------------------------------- */
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_unicode_out_of_range(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
/* above the scalar-value range */
|
||||||
|
expect_error(ST_TOK_STRING, "\"\\u{110000}\"", "Unicode scalar");
|
||||||
|
/* a UTF-16 surrogate is not a scalar value */
|
||||||
|
expect_error(ST_TOK_STRING, "\"\\u{D800}\"", "Unicode scalar");
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_annotation_u8(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
struct st_kdl_value v = ok_annotated(ST_TOK_NUMBER, "42", "u8");
|
||||||
|
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_UINT);
|
||||||
|
munit_assert_ullong(v.as.u, ==, 42);
|
||||||
|
munit_assert_not_null(v.annotation);
|
||||||
|
munit_assert_string_equal(v.annotation, "u8");
|
||||||
|
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_annotation_unsigned_rejects_negative(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
struct st_kdl_token_ref r = mkref(ST_TOK_NUMBER, "-1");
|
||||||
|
struct st_kdl_token_ref a = mkref(ST_TOK_TYPE, "u8");
|
||||||
|
struct st_kdl_value v;
|
||||||
|
struct st_error *err = st_kdl_value_from_token_annotated(&r, &a, &v);
|
||||||
|
|
||||||
|
munit_assert_not_null(err);
|
||||||
|
munit_assert_true(strstr(st_error_message(err), "unsigned") != NULL);
|
||||||
|
st_error_free(err);
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_annotation_unsupported_width(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
struct st_kdl_token_ref r = mkref(ST_TOK_NUMBER, "42");
|
||||||
|
struct st_kdl_token_ref a128 = mkref(ST_TOK_TYPE, "i128");
|
||||||
|
struct st_kdl_token_ref u128 = mkref(ST_TOK_TYPE, "u128");
|
||||||
|
struct st_kdl_value v;
|
||||||
|
struct st_error *err;
|
||||||
|
|
||||||
|
err = st_kdl_value_from_token_annotated(&r, &a128, &v);
|
||||||
|
munit_assert_not_null(err);
|
||||||
|
munit_assert_int(st_error_category_of(err), ==, ST_ERR_KDL_PARSE);
|
||||||
|
munit_assert_true(strstr(st_error_message(err), "unsupported") != NULL);
|
||||||
|
st_error_free(err);
|
||||||
|
|
||||||
|
err = st_kdl_value_from_token_annotated(&r, &u128, &v);
|
||||||
|
munit_assert_not_null(err);
|
||||||
|
munit_assert_true(strstr(st_error_message(err), "unsupported") != NULL);
|
||||||
|
st_error_free(err);
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- end-to-end annotation integration (through the parser) ------------ */
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_parser_annotation_arg(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
struct st_error *err = NULL;
|
||||||
|
struct st_kdl_document *doc = st_kdl_parse("node (u8)42", "t.kdl", &err);
|
||||||
|
struct st_kdl_node *n;
|
||||||
|
struct st_kdl_value v;
|
||||||
|
|
||||||
|
munit_assert_not_null(doc);
|
||||||
|
munit_assert_null(err);
|
||||||
|
|
||||||
|
n = doc->nodes;
|
||||||
|
munit_assert_not_null(n);
|
||||||
|
munit_assert_not_null(n->args);
|
||||||
|
munit_assert_int(n->args->value.kind, ==, ST_TOK_NUMBER);
|
||||||
|
munit_assert_not_null(n->args->annotation);
|
||||||
|
munit_assert_int(n->args->annotation->kind, ==, ST_TOK_TYPE);
|
||||||
|
munit_assert_memory_equal(2, n->args->annotation->text, "u8");
|
||||||
|
|
||||||
|
err = st_kdl_value_from_token_annotated(&n->args->value,
|
||||||
|
n->args->annotation, &v);
|
||||||
|
munit_assert_null(err);
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_UINT);
|
||||||
|
munit_assert_ullong(v.as.u, ==, 42);
|
||||||
|
munit_assert_string_equal(v.annotation, "u8");
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
|
||||||
|
st_kdl_document_free(doc);
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_parser_annotation_prop(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
struct st_error *err = NULL;
|
||||||
|
struct st_kdl_document *doc = st_kdl_parse("node key=(u8)42", "t.kdl", &err);
|
||||||
|
struct st_kdl_node *n;
|
||||||
|
struct st_kdl_value v;
|
||||||
|
|
||||||
|
munit_assert_not_null(doc);
|
||||||
|
munit_assert_null(err);
|
||||||
|
|
||||||
|
n = doc->nodes;
|
||||||
|
munit_assert_not_null(n);
|
||||||
|
munit_assert_not_null(n->props);
|
||||||
|
munit_assert_int(n->props->value.kind, ==, ST_TOK_NUMBER);
|
||||||
|
munit_assert_not_null(n->props->annotation);
|
||||||
|
munit_assert_int(n->props->annotation->kind, ==, ST_TOK_TYPE);
|
||||||
|
munit_assert_memory_equal(2, n->props->annotation->text, "u8");
|
||||||
|
|
||||||
|
err = st_kdl_value_from_token_annotated(&n->props->value,
|
||||||
|
n->props->annotation, &v);
|
||||||
|
munit_assert_null(err);
|
||||||
|
munit_assert_int(v.kind, ==, ST_KDL_VAL_UINT);
|
||||||
|
munit_assert_ullong(v.as.u, ==, 42);
|
||||||
|
st_kdl_value_free(&v);
|
||||||
|
|
||||||
|
st_kdl_document_free(doc);
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_free_null(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
st_kdl_value_free(NULL);
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitResult
|
||||||
|
test_kind_name(const MunitParameter params[], void *data)
|
||||||
|
{
|
||||||
|
(void)params;
|
||||||
|
(void)data;
|
||||||
|
|
||||||
|
munit_assert_string_equal(st_kdl_value_kind_name(ST_KDL_VAL_STRING),
|
||||||
|
"string");
|
||||||
|
munit_assert_string_equal(st_kdl_value_kind_name(ST_KDL_VAL_NULL), "null");
|
||||||
|
munit_assert_string_equal(st_kdl_value_kind_name((enum st_kdl_value_kind)99),
|
||||||
|
"?");
|
||||||
|
return MUNIT_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static MunitTest tests[] = {
|
||||||
|
{ "/value/radix-ints", test_radix_ints, NULL, NULL,
|
||||||
|
MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ "/value/decimal-and-sign", test_decimal_and_sign, NULL, NULL,
|
||||||
|
MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ "/value/floats", test_floats, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ "/value/exponents", test_exponents, NULL, NULL, MUNIT_TEST_OPTION_NONE,
|
||||||
|
NULL },
|
||||||
|
{ "/value/keyword-numbers", test_keyword_numbers, NULL, NULL,
|
||||||
|
MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ "/value/bool-null", test_bool_null, NULL, NULL, MUNIT_TEST_OPTION_NONE,
|
||||||
|
NULL },
|
||||||
|
{ "/value/ident-and-quoted", test_ident_and_quoted, NULL, NULL,
|
||||||
|
MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ "/value/escapes", test_escapes, NULL, NULL, MUNIT_TEST_OPTION_NONE,
|
||||||
|
NULL },
|
||||||
|
{ "/value/raw-string", test_raw_string, NULL, NULL, MUNIT_TEST_OPTION_NONE,
|
||||||
|
NULL },
|
||||||
|
{ "/value/multiline-dedent", test_multiline_dedent, NULL, NULL,
|
||||||
|
MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ "/value/multiline-escaped-newline", test_multiline_escaped_newline, NULL,
|
||||||
|
NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ "/value/unknown-escape", test_unknown_escape, NULL, NULL,
|
||||||
|
MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ "/value/unterminated-escape", test_unterminated_escape, NULL, NULL,
|
||||||
|
MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ "/value/unicode-out-of-range", test_unicode_out_of_range, NULL, NULL,
|
||||||
|
MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ "/value/bad-radix", test_bad_radix, NULL, NULL, MUNIT_TEST_OPTION_NONE,
|
||||||
|
NULL },
|
||||||
|
{ "/value/integer-overflow", test_integer_overflow, NULL, NULL,
|
||||||
|
MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ "/value/annotation-u8", test_annotation_u8, NULL, NULL,
|
||||||
|
MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ "/value/annotation-unsigned-negative", test_annotation_unsigned_rejects_negative,
|
||||||
|
NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ "/value/annotation-unsupported-width", test_annotation_unsupported_width,
|
||||||
|
NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ "/value/parser-annotation-arg", test_parser_annotation_arg, NULL, NULL,
|
||||||
|
MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ "/value/parser-annotation-prop", test_parser_annotation_prop, NULL, NULL,
|
||||||
|
MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
{ "/value/free-null", test_free_null, NULL, NULL, MUNIT_TEST_OPTION_NONE,
|
||||||
|
NULL },
|
||||||
|
{ "/value/kind-name", test_kind_name, NULL, NULL, MUNIT_TEST_OPTION_NONE,
|
||||||
|
NULL },
|
||||||
|
{ NULL, NULL, NULL, NULL, MUNIT_TEST_OPTION_NONE, NULL },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const MunitSuite suite = {
|
||||||
|
"/value", tests, NULL, 1, MUNIT_SUITE_OPTION_NONE,
|
||||||
|
};
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char *argv[MUNIT_ARRAY_PARAM(argc + 1)])
|
||||||
|
{
|
||||||
|
return munit_suite_main(&suite, NULL, argc, argv);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user