allow repeating elements and groups - useful if you need e.g. a lot of numbered labels, but it limits complay.py's ability to check for invalid references as it can't evaluate expressions (nw)

This commit is contained in:
Vas Crabb 2018-07-22 09:52:50 +10:00
parent 7809e9005d
commit 6669489679
22 changed files with 280 additions and 223 deletions

View File

@ -274,6 +274,7 @@ class LayoutChecker(Minifyer):
except:
self.handleError('Element mamelayout attribute version "%s" is not an integer' % (attrs['version'], ))
self.variable_scopes.append({ })
self.repeat_depth.append(0)
self.handlers.append((self.layoutStartHandler, self.layoutEndHandler))
def rootEndHandler(self, name, attrs):
@ -284,18 +285,24 @@ class LayoutChecker(Minifyer):
if 'name' not in attrs:
self.handleError('Element element missing attribute name')
else:
generated_name = self.VARPATTERN.match(attrs['name'])
if generated_name:
self.generated_element_names = True
if attrs['name'] not in self.elements:
self.elements[attrs['name']] = self.formatLocation()
elif not self.VARPATTERN.match(attrs['name']):
elif not generated_name:
self.handleError('Element element has duplicate name (previous %s)' % (self.elements[attrs['name']], ))
self.handlers.append((self.elementStartHandler, self.elementEndHandler))
elif 'group' == name:
if 'name' not in attrs:
self.handleError('Element group missing attribute name')
else:
generated_name = self.VARPATTERN.match(attrs['name'])
if generated_name:
self.generated_group_names = True
if attrs['name'] not in self.groups:
self.groups[attrs['name']] = self.formatLocation()
elif not self.VARPATTERN.match(attrs['name']):
elif not generated_name:
self.handleError('Element group has duplicate name (previous %s)' % (self.groups[attrs['name']], ))
self.handlers.append((self.groupViewStartHandler, self.groupViewEndHandler))
self.variable_scopes.append({ })
@ -313,6 +320,15 @@ class LayoutChecker(Minifyer):
self.variable_scopes.append({ })
self.repeat_depth.append(0)
self.have_bounds.append(False)
elif 'repeat' == name:
if 'count' not in attrs:
self.handleError('Element repeat missing attribute count')
else:
count = self.checkIntAttribute(name, attrs, 'count', None)
if (count is not None) and (0 >= count):
self.handleError('Element repeat attribute count "%s" is negative' % (attrs['count'], ))
self.variable_scopes.append({ })
self.repeat_depth[-1] += 1
elif 'param' == name:
self.checkParameter(attrs)
self.ignored_depth = 1
@ -323,13 +339,18 @@ class LayoutChecker(Minifyer):
self.ignored_depth = 1
def layoutEndHandler(self, name):
self.variable_scopes.pop()
if self.repeat_depth[-1]:
self.repeat_depth[-1] -= 1
else:
if not self.generated_element_names:
for element in self.referenced_elements:
if (element not in self.elements) and (not self.VARPATTERN.match(element)):
self.handleError('Element "%s" not found (first referenced at %s)' % (element, self.referenced_elements[element]))
if not self.generated_group_names:
for group in self.referenced_groups:
if (group not in self.groups) and (not self.VARPATTERN.match(group)):
self.handleError('Group "%s" not found (first referenced at %s)' % (group, self.referenced_groups[group]))
self.variable_scopes.pop()
self.handlers.pop()
def elementStartHandler(self, name, attrs):
@ -456,6 +477,8 @@ class LayoutChecker(Minifyer):
self.repeat_depth = [ ]
self.have_bounds = [ ]
self.have_color = [ ]
self.generated_element_names = False
self.generated_group_names = False
super(LayoutChecker, self).startDocument()
def endDocument(self):
@ -471,6 +494,8 @@ class LayoutChecker(Minifyer):
del self.repeat_depth
del self.have_bounds
del self.have_color
del self.generated_element_names
del self.generated_group_names
super(LayoutChecker, self).endDocument()
def startElement(self, name, attrs):

View File

@ -932,6 +932,17 @@ public:
view_list const &views() const { return m_viewlist; }
private:
using environment = emu::render::detail::layout_environment;
// add elements and parameters
void add_elements(
char const *dirname,
environment &env,
util::xml::data_node const &parentnode,
group_map &groupmap,
bool repeat,
bool init);
// internal state
element_map m_elemmap; // list of shared layout elements
view_list m_viewlist; // list of views

View File

