Skip to content

Commit 68479eb

Browse files
authored
Merge pull request #150 from rime/config
feat(config): config compiler plugins for auto-patching and supporting legacy import_preset syntax
2 parents 2387dfb + 88f5a0c commit 68479eb

19 files changed

+494
-237
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//
2+
// Copyright RIME Developers
3+
// Distributed under the BSD License
4+
//
5+
#include <boost/algorithm/string.hpp>
6+
#include <rime/config/config_compiler_impl.h>
7+
#include <rime/config/plugins.h>
8+
9+
namespace rime {
10+
11+
static string remove_suffix(const string& input, const string& suffix) {
12+
return boost::ends_with(input, suffix) ?
13+
input.substr(0, input.length() - suffix.length()) : input;
14+
}
15+
16+
// auto-patch applies to all loaded config resources, including dependencies.
17+
// therefore it's done at the end of Compile phase.
18+
bool AutoPatchConfigPlugin::ReviewCompileOutput(ConfigCompiler* compiler,
19+
an<ConfigResource> resource) {
20+
if (boost::ends_with(resource->resource_id, ".custom"))
21+
return true;
22+
// skip auto-patch if there is already an explicit `__patch` at the root node
23+
auto root_deps = compiler->GetDependencies(resource->resource_id + ":");
24+
if (!root_deps.empty() && root_deps.back()->priority() >= kPatch)
25+
return true;
26+
auto patch_resource_id =
27+
remove_suffix(resource->resource_id, ".schema") + ".custom";
28+
LOG(INFO) << "auto-patch " << resource->resource_id << ":/__patch: "
29+
<< patch_resource_id << ":/patch?";
30+
compiler->Push(resource);
31+
compiler->AddDependency(
32+
New<PatchReference>(Reference{patch_resource_id, "patch", true}));
33+
compiler->Pop();
34+
return true;
35+
}
36+
37+
bool AutoPatchConfigPlugin::ReviewLinkOutput(ConfigCompiler* compiler,
38+
an<ConfigResource> resource) {
39+
return true;
40+
}
41+
42+
} // namespace rime

src/rime/config/config_compiler.cc

+41-103
Original file line numberDiff line numberDiff line change
@@ -2,100 +2,13 @@
22
#include <rime/common.h>
33
#include <rime/resource.h>
44
#include <rime/config/config_compiler.h>
5+
#include <rime/config/config_compiler_impl.h>
56
#include <rime/config/config_data.h>
67
#include <rime/config/config_types.h>
8+
#include <rime/config/plugins.h>
79

