2.1 --- a/lplc Fri Mar 03 13:41:13 2017 +0100
2.2 +++ b/lplc Fri Mar 03 13:44:32 2017 +0100
2.3 @@ -82,6 +82,12 @@
2.4 else:
2.5 return l, needed
2.6
2.7 +def getvalue(l, i):
2.8 + if l and len(l) > i:
2.9 + return l[i]
2.10 + else:
2.11 + return None
2.12 +
2.13 def remove_all(dirname):
2.14
2.15 "Remove 'dirname' and its contents."
2.16 @@ -145,6 +151,15 @@
2.17 args Show invocations where a callable may be involved that cannot accept
2.18 the arguments provided
2.19
2.20 +Control over program organisation can be exercised using the following options
2.21 +with each requiring an input filename providing a particular form of
2.22 +information:
2.23 +
2.24 +--attr-codes Attribute codes identifying named object attributes
2.25 +--attr-locations Attribute locations in objects
2.26 +--param-codes Parameter codes identifying named parameters
2.27 +--param-locations Parameter locations in signatures
2.28 +
2.29 The following informational options can be specified to produce output instead
2.30 of compiling a program:
2.31
2.32 @@ -171,11 +186,16 @@
2.33
2.34 # Determine the options and arguments.
2.35
2.36 + attrnames = []
2.37 + attrlocations = []
2.38 debug = False
2.39 gc_sections = False
2.40 ignore_env = False
2.41 make = True
2.42 make_verbose = True
2.43 + outputs = []
2.44 + paramnames = []
2.45 + paramlocations = []
2.46 reset = False
2.47 reset_all = False
2.48 timings = True
2.49 @@ -185,7 +205,6 @@
2.50
2.51 unrecognised = []
2.52 filenames = []
2.53 - outputs = []
2.54
2.55 # Obtain program filenames by default.
2.56
2.57 @@ -193,10 +212,15 @@
2.58 needed = None
2.59
2.60 for arg in args:
2.61 - if arg in ("-c", "--compile"): make = False
2.62 + if arg == "--attr-codes": l = attrnames; needed = 1
2.63 + elif arg == "--attr-locations": l = attrlocations; needed = 1
2.64 + elif arg in ("-c", "--compile"): make = False
2.65 elif arg in ("-E", "--no-env"): ignore_env = True
2.66 elif arg in ("-g", "--debug"): debug = True
2.67 elif arg in ("-G", "--gc-sections"): gc_sections = True
2.68 + # "P" handled below.
2.69 + elif arg == "--param-codes": l = paramnames; needed = 1
2.70 + elif arg == "--param-locations": l = paramlocations; needed = 1
2.71 elif arg in ("-q", "--quiet"): make_verbose = False
2.72 elif arg in ("-r", "--reset"): reset = True
2.73 elif arg in ("-R", "--reset-all"): reset_all = True
2.74 @@ -290,18 +314,20 @@
2.75
2.76 if timings: now = stopwatch("Deduction", now)
2.77
2.78 - o = optimiser.Optimiser(i, d, output_dir)
2.79 + o = optimiser.Optimiser(i, d, output_dir,
2.80 + getvalue(attrnames, 0), getvalue(attrlocations, 0),
2.81 + getvalue(paramnames, 0), getvalue(paramlocations, 0))
2.82 o.to_output()
2.83
2.84 + if timings: now = stopwatch("Optimisation", now)
2.85 +
2.86 # Detect structure or signature changes demanding a reset of the
2.87 # generated sources.
2.88
2.89 reset = reset or o.need_reset()
2.90
2.91 - if timings: now = stopwatch("Optimisation", now)
2.92 -
2.93 g = generator.Generator(i, o, generated_dir)
2.94 - g.to_output(debug, gc_sections)
2.95 + g.to_output(reset, debug, gc_sections)
2.96
2.97 if timings: now = stopwatch("Generation", now)
2.98
3.1 --- a/optimiser.py Fri Mar 03 13:41:13 2017 +0100
3.2 +++ b/optimiser.py Fri Mar 03 13:44:32 2017 +0100
3.3 @@ -31,18 +31,32 @@
3.4
3.5 "Optimise objects in a program."
3.6
3.7 - def __init__(self, importer, deducer, output):
3.8 + def __init__(self, importer, deducer, output,
3.9 + attrnames_filename=None, locations_filename=None,
3.10 + paramnames_filename=None, parameter_locations_filename=None):
3.11
3.12 """
3.13 Initialise an instance using the given 'importer' and 'deducer' that
3.14 will perform the arrangement of attributes for program objects, writing
3.15 the results to the given 'output' directory.
3.16 +
3.17 + If 'attrnames_filename', 'locations_filename', 'paramnames_filename', or
3.18 + 'parameter_locations_filename' are given, they will be used to
3.19 + explicitly indicate existing attribute code, attribute position,
3.20 + parameter code, and parameter position information respectively.
3.21 """
3.22
3.23 self.importer = importer
3.24 self.deducer = deducer
3.25 self.output = output
3.26
3.27 + # Explicitly-specified attribute and parameter sources.
3.28 +
3.29 + self.attrnames_filename = attrnames_filename
3.30 + self.locations_filename = locations_filename
3.31 + self.paramnames_filename = paramnames_filename
3.32 + self.parameter_locations_filename = parameter_locations_filename
3.33 +
3.34 # Detection of differences between any existing structure or signature
3.35 # information and the generated information.
3.36
3.37 @@ -60,6 +74,7 @@
3.38
3.39 self.all_attrnames = None
3.40 self.existing_attrnames = None
3.41 + self.indicated_attrnames = None
3.42
3.43 # Locations of parameters in parameter tables.
3.44
3.45 @@ -72,6 +87,7 @@
3.46
3.47 self.all_paramnames = None
3.48 self.existing_paramnames = None
3.49 + self.indicated_paramnames = None
3.50
3.51 # Specific attribute access information.
3.52
3.53 @@ -127,20 +143,49 @@
3.54
3.55 self.check_output()
3.56
3.57 - # Existing attribute and parameter positioning information.
3.58 + # Existing attribute and parameter positioning information. This
3.59 + # influences the positions of attributes and parameters found in the
3.60 + # program.
3.61 +
3.62 + locations_filename = self.locations_filename or \
3.63 + join(self.output, "locations")
3.64 +
3.65 + parameter_locations_filename = self.parameter_locations_filename or \
3.66 + join(self.output, "parameter_locations")
3.67
3.68 - self.existing_locations = self.read_locations("locations", self._line_to_list, list)
3.69 - self.existing_arg_locations = self.read_locations("parameter_locations", self._line_to_list, list)
3.70 + self.existing_locations = self.read_data(locations_filename, self._line_to_list, list)
3.71 + self.existing_arg_locations = self.read_data(parameter_locations_filename, self._line_to_list, list)
3.72
3.73 - # Existing attribute and parameter code information.
3.74 + # Existing attribute and parameter code information. This is used to
3.75 + # check the compatibility of the output against any assignments
3.76 + # previously made.
3.77 +
3.78 + identity = lambda x: x
3.79 + none = lambda x: None
3.80
3.81 - self.existing_attrnames = self.read_locations("attrnames", lambda x: x, lambda x: None)
3.82 - self.existing_paramnames = self.read_locations("paramnames", lambda x: x, lambda x: None)
3.83 + attrnames_filename = join(self.output, "attrnames")
3.84 + paramnames_filename = join(self.output, "paramnames")
3.85 +
3.86 + self.existing_attrnames = self.read_data(attrnames_filename, identity, none)
3.87 + self.existing_paramnames = self.read_data(paramnames_filename, identity, none)
3.88 +
3.89 + # Explicitly-specified attribute name and parameter name codes. These
3.90 + # direct assignment of codes in the program.
3.91 +
3.92 + self.indicated_attrnames = self.attrnames_filename and \
3.93 + self.read_data(self.attrnames_filename, identity, none)
3.94
3.95 - # Existing structure and signature information.
3.96 + self.indicated_paramnames = self.paramnames_filename and \
3.97 + self.read_data(self.paramnames_filename, identity, none)
3.98 +
3.99 + # Existing structure and signature information. This is used to check
3.100 + # the output and detect whether structures or signatures have changed.
3.101
3.102 - self.existing_structures = dict(self.read_locations("structures", self._line_to_structure_pairs, list))
3.103 - self.existing_parameters = dict(self.read_locations("parameters", self._line_to_signature_pairs, list))
3.104 + structures_filename = join(self.output, "structures")
3.105 + parameters_filename = join(self.output, "parameters")
3.106 +
3.107 + self.existing_structures = dict(self.read_data(structures_filename, self._line_to_structure_pairs, list))
3.108 + self.existing_parameters = dict(self.read_data(parameters_filename, self._line_to_signature_pairs, list))
3.109
3.110 def _line_to_list(self, line):
3.111
3.112 @@ -171,7 +216,7 @@
3.113 values = map(lambda x: x != '-' and x or None, line.split(", "))
3.114 return (decode_reference(ref), values)
3.115
3.116 - def read_locations(self, filename, decode, empty):
3.117 + def read_data(self, filename, decode, empty):
3.118
3.119 """
3.120 Read location details from 'filename', using 'decode' to convert each
3.121 @@ -179,7 +224,6 @@
3.122 line, returning a collection.
3.123 """
3.124
3.125 - filename = join(self.output, filename)
3.126 collection = []
3.127
3.128 if exists(filename):
3.129 @@ -839,7 +883,13 @@
3.130 these identifiers.
3.131 """
3.132
3.133 - self.all_attrnames, d = self._get_name_mapping(self.attr_locations, self.existing_attrnames)
3.134 + # Initialise the mapping from attribute names to codes.
3.135 +
3.136 + l = self.all_attrnames = []; d = {}
3.137 + self._init_name_mapping(l, d, self.existing_attrnames)
3.138 + if self.indicated_attrnames:
3.139 + self._init_name_mapping(l, d, self.indicated_attrnames)
3.140 + self._update_name_mapping(l, d, self.attr_locations)
3.141
3.142 # Record the numbers indicating the locations of the names.
3.143
3.144 @@ -851,7 +901,13 @@
3.145 else:
3.146 l.append(d[attrname])
3.147
3.148 - self.all_paramnames, d = self._get_name_mapping(self.param_locations, self.existing_paramnames)
3.149 + # Initialise the mapping from parameter names to codes.
3.150 +
3.151 + l = self.all_paramnames = []; d = {}
3.152 + self._init_name_mapping(l, d, self.existing_paramnames)
3.153 + if self.indicated_paramnames:
3.154 + self._init_name_mapping(l, d, self.indicated_paramnames)
3.155 + self._update_name_mapping(l, d, self.param_locations)
3.156
3.157 # Record the numbers indicating the locations of the names.
3.158
3.159 @@ -864,43 +920,57 @@
3.160 name, pos = value
3.161 l.append((d[name], pos))
3.162
3.163 - def _get_name_mapping(self, locations, existing=None):
3.164 + def _init_name_mapping(self, l, d, existing):
3.165
3.166 """
3.167 - Get a sorted list of names from 'locations', then map them to
3.168 - identifying numbers. Preserve the identifiers from the 'existing' list,
3.169 - if specified. Return the list and the mapping.
3.170 + Initialise the name collection 'l', with mapping 'd', using the
3.171 + 'existing' mapping.
3.172 """
3.173
3.174 - d = {}
3.175 - l = []
3.176 -
3.177 i = 0
3.178 - all_names = set(locations.keys())
3.179 +
3.180 + for name in existing:
3.181 +
3.182 + # Test for the name in another position.
3.183 +
3.184 + if d.has_key(name):
3.185 + if d[name] != i:
3.186 + raise OptimiseError, "Name %s has conflicting codes: %d and %d." % \
3.187 + (name, d[name], i)
3.188 + else:
3.189
3.190 - # Preserve the existing identifiers, if available.
3.191 + # Test for other usage of the position.
3.192
3.193 - if existing:
3.194 - for name in existing:
3.195 + if i < len(l):
3.196 + if l[i] != name:
3.197 + raise OptimiseError, "Position %d has conflicting names: %s and %s." % \
3.198 + (i, name, d[name])
3.199 + l[i] = name
3.200 + else:
3.201 + l.append(name)
3.202 +
3.203 d[name] = i
3.204 - l.append(name)
3.205 - if name in all_names:
3.206 - all_names.remove(name)
3.207 - i += 1
3.208 +
3.209 + i += 1
3.210 +
3.211 + def _update_name_mapping(self, l, d, locations):
3.212
3.213 - # Include all remaining names in order.
3.214 + """
3.215 + Using any existing identifiers supplied by 'l' and 'd', update the
3.216 + identifiers using a sorted list of names from 'locations'.
3.217 + """
3.218
3.219 - all_names = list(all_names)
3.220 + all_names = list(locations.keys())
3.221 all_names.sort()
3.222
3.223 + i = len(l)
3.224 +
3.225 for name in all_names:
3.226 if not d.has_key(name):
3.227 d[name] = i
3.228 l.append(name)
3.229 i += 1
3.230
3.231 - return l, d
3.232 -
3.233 def populate_constants(self):
3.234
3.235 """