1.1 --- a/common.py Mon Feb 20 18:50:16 2017 +0100
1.2 +++ b/common.py Tue Feb 21 00:20:43 2017 +0100
1.3 @@ -23,7 +23,7 @@
1.4 from compiler.transformer import Transformer
1.5 from errors import InspectError
1.6 from os import listdir, makedirs, remove
1.7 -from os.path import exists, isdir, join, split
1.8 +from os.path import exists, getmtime, isdir, join, split
1.9 from results import ConstantValueRef, LiteralSequenceRef, NameRef
1.10 import compiler.ast
1.11
1.12 @@ -76,6 +76,36 @@
1.13 else:
1.14 remove(path)
1.15
1.16 +def copy(source, target, only_if_newer=True):
1.17 +
1.18 + "Copy a text file from 'source' to 'target'."
1.19 +
1.20 + if isdir(target):
1.21 + target = join(target, split(source)[-1])
1.22 +
1.23 + if only_if_newer and not is_newer(source, target):
1.24 + return
1.25 +
1.26 + infile = open(source)
1.27 + outfile = open(target, "w")
1.28 +
1.29 + try:
1.30 + outfile.write(infile.read())
1.31 + finally:
1.32 + outfile.close()
1.33 + infile.close()
1.34 +
1.35 +def is_newer(source, target):
1.36 +
1.37 + "Return whether 'source' is newer than 'target'."
1.38 +
1.39 + if exists(target):
1.40 + target_mtime = getmtime(target)
1.41 + source_mtime = getmtime(source)
1.42 + return source_mtime > target_mtime
1.43 +
1.44 + return True
1.45 +
1.46 class CommonModule:
1.47
1.48 "A common module representation."
2.1 --- a/encoders.py Mon Feb 20 18:50:16 2017 +0100
2.2 +++ b/encoders.py Tue Feb 21 00:20:43 2017 +0100
2.3 @@ -21,6 +21,21 @@
2.4
2.5 from common import first, InstructionSequence
2.6
2.7 +
2.8 +
2.9 +# Value digest computation.
2.10 +
2.11 +from base64 import b64encode
2.12 +from hashlib import sha1
2.13 +
2.14 +def digest(values):
2.15 + m = sha1()
2.16 + for value in values:
2.17 + m.update(str(value))
2.18 + return b64encode(m.digest()).replace("+", "__").replace("/", "_").rstrip("=")
2.19 +
2.20 +
2.21 +
2.22 # Output encoding and decoding for the summary files.
2.23
2.24 def encode_attrnames(attrnames):
2.25 @@ -394,7 +409,7 @@
2.26
2.27 "Encode a name for the literal constant with the number 'n'."
2.28
2.29 - return "__const%d" % n
2.30 + return "__const%s" % n
2.31
2.32 def encode_literal_constant_size(value):
2.33
2.34 @@ -454,7 +469,7 @@
2.35
2.36 "Encode a reference to a literal constant with the number 'n'."
2.37
2.38 - return "__constvalue%d" % n
2.39 + return "__constvalue%s" % n
2.40
2.41
2.42
3.1 --- a/generator.py Mon Feb 20 18:50:16 2017 +0100
3.2 +++ b/generator.py Tue Feb 21 00:20:43 2017 +0100
3.3 @@ -19,7 +19,7 @@
3.4 this program. If not, see <http://www.gnu.org/licenses/>.
3.5 """
3.6
3.7 -from common import CommonOutput
3.8 +from common import CommonOutput, copy
3.9 from encoders import encode_function_pointer, \
3.10 encode_instantiator_pointer, \
3.11 encode_literal_constant, encode_literal_constant_member, \
3.12 @@ -31,24 +31,10 @@
3.13 encode_symbol, encode_tablename, \
3.14 encode_type_attribute, decode_type_attribute, \
3.15 is_type_attribute
3.16 -from os import listdir, mkdir
3.17 -from os.path import exists, isdir, join, split
3.18 +from os import listdir, mkdir, remove
3.19 +from os.path import exists, isdir, join, split, splitext
3.20 from referencing import Reference
3.21
3.22 -def copy(source, target):
3.23 -
3.24 - "Copy a text file from 'source' to 'target'."
3.25 -
3.26 - if isdir(target):
3.27 - target = join(target, split(source)[-1])
3.28 - infile = open(source)
3.29 - outfile = open(target, "w")
3.30 - try:
3.31 - outfile.write(infile.read())
3.32 - finally:
3.33 - outfile.close()
3.34 - infile.close()
3.35 -
3.36 class Generator(CommonOutput):
3.37
3.38 "A code generator."
3.39 @@ -124,9 +110,34 @@
3.40 if not exists(target):
3.41 mkdir(target)
3.42
3.43 - for filename in listdir(pathname):
3.44 + existing = listdir(target)
3.45 + needed = listdir(pathname)
3.46 +
3.47 + # Determine which files are superfluous by comparing their
3.48 + # basenames (without extensions) to those of the needed
3.49 + # filenames. This should preserve object files for needed source
3.50 + # files, only discarding completely superfluous files from the
3.51 + # target directory.
3.52 +
3.53 + needed_basenames = set()
3.54 + for filename in needed:
3.55 + needed_basenames.add(splitext(filename)[0])
3.56 +
3.57 + superfluous = []
3.58 + for filename in existing:
3.59 + if splitext(filename)[0] not in needed_basenames:
3.60 + superfluous.append(filename)
3.61 +
3.62 + # Copy needed files.
3.63 +
3.64 + for filename in needed:
3.65 copy(join(pathname, filename), target)
3.66
3.67 + # Remove superfluous files.
3.68 +
3.69 + for filename in superfluous:
3.70 + remove(join(target, filename))
3.71 +
3.72 def write_structures(self):
3.73
3.74 "Write structures used by the program."
3.75 @@ -522,7 +533,7 @@
3.76 # Employ a special alias that will be tested specifically in
3.77 # encode_member.
3.78
3.79 - encoding_ref = Reference("<instance>", self.string_type, "$c%d" % n)
3.80 + encoding_ref = Reference("<instance>", self.string_type, "$c%s" % n)
3.81
3.82 # Use None where no encoding was indicated.
3.83
3.84 @@ -959,7 +970,7 @@
3.85 constant_name = "$c%d" % local_number
3.86 attr_path = "%s.%s" % (path, constant_name)
3.87 constant_number = self.optimiser.constant_numbers[attr_path]
3.88 - constant_value = "__const%d" % constant_number
3.89 + constant_value = "__const%s" % constant_number
3.90 structure.append("%s /* %s */" % (constant_value, attrname))
3.91 continue
3.92
3.93 @@ -1021,7 +1032,7 @@
3.94 # Use the alias directly if appropriate.
3.95
3.96 if alias.startswith("$c"):
3.97 - constant_value = encode_literal_constant(int(alias[2:]))
3.98 + constant_value = encode_literal_constant(alias[2:])
3.99 return "%s /* %s */" % (constant_value, name)
3.100
3.101 # Obtain a constant value directly assigned to the attribute.
4.1 --- a/lplc Mon Feb 20 18:50:16 2017 +0100
4.2 +++ b/lplc Tue Feb 21 00:20:43 2017 +0100
4.3 @@ -270,9 +270,7 @@
4.4 make_clean_cmd = ["make", "-C", generated_dir, "clean"]
4.5 make_cmd = make_clean_cmd[:-1]
4.6
4.7 - retval = call(make_clean_cmd, make_verbose)
4.8 - if not retval:
4.9 - retval = call(make_cmd, make_verbose)
4.10 + retval = call(make_cmd, make_verbose)
4.11
4.12 if not retval:
4.13 if timings: stopwatch("Compilation", now)
5.1 --- a/optimiser.py Mon Feb 20 18:50:16 2017 +0100
5.2 +++ b/optimiser.py Tue Feb 21 00:20:43 2017 +0100
5.3 @@ -21,7 +21,7 @@
5.4
5.5 from common import add_counter_item, get_attrname_from_location, init_item, \
5.6 sorted_output
5.7 -from encoders import encode_access_location, encode_instruction, get_kinds
5.8 +from encoders import digest, encode_access_location, encode_instruction, get_kinds
5.9 from os.path import exists, join
5.10 from os import makedirs
5.11 from referencing import Reference
5.12 @@ -723,7 +723,7 @@
5.13 # Each constant is actually (value, value_type, encoding).
5.14
5.15 for constant, n in constants.items():
5.16 - add_counter_item(self.constants, constant)
5.17 + self.constants[constant] = digest(constant)
5.18
5.19 self.constant_numbers = {}
5.20
6.1 --- a/translator.py Mon Feb 20 18:50:16 2017 +0100
6.2 +++ b/translator.py Tue Feb 21 00:20:43 2017 +0100
6.3 @@ -20,7 +20,8 @@
6.4 """
6.5
6.6 from common import CommonModule, CommonOutput, InstructionSequence, \
6.7 - first, get_builtin_class, init_item, predefined_constants
6.8 + first, get_builtin_class, init_item, is_newer, \
6.9 + predefined_constants
6.10 from encoders import encode_access_instruction, \
6.11 encode_function_pointer, encode_literal_constant, \
6.12 encode_literal_instantiator, encode_instantiator_pointer, \
6.13 @@ -45,22 +46,33 @@
6.14 self.deducer = deducer
6.15 self.optimiser = optimiser
6.16 self.output = output
6.17 - self.modules = {}
6.18
6.19 def to_output(self):
6.20 +
6.21 + "Write a program to the configured output directory."
6.22 +
6.23 + # Make a directory for the final sources.
6.24 +
6.25 output = join(self.output, "src")
6.26
6.27 if not exists(output):
6.28 makedirs(output)
6.29
6.30 + # Clean the output directory of irrelevant data.
6.31 +
6.32 self.check_output()
6.33
6.34 for module in self.importer.modules.values():
6.35 + output_filename = join(output, "%s.c" % module.name)
6.36 +
6.37 + # Do not generate modules in the native package. They are provided
6.38 + # by native functionality source files.
6.39 +
6.40 parts = module.name.split(".")
6.41 - if parts[0] != "native":
6.42 +
6.43 + if parts[0] != "native" and is_newer(module.filename, output_filename):
6.44 tm = TranslatedModule(module.name, self.importer, self.deducer, self.optimiser)
6.45 - tm.translate(module.filename, join(output, "%s.c" % module.name))
6.46 - self.modules[module.name] = tm
6.47 + tm.translate(module.filename, output_filename)
6.48
6.49 # Classes representing intermediate translation results.
6.50