810
namespace rime {
911

10-
enum DependencyPriority {
11-
kPendingChild = 0,
12-
kInclude = 1,
13-
kPatch = 2,
14-
};
15-
16-
struct Dependency {
17-
an<ConfigItemRef> target;
18-
19-
virtual DependencyPriority priority() const = 0;
20-
bool blocking() const {
21-
return priority() > kPendingChild;
22-
}
23-
virtual string repr() const = 0;
24-
virtual bool Resolve(ConfigCompiler* compiler) = 0;
25-
};
26-
27-
template <class StreamT>
28-
StreamT& operator<< (StreamT& stream, const Dependency& dep) {
29-
return stream << dep.repr();
30-
}
31-
32-
struct PendingChild : Dependency {
33-
string child_path;
34-
an<ConfigItemRef> child_ref;
35-
36-
PendingChild(const string& path, const an<ConfigItemRef>& ref)
37-
: child_path(path), child_ref(ref) {
38-
}
39-
DependencyPriority priority() const override {
40-
return kPendingChild;
41-
}
42-
string repr() const override {
43-
return "PendingChild(" + child_path + ")";
44-
}
45-
bool Resolve(ConfigCompiler* compiler) override;
46-
};
47-
48-
string Reference::repr() const {
49-
return resource_id + ":" + local_path + (optional ? " <optional>" : "");
50-
}
51-
52-
template <class StreamT>
53-
StreamT& operator<< (StreamT& stream, const Reference& reference) {
54-
return stream << reference.repr();
55-
}
56-
57-
struct IncludeReference : Dependency {
58-
IncludeReference(const Reference& r) : reference(r) {
59-
}
60-
DependencyPriority priority() const override {
61-
return kInclude;
62-
}
63-
string repr() const override {
64-
return "Include(" + reference.repr() + ")";
65-
}
66-
bool Resolve(ConfigCompiler* compiler) override;
67-
68-
Reference reference;
69-
};
70-
71-
struct PatchReference : Dependency {
72-
PatchReference(const Reference& r) : reference(r) {
73-
}
74-
DependencyPriority priority() const override {
75-
return kPatch;
76-
}
77-
string repr() const override {
78-
return "Patch(" + reference.repr() + ")";
79-
}
80-
bool Resolve(ConfigCompiler* compiler) override;
81-
82-
Reference reference;
83-
};
84-
85-
struct PatchLiteral : Dependency {
86-
an<ConfigMap> patch;
87-
88-
PatchLiteral(an<ConfigMap> map) : patch(map) {
89-
}
90-
DependencyPriority priority() const override {
91-
return kPatch;
92-
}
93-
string repr() const override {
94-
return "Patch(<literal>)";
95-
}
96-
bool Resolve(ConfigCompiler* compiler) override;
97-
};
98-
9912
struct ConfigDependencyGraph {
10013
map<string, of<ConfigResource>> resources;
10114
vector<of<ConfigItemRef>> node_stack;
@@ -114,12 +27,18 @@ struct ConfigDependencyGraph {
11427
key_stack.pop_back();
11528
}
11629

117-
string current_resource_id() const {
118-
return key_stack.empty() ? string()
119-
: boost::trim_right_copy_if(key_stack.front(), boost::is_any_of(":"));
120-
}
30+
string current_resource_id() const;
12131
};
12232

33+
string ConfigDependencyGraph::current_resource_id() const {
34+
return key_stack.empty() ? string()
35+
: boost::trim_right_copy_if(key_stack.front(), boost::is_any_of(":"));
36+
}
37+
38+
string Reference::repr() const {
39+
return resource_id + ":" + local_path + (optional ? " <optional>" : "");
40+
}
41+
12342
bool PendingChild::Resolve(ConfigCompiler* compiler) {
12443
return compiler->ResolveDependencies(child_path);
12544
}
@@ -156,8 +75,7 @@ bool PatchReference::Resolve(ConfigCompiler* compiler) {
15675
return false;
15776
}
15877
PatchLiteral patch{As<ConfigMap>(item)};
159-
patch.target = target;
160-
return patch.Resolve(compiler);
78+
return patch.TargetedAt(target).Resolve(compiler);
16179
}
16280

