aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.ucl/parser.ccm
blob: f817b764af921c0a48ad07b549db931125c7fa2c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
/*
 * This source code is released into the public domain.
 */

module;

#include <format>
#include <functional>
#include <memory>
#include <string>
#include <vector>

#include <ucl.h>

export module nihil.ucl:parser;

import nihil;
import :error;
import :object;
import :map;

namespace nihil::ucl {

/*
 * Exception thrown when an issue occurs parsing UCL.
 */
export struct parse_error : error {
	parse_error(std::string what);
};

// UCL parser flags.
export inline constexpr int parser_key_lower = UCL_PARSER_KEY_LOWERCASE;
export inline constexpr int parser_zerocopy = UCL_PARSER_ZEROCOPY;
export inline constexpr int parser_no_time = UCL_PARSER_NO_TIME;

// A macro handler.  This proxies the C API callback to the C++ API.
using macro_callback_t = bool (std::string_view);

struct macro_handler {
	std::function<macro_callback_t> callback;

	// Handle a callback from the C API.
	static auto handle(
		unsigned char const *data,
		std::size_t len, void
		*ud)
	-> bool;
};

/*
 * A UCL parser.  This wraps the C ucl_parser API.
 */
export struct parser {
	// Create a new parser with the given flags.
	parser(int flags);

	// Create a new parser with the default flags.
	parser();

	// Destroy our parser when we're destroyed.
	~parser();

	// Add a parser macro.  Unlike ucl_parser_register_macro, this doesn't
	// take a userdata parameter; it's assumed the user will use lambda
	// capture or similar if needed.
	template<std::invocable<std::string_view> F>
	auto register_macro(this parser &self,
			    std::string_view name,
			    F &&func) -> void
	requires (std::same_as<bool, std::invoke_result<F>>)
	{
		auto handler = std::make_unique<macro_handler>(
					std::move(func));

		auto cname = std::string(name);
		::ucl_parser_register_macro(self._parser, cname.c_str(),
					    &macro_handler::handle,
					    handler.get());

		self._macros.emplace_back(std::move(handler));
	}

	// Add a parser variable.
	auto register_value(this parser &self,
			    std::string_view variable,
			    std::string_view value) -> void;

	// Add data to the parser.
	auto add(this parser &self,
		 std::ranges::contiguous_range auto &&data)
	-> void
	// Only bytes (chars) are permitted.
	requires(sizeof(std::ranges::range_value_t<decltype(data)>) == 1)
	{
		// UCL accepts unsigned chars, but this is quite unhelpful
		// when reading from files or strings.
		auto dptr = reinterpret_cast<unsigned char const *>(
					std::ranges::data(data));

		auto ret = ::ucl_parser_add_chunk(self._parser, dptr,
						  std::ranges::size(data));
		if (ret == false)
			throw parse_error(::ucl_parser_get_error(self._parser));
	}

	auto add(this parser &self, std::ranges::range auto &&data)
	-> void
	requires (!std::ranges::contiguous_range<decltype(data)>)
	{
		auto cdata = std::vector<char>(
				std::from_range,
				std::forward<decltype(data)>(data));
		return self.add(std::move(cdata));
	}

	// Return the top object of this parser.
	auto top(this parser &self) -> map<object>;

private:
	// The parser object.  Should never be null, unless we've been
	// moved-from.
	ucl_parser *_parser = nullptr;

	// Functions added by register_macro.  We have to store these as
	// pointers because we pass the address to libucl.
	std::vector<std::unique_ptr<macro_handler>> _macros;
};

// Utility function to parse something and return the top-level object.
export auto parse(std::ranges::range auto &&data) -> map<object> {
	auto p = parser();
	p.add(std::forward<decltype(data)>(data));
	return p.top();
}

} // namespace nihil::ucl