Ejemplo n.º 1
0
        def go(i):
            if i in on_stack:
                idx = stack.index(i)
                names = [self.compiled[j]['name'] for j in stack[idx:]]
                names.append(names[0])
                util.err('detected cycle among loot tables: %s' % names)

            if i in visited:
                return

            stack.append(i)
            on_stack.add(i)
            visited.add(i)

            c = self.compiled[i]
            if c['type'] == 'choose':
                for v in c['variants']:
                    go(v['id'])
            elif c['type'] == 'multi':
                for v in c['parts']:
                    go(v['id'])
            elif c['type'] == 'object':
                pass
            else:
                assert False, 'unrecognized compiled table type: %r' % (c['type'],)

            on_stack.remove(i)
            stack.pop()
Ejemplo n.º 2
0
    def check_group(self, fields, optional=()):
        """Get all fields in a group.  If any field in the group is set, then
        all required fields must be set or an error will be reported."""
        result = [None] * (len(fields) + len(optional))
        first_set = None
        all_set = True

        i = 0

        for f in fields:
            val = getattr(self, f)
            if val is not None:
                first_set = first_set or f
                result[i] = val
            else:
                all_set = False
            i += 1

        for f in optional:
            val = getattr(self, f)
            if val is not None:
                first_set = first_set or f
                result[i] = val
            i += 1

        if first_set is not None and not all_set:
            for f in fields:
                if getattr(self, f) is None:
                    util.err('%s %r: field %r must be set because %r is set' %
                            (self.KIND, self.name, f, first_set))

        return result
Ejemplo n.º 3
0
    def check_group(self, fields, optional=()):
        """Get all fields in a group.  If any field in the group is set, then
        all required fields must be set or an error will be reported."""
        result = [None] * (len(fields) + len(optional))
        first_set = None
        all_set = True

        i = 0

        for f in fields:
            val = getattr(self, f)
            if val is not None:
                first_set = first_set or f
                result[i] = val
            else:
                all_set = False
            i += 1

        for f in optional:
            val = getattr(self, f)
            if val is not None:
                first_set = first_set or f
                result[i] = val
            i += 1

        if first_set is not None and not all_set:
            for f in fields:
                if getattr(self, f) is None:
                    util.err('%s %r: field %r must be set because %r is set' %
                             (self.KIND, self.name, f, first_set))

        return result
Ejemplo n.º 4
0
 def error(self, expected, token=None):
     token = token or self.peek()
     text = token.text
     if len(text) > 13:
         text = text[:10] + '...'
     util.err('%s:%d:%d: parse error: expected %s, but saw %s "%s"' %
             (self.filename, token.line, token.col, expected, token.kind, text))
     raise ParseError()
Ejemplo n.º 5
0
 def error(self, expected, token=None):
     token = token or self.peek()
     text = token.text
     if len(text) > 13:
         text = text[:10] + '...'
     util.err('%s:%d:%d: parse error: expected %s, but saw %s "%s"' %
             (self.filename, token.line, token.col, expected, token.kind, text))
     raise ParseError()
Ejemplo n.º 6
0
    def parse_section(self):
        t_sect = self.peek()
        self.take_punct('[')
        ty = self.take_word('section type')
        name = self.take_word('section name')
        self.take_punct(']')
        self.take_eol()

        ext = ty.endswith('_ext')
        if ext:
            ty = ty[:-len('_ext')]

        mode, _, obj_kind = ty.partition('_')
        if mode not in ('choose', 'multi') or obj_kind not in ('structure',
                                                               'item'):
            util.err('%s:%d:%d: unknown section type %r' %
                     (self.filename, t_sect.line, t_sect.col, ty))

        entries = []
        while True:
            t_field = self.peek()
            if t_field.kind == 'eol':
                self.take()
                continue
            if t_field.text == '[' or t_field.kind == 'eof':
                break

            try:
                weight, chance = None, None
                if self.peek().text == '(':
                    weight, chance = self.parse_weight_or_chance()

                min_count, max_count = 1, 1
                if INT_RE.match(self.peek().text):
                    min_count, max_count = self.parse_counts()

                table_ref = self.peek().text == '*'
                if table_ref:
                    self.take_punct('*')

                    if min_count != 1 or max_count != 1:
                        util.error(
                            '%s:%d:%d: cannot specify count for table reference'
                            % (self.filename, t_field.line, t_field.col))

                ref_name = self.take_word()
                self.take_eol()

                entries.append(
                    Entry('table' if table_ref else 'object', ref_name,
                          min_count, max_count, weight, chance, t_field.line,
                          t_field.col))
            except ParseError:
                self.skip_to_eol()

        return Section(mode, obj_kind, ext, name, entries, t_sect.line,
                       t_sect.col)
