Skip to content

Commit 06c72b2

Browse files
authored
Greatly improve the compile contrib CLI (#680)
Signed-off-by: Juan Cruz Viotti <jv@jviotti.com>
1 parent e8b97a5 commit 06c72b2

2 files changed

Lines changed: 158 additions & 32 deletions

File tree

contrib/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@ if(BLAZE_COMPILER AND BLAZE_EVALUATOR)
33
FOLDER "Blaze/Contrib" SOURCES compile.cc)
44
target_link_libraries(sourcemeta_blaze_contrib_compile
55
PRIVATE sourcemeta::core::json)
6+
target_link_libraries(sourcemeta_blaze_contrib_compile
7+
PRIVATE sourcemeta::core::jsonpointer)
68
target_link_libraries(sourcemeta_blaze_contrib_compile
79
PRIVATE sourcemeta::core::jsonschema)
10+
target_link_libraries(sourcemeta_blaze_contrib_compile
11+
PRIVATE sourcemeta::core::options)
812
target_link_libraries(sourcemeta_blaze_contrib_compile
913
PRIVATE sourcemeta::blaze::compiler)
1014

contrib/compile.cc

Lines changed: 154 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,168 @@
11
#include <sourcemeta/blaze/compiler.h>
22
#include <sourcemeta/core/json.h>
3+
#include <sourcemeta/core/jsonpointer.h>
4+
#include <sourcemeta/core/options.h>
35

4-
#include <chrono> // std::chrono
5-
#include <cstdlib> // EXIT_SUCCESS, EXIT_FAILURE
6-
#include <iostream> // std::cerr
6+
#include <chrono> // std::chrono
7+
#include <cstdlib> // EXIT_SUCCESS, EXIT_FAILURE
8+
#include <filesystem> // std::filesystem
9+
#include <iostream> // std::cerr, std::cout
10+
#include <optional> // std::optional, std::nullopt
11+
#include <string> // std::string
12+
#include <string_view> // std::string_view
13+
#include <utility> // std::pair
14+
#include <vector> // std::vector
15+
16+
using ResolveDirectories =
17+
std::vector<std::pair<std::string, std::filesystem::path>>;
18+
19+
static auto parse_resolve_directory(const std::string_view value)
20+
-> std::optional<std::pair<std::string, std::filesystem::path>> {
21+
const auto comma{value.find(',')};
22+
if (comma == std::string_view::npos || comma == 0 ||
23+
comma == value.size() - 1) {
24+
return std::nullopt;
25+
}
26+
27+
return std::pair{std::string{value.substr(0, comma)},
28+
std::filesystem::path{value.substr(comma + 1)}};
29+
}
30+
31+
static auto resolve_schema(const std::string_view identifier,
32+
const ResolveDirectories &resolve_directories)
33+
-> std::optional<sourcemeta::core::JSON> {
34+
for (const auto &[prefix, directory] : resolve_directories) {
35+
if (!identifier.starts_with(prefix)) {
36+
continue;
37+
}
38+
39+
auto suffix{identifier.substr(prefix.size())};
40+
if (!suffix.empty() && suffix.front() == '/') {
41+
suffix.remove_prefix(1);
42+
}
43+
44+
const auto file_path{std::filesystem::weakly_canonical(
45+
directory / std::filesystem::path{suffix})};
46+
const auto directory_string{directory.string()};
47+
const auto file_string{file_path.string()};
48+
if (!file_string.starts_with(directory_string)) {
49+
continue;
50+
}
51+
52+
if (std::filesystem::exists(file_path)) {
53+
return sourcemeta::core::read_json(file_path);
54+
}
55+
}
56+
57+
return sourcemeta::core::schema_resolver(identifier);
58+
}
759

