1 #!/usr/bin/env python 2 3 """ 4 Generate RSVP code from simplified nodes. 5 6 Copyright (C) 2007 Paul Boddie <paul@boddie.org.uk> 7 8 This program is free software; you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation; either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT 14 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 15 FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 16 details. 17 18 You should have received a copy of the GNU General Public License along with 19 this program. If not, see <http://www.gnu.org/licenses/>. 20 """ 21 22 from simplify.simplified import * 23 import rsvp 24 25 # Classes. 26 27 class Generator(Visitor): 28 29 "An RSVP code generator." 30 31 def __init__(self, debug=0): 32 33 "Initialise the visitor." 34 35 self.debug = debug 36 Visitor.__init__(self) 37 self.assemblers = [] 38 self.current_assemblers = [] 39 40 # Satisfy visitor issues. 41 42 self.visitor = self 43 44 def get_code(self): 45 return rsvp.get_merged(*self.assemblers) 46 47 def assembler(self): 48 return self.current_assemblers[-1] 49 50 def process(self, module): 51 52 """ 53 Process the given 'module', creating RSVP assemblers to store code and 54 data. 55 """ 56 57 self.module = module 58 59 main = rsvp.RSVPAssembler(self.debug) 60 self.current_assemblers.append(main) 61 62 # Store constants. 63 64 for name, const in module.simplifier.constants.items(): 65 main.label("const$%s$%s" % (self.module.name, name)) 66 main.add(const.value) 67 68 # Reserve the namespace. 69 70 for name in module.namespace.keys(): 71 main.label("global$%s$%s" % (self.module.name, name)) 72 main.add(None) 73 74 # Generate subprograms. 75 76 for subprogram in module.simplifier.subprograms: 77 if not subprogram.internal: 78 79 # Generate specific subprograms. 80 81 for specialisation in subprogram.specialisations(): 82 self.process_subprogram(specialisation) 83 84 # Generate the initialisation. 85 86 self.dispatch(module) 87 88 # Remember the assembler used. 89 90 self.current_assemblers.pop() 91 self.assemblers.append(main) 92 93 def default(self, node, *args): 94 if hasattr(node, "code"): 95 self.dispatches(node.code) 96 97 def visitInvoke(self, invoke): 98 99 "Process the given 'invoke' node." 100 101 sub = self.assembler() 102 103 # NOTE: Generate arguments in a way compatible with subprogram 104 # NOTE: consumption as parameters. 105 106 for subprogram in invoke.invocations: 107 prepare_args = not isinstance(invoke, InvokeRef) and not invoke.share_locals 108 if prepare_args: 109 consumed_args = invoke.consumed_args[subprogram] 110 111 # NOTE: Generate switch table. 112 113 # Save the current frame. 114 115 sub.add("SCF") 116 117 # Extend the stack for the arguments. 118 119 nparams = len(consumed_args) 120 sub.add("ESF", nparams) 121 122 # For each argument, evaluate the expression. 123 124 for arg in invoke.args: 125 if arg is None: 126 continue 127 128 self.dispatch(arg) 129 130 # Then, save the argument to the correct part of the arguments 131 # frame. 132 133 pos = self.arg_position(consumed_args, arg) 134 sub.add("MVA", pos) 135 136 # Reserve the namespace. 137 138 sub.add("NSF", nparams) 139 140 sub.add("JAS", "sub$%s$%s" % (subprogram.module.name, subprogram.full_name())) 141 142 if prepare_args: 143 sub.add("PSF") # previous stack frame 144 145 visitInvokeFunction = visitInvoke 146 visitInvokeRef = visitInvoke 147 148 def visitModule(self, module): 149 150 "Process the given 'module'." 151 152 self.dispatches(module.code) 153 154 def visitSubprogram(self, subprogram): 155 156 "Process the 'subprogram'." 157 158 if subprogram.internal: 159 self.process_subprogram(subprogram) 160 161 def process_subprogram(self, subprogram): 162 163 sub = rsvp.RSVPAssembler(self.debug) 164 self.current_assemblers.append(sub) 165 166 sub.label("sub$%s$%s" % (self.module.name, subprogram.full_name())) 167 168 locals = subprogram.namespace.keys() 169 params = subprogram.paramtypes.keys() 170 nparams = len(params) 171 non_params = len(locals) - nparams 172 173 if non_params > 0: 174 sub.add("ESF", non_params) # extend stack frame for locals 175 176 # Produce the standard end of subprogram return. 177 178 sub.add("RAC") # return 179 180 self.current_assemblers.pop() 181 self.assemblers.append(sub) 182 183 def arg_position(self, consumed_args, arg): 184 185 """ 186 Return, for the specified 'consumed_args', the position of 'arg' in the 187 list, handling keyword arguments appropriately. 188 """ 189 190 if isinstance(arg, Keyword): 191 arg = arg.expr 192 return consumed_args.index(arg) 193 194 # Convenience functions. 195 196 def get_attribute_table(importer): 197 198 """ 199 Return an attribute table for all structures known to the given 'importer'. 200 """ 201 202 master_table = {} 203 for module in importer.get_modules(): 204 for structure in module.simplifier.structures: 205 classname = structure.fully_qualified_name() 206 master_table[classname] = [] 207 208 # NOTE: Assume enquiries of class attribute items produce the same 209 # NOTE: ordering every time. 210 211 positions = {} 212 for name, (defining_cls, defs) in structure.get_visible_class_attributes().items(): 213 first = 1 214 for attr in defs: 215 if not first: 216 print "Ambiguous attribute '%s' in '%s'." % (name, classname) 217 break 218 if not positions.has_key(defining_cls): 219 positions[defining_cls] = 1 220 master_table[classname].append((name, ("A", defining_cls.fully_qualified_name(), positions[defining_cls]))) 221 positions[defining_cls] += 1 222 first = 0 223 224 i = 1 225 for name in structure.get_instance_attribute_names(): 226 master_table[classname].append((name, ("A", "I", i))) 227 i += 1 228 229 return master_table 230 231 def generate(module): 232 generator = Generator() 233 generator.process(module) 234 return generator.get_code() 235 236 # vim: tabstop=4 expandtab shiftwidth=4