Skip to content

Commit

Permalink
Merge pull request #150 from rime/config
Browse files Browse the repository at this point in the history
feat(config): config compiler plugins for auto-patching and supporting legacy import_preset syntax
  • Loading branch information
kionz authored Sep 17, 2017
2 parents 2387dfb + 88f5a0c commit 68479eb
Show file tree
Hide file tree
Showing 19 changed files with 494 additions and 237 deletions.
42 changes: 42 additions & 0 deletions src/rime/config/auto_patch_config_plugin.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
//
// Copyright RIME Developers
// Distributed under the BSD License
//
#include <boost/algorithm/string.hpp>
#include <rime/config/config_compiler_impl.h>
#include <rime/config/plugins.h>

namespace rime {

static string remove_suffix(const string& input, const string& suffix) {
return boost::ends_with(input, suffix) ?
input.substr(0, input.length() - suffix.length()) : input;
}

// auto-patch applies to all loaded config resources, including dependencies.
// therefore it's done at the end of Compile phase.
bool AutoPatchConfigPlugin::ReviewCompileOutput(ConfigCompiler* compiler,
an<ConfigResource> resource) {
if (boost::ends_with(resource->resource_id, ".custom"))
return true;
// skip auto-patch if there is already an explicit `__patch` at the root node
auto root_deps = compiler->GetDependencies(resource->resource_id + ":");
if (!root_deps.empty() && root_deps.back()->priority() >= kPatch)
return true;
auto patch_resource_id =
remove_suffix(resource->resource_id, ".schema") + ".custom";
LOG(INFO) << "auto-patch " << resource->resource_id << ":/__patch: "
<< patch_resource_id << ":/patch?";
compiler->Push(resource);
compiler->AddDependency(
New<PatchReference>(Reference{patch_resource_id, "patch", true}));
compiler->Pop();
return true;
}

bool AutoPatchConfigPlugin::ReviewLinkOutput(ConfigCompiler* compiler,
an<ConfigResource> resource) {
return true;
}

} // namespace rime
144 changes: 41 additions & 103 deletions src/rime/config/config_compiler.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,100 +2,13 @@
#include <rime/common.h>
#include <rime/resource.h>
#include <rime/config/config_compiler.h>
#include <rime/config/config_compiler_impl.h>
#include <rime/config/config_data.h>
#include <rime/config/config_types.h>
#include <rime/config/plugins.h>

namespace rime {

enum DependencyPriority {
kPendingChild = 0,
kInclude = 1,
kPatch = 2,
};

struct Dependency {
an<ConfigItemRef> target;

virtual DependencyPriority priority() const = 0;
bool blocking() const {
return priority() > kPendingChild;
}
virtual string repr() const = 0;
virtual bool Resolve(ConfigCompiler* compiler) = 0;
};

template <class StreamT>
StreamT& operator<< (StreamT& stream, const Dependency& dep) {
return stream << dep.repr();
}

struct PendingChild : Dependency {
string child_path;
an<ConfigItemRef> child_ref;

PendingChild(const string& path, const an<ConfigItemRef>& ref)
: child_path(path), child_ref(ref) {
}
DependencyPriority priority() const override {
return kPendingChild;
}
string repr() const override {
return "PendingChild(" + child_path + ")";
}
bool Resolve(ConfigCompiler* compiler) override;
};

string Reference::repr() const {
return resource_id + ":" + local_path + (optional ? " <optional>" : "");
}

template <class StreamT>
StreamT& operator<< (StreamT& stream, const Reference& reference) {
return stream << reference.repr();
}

struct IncludeReference : Dependency {
IncludeReference(const Reference& r) : reference(r) {
}
DependencyPriority priority() const override {
return kInclude;
}
string repr() const override {
return "Include(" + reference.repr() + ")";
}
bool Resolve(ConfigCompiler* compiler) override;

Reference reference;
};

struct PatchReference : Dependency {
PatchReference(const Reference& r) : reference(r) {
}
DependencyPriority priority() const override {
return kPatch;
}
string repr() const override {
return "Patch(" + reference.repr() + ")";
}
bool Resolve(ConfigCompiler* compiler) override;

Reference reference;
};

struct PatchLiteral : Dependency {
an<ConfigMap> patch;

PatchLiteral(an<ConfigMap> map) : patch(map) {
}
DependencyPriority priority() const override {
return kPatch;
}
string repr() const override {
return "Patch(<literal>)";
}
bool Resolve(ConfigCompiler* compiler) override;
};

struct ConfigDependencyGraph {
map<string, of<ConfigResource>> resources;
vector<of<ConfigItemRef>> node_stack;
Expand All @@ -114,12 +27,18 @@ struct ConfigDependencyGraph {
key_stack.pop_back();
}

string current_resource_id() const {
return key_stack.empty() ? string()
: boost::trim_right_copy_if(key_stack.front(), boost::is_any_of(":"));
}
string current_resource_id() const;
};

string ConfigDependencyGraph::current_resource_id() const {
return key_stack.empty() ? string()
: boost::trim_right_copy_if(key_stack.front(), boost::is_any_of(":"));
}

string Reference::repr() const {
return resource_id + ":" + local_path + (optional ? " <optional>" : "");
}

bool PendingChild::Resolve(ConfigCompiler* compiler) {
return compiler->ResolveDependencies(child_path);
}
Expand Down Expand Up @@ -156,8 +75,7 @@ bool PatchReference::Resolve(ConfigCompiler* compiler) {
return false;
}
PatchLiteral patch{As<ConfigMap>(item)};
patch.target = target;
return patch.Resolve(compiler);
return patch.TargetedAt(target).Resolve(compiler);
}