@ -3356,13 +3356,14 @@ void layout_view::item::resolve_tags()
// layout_file - constructor
//-------------------------------------------------
layout_file::layout_file(device_t &device, util::xml::data_node const &rootnode, const char *dirname)
layout_file::layout_file(device_t &device, util::xml::data_node const &rootnode, char const *dirname)
: m_elemmap()
, m_viewlist()
{
emu::render::detail::layout_environment env(device);
try
{
environment env(device);
// find the layout node
util::xml::data_node const *const mamelayoutnode = rootnode.get_child("mamelayout");
if (!mamelayoutnode)
@ -3373,30 +3374,9 @@ layout_file::layout_file(device_t &device, util::xml::data_node const &rootnode,
if (version != LAYOUT_VERSION)
throw layout_syntax_error(util::string_format("unsupported version %d", version));
// parse all the parameters
for (util::xml::data_node const *elemnode = mamelayoutnode->get_child("param") ; elemnode; elemnode = elemnode->get_next_sibling("param"))
env.set_parameter(*elemnode);
// parse all the elements
for (util::xml::data_node const *elemnode = mamelayoutnode->get_child("element"); elemnode; elemnode = elemnode->get_next_sibling("element"))
{
char const *const name(env.get_attribute_string(*elemnode, "name", nullptr));
if (!name)
throw layout_syntax_error("element lacks name attribute");
if (!m_elemmap.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(env, *elemnode, dirname)).second)
throw layout_syntax_error(util::string_format("duplicate element name %s", name));
}
// parse all the groups
// parse all the parameters, elements and groups
group_map groupmap;
for (util::xml::data_node const *groupnode = mamelayoutnode->get_child("group"); groupnode; groupnode = groupnode->get_next_sibling("group"))
{
char const *const name(env.get_attribute_string(*groupnode, "name", nullptr));
if (!name)
throw layout_syntax_error("group lacks name attribute");
if (!groupmap.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(*groupnode)).second)
throw layout_syntax_error(util::string_format("duplicate group name %s", name));
}
add_elements(dirname, env, *mamelayoutnode, groupmap, false, true);
// parse all the views
for (util::xml::data_node const *viewnode = mamelayoutnode->get_child("view"); viewnode != nullptr; viewnode = viewnode->get_next_sibling("view"))
@ -3430,3 +3410,56 @@ layout_file::layout_file(device_t &device, util::xml::data_node const &rootnode,
layout_file::~layout_file()
{
}
void layout_file::add_elements(
char const *dirname,
environment &env,
util::xml::data_node const &parentnode,
group_map &groupmap,
bool repeat,
bool init)
{
for (util::xml::data_node const *childnode = parentnode.get_first_child(); childnode; childnode = childnode->get_next_sibling())
{
if (!strcmp(childnode->get_name(), "param"))
{
if (!repeat)
env.set_parameter(*childnode);
else
env.set_repeat_parameter(*childnode, init);
}
else if (!strcmp(childnode->get_name(), "element"))
{
char const *const name(env.get_attribute_string(*childnode, "name", nullptr));
if (!name)
throw layout_syntax_error("element lacks name attribute");
if (!m_elemmap.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(env, *childnode, dirname)).second)
throw layout_syntax_error(util::string_format("duplicate element name %s", name));
}
else if (!strcmp(childnode->get_name(), "group"))
{
char const *const name(env.get_attribute_string(*childnode, "name", nullptr));
if (!name)
throw layout_syntax_error("group lacks name attribute");
if (!groupmap.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(*childnode)).second)
throw layout_syntax_error(util::string_format("duplicate group name %s", name));
}
else if (!strcmp(childnode->get_name(), "repeat"))
{
int const count(env.get_attribute_int(*childnode, "count", -1));
if (0 >= count)
throw layout_syntax_error("repeat must have positive integer count attribute");
environment local(env);
for (int i = 0; count > i; ++i)
{
add_elements(dirname, local, *childnode, groupmap, true, !i);
local.increment_parameters();
}
}
else if (repeat || (strcmp(childnode->get_name(), "view") && strcmp(childnode->get_name(), "script")))
{
throw layout_syntax_error(util::string_format("unknown layout item %s", childnode->get_name()));
}
}
}

View File

@ -41,18 +41,12 @@ Intel INTELLEC® 4/MOD 40 layout
</rect>
</element>
<element name="label_0"><text string="0"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_1"><text string="1"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_2"><text string="2"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_3"><text string="3"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_4"><text string="4"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_5"><text string="5"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_6"><text string="6"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_7"><text string="7"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_8"><text string="8"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_9"><text string="9"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_10"><text string="10"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_11"><text string="11"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<repeat count="12">
<param name="labelnum" start="0" increment="1" />
<element name="label_~labelnum~">
<text string="~labelnum~"><color red="1.0" green="1.0" blue="1.0" /></text>
</element>
</repeat>
<element name="label_a1"><text string="A1"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_a2"><text string="A2"><color red="1.0" green="1.0" blue="1.0" /></text></element>

View File

@ -41,18 +41,12 @@ Intel INTELLEC® 4/MOD 40 layout
</rect>
</element>
<element name="label_0"><text string="0"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_1"><text string="1"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_2"><text string="2"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_3"><text string="3"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_4"><text string="4"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_5"><text string="5"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_6"><text string="6"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_7"><text string="7"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_8"><text string="8"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_9"><text string="9"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_10"><text string="10"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_11"><text string="11"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<repeat count="12">
<param name="labelnum" start="0" increment="1" />
<element name="label_~labelnum~">
<text string="~labelnum~"><color red="1.0" green="1.0" blue="1.0" /></text>
</element>
</repeat>
<element name="label_a1"><text string="A1"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_a2"><text string="A2"><color red="1.0" green="1.0" blue="1.0" /></text></element>