From 76755cf57def1d77105416e2f67b583943b4bf17 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 2 Feb 2018 22:42:09 +0100 Subject: [PATCH] stuff --- il65/compile.py | 35 ++++++++++++++++++++++++++++------- il65/emit/generate.py | 7 +++++-- il65/emit/incrdecr.py | 15 +++++++++++++-- il65/lib/c64lib.ill | 2 ++ il65/main.py | 5 +++-- il65/optimize.py | 4 ++-- il65/plyparse.py | 1 + tests/test_compiler.py | 5 ----- tests/test_optimizer.py | 6 ------ tests/test_zp.py | 17 +++++++++++++---- todo.ill | 6 ++++-- 11 files changed, 71 insertions(+), 32 deletions(-) delete mode 100644 tests/test_compiler.py delete mode 100644 tests/test_optimizer.py diff --git a/il65/compile.py b/il65/compile.py index c834f1af4..b5061fdcd 100644 --- a/il65/compile.py +++ b/il65/compile.py @@ -21,9 +21,10 @@ class CompileError(Exception): class PlyParser: - def __init__(self, imported_module: bool=False) -> None: + def __init__(self, *, enable_floats: bool=False, imported_module: bool=False) -> None: self.parse_errors = 0 self.imported_module = imported_module + self.floats_enabled = enable_floats def parse_file(self, filename: str) -> Module: print("parsing:", filename) @@ -31,6 +32,7 @@ class PlyParser: try: module = parse_file(filename, self.lexer_error) self.check_directives(module) + self.apply_directive_options(module) module.scope.define_builtin_functions() self.process_imports(module) self.check_and_merge_zeropages(module) @@ -38,11 +40,11 @@ class PlyParser: if not self.imported_module: # the following shall only be done on the main module after all imports have been done: self.check_all_symbolnames(module) - self.apply_directive_options(module) self.determine_subroutine_usage(module) self.all_parents_connected(module) self.semantic_check(module) self.coerce_values(module) + self.check_floats_enabled(module) self.allocate_zeropage_vars(module) except ParseError as x: self.handle_parse_error(x) @@ -70,6 +72,18 @@ class PlyParser: raise ParseError("last statement in a block/subroutine must be a return or goto, " "(or %noreturn directive to silence this error)", last_stmt.sourceref) + def check_floats_enabled(self, module: Module) -> None: + if self.floats_enabled: + return + for node in module.all_nodes(): + if isinstance(node, LiteralValue): + if type(node.value) is float: + raise ParseError("floating point numbers not enabled via option", node.sourceref) + elif isinstance(node, VarDef): + if node.datatype == DataType.FLOAT: + raise ParseError("floating point numbers not enabled via option", node.sourceref) + + def coerce_values(self, module: Module) -> None: for node in module.all_nodes(): try: @@ -206,10 +220,10 @@ class PlyParser: # allocate zeropage variables to the available free zp addresses if not module.scope.nodes: return - zpnode = module.scope.nodes[0] - if zpnode.name != "ZP": + zpnode = module.zeropage() + if zpnode is None: return - zeropage = Zeropage(module.zp_options) + zeropage = Zeropage(module.zp_options, self.floats_enabled) for vardef in zpnode.all_nodes(VarDef): if vardef.datatype.isstring(): raise ParseError("cannot put strings in the zeropage", vardef.sourceref) @@ -274,6 +288,8 @@ class PlyParser: elif directive.args[0] == "basic": node.format = ProgramFormat.BASIC node.address = 0x0801 + elif directive.args[0] == "enable_floats": + self.floats_enabled = module.floats_enabled = True else: raise ParseError("invalid directive args", directive.sourceref) elif directive.name == "address": @@ -451,6 +467,8 @@ class PlyParser: self.parse_errors += import_parse_errors else: raise FileNotFoundError("missing il65lib") + if sum(m.floats_enabled for m in imported): + self.floats_enabled = module.floats_enabled = True # append the imported module's contents (blocks) at the end of the current module for block in (node for imported_module in imported for node in imported_module.scope.nodes @@ -501,7 +519,8 @@ class Zeropage: SCRATCH_W1 = 0xfb # $fb/$fc SCRATCH_W2 = 0xfd # $fd/$fe - def __init__(self, options: ZpOptions) -> None: + def __init__(self, options: ZpOptions, enable_floats: bool) -> None: + self.floats_enabled = enable_floats self.free = [] # type: List[int] self.allocations = {} # type: Dict[int, Tuple[str, DataType]] if options in (ZpOptions.CLOBBER_RESTORE, ZpOptions.CLOBBER): @@ -539,6 +558,8 @@ class Zeropage: elif vardef.datatype == DataType.WORD: size = 2 elif vardef.datatype == DataType.FLOAT: + if not self.floats_enabled: + raise TypeError("floating point numbers not enabled via option") print_bold("warning: {}: allocating a large datatype in zeropage".format(vardef.sourceref)) size = 5 elif vardef.datatype == DataType.BYTEARRAY: @@ -564,7 +585,7 @@ class Zeropage: for candidate in range(min(self.free), max(self.free)+1): if sequential_free(candidate): return make_allocation(candidate) - raise CompileError("ERROR: no more free space in ZP to allocate {:d} sequential bytes".format(size)) + raise CompileError("ERROR: no free space in ZP to allocate {:d} sequential bytes".format(size)) def available(self) -> int: return len(self.free) diff --git a/il65/emit/generate.py b/il65/emit/generate.py index e8d91e8c5..6e5da993d 100644 --- a/il65/emit/generate.py +++ b/il65/emit/generate.py @@ -28,8 +28,9 @@ class Output: class AssemblyGenerator: - def __init__(self, module: Module) -> None: + def __init__(self, module: Module, enable_floats: bool) -> None: self.module = module + self.floats_enabled = enable_floats self.cur_block = None self.output = None # type: Output @@ -175,6 +176,8 @@ class AssemblyGenerator: out("") out("; -- end block subroutines") if block.scope.float_const_values: + if not self.floats_enabled: + raise CodeError("floating point numbers not enabled via option") # generate additional float constants that are used in floating point expressions out("\n; -- float constants") for name, value in block.scope.float_const_values.items(): @@ -210,7 +213,7 @@ class AssemblyGenerator: out(stmt.assembly) out("\v; end inline asm, " + stmt.lineref + "\n") elif isinstance(stmt, IncrDecr): - generate_incrdecr(out, stmt, scope) + generate_incrdecr(out, stmt, scope, self.floats_enabled) elif isinstance(stmt, Goto): generate_goto(out, stmt) elif isinstance(stmt, SubCall): diff --git a/il65/emit/incrdecr.py b/il65/emit/incrdecr.py index 9d2822009..b52b1a9ee 100644 --- a/il65/emit/incrdecr.py +++ b/il65/emit/incrdecr.py @@ -13,7 +13,7 @@ from ..datatypes import DataType, REGISTER_BYTES from . import CodeError, preserving_registers -def generate_incrdecr(out: Callable, stmt: IncrDecr, scope: Scope) -> None: +def generate_incrdecr(out: Callable, stmt: IncrDecr, scope: Scope, floats_enabled: bool) -> None: assert isinstance(stmt.howmuch, (int, float)) and stmt.howmuch >= 0 assert stmt.operator in ("++", "--") if stmt.howmuch == 0: @@ -184,6 +184,8 @@ def generate_incrdecr(out: Callable, stmt: IncrDecr, scope: Scope) -> None: out("\vdec {:s}+1".format(what_str)) out("+") elif target.datatype == DataType.FLOAT: + if not floats_enabled: + raise CodeError("floating point numbers not enabled via option") if stmt.howmuch == 1.0: # special case for +/-1 with preserving_registers({'A', 'X', 'Y'}, scope, out, loads_a_within=True): @@ -209,5 +211,14 @@ def generate_incrdecr(out: Callable, stmt: IncrDecr, scope: Scope) -> None: else: raise CodeError("cannot in/decrement memory of type " + str(target.datatype), stmt.howmuch) + elif isinstance(target, Dereference): + if target.datatype == DataType.BYTE: + pass # @todo + elif target.datatype == DataType.WORD: + pass # @todo + elif target.datatype == DataType.FLOAT: + pass # @todo + else: + raise CodeError("cannot inc/decrement dereferenced " + str(target.datatype), stmt) else: - raise CodeError("cannot in/decrement", target) # @todo support more such as [dereference]++ + raise CodeError("cannot inc/decrement", target) # @todo support more such as [dereference]++ diff --git a/il65/lib/c64lib.ill b/il65/lib/c64lib.ill index 502d63510..884c97e2f 100644 --- a/il65/lib/c64lib.ill +++ b/il65/lib/c64lib.ill @@ -5,6 +5,8 @@ ; ; ; indent format: TABS, size=8 +%output enable_floats + ~ c64 { memory .byte SCRATCH_ZP1 = $02 ; scratch register #1 in ZP diff --git a/il65/main.py b/il65/main.py index d730ac526..88ad3e2fc 100644 --- a/il65/main.py +++ b/il65/main.py @@ -64,6 +64,7 @@ def main() -> None: description = "Compiler for IL65 language, code name 'Sick'" ap = argparse.ArgumentParser(description=description) ap.add_argument("-o", "--output", help="output directory") + ap.add_argument("-f", "--enablefloat", action="store_true", help="enable C64 (mflpt5) floating point operations") ap.add_argument("-no", "--nooptimize", action="store_true", help="do not optimize the parse tree") ap.add_argument("-sv", "--startvice", action="store_true", help="autostart vice x64 emulator after compilation") ap.add_argument("sourcefile", help="the source .ill/.il65 file to compile") @@ -79,7 +80,7 @@ def main() -> None: start = time.perf_counter() print("\nParsing program source code.") - parser = PlyParser() + parser = PlyParser(enable_floats=args.enablefloat) parsed_module = parser.parse_file(args.sourcefile) if parsed_module: if args.nooptimize: @@ -88,7 +89,7 @@ def main() -> None: print("\nOptimizing code.") optimize(parsed_module) print("\nGenerating assembly code.") - cg = AssemblyGenerator(parsed_module) + cg = AssemblyGenerator(parsed_module, args.enablefloat) cg.generate(assembly_filename) assembler = Assembler64Tass(parsed_module.format) assembler.assemble(assembly_filename, program_filename) diff --git a/il65/optimize.py b/il65/optimize.py index 9edf82f53..53ad9083a 100644 --- a/il65/optimize.py +++ b/il65/optimize.py @@ -339,8 +339,8 @@ class Optimizer: if not usages and sub.parent.name + '.' + sub.name not in never_remove: sub.parent.remove_node(sub) num_discarded += 1 - if num_discarded: - print("discarded {:d} unused subroutines".format(num_discarded)) + # if num_discarded: + # print("discarded {:d} unused subroutines".format(num_discarded)) @no_type_check def optimize_goto_compare_with_zero(self) -> None: diff --git a/il65/plyparse.py b/il65/plyparse.py index d37038817..ddf78b535 100644 --- a/il65/plyparse.py +++ b/il65/plyparse.py @@ -301,6 +301,7 @@ class Module(AstNode): format = attr.ib(type=ProgramFormat, init=False, default=ProgramFormat.PRG) # can be set via directive address = attr.ib(type=int, init=False, default=0xc000, validator=validate_address) # can be set via directive zp_options = attr.ib(type=ZpOptions, init=False, default=ZpOptions.NOCLOBBER) # can be set via directive + floats_enabled = attr.ib(type=bool, init=False, default=False) # can be set via directive @property def scope(self) -> Scope: diff --git a/tests/test_compiler.py b/tests/test_compiler.py deleted file mode 100644 index 92274de4a..000000000 --- a/tests/test_compiler.py +++ /dev/null @@ -1,5 +0,0 @@ -from il65.compile import PlyParser - - -def test_compiler(): - pass # @todo diff --git a/tests/test_optimizer.py b/tests/test_optimizer.py deleted file mode 100644 index 53bf8316a..000000000 --- a/tests/test_optimizer.py +++ /dev/null @@ -1,6 +0,0 @@ -from il65.optimize import Optimizer - - -def test_optimizer(): - pass # @todo - diff --git a/tests/test_zp.py b/tests/test_zp.py index ac87ce6f1..f1bddf0e8 100644 --- a/tests/test_zp.py +++ b/tests/test_zp.py @@ -7,7 +7,7 @@ from il65.datatypes import DataType def test_zp_names(): sref = SourceRef("test", 1, 1) - zp = Zeropage(ZpOptions.NOCLOBBER) + zp = Zeropage(ZpOptions.NOCLOBBER, False) with pytest.raises(AssertionError): zp.allocate(VarDef(name="", vartype="memory", datatype=DataType.BYTE, sourceref=sref)) zp.allocate(VarDef(name="", vartype="var", datatype=DataType.BYTE, sourceref=sref)) @@ -20,7 +20,7 @@ def test_zp_names(): def test_zp_noclobber_allocation(): sref = SourceRef("test", 1, 1) - zp = Zeropage(ZpOptions.NOCLOBBER) + zp = Zeropage(ZpOptions.NOCLOBBER, True) assert zp.available() == 9 with pytest.raises(CompileError): # in regular zp there aren't 5 sequential bytes free @@ -35,9 +35,18 @@ def test_zp_noclobber_allocation(): zp.allocate(VarDef(name="", vartype="var", datatype=DataType.WORD, sourceref=sref)) +def test_zp_float_enable(): + sref = SourceRef("test", 1, 1) + zp = Zeropage(ZpOptions.CLOBBER, False) + with pytest.raises(TypeError): + zp.allocate(VarDef(name="", vartype="var", datatype=DataType.FLOAT, sourceref=sref)) + zp = Zeropage(ZpOptions.CLOBBER, True) + zp.allocate(VarDef(name="", vartype="var", datatype=DataType.FLOAT, sourceref=sref)) + + def test_zp_clobber_allocation(): sref = SourceRef("test", 1, 1) - zp = Zeropage(ZpOptions.CLOBBER) + zp = Zeropage(ZpOptions.CLOBBER, True) assert zp.available() == 239 loc = zp.allocate(VarDef(name="", vartype="var", datatype=DataType.FLOAT, sourceref=sref)) assert loc > 3 and loc not in zp.free @@ -63,7 +72,7 @@ def test_zp_clobber_allocation(): def test_zp_efficient_allocation(): # free = [0x04, 0x05, 0x06, 0x2a, 0x52, 0xf7, 0xf8, 0xf9, 0xfa] sref = SourceRef("test", 1, 1) - zp = Zeropage(ZpOptions.NOCLOBBER) + zp = Zeropage(ZpOptions.NOCLOBBER, False) assert zp.available() == 9 assert 0x2a == zp.allocate(VarDef(name="", vartype="var", datatype=DataType.BYTE, sourceref=sref)) assert 0x52 == zp.allocate(VarDef(name="", vartype="var", datatype=DataType.BYTE, sourceref=sref)) diff --git a/todo.ill b/todo.ill index b8ef9237d..c4c001d2d 100644 --- a/todo.ill +++ b/todo.ill @@ -2,7 +2,7 @@ ~ main { -; var .float flt + ;var .float flt ; var bytevar = 22 + 23 ; @todo constant-fold before semantic check ; var .float initfloat1 = -1.234e-14 ; @todo constant-fold before semantic check / fix float parse? ; var .float initfloat2 = -555.666 ; @todo constant-fold before semantic check / fix float parse? @@ -20,7 +20,9 @@ start: [border] ++; @todo suport incr/decr on deref constant [$d020] ++ ; @todo suport incr/decr on deref memory + [XY] ++ + [X] ++ - return 44 + return 44.123 }