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