Skip to content

Commit

Permalink
Enable nesting of [type] tags
Browse files Browse the repository at this point in the history
Minor cleanup of GUI2 type definitions
Disallow stuff like border=top,all
  • Loading branch information
CelticMinstrel committed Mar 11, 2018
1 parent d4eeef0 commit e333aef
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 98 deletions.
36 changes: 24 additions & 12 deletions data/schema/gui.cfg
Expand Up @@ -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"
Expand Down
4 changes: 2 additions & 2 deletions src/serialization/schema_validator.cpp
Expand Up @@ -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(std::exception&) {
// Need to check all type values in schema-generator
}
Expand Down Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion src/serialization/schema_validator.hpp
Expand Up @@ -145,6 +145,6 @@ class schema_validator : public abstract_validator
std::stack<message_map> cache_;

/** Type validators. */
std::map<std::string, class_type> types_;
class_type::map types_;
};
} // namespace schema_validation{
134 changes: 65 additions & 69 deletions src/serialization/tag.cpp
Expand Up @@ -19,6 +19,7 @@

#include "serialization/tag.hpp"
#include "serialization/string_utils.hpp"
#include "boost/optional.hpp"

#include "config.hpp"

Expand All @@ -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"]);
}
}

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;
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));
}
}
for(const auto& link : links_) {
auto it = type_map.find(link);
return type;
}

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;
}
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;
return false;
}
cached_ = it->second;
}
switch(join) {
case UNION: return false;
case INTERSECTION: return true;
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;
}
}
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())
Expand Down
69 changes: 55 additions & 14 deletions src/serialization/tag.hpp
Expand Up @@ -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))
{}
explicit class_type(const config&);
class_type() = delete;
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);
};

bool matches(const std::string& value, const std::map<std::string, class_type>& type_map) const;
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;
};

enum CLASS {UNION, INTERSECTION};
private:
CLASS join = UNION;
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:
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:
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:
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)
{}
bool matches(const std::string& value, const map& type_map) const override;
};

/**
Expand Down

0 comments on commit e333aef

Please sign in to comment.