feat: the crafter-review skeleton, and its semantics for text, JSON and crypto
What the rest of crafter-review stands on. - project.cpp: a static crafter.review-lib and the crafter-review executable; Crafter.Network without QUIC, pinned, or with --local from the checkout next to this one; Crafter.LintRules' lite rules, pinned; the version from CRAFTER_REVIEW_VERSION, git describe or 0.0.0-dev. - :Core and :Driver: the version, PromptVersion 1, --version. - :Json: nlohmann's ordered_json, which keeps key order and unknown keys, so the files every driver on a machine shares survive a rewrite; dumps in the indented file layout, the spaced one-line form and the compact sorted form the structured-output schema needs byte for byte. - :Crypto: SHA-256/SHA-1/MD5, HMAC, PBKDF2 through libcrypto; base64 and base32 written out, because what they refuse, and with which message, is pinned. - :Unicode, Unicode 16.0.0's tables: whitespace, line breaks, decimal digits, printable characters, case mappings and case-insensitive folds. - :Text: string functions on code points, number parsing, the shortest float form and half-even rounding, printed values, shell patterns, URL splitting and unquoting, POSIX paths, universal newlines and the replacing UTF-8 decoder. Tests: Crypto (published vectors), Text (293 reference vectors). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
e176dc87b5
commit
36852edf63
16 changed files with 7845 additions and 0 deletions
68
interfaces/Crafter.Review-Json.cppm
Normal file
68
interfaces/Crafter.Review-Json.cppm
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||||
|
||||
module;
|
||||
#include "../lib/json.hpp"
|
||||
export module Crafter.Review:Json;
|
||||
import std;
|
||||
|
||||
export namespace Crafter::Review {
|
||||
// nlohmann/json, vendored unmodified at lib/json.hpp. ordered_json keeps an
|
||||
// object's keys in the order they were read or inserted, and a duplicate
|
||||
// key keeps its first position and its last value. That is what lets a
|
||||
// driver rewrite the files every driver on the machine shares — the seat
|
||||
// board, the ledger, unit-state.json — with every key in place, the ones
|
||||
// it does not know about included.
|
||||
using Json = nlohmann::ordered_json;
|
||||
|
||||
// Indented by one space per level, one key or item per line, \uXXXX
|
||||
// escapes for everything outside ASCII: every file this pipeline writes. A
|
||||
// string that is not valid UTF-8 has its bad bytes replaced rather than
|
||||
// throwing in the middle of a write.
|
||||
//
|
||||
// A float from 1e15 up to 1e16 prints with an exponent (AGENTS.md).
|
||||
// Readers parse it either way, and none of the files depends on it.
|
||||
inline std::string DumpFile(const Json& value) {
|
||||
return value.dump(1, ' ', true, Json::error_handler_t::replace);
|
||||
}
|
||||
|
||||
// Keys in code-point order, no whitespace, ASCII only. The
|
||||
// structured-output schema goes to claude in this form and its bytes are
|
||||
// part of the prompt cache, so they must not move by a byte (the goldens
|
||||
// hold them); dedupe keys use it too.
|
||||
inline std::string DumpCompactSorted(const Json& value) {
|
||||
// Converted to nlohmann::json, which keeps objects in a std::map: that
|
||||
// sorts the keys by byte, and byte order is code-point order in UTF-8.
|
||||
const nlohmann::json sorted(value);
|
||||
return sorted.dump(-1, ' ', true, nlohmann::json::error_handler_t::replace);
|
||||
}
|
||||
|
||||
// On one line, with ", " and ": " between items: where a reader matches
|
||||
// on the bytes, such as the first line of an opencode output, `{"type":
|
||||
// "review_gates_meta", …`, which salvage looks for.
|
||||
inline std::string DumpSpaced(const Json& value) {
|
||||
if (value.is_object()) {
|
||||
std::string out = "{";
|
||||
bool first = true;
|
||||
for (const auto& [key, child] : value.items()) {
|
||||
if (!first) out += ", ";
|
||||
first = false;
|
||||
out += Json(key).dump(-1, ' ', true, Json::error_handler_t::replace);
|
||||
out += ": ";
|
||||
out += DumpSpaced(child);
|
||||
}
|
||||
return std::format("{}}}", out);
|
||||
}
|
||||
if (value.is_array()) {
|
||||
std::string out = "[";
|
||||
bool first = true;
|
||||
for (const Json& child : value) {
|
||||
if (!first) out += ", ";
|
||||
first = false;
|
||||
out += DumpSpaced(child);
|
||||
}
|
||||
return std::format("{}]", out);
|
||||
}
|
||||
return value.dump(-1, ' ', true, Json::error_handler_t::replace);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue