~vyivel/libnyth

e11b855f0b535d6e82a8a649b07e8676bc9fe517 — Kirill Primak 5 months ago 0542611
Update/unify the semantics of scalars

This ensures the meaning of scalars is 1) consistent and 2) doesn't
depend on their types.
M include/nyth.h => include/nyth.h +3 -16
@@ 32,14 32,6 @@ enum nyth_error {
};

/**
 * A not null-terminated UTF-8 string.
 */
struct nyth_string {
	const char *data;
	size_t length; // In octets
};

/**
 * Scalar types.
 */
enum nyth_scalar_type {


@@ 60,8 52,8 @@ enum nyth_scalar_type_mask {
};

struct nyth_scalar {
	struct nyth_string content;
	enum nyth_scalar_type type;
	const char *data; // UTF-8, not null-terminated
	size_t length; // In octets
};

/**


@@ 194,7 186,6 @@ enum nyth_event_type {
	NYTH_EVENT_MAP_END,
	NYTH_EVENT_LIST_START,
	NYTH_EVENT_LIST_END,
	NYTH_EVENT_KEY,
	NYTH_EVENT_SCALAR,
	NYTH_EVENT_ERROR,
};


@@ 204,7 195,7 @@ enum nyth_event_type {
 * The sequence of events shall match the following set of rules:
 *
 * document → map
 * map → MAP-START (KEY object)* MAP-END
 * map → MAP-START (SCALAR object)* MAP-END
 * list → LIST-START object* LIST-END
 * object → SCALAR | map | list
 */


@@ 212,7 203,6 @@ struct nyth_event {
	enum nyth_event_type type;
	struct nyth_position pos; // 0, 0 for NYTH_EVENT_NONE
	union {
		struct nyth_string key; // Only valid for NYTH_EVENT_KEY
		struct nyth_scalar scalar; // Only valid for NYTH_EVENT_SCALAR
		enum nyth_error error; // Only valid for NYTH_EVENT_ERROR
	};


@@ 277,9 267,6 @@ enum nyth_error nyth_emit_map_end(struct nyth_emitter *emitter);
enum nyth_error nyth_emit_list_start(struct nyth_emitter *emitter);
enum nyth_error nyth_emit_list_end(struct nyth_emitter *emitter);

enum nyth_error nyth_emit_key(struct nyth_emitter *emitter,
	const char *key, size_t key_length);

enum nyth_error nyth_emit_scalar(struct nyth_emitter *emitter,
	const char *scalar, size_t scalar_length, uint32_t types);


M include/scanner.h => include/scanner.h +4 -1
@@ 40,7 40,10 @@ struct token {
	enum token_type type;
	struct nyth_position pos;
	union {
		struct nyth_scalar scalar; // Only valid for TOKEN_SCALAR
		struct {
			struct nyth_scalar scalar;
			enum nyth_scalar_type scalar_type;
		}; // Only valid for TOKEN_SCALAR
		enum nyth_error error; // Only valid for TOKEN_ERROR
	};
};

M src/emitter.c => src/emitter.c +174 -192
@@ 48,20 48,20 @@ static bool write_indent(struct nyth_emitter *emitter, int indent) {
	return true;
}

static bool write_string_simple(struct nyth_emitter *emitter,
		struct nyth_string string) {
	if (!write_bytes(emitter, string.data, string.length)) {
static bool write_scalar_simple(struct nyth_emitter *emitter,
		const char *scalar, size_t scalar_length) {
	if (!write_bytes(emitter, scalar, scalar_length)) {
		return false;
	}
	return true;
}

static bool write_string_raw(struct nyth_emitter *emitter,
		struct nyth_string string) {
static bool write_scalar_raw(struct nyth_emitter *emitter,
		const char *scalar, size_t scalar_length) {
	if (!write_byte(emitter, '`')) {
		return false;
	}
	if (!write_bytes(emitter, string.data, string.length)) {
	if (!write_bytes(emitter, scalar, scalar_length)) {
		return false;
	}
	if (!write_byte(emitter, '`')) {


@@ 70,18 70,18 @@ static bool write_string_raw(struct nyth_emitter *emitter,
	return true;
}

static bool write_string_quoted(struct nyth_emitter *emitter,
		struct nyth_string string) {
static bool write_scalar_quoted(struct nyth_emitter *emitter,
		const char *scalar, size_t scalar_length) {
	if (!write_byte(emitter, '"')) {
		return false;
	}

	size_t i = 0;
	while (i < string.length) {
	while (i < scalar_length) {
		struct utf8 utf8;
		utf8_init(&utf8);
		while (!utf8_push(&utf8, string.data[i++])) {
			assert(i != string.length);
		while (!utf8_push(&utf8, scalar[i++])) {
			assert(i != scalar_length);
		}

		int32_t c = utf8.utf32;


@@ 129,8 129,8 @@ static bool write_string_quoted(struct nyth_emitter *emitter,
	return true;
}

static bool write_string_multiline(struct nyth_emitter *emitter,
		struct nyth_string string) {
static bool write_scalar_multiline(struct nyth_emitter *emitter,
		const char *scalar, size_t scalar_length) {
	if (!write_bytes(emitter, "|\n", 2)) {
		return false;
	}


@@ 139,10 139,10 @@ static bool write_string_multiline(struct nyth_emitter *emitter,

	size_t start = 0;
	bool last_empty = false;
	while (start < string.length) {
	while (start < scalar_length) {
		size_t length = 0;
		while (string.data[start + length++] != '\n') {
			assert(start + length <= string.length);
		while (scalar[start + length++] != '\n') {
			assert(start + length <= scalar_length);
		}

		last_empty = length == 1; // The line is "\n"


@@ 154,7 154,7 @@ static bool write_string_multiline(struct nyth_emitter *emitter,
			}
		}

		if (!write_bytes(emitter, &string.data[start], length)) {
		if (!write_bytes(emitter, &scalar[start], length)) {
			return false;
		}
		start += length;


@@ 173,190 173,182 @@ static bool write_string_multiline(struct nyth_emitter *emitter,
	return true;
}

static bool write_string(struct nyth_emitter *emitter,
		struct nyth_string string, enum nyth_scalar_type type) {
	switch (type) {
	case NYTH_SCALAR_SIMPLE:
		return write_string_simple(emitter, string);
	case NYTH_SCALAR_RAW:
		return write_string_raw(emitter, string);
	case NYTH_SCALAR_QUOTED:
		return write_string_quoted(emitter, string);
	case NYTH_SCALAR_MULTILINE:
		return write_string_multiline(emitter, string);
	default:
		assert(0 && "Invalid scalar type");
	}
static bool check_ok(struct nyth_emitter *emitter) {
	assert(!emitter->done);
	assert(emitter->stack.level > 0);
	return emitter->error == NYTH_ERROR_NONE;
}

static enum nyth_error emit_key(struct nyth_emitter *emitter,
		struct nyth_string key) {
	uint32_t valid = nyth_scalar_types(key.data, key.length);
	if (valid == 0) {
		assert(0 && "Invalid UTF-8 string");
	}
	uint32_t allowed = valid & NYTH_SCALAR_INLINE;
	enum nyth_scalar_type type = pick_scalar_type(allowed);
	if (!write_string(emitter, key, type)) {
		return NYTH_ERROR_OUTPUT;
	}
	return NYTH_ERROR_NONE;
static enum state top_state(struct nyth_emitter *emitter) {
	return emitter->stack.data[emitter->stack.level - 1];
}

static enum nyth_error emit_object(struct nyth_emitter *emitter,
		struct nyth_event ev) {
	switch (ev.type) {
	case NYTH_EVENT_MAP_START:
		if (!statestack_push(&emitter->stack, STATE_MAP)) {
			return NYTH_ERROR_NO_MEMORY;
		}
		if (!write_bytes(emitter, "+\n", 2)) {
			return NYTH_ERROR_OUTPUT;
		}
		++emitter->indent;
		return NYTH_ERROR_NONE;
	case NYTH_EVENT_LIST_START:
		if (!statestack_push(&emitter->stack, STATE_LIST)) {
			return NYTH_ERROR_NO_MEMORY;
static bool write_prefix(struct nyth_emitter *emitter) {
	switch (top_state(emitter)) {
	case STATE_MAP:
		if (!write_indent(emitter, emitter->indent)) {
			return false;
		}
		if (!write_bytes(emitter, "=\n", 2)) {
			return NYTH_ERROR_OUTPUT;
		return true;
	case STATE_LIST:
		if (!write_indent(emitter, emitter->indent)) {
			return false;
		}
		++emitter->indent;
		return NYTH_ERROR_NONE;
	case NYTH_EVENT_SCALAR:
		if (!write_string(emitter, ev.scalar.content,
				ev.scalar.type)) {
			return NYTH_ERROR_OUTPUT;
		if (!write_bytes(emitter, "- ", 2)) {
			return false;
		}
		if (ev.scalar.type != NYTH_SCALAR_MULTILINE) {
			if (!write_bytes(emitter, "\n", 1)) {
				return NYTH_ERROR_OUTPUT;
			}
		return true;
	case STATE_MAP_VALUE:
		if (!write_bytes(emitter, ": ", 2)) {
			return false;
		}
		return NYTH_ERROR_NONE;
		return true;
	default:
		assert(0 && "Unexpected event type");
		assert(0 && "Invalid state");
	}
}

static enum nyth_error emit(struct nyth_emitter *emitter,
		struct nyth_event ev) {
	assert(!emitter->done);
	assert(emitter->error == NYTH_ERROR_NONE);

	// The root object is always a map
	assert(emitter->stack.level != 0 || ev.type == NYTH_EVENT_MAP_START);
	if (emitter->stack.level == 0) {
		if (!statestack_push(&emitter->stack, STATE_MAP)) {
			return NYTH_ERROR_NO_MEMORY;
		}
		return NYTH_ERROR_NONE;
static enum nyth_error emit_scalar(struct nyth_emitter *emitter,
		const char *scalar, size_t scalar_length,
		enum nyth_scalar_type scalar_type) {
	if (!write_prefix(emitter)) {
		return (emitter->error = NYTH_ERROR_OUTPUT);
	}

	switch (emitter->stack.data[emitter->stack.level - 1]) {
	case STATE_MAP:
		if (ev.type == NYTH_EVENT_MAP_END) {
			statestack_pop(&emitter->stack);
			if (emitter->stack.level == 0) {
				emitter->done = true;
			} else {
				--emitter->indent;
			}
			return NYTH_ERROR_NONE;
		}
		assert(ev.type == NYTH_EVENT_KEY);
	enum state top = top_state(emitter);
	if (top == STATE_MAP) {
		// Map key
		if (!statestack_push(&emitter->stack, STATE_MAP_VALUE)) {
			return NYTH_ERROR_NO_MEMORY;
		}
		if (!write_indent(emitter, emitter->indent)) {
			return NYTH_ERROR_OUTPUT;
		}
		return emit_key(emitter, ev.key);
	case STATE_LIST:
		if (ev.type == NYTH_EVENT_LIST_END) {
			statestack_pop(&emitter->stack);
			--emitter->indent;
			return NYTH_ERROR_NONE;
		}
		if (!write_indent(emitter, emitter->indent)) {
			return NYTH_ERROR_OUTPUT;
		}
		if (!write_bytes(emitter, "- ", 2)) {
			return NYTH_ERROR_OUTPUT;
			return (emitter->error = NYTH_ERROR_NO_MEMORY);
		}
		return emit_object(emitter, ev);
	case STATE_MAP_VALUE:
	} else if (top == STATE_MAP_VALUE) {
		statestack_pop(&emitter->stack);
		if (!write_bytes(emitter, ": ", 2)) {
			return NYTH_ERROR_OUTPUT;
	}

	bool success = false;
	switch (scalar_type) {
	case NYTH_SCALAR_SIMPLE:
		success = write_scalar_simple(emitter,
			scalar, scalar_length);
		break;
	case NYTH_SCALAR_RAW:
		success = write_scalar_raw(emitter,
			scalar, scalar_length);
		break;
	case NYTH_SCALAR_QUOTED:
		success = write_scalar_quoted(emitter,
			scalar, scalar_length);
		break;
	case NYTH_SCALAR_MULTILINE:
		success = write_scalar_multiline(emitter,
			scalar, scalar_length);
		break;
	}
	if (!success) {
		return (emitter->error = NYTH_ERROR_OUTPUT);
	}

	// Inline scalars which are list items or map values
	// are followed by a line break
	if (top != STATE_MAP && scalar_type != NYTH_SCALAR_MULTILINE) {
		if (!write_byte(emitter, '\n')) {
			return (emitter->error = NYTH_ERROR_OUTPUT);
		}
		return emit_object(emitter, ev);
	default:
		assert(0 && "Invalid state");
	}
	return NYTH_ERROR_NONE;
}

enum nyth_error nyth_emit_map_start(struct nyth_emitter *emitter) {
	if (emitter->error != NYTH_ERROR_NONE) {
	if (emitter->stack.level == 0) {
		// Root map start
		if (!statestack_push(&emitter->stack, STATE_MAP)) {
			return (emitter->error = NYTH_ERROR_NO_MEMORY);
		}
		return NYTH_ERROR_NONE;
	}

	if (!check_ok(emitter)) {
		return emitter->error;
	}
	struct nyth_event ev;
	ev.type = NYTH_EVENT_MAP_START;
	return (emitter->error = emit(emitter, ev));

	enum state top = top_state(emitter);
	assert(top != STATE_MAP);

	if (!write_prefix(emitter)) {
		return (emitter->error = NYTH_ERROR_OUTPUT);
	}
	if (top == STATE_MAP_VALUE) {
		statestack_pop(&emitter->stack);
	}

	if (!statestack_push(&emitter->stack, STATE_MAP)) {
		return (emitter->error = NYTH_ERROR_NO_MEMORY);
	}
	if (!write_bytes(emitter, "+\n", 2)) {
		return (emitter->error = NYTH_ERROR_OUTPUT);
	}
	++emitter->indent;
	return NYTH_ERROR_NONE;
}

enum nyth_error nyth_emit_map_end(struct nyth_emitter *emitter) {
	if (emitter->error != NYTH_ERROR_NONE) {
	if (!check_ok(emitter)) {
		return emitter->error;
	}
	struct nyth_event ev;
	ev.type = NYTH_EVENT_MAP_END;
	return (emitter->error = emit(emitter, ev));

	assert(top_state(emitter) == STATE_MAP);
	statestack_pop(&emitter->stack);
	if (emitter->stack.level == 0) {
		// Root map end
		emitter->done = true;
	} else {
		--emitter->indent;
	}
	return NYTH_ERROR_NONE;
}

enum nyth_error nyth_emit_list_start(struct nyth_emitter *emitter) {
	if (emitter->error != NYTH_ERROR_NONE) {
	if (!check_ok(emitter)) {
		return emitter->error;
	}
	struct nyth_event ev;
	ev.type = NYTH_EVENT_LIST_START;
	return (emitter->error = emit(emitter, ev));
}

enum nyth_error nyth_emit_list_end(struct nyth_emitter *emitter) {
	if (emitter->error != NYTH_ERROR_NONE) {
		return emitter->error;
	enum state top = top_state(emitter);
	assert(top != STATE_MAP);

	if (!write_prefix(emitter)) {
		return (emitter->error = NYTH_ERROR_OUTPUT);
	}
	if (top == STATE_MAP_VALUE) {
		statestack_pop(&emitter->stack);
	}

	if (!statestack_push(&emitter->stack, STATE_LIST)) {
		return (emitter->error = NYTH_ERROR_NO_MEMORY);
	}
	if (!write_bytes(emitter, "=\n", 2)) {
		return (emitter->error = NYTH_ERROR_OUTPUT);
	}
	struct nyth_event ev;
	ev.type = NYTH_EVENT_LIST_END;
	return (emitter->error = emit(emitter, ev));
	++emitter->indent;
	return NYTH_ERROR_NONE;
}

enum nyth_error nyth_emit_key(struct nyth_emitter *emitter,
		const char *key, size_t key_length) {
	if (emitter->error != NYTH_ERROR_NONE) {
enum nyth_error nyth_emit_list_end(struct nyth_emitter *emitter) {
	if (!check_ok(emitter)) {
		return emitter->error;
	}
	if (key_length == NYTH_NULTERM) {
		key_length = strlen(key);
	}
	struct nyth_event ev;
	ev.type = NYTH_EVENT_KEY;
	ev.key.data = key;
	ev.key.length = key_length;
	return (emitter->error = emit(emitter, ev));

	assert(top_state(emitter) == STATE_LIST);
	statestack_pop(&emitter->stack);
	--emitter->indent;
	return NYTH_ERROR_NONE;
}

enum nyth_error nyth_emit_scalar(struct nyth_emitter *emitter,
		const char *scalar, size_t scalar_length,
		uint32_t types) {
	if (emitter->error != NYTH_ERROR_NONE) {
		const char *scalar, size_t scalar_length, uint32_t types) {
	if (!check_ok(emitter)) {
		return emitter->error;
	}
	if (scalar_length == NYTH_NULTERM) {
		scalar_length = strlen(scalar);
	}

	uint32_t valid = nyth_scalar_types(scalar, scalar_length);
	if (valid == 0) {
		assert(0 && "Invalid UTF-8 string");


@@ 365,76 357,66 @@ enum nyth_error nyth_emit_scalar(struct nyth_emitter *emitter,
	if (allowed == 0) {
		assert(0 && "The scalar is unrepresentable with given types");
	}

	if (top_state(emitter) == STATE_MAP) {
		// Map key
		allowed &= NYTH_SCALAR_INLINE;
		assert(allowed != 0 &&
			"A map key can't be a multiline scalar");
	}

	if (scalar_length == NYTH_NULTERM) {
		scalar_length = strlen(scalar);
	}

	enum nyth_scalar_type type = pick_scalar_type(allowed);
	struct nyth_event ev;
	ev.type = NYTH_EVENT_SCALAR;
	ev.scalar.content.data = scalar;
	ev.scalar.content.length = scalar_length;
	ev.scalar.type = type;
	return (emitter->error = emit(emitter, ev));
	return emit_scalar(emitter, scalar, scalar_length, type);
}

enum nyth_error nyth_emit_scalar_null(struct nyth_emitter *emitter) {
	if (emitter->error != NYTH_ERROR_NONE) {
	if (!check_ok(emitter)) {
		return emitter->error;
	}
	struct nyth_event ev;
	ev.type = NYTH_EVENT_SCALAR;
	ev.scalar.type = NYTH_SCALAR_SIMPLE;
	ev.scalar.content.data = "null";
	ev.scalar.content.length = 4;
	return (emitter->error = emit(emitter, ev));

	return emit_scalar(emitter, "null", 4, NYTH_SCALAR_SIMPLE);
}

enum nyth_error nyth_emit_scalar_bool(struct nyth_emitter *emitter,
		bool value) {
	if (emitter->error != NYTH_ERROR_NONE) {
	if (!check_ok(emitter)) {
		return emitter->error;
	}
	struct nyth_event ev;
	ev.type = NYTH_EVENT_SCALAR;
	ev.scalar.type = NYTH_SCALAR_SIMPLE;

	if (value) {
		ev.scalar.content.data = "true";
		ev.scalar.content.length = 4;
		return emit_scalar(emitter, "true", 4, NYTH_SCALAR_SIMPLE);
	} else {
		ev.scalar.content.data = "false";
		ev.scalar.content.length = 5;
		return emit_scalar(emitter, "false", 5, NYTH_SCALAR_SIMPLE);
	}
	return (emitter->error = emit(emitter, ev));
}

enum nyth_error nyth_emit_scalar_int(struct nyth_emitter *emitter,
		int64_t value) {
	if (emitter->error != NYTH_ERROR_NONE) {
	if (!check_ok(emitter)) {
		return emitter->error;
	}
	struct nyth_event ev;
	ev.type = NYTH_EVENT_SCALAR;
	ev.scalar.type = NYTH_SCALAR_SIMPLE;

	char buf[SNCONV_ITOS_MAXSIZE];
	size_t length = snconv_itos(value, buf);
	ev.scalar.content.data = buf;
	ev.scalar.content.length = length;
	return (emitter->error = emit(emitter, ev));
	return emit_scalar(emitter, buf, length, NYTH_SCALAR_SIMPLE);
}

enum nyth_error nyth_emit_scalar_float(struct nyth_emitter *emitter,
		double value) {
	if (emitter->error != NYTH_ERROR_NONE) {
	if (!check_ok(emitter)) {
		return emitter->error;
	}
	struct nyth_event ev;
	ev.type = NYTH_EVENT_SCALAR;
	ev.scalar.type = NYTH_SCALAR_SIMPLE;

	char buf[SNCONV_FTOS_MAXSIZE];
	size_t length = snconv_ftos(value, buf);
	if (length == 0) {
		assert(0 && "Unrepresentable floating point number");
	}
	ev.scalar.content.data = buf;
	ev.scalar.content.length = length;
	return (emitter->error = emit(emitter, ev));
	return emit_scalar(emitter, buf, length, NYTH_SCALAR_SIMPLE);
}

enum nyth_error nyth_emitter_get_error(struct nyth_emitter *emitter) {

M src/parser.c => src/parser.c +6 -6
@@ 15,11 15,11 @@ struct nyth_parser {
	bool done;
};

static bool key_token(struct token *tok) {
	if (tok->type != TOKEN_SCALAR) {
static bool key_token(struct token tok) {
	if (tok.type != TOKEN_SCALAR) {
		return false;
	}
	if (tok->scalar.type == NYTH_SCALAR_MULTILINE) {
	if ((tok.scalar_type & NYTH_SCALAR_INLINE) == 0) {
		return false;
	}
	return true;


@@ 139,15 139,15 @@ static struct nyth_event parse_event(struct nyth_parser *parser) {
		if (tok.type == TOKEN_ERROR) {
			return scan_error(tok);
		}
		if (!key_token(&tok)) {
		if (!key_token(tok)) {
			return bad_entry(parser);
		}
		if (!statestack_push(&parser->stack, STATE_MAP_VALUE)) {
			return no_memory(tok.pos);
		}
		ev.type = NYTH_EVENT_KEY;
		ev.type = NYTH_EVENT_SCALAR;
		ev.pos = tok.pos;
		ev.key = tok.scalar.content;
		ev.scalar = tok.scalar;
		return ev;
	case STATE_LIST:
		if (tok.type == TOKEN_COLLECTION_END) {

M src/scalar.c => src/scalar.c +5 -21
@@ 79,26 79,18 @@ uint32_t nyth_scalar_types(const char *scalar, size_t scalar_length) {
}

enum nyth_error nyth_scalar_as_null(struct nyth_scalar *scalar) {
	if (scalar->type != NYTH_SCALAR_SIMPLE) {
		return NYTH_ERROR_FORMAT_MISMATCH;
	}
	struct nyth_string *str = &scalar->content;
	if (str->length == 4 && memcmp(str->data, "null", 4) == 0) {
	if (scalar->length == 4 && memcmp(scalar->data, "null", 4) == 0) {
		return NYTH_ERROR_NONE;
	}
	return NYTH_ERROR_FORMAT_MISMATCH;
}

enum nyth_error nyth_scalar_as_bool(struct nyth_scalar *scalar, bool *out) {
	if (scalar->type != NYTH_SCALAR_SIMPLE) {
		return NYTH_ERROR_FORMAT_MISMATCH;
	}
	struct nyth_string *str = &scalar->content;
	if (str->length == 4 && memcmp(str->data, "true", 4) == 0) {
	if (scalar->length == 4 && memcmp(scalar->data, "true", 4) == 0) {
		*out = true;
		return NYTH_ERROR_NONE;
	}
	if (str->length == 5 && memcmp(str->data, "false", 5) == 0) {
	if (scalar->length == 5 && memcmp(scalar->data, "false", 5) == 0) {
		*out = false;
		return NYTH_ERROR_NONE;
	}


@@ 106,11 98,7 @@ enum nyth_error nyth_scalar_as_bool(struct nyth_scalar *scalar, bool *out) {
}

enum nyth_error nyth_scalar_as_int(struct nyth_scalar *scalar, int64_t *out) {
	if (scalar->type != NYTH_SCALAR_SIMPLE) {
		return NYTH_ERROR_FORMAT_MISMATCH;
	}
	struct nyth_string *str = &scalar->content;
	switch (snconv_stoi(str->data, str->length, out)) {
	switch (snconv_stoi(scalar->data, scalar->length, out)) {
	case SNCONV_OK:
		return NYTH_ERROR_NONE;
	case SNCONV_BAD_FORMAT:


@@ 123,11 111,7 @@ enum nyth_error nyth_scalar_as_int(struct nyth_scalar *scalar, int64_t *out) {
}

enum nyth_error nyth_scalar_as_float(struct nyth_scalar *scalar, double *out) {
	if (scalar->type != NYTH_SCALAR_SIMPLE) {
		return NYTH_ERROR_FORMAT_MISMATCH;
	}
	struct nyth_string *str = &scalar->content;
	switch (snconv_stof(str->data, str->length, out)) {
	switch (snconv_stof(scalar->data, scalar->length, out)) {
	case SNCONV_OK:
		return NYTH_ERROR_NONE;
	case SNCONV_BAD_FORMAT:

M src/scanner.c => src/scanner.c +3 -3
@@ 115,9 115,9 @@ static struct token scalar_token(struct scanner *scanner,
	struct token tok;
	tok.type = TOKEN_SCALAR;
	tok.pos = pos;
	tok.scalar.content.data = scanner->strbuf.data;
	tok.scalar.content.length = scanner->strbuf.length;
	tok.scalar.type = type;
	tok.scalar.data = scanner->strbuf.data;
	tok.scalar.length = scanner->strbuf.length;
	tok.scalar_type = type;
	return tok;
}


M tests/lib.c => tests/lib.c +11 -52
@@ 6,7 6,6 @@

static const char *event_names[] = {
	[NYTH_EVENT_NONE] = "None",
	[NYTH_EVENT_KEY] = "Key",
	[NYTH_EVENT_SCALAR] = "Scalar",
	[NYTH_EVENT_MAP_START] = "Map start",
	[NYTH_EVENT_MAP_END] = "Map end",


@@ 15,13 14,6 @@ static const char *event_names[] = {
	[NYTH_EVENT_ERROR] = "Error",
};

static const char *scalar_names[] = {
	[NYTH_SCALAR_SIMPLE] = "Simple",
	[NYTH_SCALAR_RAW] = "Raw",
	[NYTH_SCALAR_QUOTED] = "Quoted",
	[NYTH_SCALAR_MULTILINE] = "Multiline",
};

static struct nyth_parser *parser = NULL;
static struct nyth_emitter *emitter = NULL;



@@ 33,13 25,7 @@ static char *emitter_output_buf = NULL;
static const char *emitter_success = NULL;
static enum nyth_error emitter_error = NYTH_ERROR_NONE;

static void print_string(struct nyth_string string) {
	fprintf(stderr, "///\n");
	fprintf(stderr, "%.*s\n", (int)string.length, string.data);
	fprintf(stderr, "///\n");
}

static bool strings_equal(struct nyth_string a, struct nyth_string b) {
static bool scalars_equal(struct nyth_scalar a, struct nyth_scalar b) {
	return a.length == b.length && memcmp(a.data, b.data, a.length) == 0;
}



@@ 50,13 36,8 @@ static bool events_equal(struct nyth_event a, struct nyth_event b) {
	if (a.pos.line != b.pos.line || a.pos.column != b.pos.column) {
		return false;
	}
	if (a.type == NYTH_EVENT_KEY) {
		return strings_equal(a.key, b.key);
	} else if (a.type == NYTH_EVENT_SCALAR) {
		if (a.scalar.type != b.scalar.type) {
			return false;
		}
		return strings_equal(a.scalar.content, b.scalar.content);
	if (a.type == NYTH_EVENT_SCALAR) {
		return scalars_equal(a.scalar, b.scalar);
	} else if (a.type == NYTH_EVENT_ERROR) {
		return a.error == b.error;
	}


@@ 66,14 47,12 @@ static bool events_equal(struct nyth_event a, struct nyth_event b) {
static void print_event(struct nyth_event ev) {
	fprintf(stderr, "- Type: %d (%s)\n", ev.type, event_names[ev.type]);
	fprintf(stderr, "- Position: %d:%d\n", ev.pos.line, ev.pos.column);
	if (ev.type == NYTH_EVENT_KEY) {
		fprintf(stderr, "- Key:\n");
		print_string(ev.key);
	} else if (ev.type == NYTH_EVENT_SCALAR) {
		fprintf(stderr, "- Scalar type: %d (%s)\n", ev.scalar.type,
			scalar_names[ev.scalar.type]);
	if (ev.type == NYTH_EVENT_SCALAR) {
		fprintf(stderr, "- Scalar:\n");
		print_string(ev.scalar.content);
		fprintf(stderr, "///\n");
		fprintf(stderr, "%.*s\n", (int)ev.scalar.length,
			ev.scalar.data);
		fprintf(stderr, "///\n");
	} else if (ev.type == NYTH_EVENT_ERROR) {
		fprintf(stderr, "- Error: %d (%s)\n", ev.error,
			nyth_error_description(ev.error));


@@ 144,32 123,17 @@ void test_parse(enum nyth_event_type type, int line, int column) {
	event(want);
}

void test_parse_key(const char *key, size_t key_length,
		int line, int column) {
	struct nyth_event want;
	want.type = NYTH_EVENT_KEY;
	want.pos.line = line;
	want.pos.column = column;
	want.key.data = key;
	if (key_length == NYTH_NULTERM) {
		key_length = strlen(key);
	}
	want.key.length = key_length;
	event(want);
}

void test_parse_scalar(const char *scalar, size_t scalar_length,
		enum nyth_scalar_type scalar_type, int line, int column) {
		int line, int column) {
	struct nyth_event want;
	want.type = NYTH_EVENT_SCALAR;
	want.pos.line = line;
	want.pos.column = column;
	want.scalar.type = scalar_type;
	want.scalar.content.data = scalar;
	want.scalar.data = scalar;
	if (scalar_length == NYTH_NULTERM) {
		scalar_length = strlen(scalar);
	}
	want.scalar.content.length = scalar_length;
	want.scalar.length = scalar_length;
	event(want);
}



@@ 227,11 191,6 @@ void test_emit_list_end(void) {
	nyth_emit_list_end(emitter);
}

void test_emit_key(const char *key, size_t key_length) {
	check_emitter_ok();
	nyth_emit_key(emitter, key, key_length);
}

void test_emit_scalar(const char *scalar, size_t scalar_length,
		uint32_t scalar_types) {
	check_emitter_ok();

M tests/lib.h => tests/lib.h +2 -5
@@ 7,10 7,8 @@ void test_parse_start(const char *input);
void test_parse_cleanup(void);

void test_parse(enum nyth_event_type type, int line, int column);
void test_parse_key(const char *key, size_t key_length,
	int line, int column);
void test_parse_scalar(const char *scalar, size_t scalar_length,
	enum nyth_scalar_type scalar_type, int line, int column);
	int line, int column);
void test_parse_error(enum nyth_error error, int line, int column);
void test_parse_none(void);



@@ 21,9 19,8 @@ void test_emit_map_start(void);
void test_emit_map_end(void);
void test_emit_list_start(void);
void test_emit_list_end(void);
void test_emit_key(const char *key, size_t key_length);
void test_emit_scalar(const char *scalar, size_t scalar_length,
	uint32_t scalar_types);
	uint32_t types);

void test_emit_scalar_null(void);
void test_emit_scalar_bool(bool value);

M tests/test_emit_collection.c => tests/test_emit_collection.c +10 -10
@@ 3,7 3,7 @@
int main(void) {
	test_emit_start_success("map: +\n");
	test_emit_map_start();
	test_emit_key("map", NYTH_NULTERM);
	test_emit_scalar("map", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_map_start();
	test_emit_map_end();
	test_emit_map_end();


@@ 14,11 14,11 @@ int main(void) {
		"\tkey: value\n"
		"\tkey: value\n");
	test_emit_map_start();
	test_emit_key("map", NYTH_NULTERM);
	test_emit_scalar("map", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_map_start();
	test_emit_key("key", NYTH_NULTERM);
	test_emit_scalar("key", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar("value", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_key("key", NYTH_NULTERM);
	test_emit_scalar("key", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar("value", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_map_end();
	test_emit_map_end();


@@ 26,7 26,7 @@ int main(void) {

	test_emit_start_success("list: =\n");
	test_emit_map_start();
	test_emit_key("list", NYTH_NULTERM);
	test_emit_scalar("list", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_list_start();
	test_emit_list_end();
	test_emit_map_end();


@@ 37,7 37,7 @@ int main(void) {
		"\t- item\n"
		"\t- item\n");
	test_emit_map_start();
	test_emit_key("list", NYTH_NULTERM);
	test_emit_scalar("list", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_list_start();
	test_emit_scalar("item", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar("item", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);


@@ 54,19 54,19 @@ int main(void) {
		"\t\tkey: value\n"
		"key: value\n");
	test_emit_map_start();
	test_emit_key("list", NYTH_NULTERM);
	test_emit_scalar("list", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_list_start();
	test_emit_map_start();
	test_emit_key("key", NYTH_NULTERM);
	test_emit_scalar("key", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar("value", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_map_end();
	test_emit_scalar("item", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_map_start();
	test_emit_key("key", NYTH_NULTERM);
	test_emit_scalar("key", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar("value", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_map_end();
	test_emit_list_end();
	test_emit_key("key", NYTH_NULTERM);
	test_emit_scalar("key", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar("value", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_map_end();
	test_emit_stop();

M tests/test_emit_scalars_basic.c => tests/test_emit_scalars_basic.c +8 -8
@@ 6,11 6,11 @@ int main(void) {
		"raw: `hello, world`\n"
		"quoted: \"hello, world\"\n");
	test_emit_map_start();
	test_emit_key("simple", NYTH_NULTERM);
	test_emit_scalar("simple", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar("hello", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_key("raw", NYTH_NULTERM);
	test_emit_scalar("raw", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar("hello, world", NYTH_NULTERM, NYTH_SCALAR_RAW);
	test_emit_key("quoted", NYTH_NULTERM);
	test_emit_scalar("quoted", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar("hello, world", NYTH_NULTERM, NYTH_SCALAR_QUOTED);
	test_emit_map_end();
	test_emit_stop();


@@ 22,15 22,15 @@ int main(void) {
		"last-colon: `hello:`\n"
		"plus: `+`\n");
	test_emit_map_start();
	test_emit_key("blank", NYTH_NULTERM);
	test_emit_scalar("blank", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar("hello world", NYTH_NULTERM, NYTH_SCALAR_ANY);
	test_emit_key("first-hash", NYTH_NULTERM);
	test_emit_scalar("first-hash", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar("#hello", NYTH_NULTERM, NYTH_SCALAR_ANY);
	test_emit_key("first-quote", NYTH_NULTERM);
	test_emit_scalar("first-quote", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar("\"hello", NYTH_NULTERM, NYTH_SCALAR_ANY);
	test_emit_key("last-colon", NYTH_NULTERM);
	test_emit_scalar("last-colon", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar("hello:", NYTH_NULTERM, NYTH_SCALAR_ANY);
	test_emit_key("plus", NYTH_NULTERM);
	test_emit_scalar("plus", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar("+", NYTH_NULTERM, NYTH_SCALAR_ANY);
	test_emit_map_end();
	test_emit_stop();

M tests/test_emit_scalars_escaped.c => tests/test_emit_scalars_escaped.c +5 -5
@@ 8,15 8,15 @@ int main(void) {
		"`carriage return`: \"\\r\"\n"
		"nul: \"hello\\x00world\"\n");
	test_emit_map_start();
	test_emit_key("single", NYTH_NULTERM);
	test_emit_scalar("single", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar("\b", NYTH_NULTERM, NYTH_SCALAR_QUOTED);
	test_emit_key("special", NYTH_NULTERM);
	test_emit_scalar("special", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar("\\\"", NYTH_NULTERM, NYTH_SCALAR_QUOTED);
	test_emit_key("2 bytes", NYTH_NULTERM);
	test_emit_scalar("2 bytes", NYTH_NULTERM, NYTH_SCALAR_RAW);
	test_emit_scalar("\x7f", NYTH_NULTERM, NYTH_SCALAR_QUOTED);
	test_emit_key("carriage return", NYTH_NULTERM);
	test_emit_scalar("carriage return", NYTH_NULTERM, NYTH_SCALAR_RAW);
	test_emit_scalar("\r", NYTH_NULTERM, NYTH_SCALAR_QUOTED);
	test_emit_key("nul", NYTH_NULTERM);
	test_emit_scalar("nul", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar("hello\x00world", 11, NYTH_SCALAR_QUOTED);
	test_emit_map_end();
	test_emit_stop();

M tests/test_emit_scalars_multiline.c => tests/test_emit_scalars_multiline.c +4 -4
@@ 7,7 7,7 @@ int main(void) {
		"\n"
		"\tworld\n");
	test_emit_map_start();
	test_emit_key("implicit", NYTH_NULTERM);
	test_emit_scalar("implicit", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar("hello\n\nworld\n", NYTH_NULTERM,
		NYTH_SCALAR_MULTILINE);
	test_emit_map_end();


@@ 18,7 18,7 @@ int main(void) {
		"\t \n"
		"|\n");
	test_emit_map_start();
	test_emit_key("explicit", NYTH_NULTERM);
	test_emit_scalar("explicit", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar(" \n", NYTH_NULTERM, NYTH_SCALAR_MULTILINE);
	test_emit_map_end();
	test_emit_stop();


@@ 31,7 31,7 @@ int main(void) {
		"\n"
		"|\n");
	test_emit_map_start();
	test_emit_key("explicit", NYTH_NULTERM);
	test_emit_scalar("explicit", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar("hello\n\nworld\n\n", NYTH_NULTERM,
		NYTH_SCALAR_MULTILINE);
	test_emit_map_end();


@@ 39,7 39,7 @@ int main(void) {

	test_emit_start_success("empty: |\n");
	test_emit_map_start();
	test_emit_key("empty", NYTH_NULTERM);
	test_emit_scalar("empty", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar("", NYTH_NULTERM, NYTH_SCALAR_MULTILINE);
	test_emit_map_end();
	test_emit_stop();

M tests/test_emit_scalars_typed.c => tests/test_emit_scalars_typed.c +27 -27
@@ 6,7 6,7 @@
int main(void) {
	test_emit_start_success("null: null\n");
	test_emit_map_start();
	test_emit_key("null", NYTH_NULTERM);
	test_emit_scalar("null", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_null();
	test_emit_stop();



@@ 14,9 14,9 @@ int main(void) {
		"bool: true\n"
		"bool: false\n");
	test_emit_map_start();
	test_emit_key("bool", NYTH_NULTERM);
	test_emit_scalar("bool", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_bool(true);
	test_emit_key("bool", NYTH_NULTERM);
	test_emit_scalar("bool", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_bool(false);
	test_emit_stop();



@@ 27,15 27,15 @@ int main(void) {
		"int: 9223372036854775807\n"
		"int: -9223372036854775808\n");
	test_emit_map_start();
	test_emit_key("int", NYTH_NULTERM);
	test_emit_scalar("int", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_int(0);
	test_emit_key("int", NYTH_NULTERM);
	test_emit_scalar("int", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_int(123456789);
	test_emit_key("int", NYTH_NULTERM);
	test_emit_scalar("int", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_int(-987654321);
	test_emit_key("int", NYTH_NULTERM);
	test_emit_scalar("int", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_int(INT64_MAX);
	test_emit_key("int", NYTH_NULTERM);
	test_emit_scalar("int", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_int(INT64_MIN);
	test_emit_stop();



@@ 46,15 46,15 @@ int main(void) {
		"int: 9223372036854775807\n"
		"int: -9223372036854775808\n");
	test_emit_map_start();
	test_emit_key("int", NYTH_NULTERM);
	test_emit_scalar("int", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_int(0);
	test_emit_key("int", NYTH_NULTERM);
	test_emit_scalar("int", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_int(123456789);
	test_emit_key("int", NYTH_NULTERM);
	test_emit_scalar("int", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_int(-987654321);
	test_emit_key("int", NYTH_NULTERM);
	test_emit_scalar("int", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_int(INT64_MAX);
	test_emit_key("int", NYTH_NULTERM);
	test_emit_scalar("int", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_int(INT64_MIN);
	test_emit_stop();



@@ 65,15 65,15 @@ int main(void) {
		"int: 9223372036854775807\n"
		"int: -9223372036854775808\n");
	test_emit_map_start();
	test_emit_key("int", NYTH_NULTERM);
	test_emit_scalar("int", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_int(0);
	test_emit_key("int", NYTH_NULTERM);
	test_emit_scalar("int", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_int(123456789);
	test_emit_key("int", NYTH_NULTERM);
	test_emit_scalar("int", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_int(-987654321);
	test_emit_key("int", NYTH_NULTERM);
	test_emit_scalar("int", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_int(INT64_MAX);
	test_emit_key("int", NYTH_NULTERM);
	test_emit_scalar("int", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_int(INT64_MIN);
	test_emit_stop();



@@ 88,23 88,23 @@ int main(void) {
		"float: -1e+30\n"
		"float: 2.9802322387695312e-08\n");
	test_emit_map_start();
	test_emit_key("float", NYTH_NULTERM);
	test_emit_scalar("float", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_float(0.0);
	test_emit_key("float", NYTH_NULTERM);
	test_emit_scalar("float", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_float(-0.0);
	test_emit_key("float", NYTH_NULTERM);
	test_emit_scalar("float", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_float(90000.0);
	test_emit_key("float", NYTH_NULTERM);
	test_emit_scalar("float", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_float(0.0009);
	test_emit_key("float", NYTH_NULTERM);
	test_emit_scalar("float", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_float(9e-05);
	test_emit_key("float", NYTH_NULTERM);
	test_emit_scalar("float", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_float(12345.0);
	test_emit_key("float", NYTH_NULTERM);
	test_emit_scalar("float", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_float(12345.6789);
	test_emit_key("float", NYTH_NULTERM);
	test_emit_scalar("float", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_float(-1e+30);
	test_emit_key("float", NYTH_NULTERM);
	test_emit_scalar("float", NYTH_NULTERM, NYTH_SCALAR_SIMPLE);
	test_emit_scalar_float(2.9802322387695312e-08);
	test_emit_stop();


M tests/test_io_input.c => tests/test_io_input.c +1 -1
@@ 13,7 13,7 @@ static void test_input(struct nyth_input *input) {
	ev = nyth_parse_event(parser);
	assert(ev.type == NYTH_EVENT_MAP_START);
	ev = nyth_parse_event(parser);
	assert(ev.type == NYTH_EVENT_KEY);
	assert(ev.type == NYTH_EVENT_SCALAR);
	ev = nyth_parse_event(parser);
	assert(ev.type == NYTH_EVENT_SCALAR);
	ev = nyth_parse_event(parser);

M tests/test_io_output.c => tests/test_io_output.c +2 -1
@@ 12,7 12,8 @@ static enum nyth_error test_output(struct nyth_output *output) {
	struct nyth_emitter *emitter = nyth_emitter_create(output);
	assert(emitter != NULL);
	nyth_emit_map_start(emitter);
	nyth_emit_key(emitter, "never gonna", NYTH_NULTERM);
	nyth_emit_scalar(emitter, "never gonna",
		NYTH_NULTERM, NYTH_SCALAR_ANY);
	assert(nyth_emitter_get_error(emitter) == NYTH_ERROR_NONE);
	nyth_emit_scalar(emitter, "give you up",
		NYTH_NULTERM, NYTH_SCALAR_ANY);

M tests/test_parse_indentation.c => tests/test_parse_indentation.c +5 -6
@@ 5,10 5,9 @@ int main(void) {
		"list: =\n"
		"\t- item\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("list", NYTH_NULTERM, 1, 1);
	test_parse_scalar("list", NYTH_NULTERM, 1, 1);
	test_parse(NYTH_EVENT_LIST_START, 1, 7);
	test_parse_scalar("item", NYTH_NULTERM,
		NYTH_SCALAR_SIMPLE, 2, 4);
	test_parse_scalar("item", NYTH_NULTERM, 2, 4);
	test_parse(NYTH_EVENT_LIST_END, 3, 1);
	test_parse(NYTH_EVENT_MAP_END, 3, 1);
	test_parse_none();


@@ 17,7 16,7 @@ int main(void) {
		"list: =\n"
		"\t\t- item\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("list", NYTH_NULTERM, 1, 1);
	test_parse_scalar("list", NYTH_NULTERM, 1, 1);
	test_parse(NYTH_EVENT_LIST_START, 1, 7);
	test_parse_error(NYTH_ERROR_BAD_INDENTATION, 2, 2);
	test_parse_none();


@@ 26,7 25,7 @@ int main(void) {
		"list: =\n"
		" - item\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("list", NYTH_NULTERM, 1, 1);
	test_parse_scalar("list", NYTH_NULTERM, 1, 1);
	test_parse(NYTH_EVENT_LIST_START, 1, 7);
	test_parse_error(NYTH_ERROR_BAD_INDENTATION, 2, 1);
	test_parse_none();


@@ 35,7 34,7 @@ int main(void) {
		"list: =\n"
		"\t - item\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("list", NYTH_NULTERM, 1, 1);
	test_parse_scalar("list", NYTH_NULTERM, 1, 1);
	test_parse(NYTH_EVENT_LIST_START, 1, 7);
	test_parse_error(NYTH_ERROR_BAD_INDENTATION, 2, 2);
	test_parse_none();

M tests/test_parse_list.c => tests/test_parse_list.c +6 -9
@@ 6,12 6,10 @@ int main(void) {
		"\t- item1\n"
		"\t- item2\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("list", NYTH_NULTERM, 1, 1);
	test_parse_scalar("list", NYTH_NULTERM, 1, 1);
	test_parse(NYTH_EVENT_LIST_START, 1, 7);
	test_parse_scalar("item1", NYTH_NULTERM,
		NYTH_SCALAR_SIMPLE, 2, 4);
	test_parse_scalar("item2", NYTH_NULTERM,
		NYTH_SCALAR_SIMPLE, 3, 4);
	test_parse_scalar("item1", NYTH_NULTERM, 2, 4);
	test_parse_scalar("item2", NYTH_NULTERM, 3, 4);
	test_parse(NYTH_EVENT_LIST_END, 4, 1);
	test_parse(NYTH_EVENT_MAP_END, 4, 1);
	test_parse_none();


@@ 20,7 18,7 @@ int main(void) {
		"list: =\n"
		"\tbad\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("list", NYTH_NULTERM, 1, 1);
	test_parse_scalar("list", NYTH_NULTERM, 1, 1);
	test_parse(NYTH_EVENT_LIST_START, 1, 7);
	test_parse_error(NYTH_ERROR_BAD_LIST_ENTRY, 2, 2);
	test_parse_none();


@@ 29,10 27,9 @@ int main(void) {
		"list: =\n"
		"\t- good bad\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("list", NYTH_NULTERM, 1, 1);
	test_parse_scalar("list", NYTH_NULTERM, 1, 1);
	test_parse(NYTH_EVENT_LIST_START, 1, 7);
	test_parse_scalar("good", NYTH_NULTERM,
		NYTH_SCALAR_SIMPLE, 2, 4);
	test_parse_scalar("good", NYTH_NULTERM, 2, 4);
	test_parse_error(NYTH_ERROR_BAD_LIST_ENTRY, 2, 2);
	test_parse_none();


M tests/test_parse_map.c => tests/test_parse_map.c +16 -19
@@ 6,14 6,12 @@ int main(void) {
		"\tkey1: value1\n"
		"\tkey2: value2\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("map", NYTH_NULTERM, 1, 1);
	test_parse_scalar("map", NYTH_NULTERM, 1, 1);
	test_parse(NYTH_EVENT_MAP_START, 1, 6);
	test_parse_key("key1", NYTH_NULTERM, 2, 2);
	test_parse_scalar("value1", NYTH_NULTERM,
		NYTH_SCALAR_SIMPLE, 2, 8);
	test_parse_key("key2", NYTH_NULTERM, 3, 2);
	test_parse_scalar("value2", NYTH_NULTERM,
		NYTH_SCALAR_SIMPLE, 3, 8);
	test_parse_scalar("key1", NYTH_NULTERM, 2, 2);
	test_parse_scalar("value1", NYTH_NULTERM, 2, 8);
	test_parse_scalar("key2", NYTH_NULTERM, 3, 2);
	test_parse_scalar("value2", NYTH_NULTERM, 3, 8);
	test_parse(NYTH_EVENT_MAP_END, 4, 1);
	test_parse(NYTH_EVENT_MAP_END, 4, 1);
	test_parse_none();


@@ 22,7 20,7 @@ int main(void) {
		"map: +\n"
		"\t- bad\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("map", NYTH_NULTERM, 1, 1);
	test_parse_scalar("map", NYTH_NULTERM, 1, 1);
	test_parse(NYTH_EVENT_MAP_START, 1, 6);
	test_parse_error(NYTH_ERROR_BAD_MAP_ENTRY, 2, 2);
	test_parse_none();


@@ 31,30 29,29 @@ int main(void) {
		"map: +\n"
		"\tkey: good bad");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("map", NYTH_NULTERM, 1, 1);
	test_parse_scalar("map", NYTH_NULTERM, 1, 1);
	test_parse(NYTH_EVENT_MAP_START, 1, 6);
	test_parse_key("key", NYTH_NULTERM, 2, 2);
	test_parse_scalar("good", NYTH_NULTERM,
		NYTH_SCALAR_SIMPLE, 2, 7);
	test_parse_scalar("key", NYTH_NULTERM, 2, 2);
	test_parse_scalar("good", NYTH_NULTERM, 2, 7);
	test_parse_error(NYTH_ERROR_BAD_MAP_ENTRY, 2, 2);
	test_parse_none();

	test_parse_start("key: = bad\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("key", NYTH_NULTERM, 1, 1);
	test_parse_scalar("key", NYTH_NULTERM, 1, 1);
	test_parse(NYTH_EVENT_LIST_START, 1, 6);
	test_parse_error(NYTH_ERROR_BAD_MAP_ENTRY, 1, 1);
	test_parse_none();

	test_parse_start("key bad");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("key", NYTH_NULTERM, 1, 1);
	test_parse_scalar("key", NYTH_NULTERM, 1, 1);
	test_parse_error(NYTH_ERROR_BAD_MAP_ENTRY, 1, 1);
	test_parse_none();

	test_parse_start("bad:");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("bad", NYTH_NULTERM, 1, 1);
	test_parse_scalar("bad", NYTH_NULTERM, 1, 1);
	test_parse_error(NYTH_ERROR_BAD_MAP_ENTRY, 1, 1);
	test_parse_none();



@@ 62,9 59,9 @@ int main(void) {
		"map: +\n"
		"\tbad:\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("map", NYTH_NULTERM, 1, 1);
	test_parse_scalar("map", NYTH_NULTERM, 1, 1);
	test_parse(NYTH_EVENT_MAP_START, 1, 6);
	test_parse_key("bad", NYTH_NULTERM, 2, 2);
	test_parse_scalar("bad", NYTH_NULTERM, 2, 2);
	test_parse_error(NYTH_ERROR_BAD_MAP_ENTRY, 2, 2);
	test_parse_none();



@@ 73,9 70,9 @@ int main(void) {
		"\tbad:\n"
		"\tkey: value\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("map", NYTH_NULTERM, 1, 1);
	test_parse_scalar("map", NYTH_NULTERM, 1, 1);
	test_parse(NYTH_EVENT_MAP_START, 1, 6);
	test_parse_key("bad", NYTH_NULTERM, 2, 2);
	test_parse_scalar("bad", NYTH_NULTERM, 2, 2);
	test_parse_error(NYTH_ERROR_BAD_MAP_ENTRY, 2, 2);
	test_parse_none();


M tests/test_parse_nulterm.c => tests/test_parse_nulterm.c +2 -3
@@ 3,9 3,8 @@
int main(void) {
	test_parse_start("\"hello\\x00\": \"\\x00world\"\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("hello\x00", 6, 1, 1);
	test_parse_scalar("\x00world", 6,
		NYTH_SCALAR_QUOTED, 1, 14);
	test_parse_scalar("hello\x00", 6, 1, 1);
	test_parse_scalar("\x00world", 6, 1, 14);
	test_parse(NYTH_EVENT_MAP_END, 2, 1);
	test_parse_none();


M tests/test_parse_object.c => tests/test_parse_object.c +6 -7
@@ 3,15 3,14 @@
int main(void) {
	test_parse_start("scalar: hello\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("scalar", NYTH_NULTERM, 1, 1);
	test_parse_scalar("hello", NYTH_NULTERM,
		NYTH_SCALAR_SIMPLE, 1, 9);
	test_parse_scalar("scalar", NYTH_NULTERM, 1, 1);
	test_parse_scalar("hello", NYTH_NULTERM, 1, 9);
	test_parse(NYTH_EVENT_MAP_END, 2, 1);
	test_parse_none();

	test_parse_start("map: +\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("map", NYTH_NULTERM, 1, 1);
	test_parse_scalar("map", NYTH_NULTERM, 1, 1);
	test_parse(NYTH_EVENT_MAP_START, 1, 6);
	test_parse(NYTH_EVENT_MAP_END, 2, 1);
	test_parse(NYTH_EVENT_MAP_END, 2, 1);


@@ 19,7 18,7 @@ int main(void) {

	test_parse_start("list: =\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("list", NYTH_NULTERM, 1, 1);
	test_parse_scalar("list", NYTH_NULTERM, 1, 1);
	test_parse(NYTH_EVENT_LIST_START, 1, 7);
	test_parse(NYTH_EVENT_LIST_END, 2, 1);
	test_parse(NYTH_EVENT_MAP_END, 2, 1);


@@ 27,13 26,13 @@ int main(void) {

	test_parse_start("bad: -");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("bad", NYTH_NULTERM, 1, 1);
	test_parse_scalar("bad", NYTH_NULTERM, 1, 1);
	test_parse_error(NYTH_ERROR_BAD_OBJECT, 1, 6);
	test_parse_none();

	test_parse_start("bad: :");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("bad", NYTH_NULTERM, 1, 1);
	test_parse_scalar("bad", NYTH_NULTERM, 1, 1);
	test_parse_error(NYTH_ERROR_BAD_OBJECT, 1, 6);
	test_parse_none();


M tests/test_parse_scalars_basic.c => tests/test_parse_scalars_basic.c +17 -24
@@ 6,15 6,12 @@ int main(void) {
		"raw: `hello, world`\n"
		"quoted: \"hello, world\"\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("simple", NYTH_NULTERM, 1, 1);
	test_parse_scalar("hello", NYTH_NULTERM,
		NYTH_SCALAR_SIMPLE, 1, 9);
	test_parse_key("raw", NYTH_NULTERM, 2, 1);
	test_parse_scalar("hello, world", NYTH_NULTERM,
		NYTH_SCALAR_RAW, 2, 6);
	test_parse_key("quoted", NYTH_NULTERM, 3, 1);
	test_parse_scalar("hello, world", NYTH_NULTERM,
		NYTH_SCALAR_QUOTED, 3, 9);
	test_parse_scalar("simple", NYTH_NULTERM, 1, 1);
	test_parse_scalar("hello", NYTH_NULTERM, 1, 9);
	test_parse_scalar("raw", NYTH_NULTERM, 2, 1);
	test_parse_scalar("hello, world", NYTH_NULTERM, 2, 6);
	test_parse_scalar("quoted", NYTH_NULTERM, 3, 1);
	test_parse_scalar("hello, world", NYTH_NULTERM, 3, 9);
	test_parse(NYTH_EVENT_MAP_END, 4, 1);
	test_parse_none();



@@ 23,41 20,37 @@ int main(void) {
		"raw: `hello`#world\n"
		"quoted: \"hello\"#world\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("simple", NYTH_NULTERM, 1, 1);
	test_parse_scalar("hello#world", NYTH_NULTERM,
		NYTH_SCALAR_SIMPLE, 1, 9);
	test_parse_key("raw", NYTH_NULTERM, 2, 1);
	test_parse_scalar("hello", NYTH_NULTERM,
		NYTH_SCALAR_RAW, 2, 6);
	test_parse_key("quoted", NYTH_NULTERM, 3, 1);
	test_parse_scalar("hello", NYTH_NULTERM,
		NYTH_SCALAR_QUOTED, 3, 9);
	test_parse_scalar("simple", NYTH_NULTERM, 1, 1);
	test_parse_scalar("hello#world", NYTH_NULTERM, 1, 9);
	test_parse_scalar("raw", NYTH_NULTERM, 2, 1);
	test_parse_scalar("hello", NYTH_NULTERM, 2, 6);
	test_parse_scalar("quoted", NYTH_NULTERM, 3, 1);
	test_parse_scalar("hello", NYTH_NULTERM, 3, 9);
	test_parse(NYTH_EVENT_MAP_END, 4, 1);
	test_parse_none();

	test_parse_start("12:34:56: 2001:db8::8a2e:370:7334\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("12:34:56", NYTH_NULTERM, 1, 1);
	test_parse_scalar("2001:db8::8a2e:370:7334", NYTH_NULTERM,
		NYTH_SCALAR_SIMPLE, 1, 11);
	test_parse_scalar("12:34:56", NYTH_NULTERM, 1, 1);
	test_parse_scalar("2001:db8::8a2e:370:7334", NYTH_NULTERM, 1, 11);
	test_parse(NYTH_EVENT_MAP_END, 2, 1);
	test_parse_none();

	test_parse_start("bad: hello`");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("bad", NYTH_NULTERM, 1, 1);
	test_parse_scalar("bad", NYTH_NULTERM, 1, 1);
	test_parse_error(NYTH_ERROR_UNEXPECTED_QUOTE, 1, 11);
	test_parse_none();

	test_parse_start("bad: `hello");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("bad", NYTH_NULTERM, 1, 1);
	test_parse_scalar("bad", NYTH_NULTERM, 1, 1);
	test_parse_error(NYTH_ERROR_UNMATCHED_QUOTE, 1, 6);
	test_parse_none();

	test_parse_start("bad: \"hello");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("bad", NYTH_NULTERM, 1, 1);
	test_parse_scalar("bad", NYTH_NULTERM, 1, 1);
	test_parse_error(NYTH_ERROR_UNMATCHED_QUOTE, 1, 6);
	test_parse_none();


M tests/test_parse_scalars_escaped.c => tests/test_parse_scalars_escaped.c +15 -21
@@ 3,67 3,61 @@
int main(void) {
	test_parse_start("single: \"\\b\"\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("single", NYTH_NULTERM, 1, 1);
	test_parse_scalar("\b", NYTH_NULTERM,
		NYTH_SCALAR_QUOTED, 1, 9);
	test_parse_scalar("single", NYTH_NULTERM, 1, 1);
	test_parse_scalar("\b", NYTH_NULTERM, 1, 9);
	test_parse(NYTH_EVENT_MAP_END, 2, 1);
	test_parse_none();

	test_parse_start("special: \"\\\\\\\"\"\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("special", NYTH_NULTERM, 1, 1);
	test_parse_scalar("\\\"", NYTH_NULTERM,
		NYTH_SCALAR_QUOTED, 1, 10);
	test_parse_scalar("special", NYTH_NULTERM, 1, 1);
	test_parse_scalar("\\\"", NYTH_NULTERM, 1, 10);
	test_parse(NYTH_EVENT_MAP_END, 2, 1);
	test_parse_none();

	test_parse_start("`2 bytes`: \"\\xfd\"\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("2 bytes", NYTH_NULTERM, 1, 1);
	test_parse_scalar("ý", NYTH_NULTERM,
		NYTH_SCALAR_QUOTED, 1, 12);
	test_parse_scalar("2 bytes", NYTH_NULTERM, 1, 1);
	test_parse_scalar("ý", NYTH_NULTERM, 1, 12);
	test_parse(NYTH_EVENT_MAP_END, 2, 1);
	test_parse_none();

	test_parse_start("`2 BYTES`: \"\\xFD\"\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("2 BYTES", NYTH_NULTERM, 1, 1);
	test_parse_scalar("ý", NYTH_NULTERM,
		NYTH_SCALAR_QUOTED, 1, 12);
	test_parse_scalar("2 BYTES", NYTH_NULTERM, 1, 1);
	test_parse_scalar("ý", NYTH_NULTERM, 1, 12);
	test_parse(NYTH_EVENT_MAP_END, 2, 1);
	test_parse_none();

	test_parse_start("`4 bytes`: \"\\uc702\"\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("4 bytes", NYTH_NULTERM, 1, 1);
	test_parse_scalar("윂", NYTH_NULTERM,
		NYTH_SCALAR_QUOTED, 1, 12);
	test_parse_scalar("4 bytes", NYTH_NULTERM, 1, 1);
	test_parse_scalar("윂", NYTH_NULTERM, 1, 12);
	test_parse(NYTH_EVENT_MAP_END, 2, 1);
	test_parse_none();

	test_parse_start("`8 bytes`: \"\\U0001f382\"\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("8 bytes", NYTH_NULTERM, 1, 1);
	test_parse_scalar("🎂", NYTH_NULTERM,
		NYTH_SCALAR_QUOTED, 1, 12);
	test_parse_scalar("8 bytes", NYTH_NULTERM, 1, 1);
	test_parse_scalar("🎂", NYTH_NULTERM, 1, 12);
	test_parse(NYTH_EVENT_MAP_END, 2, 1);
	test_parse_none();

	test_parse_start("truncated: \"\\U0001f3\"\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("truncated", NYTH_NULTERM, 1, 1);
	test_parse_scalar("truncated", NYTH_NULTERM, 1, 1);
	test_parse_error(NYTH_ERROR_BAD_ESCAPE_SEQUENCE, 1, 13);
	test_parse_none();

	test_parse_start("`bad hex`: \"\\U0001f3zx\"\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("bad hex", NYTH_NULTERM, 1, 1);
	test_parse_scalar("bad hex", NYTH_NULTERM, 1, 1);
	test_parse_error(NYTH_ERROR_BAD_ESCAPE_SEQUENCE, 1, 13);
	test_parse_none();

	test_parse_start("`bad unicode`: \"\\U00110000\"\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("bad unicode", NYTH_NULTERM, 1, 1);
	test_parse_scalar("bad unicode", NYTH_NULTERM, 1, 1);
	test_parse_error(NYTH_ERROR_BAD_ESCAPE_SEQUENCE, 1, 17);
	test_parse_none();


M tests/test_parse_scalars_multiline.c => tests/test_parse_scalars_multiline.c +24 -35
@@ 8,9 8,8 @@ int main(void) {
		"\tworld\n"
		"\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("implicit", NYTH_NULTERM, 1, 1);
	test_parse_scalar("hello\n\nworld\n", NYTH_NULTERM,
		NYTH_SCALAR_MULTILINE, 1, 11);
	test_parse_scalar("implicit", NYTH_NULTERM, 1, 1);
	test_parse_scalar("hello\n\nworld\n", NYTH_NULTERM, 1, 11);
	test_parse(NYTH_EVENT_MAP_END, 6, 1);
	test_parse_none();



@@ 19,9 18,8 @@ int main(void) {
		"\thello\n"
		"\t\tworld\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("implicit", NYTH_NULTERM, 1, 1);
	test_parse_scalar("hello\n\tworld\n", NYTH_NULTERM,
		NYTH_SCALAR_MULTILINE, 1, 11);
	test_parse_scalar("implicit", NYTH_NULTERM, 1, 1);
	test_parse_scalar("hello\n\tworld\n", NYTH_NULTERM, 1, 11);
	test_parse(NYTH_EVENT_MAP_END, 4, 1);
	test_parse_none();



@@ 31,9 29,8 @@ int main(void) {
		"\n"
		"\tworld");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("implicit", NYTH_NULTERM, 1, 1);
	test_parse_scalar("hello\n\nworld\n", NYTH_NULTERM,
		NYTH_SCALAR_MULTILINE, 1, 11);
	test_parse_scalar("implicit", NYTH_NULTERM, 1, 1);
	test_parse_scalar("hello\n\nworld\n", NYTH_NULTERM, 1, 11);
	test_parse(NYTH_EVENT_MAP_END, 4, 7);
	test_parse_none();



@@ 45,9 42,8 @@ int main(void) {
		"\n"
		"\t\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("implicit", NYTH_NULTERM, 1, 1);
	test_parse_scalar("hello\nworld\n", NYTH_NULTERM,
		NYTH_SCALAR_MULTILINE, 1, 11);
	test_parse_scalar("implicit", NYTH_NULTERM, 1, 1);
	test_parse_scalar("hello\nworld\n", NYTH_NULTERM, 1, 11);
	test_parse(NYTH_EVENT_MAP_END, 7, 1);
	test_parse_none();



@@ 59,9 55,8 @@ int main(void) {
		"\n"
		"|");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("explicit", NYTH_NULTERM, 1, 1);
	test_parse_scalar("hello\n\nworld\n\n", NYTH_NULTERM,
		NYTH_SCALAR_MULTILINE, 1, 11);
	test_parse_scalar("explicit", NYTH_NULTERM, 1, 1);
	test_parse_scalar("hello\n\nworld\n\n", NYTH_NULTERM, 1, 11);
	test_parse(NYTH_EVENT_MAP_END, 6, 2);
	test_parse_none();



@@ 71,25 66,22 @@ int main(void) {
		"\thello\n"
		"|");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("explicit", NYTH_NULTERM, 1, 1);
	test_parse_scalar("\nhello\n", NYTH_NULTERM,
		NYTH_SCALAR_MULTILINE, 1, 11);
	test_parse_scalar("explicit", NYTH_NULTERM, 1, 1);
	test_parse_scalar("\nhello\n", NYTH_NULTERM, 1, 11);
	test_parse(NYTH_EVENT_MAP_END, 4, 2);
	test_parse_none();

	test_parse_start("empty: |");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("empty", NYTH_NULTERM, 1, 1);
	test_parse_scalar("", NYTH_NULTERM,
		NYTH_SCALAR_MULTILINE, 1, 8);
	test_parse_scalar("empty", NYTH_NULTERM, 1, 1);
	test_parse_scalar("", NYTH_NULTERM, 1, 8);
	test_parse(NYTH_EVENT_MAP_END, 1, 9);
	test_parse_none();

	test_parse_start("empty: |\n\n\n\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("empty", NYTH_NULTERM, 1, 1);
	test_parse_scalar("", NYTH_NULTERM,
		NYTH_SCALAR_MULTILINE, 1, 8);
	test_parse_scalar("empty", NYTH_NULTERM, 1, 1);
	test_parse_scalar("", NYTH_NULTERM, 1, 8);
	test_parse(NYTH_EVENT_MAP_END, 5, 1);
	test_parse_none();



@@ 97,9 89,8 @@ int main(void) {
		"empty: |\n"
		"|\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("empty", NYTH_NULTERM, 1, 1);
	test_parse_scalar("", NYTH_NULTERM,
		NYTH_SCALAR_MULTILINE, 1, 8);
	test_parse_scalar("empty", NYTH_NULTERM, 1, 1);
	test_parse_scalar("", NYTH_NULTERM, 1, 8);
	test_parse(NYTH_EVENT_MAP_END, 3, 1);
	test_parse_none();



@@ 109,18 100,16 @@ int main(void) {
		"\tworld\n"
		"|key|: value\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("implicit", NYTH_NULTERM, 1, 1);
	test_parse_scalar("hello\nworld\n", NYTH_NULTERM,
		NYTH_SCALAR_MULTILINE, 1, 11);
	test_parse_key("|key|", NYTH_NULTERM, 4, 1);
	test_parse_scalar("value", NYTH_NULTERM,
		NYTH_SCALAR_SIMPLE, 4, 8);
	test_parse_scalar("implicit", NYTH_NULTERM, 1, 1);
	test_parse_scalar("hello\nworld\n", NYTH_NULTERM, 1, 11);
	test_parse_scalar("|key|", NYTH_NULTERM, 4, 1);
	test_parse_scalar("value", NYTH_NULTERM, 4, 8);
	test_parse(NYTH_EVENT_MAP_END, 5, 1);
	test_parse_none();

	test_parse_start("bad: | world\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("bad", NYTH_NULTERM, 1, 1);
	test_parse_scalar("bad", NYTH_NULTERM, 1, 1);
	test_parse_error(NYTH_ERROR_BAD_MULTILINE_SCALAR, 1, 6);
	test_parse_none();



@@ 129,7 118,7 @@ int main(void) {
		"\thello\n"
		"| world");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("bad", NYTH_NULTERM, 1, 1);
	test_parse_scalar("bad", NYTH_NULTERM, 1, 1);
	test_parse_error(NYTH_ERROR_BAD_MULTILINE_SCALAR, 1, 6);
	test_parse_none();


M tests/test_parse_unicode.c => tests/test_parse_unicode.c +12 -16
@@ 7,42 7,38 @@ int main(void) {
		"korean: 시현\n"
		"emoji: 🥺😻🍓\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("ascii", NYTH_NULTERM, 1, 1);
	test_parse_scalar("abcdef", NYTH_NULTERM,
		NYTH_SCALAR_SIMPLE, 1, 8);
	test_parse_key("cyrillic", NYTH_NULTERM, 2, 1);
	test_parse_scalar("кириллица", NYTH_NULTERM,
		NYTH_SCALAR_SIMPLE, 2, 11);
	test_parse_key("korean", NYTH_NULTERM, 3, 1);
	test_parse_scalar("시현", NYTH_NULTERM,
		NYTH_SCALAR_SIMPLE, 3, 9);
	test_parse_key("emoji", NYTH_NULTERM, 4, 1);
	test_parse_scalar("🥺😻🍓", NYTH_NULTERM,
		NYTH_SCALAR_SIMPLE, 4, 8);
	test_parse_scalar("ascii", NYTH_NULTERM, 1, 1);
	test_parse_scalar("abcdef", NYTH_NULTERM, 1, 8);
	test_parse_scalar("cyrillic", NYTH_NULTERM, 2, 1);
	test_parse_scalar("кириллица", NYTH_NULTERM, 2, 11);
	test_parse_scalar("korean", NYTH_NULTERM, 3, 1);
	test_parse_scalar("시현", NYTH_NULTERM, 3, 9);
	test_parse_scalar("emoji", NYTH_NULTERM, 4, 1);
	test_parse_scalar("🥺😻🍓", NYTH_NULTERM, 4, 8);
	test_parse(NYTH_EVENT_MAP_END, 5, 1);
	test_parse_none();

	test_parse_start("bad-leading: \xff\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("bad-leading", NYTH_NULTERM, 1, 1);
	test_parse_scalar("bad-leading", NYTH_NULTERM, 1, 1);
	test_parse_error(NYTH_ERROR_BAD_UTF8, 1, 14);
	test_parse_none();

	test_parse_start("bad-cont: \xe0\xff\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("bad-cont", NYTH_NULTERM, 1, 1);
	test_parse_scalar("bad-cont", NYTH_NULTERM, 1, 1);
	test_parse_error(NYTH_ERROR_BAD_UTF8, 1, 11);
	test_parse_none();

	test_parse_start("overlong: \xf0\x82\x82\xac\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("overlong", NYTH_NULTERM, 1, 1);
	test_parse_scalar("overlong", NYTH_NULTERM, 1, 1);
	test_parse_error(NYTH_ERROR_BAD_UTF8, 1, 11);
	test_parse_none();

	test_parse_start("surrogate: \xed\xa1\xb7\n");
	test_parse(NYTH_EVENT_MAP_START, 1, 1);
	test_parse_key("surrogate", NYTH_NULTERM, 1, 1);
	test_parse_scalar("surrogate", NYTH_NULTERM, 1, 1);
	test_parse_error(NYTH_ERROR_BAD_UTF8, 1, 12);
	test_parse_none();


M tests/test_scalar_as.c => tests/test_scalar_as.c +2 -7
@@ 5,9 5,8 @@

static struct nyth_scalar scalar(const char *content) {
	struct nyth_scalar scalar;
	scalar.content.data = content;
	scalar.content.length = strlen(content);
	scalar.type = NYTH_SCALAR_SIMPLE;
	scalar.data = content;
	scalar.length = strlen(content);
	return scalar;
}



@@ 20,10 19,6 @@ int main(void) {
	s = scalar("NULL");
	assert(nyth_scalar_as_null(&s) == NYTH_ERROR_FORMAT_MISMATCH);

	s = scalar("null");
	s.type = NYTH_SCALAR_RAW;
	assert(nyth_scalar_as_null(&s) == NYTH_ERROR_FORMAT_MISMATCH);

	bool b;

	s = scalar("true");