16381
static bool AppendToString(an<ConfigItemRef> target, an<ConfigValue> value) {
@@ -223,7 +141,7 @@ inline static bool IsMerging(const string& key,
223141
bool merge_tree) {
224142
return key == ConfigCompiler::MERGE_DIRECTIVE ||
225143
boost::ends_with(key, ADD_SUFFIX_OPERATOR) ||
226-
(merge_tree && Is<ConfigMap>(value) &&
144+
(merge_tree && (!value || Is<ConfigMap>(value)) &&
227145
!boost::ends_with(key, EQU_SUFFIX_OPERATOR));
228146
}
229147

@@ -294,7 +212,7 @@ void ConfigDependencyGraph::Add(an<Dependency> dependency) {
294212
<< node_stack.size();
295213
if (node_stack.empty()) return;
296214
const auto& target = node_stack.back();
297-
dependency->target = target;
215+
dependency->TargetedAt(target);
298216
auto target_path = ConfigData::JoinPath(key_stack);
299217
auto& target_deps = deps[target_path];
300218
bool target_was_pending = !target_deps.empty();
@@ -326,8 +244,10 @@ void ConfigDependencyGraph::Add(an<Dependency> dependency) {
326244
}
327245
}
328246

329-
ConfigCompiler::ConfigCompiler(ResourceResolver* resource_resolver)
247+
ConfigCompiler::ConfigCompiler(ResourceResolver* resource_resolver,
248+
ConfigCompilerPlugin* plugin)
330249
: resource_resolver_(resource_resolver),
250+
plugin_(plugin),
331251
graph_(new ConfigDependencyGraph) {
332252
}
333253

@@ -353,6 +273,10 @@ void ConfigCompiler::AddDependency(an<Dependency> dependency) {
353273
graph_->Add(dependency);
354274
}
355275

276+
void ConfigCompiler::Push(an<ConfigResource> resource) {
277+
graph_->Push(resource, resource->resource_id + ":");
278+
}
279+
356280
void ConfigCompiler::Push(an<ConfigList> config_list, size_t index) {
357281
graph_->Push(
358282
New<ConfigListEntryRef>(nullptr, config_list, index),
@@ -378,10 +302,12 @@ an<ConfigResource> ConfigCompiler::Compile(const string& file_name) {
378302
auto resource_id = resource_resolver_->ToResourceId(file_name);
379303
auto resource = New<ConfigResource>(resource_id, New<ConfigData>());
380304
graph_->resources[resource_id] = resource;
381-
graph_->Push(resource, resource_id + ":");
305+
Push(resource);
382306
resource->loaded = resource->data->LoadFromFile(
383307
resource_resolver_->ResolvePath(resource_id).string(), this);
384-
graph_->Pop();
308+
Pop();
309+
if (plugin_)
310+
plugin_->ReviewCompileOutput(this, resource);
385311
return resource;
386312
}
387313

@@ -462,12 +388,22 @@ bool ConfigCompiler::resolved(const string& full_path) const {
462388
return found == graph_->deps.end() || found->second.empty();
463389
}
464390

391+
vector<of<Dependency>> ConfigCompiler::GetDependencies(const string& path) {
392+
auto found = graph_->deps.find(path);
393+
return found == graph_->deps.end() ? vector<of<Dependency>>() : found->second;
394+
}
395+
465396
static an<ConfigItem> ResolveReference(ConfigCompiler* compiler,
466397
const Reference& reference) {
467398
auto resource = compiler->GetCompiledResource(reference.resource_id);
468399
if (!resource) {
469400
DLOG(INFO) << "resource not loaded, compiling: " << reference.resource_id;
470401
resource = compiler->Compile(reference.resource_id);
402+
// dependency doesn't require full resolution, this allows non conflicting
403+
// mutual references between config files.
404+
// this call is made even if resource is not loaded because plugins can
405+
// edit the empty config data, adding new dependencies.
406+
ResolveBlockingDependencies(compiler, reference.resource_id + ":");
471407
if (!resource->loaded) {
472408
if (reference.optional) {
473409
LOG(INFO) << "optional resource not loaded: " << reference.resource_id;
@@ -552,15 +488,17 @@ bool ConfigCompiler::Link(an<ConfigResource> target) {
552488
LOG(ERROR) << "resource not found: " << target->resource_id;
553489
return false;
554490
}
555-
return ResolveDependencies(found->first + ":");
491+
return ResolveDependencies(found->first + ":") &&
492+
(plugin_ ? plugin_->ReviewLinkOutput(this, target) : true);
556493
}
557494

558495
bool ConfigCompiler::ResolveDependencies(const string& path) {
559496
DLOG(INFO) << "ResolveDependencies(" << path << ")";
560-
if (!graph_->deps.count(path)) {
497+
auto found = graph_->deps.find(path);
498+
if (found == graph_->deps.end()) {
561499
return true;
562500
}
563-
auto& deps = graph_->deps[path];
501+
auto& deps = found->second;
564502
for (auto iter = deps.begin(); iter != deps.end(); ) {
565503
if (!(*iter)->Resolve(this)) {
566504
LOG(ERROR) << "unresolved dependency: " << **iter;

src/rime/config/config_compiler.h

+6-1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ struct Reference {
3535
string repr() const;
3636
};
3737

38+
class ConfigCompilerPlugin;
3839
class ResourceResolver;
3940
struct Dependency;
4041
struct ConfigDependencyGraph;
@@ -46,11 +47,13 @@ class ConfigCompiler {
4647
static constexpr const char* APPEND_DIRECTIVE = "__append";
4748
static constexpr const char* MERGE_DIRECTIVE = "__merge";
4849

49-
explicit ConfigCompiler(ResourceResolver* resource_resolver);
50+
ConfigCompiler(ResourceResolver* resource_resolver,
51+
ConfigCompilerPlugin* plugin);
5052
virtual ~ConfigCompiler();
5153

5254
Reference CreateReference(const string& qualified_path);
5355
void AddDependency(an<Dependency> dependency);
56+
void Push(an<ConfigResource> resource);
5457
void Push(an<ConfigList> config_list, size_t index);
5558
void Push(an<ConfigMap> config_map, const string& key);
5659
bool Parse(const string& key, const an<ConfigItem>& item);
@@ -63,10 +66,12 @@ class ConfigCompiler {
6366
bool blocking(const string& full_path) const;
6467
bool pending(const string& full_path) const;
6568
bool resolved(const string& full_path) const;
69+
vector<of<Dependency>> GetDependencies(const string& path);
6670
bool ResolveDependencies(const string& path);
6771

6872
private:
6973
ResourceResolver* resource_resolver_;
74+
ConfigCompilerPlugin* plugin_;
7075
the<ConfigDependencyGraph> graph_;
7176
};
7277

0 commit comments

Comments
 (0)