Ejemplo n.º 7
0
 def require(self, field, default=None, reason=None):
     val = getattr(self, field)
     if val is None:
         if reason:
             util.err('%s %r: field %r must be set because %r is set' %
                     (self.KIND, self.name, field, reason))
         else:
             util.err('%s %r: field %r must be set' %
                     (self.KIND, self.name, field))
         return default
     else:
         return val
Ejemplo n.º 8
0
 def require(self, field, default=None, reason=None):
     val = getattr(self, field)
     if val is None:
         if reason:
             util.err('%s %r: field %r must be set because %r is set' %
                      (self.KIND, self.name, field, reason))
         else:
             util.err('%s %r: field %r must be set' %
                      (self.KIND, self.name, field))
         return default
     else:
         return val
Ejemplo n.º 9
0
    def parse_section(self):
        t_sect = self.peek()
        self.take_punct('[')
        ty = self.take_word('section type')
        name = self.take_word('section name')
        self.take_punct(']')
        self.take_eol()

        ext = ty.endswith('_ext')
        if ext:
            ty = ty[:-len('_ext')]

        mode, _, obj_kind = ty.partition('_')
        if mode not in ('choose', 'multi') or obj_kind not in ('structure', 'item'):
            util.err('%s:%d:%d: unknown section type %r' %
                    (self.filename, t_sect.line, t_sect.col, ty))

        entries = []
        while True:
            t_field = self.peek()
            if t_field.kind == 'eol':
                self.take()
                continue
            if t_field.text == '[' or t_field.kind == 'eof':
                break

            try:
                weight, chance = None, None
                if self.peek().text == '(':
                    weight, chance = self.parse_weight_or_chance()

                min_count, max_count = 1, 1
                if INT_RE.match(self.peek().text):
                    min_count, max_count = self.parse_counts()

                table_ref = self.peek().text == '*'
                if table_ref:
                    self.take_punct('*')

                    if min_count != 1 or max_count != 1:
                        util.error('%s:%d:%d: cannot specify count for table reference' %
                                (self.filename, t_field.line, t_field.col))

                ref_name = self.take_word()
                self.take_eol()

                entries.append(Entry('table' if table_ref else 'object',
                    ref_name, min_count, max_count, weight, chance,
                    t_field.line, t_field.col))
            except ParseError:
                self.skip_to_eol()

        return Section(mode, obj_kind, ext, name, entries, t_sect.line, t_sect.col)
Ejemplo n.º 10
0
    def parse_section(self):
        t_sect = self.peek()
        self.take_punct('[')
        ty = self.take_word('section type')
        name = self.take_word('section name')
        self.take_punct(']')
        self.take_eol()

        if ty in FIELD_MAP:
            field_map = FIELD_MAP[ty]
        else:
            util.err('%s:%d:%d: unknown section type %r' %
                    (self.filename, t_sect.line, t_sect.col, ty))
            field_map = {}
            ty = None

        parts = []
        while True:
            t_field = self.peek()
            if t_field.kind == 'eol':
                self.take()
                continue
            if t_field.text == '[' or t_field.kind == 'eof':
                break

            try:
                key = self.take_word()
                self.take_punct(':')

                if key not in field_map:
                    if ty is not None:
                        util.err('%s:%d:%d: unknown field %r for section type %r' %
                                (self.filename, t_field.line, t_field.col, key, ty))
                    raise ParseError()

                if self.peek().kind == 'backticked':
                    t_val = self.take()
                    val = Backticked(t_val.text, t_val.line, t_val.col)
                else:
                    t_val = self.peek()
                    val = Value(field_map[key].parse(self), t_val.line, t_val.col)

                self.take_eol()

                parts.append(Field(key, val, t_field.line, t_field.col))
            except ParseError as e:
                self.skip_to_eol()

        return Section(ty, name, parts, t_sect.line, t_sect.col)
