pykiss

Python Kisekae set viewer
git clone https://code.literati.org/pykiss.git
Log | Files | Refs | LICENSE

kisscompiler.py (15377B)


      1 # Copyright (C) 2003 Sean R. Lynch <seanl@chaosring.org>
      2 #
      3 # This program is free software; you can redistribute it and/or modify
      4 # it under the terms of the GNU General Public License as published by
      5 # the Free Software Foundation; either version 2 of the License, or
      6 # (at your option) any later version.
      7 #
      8 # This program is distributed in the hope that it will be useful,
      9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
     10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11 # GNU General Public License for more details.
     12 #
     13 # You should have received a copy of the GNU General Public License
     14 # along with this program; if not, write to the Free Software
     15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     16 
     17 import string, re, random
     18 
     19 class ConversionError(Exception): pass
     20 
     21 class VarDict(dict):
     22     """A dictionary that creates local variables automatically"""
     23     def __init__(self, compiler):
     24         # Need a reference to the compiler to create local variables
     25         self.compiler = compiler
     26 
     27     def __getitem__(self, key):
     28         """Return the value that's in the dictionary if it exists,
     29         create a new entry with a local variable otherwise."""
     30         
     31         if self.has_key(key): return dict.__getitem__(self, key)
     32     
     33         var = self.compiler.local(key)
     34         self[key] = var
     35         return var
     36         
     37 
     38 class Action:
     39     """A defined action."""
     40     def __init__(self, compiler, cmd, mode, *lines):
     41         # Extract the name and args from the command
     42         m = re.match("(\w+)\(([^)]*)\)", cmd)
     43         if not m: raise ValueError, "Bad command desc: %r" % cmd
     44         name = m.group(1)
     45         argstr = m.group(2)
     46         args = map(lambda a: a.split(":"), map(string.strip, argstr.split(',')))
     47 
     48         self.compiler = compiler
     49         self.name = name
     50         self.args = args
     51         self.mode = mode
     52         self.lines = lines
     53 
     54     def compile(self, args):
     55         if len(args) != len(self.args):
     56             raise ValueError, "Wrong number of arguments for %s: %r, %r" % (self.name, self.args, args) 
     57         # First, convert the arguments
     58         compiler = self.compiler
     59         vars = VarDict(compiler)
     60         try:
     61             for arg, (name, typechar) in zip(args, self.args):
     62                 if typechar == 'i': vars[name] = compiler.var_or_int(arg)
     63                 elif typechar == 'I':
     64                     if type(arg) != type(0): raise ConversionError
     65                     vars[name] = str(arg)
     66                 elif typechar == 'o': vars[name] = compiler.cel_or_obj(arg)
     67                 elif typechar == 'O':
     68                     if not hasattr(arg, 'num'): raise ConversionError
     69                     vars[name] = compiler.obj(arg)
     70                 elif typechar == 'c': vars[name] = compiler.cel(arg)
     71                 elif typechar == 's':
     72                     if type(arg) != type(""): raise ConversionError
     73                     else: vars[name] = arg
     74                 elif typechar == 'S':
     75                     # Lowercase string
     76                     if type(arg) != type(""): raise ConversionError
     77                     else: vars[name] = arg.lower()
     78                 elif typechar == 't':
     79                     # Timers are just ints right now
     80                     # Don't yet allow symbolic names.
     81                     vars[name] = compiler.var_or_int(arg)
     82                 elif typechar == 'l': vars[name] = compiler.label(arg)
     83                 else: raise ValueError, "Unknown type character: %r" % typechar
     84         except ConversionError, expl:
     85             print self.args, args
     86             self.compiler.kiss.warn("ConversionError: %s" % expl)
     87             return ['pass']
     88         try: return [l % vars for l in self.lines]
     89         except:
     90             import traceback
     91             print self.lines, vars
     92             traceback.print_exc()
     93             raise SystemExit
     94 
     95 
     96 class Event:
     97     def __init__(self, code, f):
     98         self.code = code
     99         self.f = f
    100 
    101     def __call__(self, *args):
    102         try: self.f(*args)
    103         except:
    104             print self.code
    105             import traceback
    106             traceback.print_exc()
    107             raise SystemExit
    108     
    109 
    110 class EventCompiler:
    111     tr = string.maketrans(".-()'\", ", "PMOCSDA_")
    112     def __init__(self, kiss):
    113         self.kiss = kiss
    114         self.cmds = {}
    115         self.vars = {'kiss': kiss, 'random': random}
    116 
    117 ##        # Add all objects and cels to the variable list
    118 ##        for obj in kiss.objs:
    119 ##            if obj is None: continue
    120 ##            self.vars['obj_%d' % obj.num] = obj
    121 
    122 ##        for name, cel in kiss.cels:
    123 ##            # Don't use varname because that will check for existence
    124 ##            # of the variable
    125 ##            self.vars['cel_%s' % name.translate(self.tr)] = cel
    126 
    127         self.add_cmds()
    128 
    129     def add_label(self, num, func):
    130         self.vars['label_%d' % num] = func
    131 
    132     def add_cmd(self, cmd, mode, *lines):
    133         # Exctract the name and args from the command
    134         action = Action(self, cmd, mode, *lines)
    135         if self.cmds.has_key(action.name):
    136             raise ValueError, "Action %s defined twice" % action.name
    137         self.cmds[action.name] = action
    138 
    139     def compile(self, eventname, actionlist):
    140         """Compile a list of actions into a Python function"""
    141         eventname = intern(eventname)
    142         #print eventname
    143         # Initialize state
    144         self.local_index = 0
    145         self.defaults = {'kiss': 'kiss'}
    146         source = [None]
    147         indent = 1
    148         funcname = 'e_%x' % id(eventname)
    149         
    150         for cmd, args in actionlist:
    151             #print cmd, args
    152             action = self.cmds[cmd]
    153             lines = action.compile(args)
    154             if action.mode == 2: indent -= 1
    155 	    spaces = ' ' * indent
    156 	    source.append('%s# %s%r' % (spaces, cmd, args))
    157             source.extend([spaces + line for line in lines])
    158             if action.mode == 1: indent += 1
    159             elif action.mode == -1: indent -= 1
    160 
    161         if len(source) == 1:
    162             # Empty
    163             return None
    164 
    165         source.append('')
    166         source[0] = 'def %s(%s):' % (funcname, ','.join(map(lambda x: '%s=%s' % x, self.defaults.items())))
    167     
    168         source = '\n'.join(source)
    169         try: exec source in self.vars
    170         except:
    171             import traceback, sys
    172             print >> sys.stderr, source
    173             traceback.print_exc()
    174             sys.exit(1)
    175 
    176         func = self.vars[funcname]
    177         func.__doc__ = eventname
    178         func.source = source
    179         return func
    180             
    181     def local(self, name):
    182         """Return a new local variable name"""
    183         name = "x_%s_%x" % (name, self.local_index)
    184         self.local_index += 1
    185         return name
    186     
    187             
    188     def varname(self, prefix, name, default=None):
    189         """Convert a string to something that's suitable for a variable name"""
    190         var = prefix + string.translate(name, self.tr)
    191         if default is None:
    192             if self.vars.has_key(var): return var
    193             else:
    194                 raise ValueError, "Nonexistent variable: %r" % var
    195                 return None
    196         else:
    197             self.vars[var] = default
    198             return var
    199 
    200     # Type conversion methods
    201     def cel_or_obj(self, what):
    202         if type(what) == type(""):
    203             return self.cel(what.lower())
    204         else: return self.obj(what)
    205 
    206     def var_or_int(self, what):
    207         if type(what) == type(0): return str(what)
    208         else: return self.var(name)
    209 
    210     def var(self, name):
    211         """Return the translated name of a variable, creating it if necessary"""
    212         return self.varname('var_', name, 0)
    213 
    214     def obj(self, obj):
    215         name = 'obj_%d' % obj.num
    216         if not self.defaults.has_key(name):
    217             self.defaults[name] = 'kiss.objs[%d]' % obj.num
    218         return name
    219 
    220     def cel(self, celname):
    221         if not self.kiss.cels.has_key(celname):
    222             raise ConversionError, "Missing cel %r" % celname
    223         name = 'cel_%s' % celname.translate(self.tr)
    224         if not self.defaults.has_key(name):
    225             self.defaults[name] = 'kiss.cels["%s"]' % celname
    226         return name
    227 
    228     def add_cmds(self):
    229         self.add_cmd("altmap(what:o)", 0, "%(what)s.altmap()")
    230         self.add_cmd("map(what:o)", 0, "%(what)s.map()")
    231         self.add_cmd("move(obj:O, xoff:i, yoff:i)", 0,
    232                      "%(x)s, %(y)s = %(obj)s.get_pos()",
    233                      "%(obj)s.move((%(x)s+%(xoff)s, %(y)s+%(yoff)s))")
    234         self.add_cmd("movebyx(obj1:O, obj2:O, xoff:i)", 0,
    235                      "%(x)s, %(y)s = %(obj1)s.get_pos()",
    236                      "%(x1)s, %(y1)s = %(obj2)s.get_pos()",
    237                      "%(x)s = %(x1)s + %(xoff)s",
    238                      "%(obj1)s.move((%(x)s, %(y)s))")
    239         self.add_cmd("movebyy(obj1:O, obj2:O, yoff:i)", 0,
    240                      "%(x)s, %(y)s = %(obj1)s.get_pos()",
    241                      "%(x1)s, %(y1)s = %(obj2)s.get_pos()",
    242                      "%(y)s = %(y1)s + %(yoff)s",
    243                      "%(obj1)s.move((%(x)s, %(y)s))")
    244         self.add_cmd("randomtimer(timer:t, min:i, max:i)", 0, 
    245                      "kiss.timer(%(timer)s, random.randint(%(min)s, %(min)s+%(max)s))")
    246         self.add_cmd("setfix(obj:O, fixval:i)", 0,
    247                      "kiss.setfix(%(obj)s, %(fixval)s)")
    248         self.add_cmd("timer(timer:t, duration:i)", 0,
    249                      "kiss.timer(%(timer)s, %(duration)s)")
    250         self.add_cmd("unmap(what:o)", 0, "%(what)s.unmap()")
    251         self.add_cmd("ifmapped(cel:c, timer:t, value:i)", 0,
    252                      "if %(cel)s.mapped: kiss.timer(%(timer)s, %(value)s)")
    253         self.add_cmd("ifnotmapped(cel:c, timer:t, value:i)", 0,
    254                      "if not %(cel)s.mapped: kiss.timer(%(timer)s, %(value)s)")
    255         self.add_cmd("sound(filename:S)", 0,
    256                      'kiss.canvas.sound("%(filename)s")')
    257         self.add_cmd("moveto(obj:O, x:i, y:i)", 0,
    258                      "%(obj)s.move((%(x)s, %(y)s))")
    259         self.add_cmd("moverandx(obj:O, min:i, max:i)", 0,
    260                      "%(x)s, %(y)s = %(obj)s.get_pos()",
    261                      "%(obj)s.move((random.randint(%(x)s+%(min)s, %(x)s+%(max)s), %(y)s))")
    262         self.add_cmd("moverandy(obj:O, min:i, max:i)", 0,
    263                      "%(x)s, %(y)s = %(obj)s.get_pos()",
    264                      "%(obj)s.move((%(x)s, random.randint(%(y)s+%(min)s, %(y)s+%(max)s)))")
    265         self.add_cmd("movetorand(obj:O)", 0,
    266                      "%(obj)s.move((random.randrange(kiss.width), random.randrange(kiss.height)))")
    267         self.add_cmd("transparent(what:o, offset:i)", 0,
    268                      "%(what)s.set_transparent(%(offset)s)")
    269         self.add_cmd("viewport(x:i, y:i)", 0,
    270                      "kiss.canvas.viewport(%(x)s, %(y)s)")
    271         self.add_cmd("windowsize(w:i, h:i)", 0,
    272                      "kiss.canvas.windowsize(%(w)s, %(h)s)")
    273         self.add_cmd("changecol(palette:i)", 0,
    274                      "kiss.changecol(%(palette)s)")
    275         self.add_cmd("changeset(set:i)", 0,
    276                      "kiss.changeset(%(set)s)")
    277         self.add_cmd("debug(message:s)", 0,
    278                      'kiss.debug("%(message)s")')
    279         self.add_cmd("iffixed(what:o, timer:t, duration:i)", 0,
    280                      "if %(what)s.is_fixed(): kiss.timer(%(timer)s, %(duration)s)")
    281         self.add_cmd("ifmoved(what:o, timer:t, duration:i)", 0,
    282                      "if %(what)s.is_moved():",
    283                      " kiss.timer(%(timer)s, %(duration)s)")
    284         self.add_cmd("ifnotfixed(what:o, timer:t, duration:i)", 0,
    285                      "if not %(what)s.is_fixed():",
    286                      " kiss.timer(%(timer)s, %(duration)s)")
    287         self.add_cmd("ifnotmoved(what:o, timer:t, duration:i)", 0,
    288                      "if %(what)s.is_moved():",
    289                      " kiss.timer(%(timer)s, %(duration)s)")
    290         self.add_cmd("music(filename:s)", 0,
    291                      'kiss.canvas.music("%(filename)s")')
    292         self.add_cmd("nop()", 0)
    293         self.add_cmd("notify(message:s)", 0,
    294                      'kiss.notify("%(message)s")')
    295         self.add_cmd("quit()", 0,
    296                      "kiss.canvas.quit()")
    297 
    298         # FKiSS 3
    299         # I suppose it's possible that the stack could fill up, but
    300         # I don't know how otherwise to handle labels
    301         self.add_cmd("goto(label:l)", 0,
    302                      "%(label)s()",
    303                      "return")
    304         self.add_cmd("gosub(label:l)", 0,
    305                      "%(label)s()")
    306         self.add_cmd("gotorandom(percent:i, label1:l, label2:l)", 0,
    307                      "if random.randrange(100) < %(percent)s: %(label1)s()"
    308                      "else: %(label2)s()"
    309                      "return")
    310         self.add_cmd("gosubrandom(percent:i, label1:l, label2:l)", 0,
    311                      "if random.randrange(100) < %(percent)s: %(label1)s()"
    312                      "else: %(label2)s()")
    313         self.add_cmd("exitevent()", 0,
    314                      "return")
    315         self.add_cmd("ifequal(a:i, b:i)", 1,
    316                      "if %(a)s == %(b)s:")
    317         self.add_cmd("ifgreaterthan(a:i, b:i)", 1,
    318                      "if %(a)s > %(b)s:")
    319         self.add_cmd("iflessthan(a:i, b:i)", 1,
    320                      "if %(a)s < %(b)s:")
    321         self.add_cmd("ifnotequal(a:i, b:i)", 1,
    322                      "if %(a)s != %(b)s:")
    323         self.add_cmd("else()", 2,
    324                      "else:")
    325         self.add_cmd("endif()", -1)
    326         self.add_cmd("let(var:v, value:i)", 0,
    327                      "%(var)s = %(value)s")
    328         self.add_cmd("add(var:v, a:i, b:i)", 0,
    329                      "%(var)s = %(a)s + %(b)s")
    330         self.add_cmd("sub(var:v, a:i, b:i)", 0,
    331                      "%(var)s = %(a)s - %(b)s")
    332         self.add_cmd("mul(var:v, a:i, b:i)", 0,
    333                      "%(var)s = %(a)s * %(b)s")
    334         self.add_cmd("div(var:v, a:i, b:i)", 0,
    335                      "%(var)s = %(a)s / %(b)s")
    336         self.add_cmd("mod(var:v, a:i, b:i)", 0,
    337                      "%(var)s = %(a)s %% %(b)s")
    338         self.add_cmd("random(var:v, min:i, max:i)", 0,
    339                      "%(var)s = random.randint(%(min)s, %(max)s)")
    340         self.add_cmd("letobjectx(var:v, obj:O)", 0,
    341                      "%(var)s = %(obj)s.get_pos()[0]")
    342         self.add_cmd("letobjecty(var:v, obj:O)", 0,
    343                      "%(var)s = %(obj)s.get_pos()[1]")
    344         self.add_cmd("letfix(var:v, obj:O)", 0,
    345                      "%(var)s = %(obj)s.fixval")
    346         self.add_cmd("letmapped(var:v, obj:O)", 0,
    347                      "%(var)s = %(obj)s.mapped")
    348         self.add_cmd("letset(var:v)", 0,
    349                      "%(var)s = kiss.set")
    350         self.add_cmd("letpal(var:v)", 0,
    351                      "%(var)s = kiss.get_palgroup()")
    352         self.add_cmd("letmousex(var:v)", 0,
    353                      "%(var)s = kiss.canvas.get_mousepos()[0]")
    354         self.add_cmd("letmousey(var:v)", 0,
    355                      "%(var)s = kiss.canvas.get_mousepos()[1]")
    356         self.add_cmd("letcatch(var:v)", 0,
    357                      "%(var)s = kiss.get_catch()")
    358         self.add_cmd("letcollide(var:v, cel1:c, cel2:c)", 0, 
    359                      "%(var)s = %(cel1)s.collide(%(cel2)s)")
    360         self.add_cmd("letinside(var:v, obj1:O, obj2:O)", 0,
    361                      "%(var)s = %(obj1)s.rectcollide(%(obj2)s)")
    362         self.add_cmd("lettransparent(var:v, obj:O)", 0,
    363                      "%(var)s = %(obj)s.transparent")
    364         self.add_cmd("shell(cmd:s)", 0,
    365                      'kiss.canvas.shell("%(cmd)s")')
    366 
    367