Loading

Paste #pcpqgqvhc

  1. diff --git a/nml/actions/real_sprite.py b/nml/actions/real_sprite.py
  2. --- a/nml/actions/real_sprite.py
  3. +++ b/nml/actions/real_sprite.py
  4. @@ -160,9 +160,12 @@ class RealSprite(object):
  5.  
  6.      @ivar flags: Cropping/warning flags.
  7.      @type flags: L{expression.ConstantNumeric}
  8. +
  9. +    @ivar poslist: Position of creation of the sprite, if available.
  10. +    @type poslist: C{list} of L{Position}
  11.      """
  12.  
  13. -    def __init__(self, param_list = None, label = None):
  14. +    def __init__(self, param_list = None, label = None, poslist = None):
  15.          self.param_list = param_list
  16.          self.label = label
  17.          self.is_empty = False
  18. @@ -170,6 +173,10 @@ class RealSprite(object):
  19.          self.ypos = None
  20.          self.xsize = None
  21.          self.ysize = None
  22. +        if poslist is None:
  23. +            self.poslist = []
  24. +        else:
  25. +            self.poslist = poslist
  26.  
  27.      def debug_print(self, indentation):
  28.          generic.print_dbg(indentation, 'Real sprite, parameters:')
  29. @@ -182,8 +189,8 @@ class RealSprite(object):
  30.              labels[self.label.value] = 0
  31.          return labels, 1
  32.  
  33. -    def expand(self, default_file, default_mask_file, id_dict):
  34. -        return [parse_real_sprite(self, default_file, default_mask_file, id_dict)]
  35. +    def expand(self, default_file, default_mask_file, poslist, id_dict):
  36. +        return [parse_real_sprite(self, default_file, default_mask_file, poslist, id_dict)]
  37.  
  38.      def check_sprite_size(self):
  39.          generic.check_range(self.xpos.value,  0, 0x7fffFFFF,   "Real sprite paramater 'xpos'", self.xpos.pos)
  40. @@ -249,6 +256,13 @@ class RealSprite(object):
  41.          return (rgb_file, rgb_rect, mask_file, mask_rect, do_crop)
  42.  
  43.  class SpriteAction(base_action.BaseAction):
  44. +    """
  45. +    @ivar sprite_num: Number of the sprite, or C{None} if not decided yet.
  46. +    @type sprite_num: C{int} or C{None}
  47. +
  48. +    @ivar last: Whether this sprite action is the last of a series.
  49. +    @type last: C{bool}
  50. +    """
  51.      def __init__(self):
  52.          self.sprite_num = None
  53.          self.last = False
  54. @@ -275,9 +289,13 @@ class RealSpriteAction(SpriteAction):
  55.          if self.last: file.newline()
  56.  
  57.  class RecolourSprite(object):
  58. -    def __init__(self, mapping, label = None):
  59. +    def __init__(self, mapping, label = None, poslist = None):
  60.          self.mapping = mapping
  61.          self.label = label
  62. +        if poslist is None:
  63. +            self.poslist = []
  64. +        else:
  65. +            self.poslist = poslist
  66.  
  67.      def debug_print(self, indentation):
  68.          generic.print_dbg(indentation, 'Recolour sprite, mapping:')
  69. @@ -290,7 +308,7 @@ class RecolourSprite(object):
  70.              labels[self.label.value] = 0
  71.          return labels, 1
  72.  
  73. -    def expand(self, default_file, default_mask_file, id_dict):
  74. +    def expand(self, default_file, default_mask_file, poslist, id_dict):
  75.          # create new struct, needed for template expansion
  76.          new_mapping = []
  77.          for old_assignment in self.mapping:
  78. @@ -299,7 +317,7 @@ class RecolourSprite(object):
  79.              to_min_value = old_assignment.value.min.reduce_constant([id_dict])
  80.              to_max_value = None if old_assignment.value.max is None else old_assignment.value.max.reduce_constant([id_dict])
  81.              new_mapping.append(assignment.Assignment(assignment.Range(from_min_value, from_max_value), assignment.Range(to_min_value, to_max_value), old_assignment.pos))
  82. -        return [RecolourSprite(new_mapping)]
  83. +        return [RecolourSprite(new_mapping, poslist = poslist)]
  84.  
  85.      def __str__(self):
  86.          ret = "" if self.label is None else str(self.label) + ": "
  87. @@ -372,7 +390,7 @@ class TemplateUsage(object):
  88.              labels[self.label.value] = 0
  89.          return labels, offset
  90.  
  91. -    def expand(self, default_file, default_mask_file, parameters):
  92. +    def expand(self, default_file, default_mask_file, poslist, parameters):
  93.          if self.name.value not in sprite_template_map:
  94.              raise generic.ScriptError("Encountered unknown template identifier: " + self.name.value, self.name.pos)
  95.          template = sprite_template_map[self.name.value]
  96. @@ -385,13 +403,13 @@ class TemplateUsage(object):
  97.                  raise generic.ScriptError("Template parameters should be compile-time constants", param.pos)
  98.              param_dict[template.param_list[i].value] = param.value
  99.  
  100. -        return parse_sprite_list(template.sprite_list, default_file, default_mask_file, param_dict)
  101. +        return parse_sprite_list(template.sprite_list, default_file, default_mask_file, poslist + [self.pos], param_dict)
  102.  
  103.      def __str__(self):
  104.          return "{}({})".format(self.name, ", ".join(str(param) for param in self.param_list))
  105.  
  106.  
  107. -def parse_real_sprite(sprite, default_file, default_mask_file, id_dict):
  108. +def parse_real_sprite(sprite, default_file, default_mask_file, poslist, id_dict):
  109.      # check the number of parameters
  110.      num_param = len(sprite.param_list)
  111.      if num_param == 0:
  112. @@ -401,7 +419,7 @@ def parse_real_sprite(sprite, default_fi
  113.          raise generic.ScriptError("Invalid number of arguments for real sprite. Expected 2..9.", sprite.param_list[0].pos)
  114.  
  115.      # create new sprite struct, needed for template expansion
  116. -    new_sprite = RealSprite()
  117. +    new_sprite = RealSprite(poslist = poslist + sprite.poslist)
  118.  
  119.      param_offset = 0
  120.  
  121. @@ -474,10 +492,10 @@ def parse_real_sprite(sprite, default_fi
  122.  
  123.  sprite_template_map = {}
  124.  
  125. -def parse_sprite_list(sprite_list, default_file, default_mask_file, parameters = {}):
  126. +def parse_sprite_list(sprite_list, default_file, default_mask_file, poslist, parameters = {}):
  127.      real_sprite_list = []
  128.      for sprite in sprite_list:
  129. -        real_sprite_list.extend(sprite.expand(default_file, default_mask_file, parameters))
  130. +        real_sprite_list.extend(sprite.expand(default_file, default_mask_file, poslist, parameters))
  131.      return real_sprite_list
  132.  
  133.  def parse_sprite_data(sprite_container):
  134. @@ -493,8 +511,8 @@ def parse_sprite_data(sprite_container):
  135.      first = True
  136.  
  137.      for sprite_data in all_sprite_data:
  138. -        sprite_list, default_file, default_mask_file, zoom_level, bit_depth = sprite_data
  139. -        new_sprite_list = parse_sprite_list(sprite_list, default_file, default_mask_file)
  140. +        sprite_list, default_file, default_mask_file, pos, zoom_level, bit_depth = sprite_data
  141. +        new_sprite_list = parse_sprite_list(sprite_list, default_file, default_mask_file, [pos])
  142.          if not first and len(new_sprite_list) != len(action_list):
  143.              msg = "Expected {:d} alternative sprites for {} '{}', got {:d}."
  144.              msg = msg.format(len(action_list), sprite_container.block_type, sprite_container.block_name.value, len(new_sprite_list))
  145. diff --git a/nml/ast/sprite_container.py b/nml/ast/sprite_container.py
  146. --- a/nml/ast/sprite_container.py
  147. +++ b/nml/ast/sprite_container.py
  148. @@ -28,7 +28,7 @@ class SpriteContainer(object):
  149.      @type block_name: L{Identifier}, or C{None} if N/A
  150.  
  151.      @ivar sprite_data: Mapping of (zoom level, bit-depth) to (sprite list, default file)
  152. -    @type sprite_data: C{dict} that maps (C{tuple} of (C{int}, C{int})) to (C{tuple} of (C{list} of (L{RealSprite}, L{RecolourSprite} or L{TemplateUsage}), L{StringLiteral} or C{None}))
  153. +    @type sprite_data: C{dict} that maps (C{tuple} of (C{int}, C{int})) to (C{tuple} of (C{list} of (L{RealSprite}, L{RecolourSprite} or L{TemplateUsage}), L{StringLiteral} or C{None}, L{Position}))
  154.      """
  155.      sprite_blocks = {}
  156.  
  157. @@ -50,14 +50,17 @@ class SpriteContainer(object):
  158.                     "level / bit depth combination. This data will be overridden.")
  159.              msg = msg.format(self.block_type, self.block_name.value)
  160.              generic.print_warning(msg, pos)
  161. -        self.sprite_data[key] = (sprite_list, default_file, default_mask_file)
  162. +        self.sprite_data[key] = (sprite_list, default_file, default_mask_file, pos)
  163.  
  164.      def get_all_sprite_data(self):
  165.          """
  166. -        Get all sprite data as a list of 5-tuples (sprite_list, default_file, default_mask_file, zoom_level, bit_depth)
  167. -        Sorting makes sure that the order is consistent, and that the normal zoom, 8bpp sprites appear first
  168. +        Get all sprite data.
  169. +        Sorting makes sure that the order is consistent, and that the normal zoom, 8bpp sprites appear first.
  170. +
  171. +        @return: List of 6-tuples (sprite_list, default_file, default_mask_file, position, zoom_level, bit_depth).
  172. +        @rtype:  C{list} of C{tuple} of (C{list} of (L{RealSprite}, L{RecolourSprite} or L{TemplateUsage}), L{StringLiteral} or C{None}, L{Position}, C{int}, C{int})
  173.          """
  174. -        return [(self.sprite_data[key][0], self.sprite_data[key][1], self.sprite_data[key][2], key[0], key[1]) for key in sorted(self.sprite_data)]
  175. +        return [val + key for key, val in sorted(self.sprite_data.items())]
  176.  
  177.      @classmethod
  178.      def resolve_sprite_block(cls, block_name):
  179. diff --git a/nml/generic.py b/nml/generic.py
  180. --- a/nml/generic.py
  181. +++ b/nml/generic.py
  182. @@ -139,8 +139,10 @@ class ImageFilePosition(Position):
  183.      """
  184.      Generic (not position-dependant) error with an image file
  185.      """
  186. -    def __init__(self, filename):
  187. -        Position.__init__(self, filename, [])
  188. +    def __init__(self, filename, pos = None):
  189. +        poslist = []
  190. +        if pos is not None: poslist.append(pos)
  191. +        Position.__init__(self, filename, poslist)
  192.  
  193.      def __str__(self):
  194.          return 'Image file "{}"'.format(self.filename)
  195. @@ -181,8 +183,8 @@ class RangeError(ScriptError):
  196.          ScriptError.__init__(self, name + " out of range " + str(min_value) + ".." + str(max_value) + ", encountered " + str(value), pos)
  197.  
  198.  class ImageError(ScriptError):
  199. -    def __init__(self, value, filename):
  200. -        ScriptError.__init__(self, value, ImageFilePosition(filename))
  201. +    def __init__(self, value, filename, pos = None):
  202. +        ScriptError.__init__(self, value, ImageFilePosition(filename, pos))
  203.  
  204.  class OnlyOnceError(ScriptError):
  205.      """
  206. diff --git a/nml/parser.py b/nml/parser.py
  207. --- a/nml/parser.py
  208. +++ b/nml/parser.py
  209. @@ -451,9 +451,9 @@ class NMLParser(object):
  210.          '''real_sprite : LBRACKET expression_list RBRACKET
  211.                         | ID COLON LBRACKET expression_list RBRACKET'''
  212.          if len(t) == 4:
  213. -            t[0] = real_sprite.RealSprite(t[2])
  214. +            t[0] = real_sprite.RealSprite(param_list = t[2], poslist = [t.lineno(1)])
  215.          else:
  216. -            t[0] = real_sprite.RealSprite(t[4], t[1])
  217. +            t[0] = real_sprite.RealSprite(param_list = t[4], label = t[1], poslist = [t.lineno(1)])
  218.  
  219.      def p_recolour_assignment_list(self, t):
  220.          '''recolour_assignment_list :
  221. diff --git a/nml/spriteencoder.py b/nml/spriteencoder.py
  222. --- a/nml/spriteencoder.py
  223. +++ b/nml/spriteencoder.py
  224. @@ -263,14 +263,16 @@ class SpriteEncoder(object):
  225.          if filename_32bpp is not None:
  226.              im = self.open_image_file(filename_32bpp.value)
  227.              if im.mode not in ("RGB", "RGBA"):
  228. -                raise generic.ImageError("32bpp image is not a full colour RGB(A) image.", filename_32bpp.value)
  229. +                pos = build_position(sprite_info.poslist)
  230. +                raise generic.ImageError("32bpp image is not a full colour RGB(A) image.", filename_32bpp.value, pos)
  231.              info_byte |= INFO_RGB
  232.              if im.mode == "RGBA":
  233.                  info_byte |= INFO_ALPHA
  234.  
  235.              (im_width, im_height) = im.size
  236.              if x < 0 or y < 0 or x + size_x > im_width or y + size_y > im_height:
  237. -                raise generic.ScriptError("Read beyond bounds of image file '{}'".format(filename_32bpp.value), filename_32bpp.pos)
  238. +                pos = build_position(sprite_info.poslist)
  239. +                raise generic.ScriptError("Read beyond bounds of image file '{}'".format(filename_32bpp.value), pos)
  240.              sprite = im.crop((x, y, x + size_x, y + size_y))
  241.              rgb_sprite_data = sprite.tostring()
  242.  
  243. @@ -281,13 +283,15 @@ class SpriteEncoder(object):
  244.          if filename_8bpp is not None:
  245.              mask_im = self.open_image_file(filename_8bpp.value)
  246.              if mask_im.mode != "P":
  247. -                raise generic.ImageError("8bpp image does not have a palette", filename_8bpp.value)
  248. +                pos = build_position(sprite_info.poslist)
  249. +                raise generic.ImageError("8bpp image does not have a palette", filename_8bpp.value, pos)
  250.              im_mask_pal = palette.validate_palette(mask_im, filename_8bpp.value)
  251.              info_byte |= INFO_PAL
  252.  
  253.              (im_width, im_height) = mask_im.size
  254.              if mask_x < 0 or mask_y < 0 or mask_x + size_x > im_width or mask_y + size_y > im_height:
  255. -                raise generic.ScriptError("Read beyond bounds of image file '{}'".format(filename_8bpp.value), filename_8bpp.pos)
  256. +                pos = build_position(sprite_info.poslist)
  257. +                raise generic.ScriptError("Read beyond bounds of image file '{}'".format(filename_8bpp.value), pos)
  258.              mask_sprite = mask_im.crop((mask_x, mask_y, mask_x + size_x, mask_y + size_y))
  259.  
  260.              mask_sprite_data = self.palconvert(mask_sprite.tostring(), im_mask_pal)
  261. @@ -497,3 +501,21 @@ class SpriteEncoder(object):
  262.          else:
  263.              return sprite_str
  264.  
  265. +def build_position(poslist):
  266. +    """
  267. +    Construct a L{Position} object that expands to positions in the template instantiation stack.
  268. +
  269. +    @param poslist: Sequence of positions to report. First entry is the innermost template,
  270. +                    last entry is the nml statement that started it all.
  271. +    @type  poslist: C{list} of L{Position}
  272. +
  273. +    @return: Position to attach to an error.
  274. +    @rtype:  L{Position}
  275. +    """
  276. +    if poslist is None or len(poslist) == 0:
  277. +        return None
  278. +    if len(poslist) == 1:
  279. +        return poslist[0]
  280. +    pos = poslist[-1]
  281. +    pos.includes = pos.includes + poslist[:-1]
  282. +    return pos

Comments