Ejemplo n.º 11
0
 def require_one(self, field1, field2):
     """Check that exactly one of the two fields is set.  Returns True if
     the first is set, otherwise False.  Reports an error and defaults to
     True if both or neither are set."""
     val1 = getattr(self, field1)
     val2 = getattr(self, field2)
     if val1 is not None and val2 is not None:
         util.err('%s %r: field %r and field %r must not both be set' %
                  (self.KIND, self.name, field1, field2))
         return True
     elif val1 is not None:
         return True
     elif val2 is not None:
         return False
     else:
         util.err('%s %r: either field %r or field %r must be set' %
                  (self.KIND, self.name, field1, field2))
         return True
Ejemplo n.º 12
0
 def require_one(self, field1, field2):
     """Check that exactly one of the two fields is set.  Returns True if
     the first is set, otherwise False.  Reports an error and defaults to
     True if both or neither are set."""
     val1 = getattr(self, field1)
     val2 = getattr(self, field2)
     if val1 is not None and val2 is not None:
         util.err('%s %r: field %r and field %r must not both be set' %
                 (self.KIND, self.name, field1, field2))
         return True
     elif val1 is not None:
         return True
     elif val2 is not None:
         return False
     else:
         util.err('%s %r: either field %r or field %r must be set' %
                 (self.KIND, self.name, field1, field2))
         return True
Ejemplo n.º 13
0
 def parse(self):
     parts = []
     while True:
         t = self.peek()
         if t.kind == 'eol':
             self.take()
         elif t.kind == 'eof':
             break
         elif t.kind == 'punct' and t.text == '[':
             parts.append(self.parse_section())
         elif t.kind == 'py_begin':
             util.err('%s:%d:%d: python blocks are not supported in loot tables' %
                     (self.filename, t.line, t.col))
             while self.peek().kind != 'py_end':
                 self.take()
             self.take()
         else:
             self.error('beginning of section', t)
     return parts
Ejemplo n.º 14
0
 def parse(self):
     parts = []
     while True:
         t = self.peek()
         if t.kind == 'eol':
             self.take()
         elif t.kind == 'eof':
             break
         elif t.kind == 'punct' and t.text == '[':
             parts.append(self.parse_section())
         elif t.kind == 'py_begin':
             util.err(
                 '%s:%d:%d: python blocks are not supported in loot tables'
                 % (self.filename, t.line, t.col))
             while self.peek().kind != 'py_end':
                 self.take()
             self.take()
         else:
             self.error('beginning of section', t)
     return parts
Ejemplo n.º 15
0
def register_mod(name, assets, override_dir, deps):
    # NB: `override_dir` is a directory containing overrides for *other*,
    # previously loaded mods.  The `overrides` field of `ModInfo` is a list of
    # override directories applied to *this* mod by others.
    if name in MOD_MAP:
        util.err('mod %r is loaded multiple times' % name)

    for dep in deps:
        if dep not in MOD_MAP:
            util.err('mod %r depends on %r, which is not (yet) loaded' % (name, dep))

    MOD_MAP[name] = ModInfo(assets, [], deps)

    if override_dir is not None and os.path.exists(override_dir):
        for override_mod in os.listdir(override_dir):
            if override_mod not in MOD_MAP:
                util.err('mod %r applies overrides to %r, which is not (yet) loaded' %
                        (name, override_mod))
                continue

            MOD_MAP[override_mod].overrides.append(os.path.join(override_dir, override_mod))
