add json tests

This commit is contained in:
sfja 2026-03-30 12:03:15 +02:00
parent a775a68771
commit 18dfc724ce
3 changed files with 59 additions and 8 deletions

View File

@ -46,7 +46,6 @@ objects = $(objects_without_main) $(main_object)
target = $(build_dir)/backend target = $(build_dir)/backend
test_sources = $(shell find $(test_dir) -name '*.cpp') test_sources = $(shell find $(test_dir) -name '*.cpp')
test_objects = $(test_sources:$(test_dir)/%.cpp=$(obj_dir)/$(test_dir)/%.o)
test_targets = $(test_sources:$(test_dir)/%.cpp=$(build_dir)/$(test_dir)/test_%) test_targets = $(test_sources:$(test_dir)/%.cpp=$(build_dir)/$(test_dir)/test_%)
all: $(target) $(test_targets) all: $(target) $(test_targets)
@ -61,7 +60,7 @@ $(obj_dir)/%.o:%.cpp
test: $(test_targets) test: $(test_targets)
printf "%s\n" $^ | xargs -I % sh -c 'echo "- %..." && ./% && echo "- %: OK" || (echo "- %: FAILED" && exit 1)' printf "%s\n" $^ | xargs -I % sh -c 'echo "- %..." && ./% && echo "- %: OK" || (echo "- %: FAILED" && exit 1)'
$(build_dir)/$(test_dir)/test_%: $(test_objects) $(objects_without_main) $(build_dir)/$(test_dir)/test_%: $(obj_dir)/tests/%.o $(objects_without_main)
@mkdir -p $(dir $@) @mkdir -p $(dir $@)
$(LD) -o $@ $(CXXFLAGS) $^ $(LDFLAGS) $(LD) -o $@ $(CXXFLAGS) $^ $(LDFLAGS)

View File

@ -47,7 +47,7 @@ public:
Loc loc = { m_idx, m_line, m_col }; Loc loc = { m_idx, m_line, m_col };
size_t* i = &m_idx; size_t* i = &m_idx;
if (*i >= m_len) [[unlikely]] { if (*i >= m_len) [[unlikely]] {
return tok(TokTy::Eof, loc); return Tok { TokTy::Eof, std::string_view(nullptr, 0), loc };
} }
bool matched = false; bool matched = false;
while (*i < m_len && std::strchr(" \t\r\n", m_text[*i]) != NULL) { while (*i < m_len && std::strchr(" \t\r\n", m_text[*i]) != NULL) {
@ -139,7 +139,7 @@ private:
} }
step(); step();
} }
if (*i < m_len && m_text[*i] != '\n') [[unlikely]] { if (*i < m_len && m_text[*i] == '\n') [[unlikely]] {
return std::unexpected(Error { loc, "malformed string" }); return std::unexpected(Error { loc, "malformed string" });
} }
if (*i >= m_len && m_text[*i] != '\"') [[unlikely]] { if (*i >= m_len && m_text[*i] != '\"') [[unlikely]] {
@ -167,7 +167,7 @@ auto literal_to_string(std::string_view text)
-> Result<std::string, std::string> -> Result<std::string, std::string>
{ {
auto result = std::string(); auto result = std::string();
for (size_t i = 1; i < text.size() - 2; ++i) { for (size_t i = 1; i < text.size() - 1; ++i) {
if (text[i] == '\\') [[unlikely]] { if (text[i] == '\\') [[unlikely]] {
i += 1; i += 1;
if (i >= text.size()) [[unlikely]] { if (i >= text.size()) [[unlikely]] {
@ -206,7 +206,8 @@ public:
Parser(std::string_view text) Parser(std::string_view text)
: m_tokenizer(text) : m_tokenizer(text)
{ {
step().value(); auto result = step();
result.value();
} }
auto parse() -> Result<std::unique_ptr<Value>> auto parse() -> Result<std::unique_ptr<Value>>
@ -711,7 +712,7 @@ auto Value::write(std::ostream& stream, WriteProfile profile) const
} }
} }
auto Value::to_string(WriteProfile profile) -> std::string auto Value::to_string(WriteProfile profile) const -> std::string
{ {
auto stream = std::stringstream(); auto stream = std::stringstream();
if (profile == WriteProfile::Minified) { if (profile == WriteProfile::Minified) {

View File

@ -9,6 +9,7 @@
#include <string> #include <string>
#include <string_view> #include <string_view>
#include <unordered_map> #include <unordered_map>
#include <utility>
#include <variant> #include <variant>
#include <vector> #include <vector>
@ -267,7 +268,7 @@ public:
auto write(std::ostream& stream, auto write(std::ostream& stream,
WriteProfile profile = WriteProfile::Minified) const; WriteProfile profile = WriteProfile::Minified) const;
auto to_string(WriteProfile profile = WriteProfile::Minified) auto to_string(WriteProfile profile = WriteProfile::Minified) const
-> std::string; -> std::string;
private: private:
@ -275,6 +276,56 @@ private:
Data m_data; Data m_data;
}; };
inline auto operator==(const Value& lhs, const Value& rhs) -> bool
{
if (lhs.type() != rhs.type())
return false;
switch (lhs.type()) {
case Type::Null:
case Type::False:
case Type::True:
return true;
case Type::I64:
return lhs.get_i64() == rhs.get_i64();
case Type::F64:
return lhs.get_f64() == rhs.get_f64();
case Type::String:
return lhs.get_string() == rhs.get_string();
case Type::Array:
if (lhs.size() != rhs.size())
return false;
for (size_t i = 0; i < lhs.size(); ++i)
if (lhs[i] != rhs[i])
return false;
return true;
case Type::Object:
if (lhs.size() != rhs.size())
return false;
for (const auto& [key, val] : lhs.get_underlying_object()) {
if (!rhs.has(key))
return false;
if (*val != rhs[key])
return false;
}
return true;
break;
}
std::unreachable();
}
auto parse(std::string_view text) -> Result<std::unique_ptr<Value>>; auto parse(std::string_view text) -> Result<std::unique_ptr<Value>>;
} }
template <> struct std::formatter<mst::json::Value> {
constexpr auto parse(std::format_parse_context& ctx)
{
return ctx.begin();
}
auto format(const mst::json::Value& value, std::format_context& ctx) const
{
return std::format_to(
ctx.out(), "{}", value.to_string(mst::json::WriteProfile::Pretty));
}
};