Enable nesting of [type] tags
Minor cleanup of GUI2 type definitions Disallow stuff like border=top,all
This commit is contained in:
parent
34604fdff6
commit
9ab1777851
5 changed files with 148 additions and 99 deletions
|
@ -3,37 +3,49 @@
|
|||
{./types/formula.cfg}
|
||||
[type]
|
||||
name=border
|
||||
value="^(top|bottom|left|right|all)?(,\s*(top|bottom|left|right|all))*$"
|
||||
[union]
|
||||
[type]
|
||||
[list]
|
||||
[element]
|
||||
value="top|bottom|left|right"
|
||||
[/element]
|
||||
[/list]
|
||||
[/type]
|
||||
[type]
|
||||
value="all"
|
||||
[/type]
|
||||
[/union]
|
||||
[/type]
|
||||
[type]
|
||||
name=font_family
|
||||
value="^(sans|monospace|light|script)?$"
|
||||
value="sans|monospace|light|script|"
|
||||
[/type]
|
||||
[type]
|
||||
name=font_style
|
||||
value="^(normal|bold|italic|underline|light)?$"
|
||||
value="normal|bold|italic|underline|light|"
|
||||
[/type]
|
||||
[type]
|
||||
name=grow_direction
|
||||
value="^horizontal|vertical$"
|
||||
value="horizontal|vertical"
|
||||
[/type]
|
||||
[type]
|
||||
name=h_align
|
||||
value="^left|right|center$"
|
||||
[/type]
|
||||
{FORMULA_TYPE h_align}
|
||||
[type]
|
||||
name=resize_mode
|
||||
value="^scale|stretch|tile|tile_center$"
|
||||
value="scale|stretch|tile|tile_center"
|
||||
[/type]
|
||||
[type]
|
||||
name=scrollbar_mode
|
||||
value="^always|never|auto|initial_auto$"
|
||||
value="always|never|auto|initial_auto"
|
||||
[/type]
|
||||
[type]
|
||||
name=h_align
|
||||
value="left|right|center"
|
||||
[/type]
|
||||
[type]
|
||||
name=v_align
|
||||
value="^top|bottom|center$"
|
||||
value="top|bottom|center"
|
||||
[/type]
|
||||
{FORMULA_TYPE h_align}
|
||||
{FORMULA_TYPE v_align}
|
||||
[tag]
|
||||
name="root"
|
||||
min="1"
|
||||
|
|
|
@ -173,7 +173,7 @@ bool schema_validator::read_config_file(const std::string& filename)
|
|||
}
|
||||
for(const config& type : g.child_range("type")) {
|
||||
try {
|
||||
types_[type["name"].str()] = class_type(type);
|
||||
types_[type["name"].str()] = class_type::from_config(type);
|
||||
} catch(const std::exception&) {
|
||||
// Need to check all type values in schema-generator
|
||||
}
|
||||
|
@ -285,7 +285,7 @@ void schema_validator::validate_key(
|
|||
cache_.top()[&cfg].emplace_back(
|
||||
WRONG_TYPE, file, start_line, 0, stack_.top()->get_name(), name, key->get_type());
|
||||
continue;
|
||||
} else if(itt->second.matches(value, types_)) {
|
||||
} else if(itt->second->matches(value, types_)) {
|
||||
matched = true;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -145,6 +145,6 @@ private:
|
|||
std::stack<message_map> cache_;
|
||||
|
||||
/** Type validators. */
|
||||
std::map<std::string, class_type> types_;
|
||||
class_type::map types_;
|
||||
};
|
||||
} // namespace schema_validation{
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
#include "serialization/tag.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
#include "boost/optional.hpp"
|
||||
|
||||
#include "config.hpp"
|
||||
|
||||
|
@ -39,93 +40,88 @@ class_tag any_tag("", 0, -1, "", true);
|
|||
* @end{tag}{name="key"}
|
||||
* @end{parent}{name="wml_schema/tag/"}
|
||||
*/
|
||||
class_type::class_type(const config& cfg)
|
||||
: name_(cfg["name"].str())
|
||||
std::shared_ptr<class_type> class_type::from_config(const config& cfg)
|
||||
{
|
||||
boost::optional<config::const_child_itors> composite_range;
|
||||
std::shared_ptr<class_type> type;
|
||||
if(cfg.has_child("union")) {
|
||||
join = UNION;
|
||||
for(const config& elem : cfg.child("union").child_range("type")) {
|
||||
if(elem.has_attribute("value")) {
|
||||
patterns_.emplace_back("^(?:" + elem["value"].str() + ")$");
|
||||
} else if(elem.has_attribute("link")) {
|
||||
links_.emplace_back(elem["link"].str());
|
||||
}
|
||||
}
|
||||
type = std::make_shared<class_type_union>(cfg["name"]);
|
||||
composite_range.emplace(cfg.child("union").child_range("type"));
|
||||
} else if(cfg.has_child("intersection")) {
|
||||
join = INTERSECTION;
|
||||
for(const config& elem : cfg.child("intersection").child_range("type")) {
|
||||
if(elem.has_attribute("value")) {
|
||||
patterns_.emplace_back("^(?:" + elem["value"].str() + ")$");
|
||||
} else if(elem.has_attribute("link")) {
|
||||
links_.emplace_back(elem["link"].str());
|
||||
}
|
||||
}
|
||||
type = std::make_shared<class_type_intersection>(cfg["name"]);
|
||||
composite_range.emplace(cfg.child("intersection").child_range("type"));
|
||||
} else if(cfg.has_child("list")) {
|
||||
const config& list_cfg = cfg.child("list");
|
||||
join = UNION;
|
||||
is_list_ = true;
|
||||
list_min_ = list_cfg["min"].to_int();
|
||||
list_max_ = list_cfg["max"].str() == "infinite" ? -1 : list_cfg["max"].to_int(-1);
|
||||
if(list_max_ < 0) list_max_ = INT_MAX;
|
||||
split_ = list_cfg["split"].str(",");
|
||||
for(const config& elem : cfg.child("list").child_range("element")) {
|
||||
if(elem.has_attribute("value")) {
|
||||
patterns_.emplace_back("^(?:" + elem["value"].str() + ")$");
|
||||
} else if(elem.has_attribute("link")) {
|
||||
links_.emplace_back(elem["link"].str());
|
||||
}
|
||||
}
|
||||
int list_min = list_cfg["min"].to_int();
|
||||
int list_max = list_cfg["max"].str() == "infinite" ? -1 : list_cfg["max"].to_int(-1);
|
||||
if(list_max < 0) list_max = INT_MAX;
|
||||
type = std::make_shared<class_type_list>(cfg["name"], list_cfg["split"].str(","), list_min, list_max);
|
||||
composite_range.emplace(list_cfg.child_range("element"));
|
||||
} else if(cfg.has_attribute("value")) {
|
||||
patterns_.emplace_back(cfg["value"].str());
|
||||
type = std::make_shared<class_type_simple>(cfg["name"], cfg["value"]);
|
||||
} else if(cfg.has_attribute("link")) {
|
||||
links_.emplace_back(cfg["link"].str());
|
||||
type = std::make_shared<class_type_alias>(cfg["name"], cfg["link"]);
|
||||
}
|
||||
if(composite_range) {
|
||||
auto composite_type = std::dynamic_pointer_cast<class_type_composite>(type);
|
||||
for(const config& elem : *composite_range) {
|
||||
composite_type->add_type(class_type::from_config(elem));
|
||||
}
|
||||
}
|
||||
return type;
|
||||
}
|
||||
|
||||
bool class_type::matches(const std::string& value, const std::map<std::string, class_type>& type_map) const {
|
||||
if(is_list_ && !in_list_match_) {
|
||||
if(this->name_ == "range") {
|
||||
in_list_match_ = true;
|
||||
}
|
||||
in_list_match_ = true;
|
||||
boost::sregex_token_iterator it(value.begin(), value.end(), split_, -1);
|
||||
int n = !value.empty();
|
||||
bool result = std::any_of(it, boost::sregex_token_iterator(), [this, &type_map, &n](const boost::ssub_match& match){
|
||||
if(!match.matched) return false;
|
||||
n++;
|
||||
return this->matches(std::string(match.first, match.second), type_map);
|
||||
});
|
||||
in_list_match_ = false;
|
||||
return result && n >= list_min_ && n <= list_max_;
|
||||
}
|
||||
for(const auto& pat : patterns_) {
|
||||
boost::smatch sub;
|
||||
bool res = boost::regex_match(value, sub, pat);
|
||||
switch(join) {
|
||||
case UNION: if(res) return true; else break;
|
||||
case INTERSECTION: if(!res) return false; else break;
|
||||
}
|
||||
}
|
||||
for(const auto& link : links_) {
|
||||
auto it = type_map.find(link);
|
||||
bool class_type_simple::matches(const std::string& value, const map&) const
|
||||
{
|
||||
boost::smatch sub;
|
||||
return boost::regex_match(value, sub, pattern_);
|
||||
}
|
||||
|
||||
bool class_type_alias::matches(const std::string& value, const map& type_map) const
|
||||
{
|
||||
if(!cached_) {
|
||||
auto it = type_map.find(link_);
|
||||
if(it == type_map.end()) {
|
||||
// TODO: Error message about the invalid type?
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
bool res = it->second.matches(value, type_map);
|
||||
switch(join) {
|
||||
case UNION: if(res) return true; else break;
|
||||
case INTERSECTION: if(!res) return false; else break;
|
||||
cached_ = it->second;
|
||||
}
|
||||
return cached_->matches(value, type_map);
|
||||
}
|
||||
|
||||
bool class_type_union::matches(const std::string& value, const map& type_map) const
|
||||
{
|
||||
for(const auto& type : subtypes_) {
|
||||
if(type->matches(value, type_map)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
switch(join) {
|
||||
case UNION: return false;
|
||||
case INTERSECTION: return true;
|
||||
}
|
||||
assert(false && "class_type::matches reached end of function because join value was corrupted");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool class_type_intersection::matches(const std::string& value, const map& type_map) const
|
||||
{
|
||||
for(const auto& type : subtypes_) {
|
||||
if(!type->matches(value, type_map)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool class_type_list::matches(const std::string& value, const map& type_map) const
|
||||
{
|
||||
boost::sregex_token_iterator it(value.begin(), value.end(), split_, -1), end;
|
||||
int n = !value.empty();
|
||||
bool result = std::all_of(it, end, [this, &type_map, &n](const boost::ssub_match& match){
|
||||
if(!match.matched) return true;
|
||||
n++;
|
||||
return this->class_type_union::matches(std::string(match.first, match.second), type_map);
|
||||
});
|
||||
return result && n >= min_ && n <= max_;
|
||||
}
|
||||
|
||||
class_key::class_key(const config& cfg)
|
||||
: name_(cfg["name"].str())
|
||||
, type_(cfg["type"].str())
|
||||
|
|
|
@ -35,24 +35,65 @@ namespace schema_validation
|
|||
{
|
||||
|
||||
class class_type {
|
||||
protected:
|
||||
std::string name_;
|
||||
std::vector<boost::regex> patterns_;
|
||||
std::vector<std::string> links_;
|
||||
boost::regex split_;
|
||||
bool is_list_ = false;
|
||||
int list_min_ = 0, list_max_ = -1;
|
||||
mutable bool in_list_match_ = false;
|
||||
public:
|
||||
class_type() : name_("") {}
|
||||
class_type(const std::string& name, const std::string& pattern) : name_(name), patterns_(1, boost::regex(pattern))
|
||||
class_type() = delete;
|
||||
explicit class_type(const std::string& name) : name_(name) {}
|
||||
using map = std::map<std::string, std::shared_ptr<class_type>>;
|
||||
virtual bool matches(const std::string& value, const map& type_map) const = 0;
|
||||
static std::shared_ptr<class_type> from_config(const config& cfg);
|
||||
};
|
||||
|
||||
class class_type_simple : public class_type {
|
||||
boost::regex pattern_;
|
||||
public:
|
||||
class_type_simple(const std::string& name, const std::string& pattern) : class_type(name), pattern_(pattern) {}
|
||||
bool matches(const std::string& value, const map& type_map) const override;
|
||||
};
|
||||
|
||||
class class_type_alias : public class_type {
|
||||
mutable std::shared_ptr<class_type> cached_;
|
||||
std::string link_;
|
||||
public:
|
||||
class_type_alias(const std::string& name, const std::string& link) : class_type(name), link_(link) {}
|
||||
bool matches(const std::string& value, const map& type_map) const override;
|
||||
};
|
||||
|
||||
class class_type_composite : public class_type {
|
||||
protected:
|
||||
std::vector<std::shared_ptr<class_type>> subtypes_;
|
||||
public:
|
||||
explicit class_type_composite(const std::string& name) : class_type(name) {}
|
||||
void add_type(std::shared_ptr<class_type> type)
|
||||
{
|
||||
subtypes_.push_back(type);
|
||||
}
|
||||
};
|
||||
|
||||
class class_type_union : public class_type_composite {
|
||||
public:
|
||||
explicit class_type_union(const std::string& name) : class_type_composite(name) {}
|
||||
bool matches(const std::string& value, const map& type_map) const override;
|
||||
};
|
||||
|
||||
class class_type_intersection : public class_type_composite {
|
||||
public:
|
||||
explicit class_type_intersection(const std::string& name) : class_type_composite(name) {}
|
||||
bool matches(const std::string& value, const map& type_map) const override;
|
||||
};
|
||||
|
||||
class class_type_list : public class_type_union {
|
||||
boost::regex split_;
|
||||
int min_ = 0, max_ = -1;
|
||||
public:
|
||||
class_type_list(const std::string& name, const std::string& pattern, int min, int max)
|
||||
: class_type_union(name)
|
||||
, split_(pattern)
|
||||
, min_(min)
|
||||
, max_(max)
|
||||
{}
|
||||
explicit class_type(const config&);
|
||||
|
||||
bool matches(const std::string& value, const std::map<std::string, class_type>& type_map) const;
|
||||
|
||||
enum CLASS {UNION, INTERSECTION};
|
||||
private:
|
||||
CLASS join = UNION;
|
||||
bool matches(const std::string& value, const map& type_map) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Reference in a new issue