static bool AppendToString(an<ConfigItemRef> target, an<ConfigValue> value) {
Expand Down Expand Up @@ -223,7 +141,7 @@ inline static bool IsMerging(const string& key,
bool merge_tree) {
return key == ConfigCompiler::MERGE_DIRECTIVE ||
boost::ends_with(key, ADD_SUFFIX_OPERATOR) ||
(merge_tree && Is<ConfigMap>(value) &&
(merge_tree && (!value || Is<ConfigMap>(value)) &&
!boost::ends_with(key, EQU_SUFFIX_OPERATOR));
}

Expand Down Expand Up @@ -294,7 +212,7 @@ void ConfigDependencyGraph::Add(an<Dependency> dependency) {
<< node_stack.size();
if (node_stack.empty()) return;
const auto& target = node_stack.back();
dependency->target = target;
dependency->TargetedAt(target);
auto target_path = ConfigData::JoinPath(key_stack);
auto& target_deps = deps[target_path];
bool target_was_pending = !target_deps.empty();
Expand Down Expand Up @@ -326,8 +244,10 @@ void ConfigDependencyGraph::Add(an<Dependency> dependency) {
}
}

ConfigCompiler::ConfigCompiler(ResourceResolver* resource_resolver)
ConfigCompiler::ConfigCompiler(ResourceResolver* resource_resolver,
ConfigCompilerPlugin* plugin)
: resource_resolver_(resource_resolver),
plugin_(plugin),
graph_(new ConfigDependencyGraph) {
}

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

void ConfigCompiler::Push(an<ConfigResource> resource) {
graph_->Push(resource, resource->resource_id + ":");
}

void ConfigCompiler::Push(an<ConfigList> config_list, size_t index) {
graph_->Push(
New<ConfigListEntryRef>(nullptr, config_list, index),
Expand All @@ -378,10 +302,12 @@ an<ConfigResource> ConfigCompiler::Compile(const string& file_name) {
auto resource_id = resource_resolver_->ToResourceId(file_name);
auto resource = New<ConfigResource>(resource_id, New<ConfigData>());
graph_->resources[resource_id] = resource;
graph_->Push(resource, resource_id + ":");
Push(resource);
resource->loaded = resource->data->LoadFromFile(
resource_resolver_->ResolvePath(resource_id).string(), this);
graph_->Pop();
Pop();
if (plugin_)
plugin_->ReviewCompileOutput(this, resource);
return resource;
}

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

vector<of<Dependency>> ConfigCompiler::GetDependencies(const string& path) {
auto found = graph_->deps.find(path);
return found == graph_->deps.end() ? vector<of<Dependency>>() : found->second;
}

static an<ConfigItem> ResolveReference(ConfigCompiler* compiler,
const Reference& reference) {
auto resource = compiler->GetCompiledResource(reference.resource_id);
if (!resource) {
DLOG(INFO) << "resource not loaded, compiling: " << reference.resource_id;
resource = compiler->Compile(reference.resource_id);
// dependency doesn't require full resolution, this allows non conflicting
// mutual references between config files.
// this call is made even if resource is not loaded because plugins can
// edit the empty config data, adding new dependencies.
ResolveBlockingDependencies(compiler, reference.resource_id + ":");
if (!resource->loaded) {
if (reference.optional) {
LOG(INFO) << "optional resource not loaded: " << reference.resource_id;
Expand Down Expand Up @@ -552,15 +488,17 @@ bool ConfigCompiler::Link(an<ConfigResource> target) {
LOG(ERROR) << "resource not found: " << target->resource_id;
return false;
}
return ResolveDependencies(found->first + ":");
return ResolveDependencies(found->first + ":") &&
(plugin_ ? plugin_->ReviewLinkOutput(this, target) : true);
}

bool ConfigCompiler::ResolveDependencies(const string& path) {
DLOG(INFO) << "ResolveDependencies(" << path << ")";
if (!graph_->deps.count(path)) {
auto found = graph_->deps.find(path);
if (found == graph_->deps.end()) {
return true;
}
auto& deps = graph_->deps[path];
auto& deps = found->second;
for (auto iter = deps.begin(); iter != deps.end(); ) {
if (!(*iter)->Resolve(this)) {
LOG(ERROR) << "unresolved dependency: " << **iter;
Expand Down
7 changes: 6 additions & 1 deletion src/rime/config/config_compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ struct Reference {
string repr() const;
};

class ConfigCompilerPlugin;
class ResourceResolver;
struct Dependency;
struct ConfigDependencyGraph;
Expand All @@ -46,11 +47,13 @@ class ConfigCompiler {
static constexpr const char* APPEND_DIRECTIVE = "__append";
static constexpr const char* MERGE_DIRECTIVE = "__merge";

explicit ConfigCompiler(ResourceResolver* resource_resolver);
ConfigCompiler(ResourceResolver* resource_resolver,
ConfigCompilerPlugin* plugin);
virtual ~ConfigCompiler();

Reference CreateReference(const string& qualified_path);
void AddDependency(an<Dependency> dependency);
void Push(an<ConfigResource> resource);
void Push(an<ConfigList> config_list, size_t index);
void Push(an<ConfigMap> config_map, const string& key);
bool Parse(const string& key, const an<ConfigItem>& item);
Expand All @@ -63,10 +66,12 @@ class ConfigCompiler {
bool blocking(const string& full_path) const;
bool pending(const string& full_path) const;
bool resolved(const string& full_path) const;
vector<of<Dependency>> GetDependencies(const string& path);
bool ResolveDependencies(const string& path);

private:
ResourceResolver* resource_resolver_;
ConfigCompilerPlugin* plugin_;
the<ConfigDependencyGraph> graph_;
};

Expand Down
Loading

0 comments on commit 68479eb

Please sign in to comment.