860
auto main(int argc, char **argv) noexcept -> int {
9-
if (argc < 3) {
10-
std::cerr << "Usage: " << argv[0] << " <fast|exhaustive> <schema.json>\n";
61+
sourcemeta::core::Options options;
62+
options.flag("fast", {"f"});
63+
options.option("default-dialect", {"d"});
64+
options.option("resolve-directory", {"r"});
65+
options.option("path", {"p"});
66+
67+
try {
68+
options.parse(argc, argv);
69+
} catch (const sourcemeta::core::OptionsError &error) {
70+
std::cerr << "error: " << error.what() << "\n";
1171
return EXIT_FAILURE;
1272
}
1373

14-
const std::string mode_string{argv[1]};
15-
auto mode{sourcemeta::blaze::Mode::FastValidation};
16-
if (mode_string == "fast") {
17-
std::cerr << "Choosing fast mode\n";
18-
} else if (mode_string == "exhaustive") {
19-
std::cerr << "Choosing exhaustive mode\n";
20-
mode = sourcemeta::blaze::Mode::Exhaustive;
21-
} else {
22-
std::cerr << "Invalid mode: " << mode_string << "\n";
74+
const auto &positional{options.positional()};
75+
if (positional.empty()) {
76+
std::cerr << "Usage: " << argv[0]
77+
<< " [--fast] [--default-dialect <uri>] "
78+
"[--resolve-directory <uri-prefix>,<dir>]... "
79+
"[--path <pointer>] <schema.json>\n";
2380
return EXIT_FAILURE;
2481
}
2582

26-
const auto schema{sourcemeta::core::read_json(argv[2])};
27-
std::cerr << "Compiling schema: " << argv[2] << "\n";
28-
const auto compile_start{std::chrono::high_resolution_clock::now()};
29-
const auto schema_template{sourcemeta::blaze::compile(
30-
schema, sourcemeta::core::schema_walker,
31-
sourcemeta::core::schema_resolver,
32-
sourcemeta::blaze::default_schema_compiler, mode)};
33-
const auto compile_end{std::chrono::high_resolution_clock::now()};
34-
const auto compile_duration{
35-
std::chrono::duration_cast<std::chrono::milliseconds>(compile_end -
36-
compile_start)};
37-
std::cerr << "Took: " << compile_duration.count() << "ms\n";
38-
std::cerr << "Number of generated instructions: "
39-
<< schema_template.targets[0].size() << "\n";
40-
41-
sourcemeta::core::prettify(sourcemeta::blaze::to_json(schema_template),
42-
std::cout);
43-
std::cout << "\n";
83+
const auto mode{options.contains("fast")
84+
? sourcemeta::blaze::Mode::FastValidation
85+
: sourcemeta::blaze::Mode::Exhaustive};
86+
std::cerr << "Mode: "
87+
<< (mode == sourcemeta::blaze::Mode::FastValidation ? "fast"
88+
: "exhaustive")
89+
<< "\n";
90+
91+
std::string default_dialect;
92+
if (options.contains("default-dialect")) {
93+
default_dialect = options.at("default-dialect").front();
94+
std::cerr << "Default dialect: " << default_dialect << "\n";
95+
}
96+
97+
try {
98+
std::vector<std::pair<std::string, std::filesystem::path>>
99+
resolve_directories;
100+
if (options.contains("resolve-directory")) {
101+
for (const auto &value : options.at("resolve-directory")) {
102+
auto entry{parse_resolve_directory(value)};
103+
if (!entry.has_value()) {
104+
std::cerr << "error: --resolve-directory value must be "
105+
"<uri-prefix>,<directory>\n";
106+
return EXIT_FAILURE;
107+
}
108+
109+
const auto canonical{
110+
std::filesystem::weakly_canonical(entry.value().second)};
111+
if (!std::filesystem::is_directory(canonical)) {
112+
std::cerr << "error: not a directory: " << entry.value().second
113+
<< "\n";
114+
return EXIT_FAILURE;
115+
}
116+
117+
entry.value().second = canonical;
118+
std::cerr << "Resolve: " << entry.value().first << " -> " << canonical
119+
<< "\n";
120+
resolve_directories.push_back(std::move(entry.value()));
121+
}
122+
}
123+
124+
const auto resolver{
125+
[&resolve_directories](const std::string_view identifier)
126+
-> std::optional<sourcemeta::core::JSON> {
127+
return resolve_schema(identifier, resolve_directories);
128+
}};
129+
130+
auto document{
131+
sourcemeta::core::read_json(std::filesystem::path{positional.front()})};
132+
std::cerr << "Input: " << positional.front() << "\n";
133+
134+
if (options.contains("path")) {
135+
const auto pointer{sourcemeta::core::to_pointer(
136+
std::string{options.at("path").front()})};
137+
const auto *result{sourcemeta::core::try_get(document, pointer)};
138+
if (result == nullptr) {
139+
std::cerr << "error: path not found in input: "
140+
<< options.at("path").front() << "\n";
141+
return EXIT_FAILURE;
142+
}
143+
144+
auto extracted{*result};
145+
document = std::move(extracted);
146+
}
147+
148+
const auto compile_start{std::chrono::high_resolution_clock::now()};
149+
const auto schema_template{sourcemeta::blaze::compile(
150+
document, sourcemeta::core::schema_walker, resolver,
151+
sourcemeta::blaze::default_schema_compiler, mode, default_dialect)};
152+
const auto compile_end{std::chrono::high_resolution_clock::now()};
153+
const auto compile_duration{
154+
std::chrono::duration_cast<std::chrono::milliseconds>(compile_end -
155+
compile_start)};
156+
std::cerr << "Compiled in " << compile_duration.count() << "ms ("
157+
<< schema_template.targets[0].size() << " instructions)\n";
158+
159+
sourcemeta::core::prettify(sourcemeta::blaze::to_json(schema_template),
160+
std::cout);
161+
std::cout << "\n";
162+
} catch (const std::exception &error) {
163+
std::cerr << "error: " << error.what() << "\n";
164+
return EXIT_FAILURE;
165+
}
44166

45167
return EXIT_SUCCESS;
46168
}

0 commit comments

Comments
 (0)