Ejemplo n.º 16
0
def build_map(tables, kind):
    table_map = {}
    for t in (t for t in tables if t.object_kind() == kind and not t.ext):
        if t.name in table_map:
            util.err('multiple definitions of table %r' % t.name)
            continue
        table_map[t.name] = [t.table]

    for t in (t for t in tables if t.object_kind() == kind and t.ext):
        if t.name not in table_map:
            util.err('found extension of nonexistent table %r' % t.name)
            continue

        ts = table_map[t.name]
        if type(t.table) is not type(ts[0]):
            util.err('extension of table %r does not match original type (%s != %s)' %
                    (t.name, type(ts[0]).__name__, type(t.table)))
            continue

        ts.append(t.table)

    return table_map
Ejemplo n.º 17
0
def build_map(tables, kind):
    table_map = {}
    for t in (t for t in tables if t.object_kind() == kind and not t.ext):
        if t.name in table_map:
            util.err('multiple definitions of table %r' % t.name)
            continue
        table_map[t.name] = [t.table]

    for t in (t for t in tables if t.object_kind() == kind and t.ext):
        if t.name not in table_map:
            util.err('found extension of nonexistent table %r' % t.name)
            continue

        ts = table_map[t.name]
        if type(t.table) is not type(ts[0]):
            util.err('extension of table %r does not match original type (%s != %s)' %
                    (t.name, type(ts[0]).__name__, type(t.table)))
            continue

        ts.append(t.table)

    return table_map
Ejemplo n.º 18
0
 def resolve_table_ref(self, src_name, ref):
     if ref.name not in self.id_map:
         util.err('table %r refers to nonexistent table %r' %
                 (src_name, ref.name))
         return None
     return self.id_map[ref.name]
Ejemplo n.º 19
0
 def resolve_object_ids(self, id_map, name):
     for v in self.variants:
         if isinstance(v.ref, ObjectRef):
             v.ref.id = id_map.get(v.ref.name)
             if v.ref.id is None:
                 util.err('loot table %r: no such %s: %r' % (name, self.OBJ_KIND, v.ref.name))
Ejemplo n.º 20
0
 def resolve_object_ids(self, id_map, name):
     for p in self.parts:
         if isinstance(p.ref, ObjectRef):
             p.ref.id = id_map.get(p.ref.name)
             if p.ref.id is None:
                 util.err('loot table %r: no such %s: %r' % (name, self.OBJ_KIND, p.ref.name))
Ejemplo n.º 21
0
 def resolve_object_ids(self, id_map, name):
     for v in self.variants:
         if isinstance(v.ref, ObjectRef):
             v.ref.id = id_map.get(v.ref.name)
             if v.ref.id is None:
                 util.err('loot table %r: no such %s: %r' % (name, self.OBJ_KIND, v.ref.name))
Ejemplo n.º 22
0
 def err(self, msg):
     util.err('%s:%d:%d: %s' % (self.filename, self.i + 1, self.j, msg))
Ejemplo n.º 23
0
 def resolve_table_ref(self, src_name, ref):
     if ref.name not in self.id_map:
         util.err('table %r refers to nonexistent table %r' %
                 (src_name, ref.name))
         return None
     return self.id_map[ref.name]
Ejemplo n.º 24
0
 def resolve_object_ids(self, id_map, name):
     for p in self.parts:
         if isinstance(p.ref, ObjectRef):
             p.ref.id = id_map.get(p.ref.name)
             if p.ref.id is None:
                 util.err('loot table %r: no such %s: %r' % (name, self.OBJ_KIND, p.ref.name))
Ejemplo n.º 25
0
 def require_unset(self, field, reason):
     if getattr(self, field) is not None:
         util.err('%s %r: field %r must not be set because %r is set' %
                 (self.KIND, self.name, field, reason))
Ejemplo n.º 26
0
 def require_unset(self, field, reason):
     if getattr(self, field) is not None:
         util.err('%s %r: field %r must not be set because %r is set' %
                  (self.KIND, self.name, field, reason))