mirror of
https://github.com/irmen/prog8.git
synced 2024-12-24 01:29:28 +00:00
stuff
This commit is contained in:
parent
43a59817bf
commit
76755cf57d
@ -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)
|
||||
|
@ -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):
|
||||
|
@ -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]++
|
||||
|
@ -5,6 +5,8 @@
|
||||
; ;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
%output enable_floats
|
||||
|
||||
|
||||
~ c64 {
|
||||
memory .byte SCRATCH_ZP1 = $02 ; scratch register #1 in ZP
|
||||
|
@ -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)
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -1,5 +0,0 @@
|
||||
from il65.compile import PlyParser
|
||||
|
||||
|
||||
def test_compiler():
|
||||
pass # @todo
|
@ -1,6 +0,0 @@
|
||||
from il65.optimize import Optimizer
|
||||
|
||||
|
||||
def test_optimizer():
|
||||
pass # @todo
|
||||
|
@ -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))
|
||||
|
6
todo.ill
6
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
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user