mirror of
https://github.com/irmen/prog8.git
synced 2025-02-04 02:30:19 +00:00
options
This commit is contained in:
parent
5e16b82418
commit
e30ba696db
141
il65/parse.py
141
il65/parse.py
@ -13,7 +13,7 @@ import sys
|
||||
import shutil
|
||||
import enum
|
||||
from collections import defaultdict
|
||||
from typing import Set, List, Tuple, Optional, Any, Dict, Union
|
||||
from typing import Set, List, Tuple, Optional, Any, Dict, Union, Generator
|
||||
from .astparse import ParseError, parse_expr_as_int, parse_expr_as_number, parse_expr_as_primitive,\
|
||||
parse_expr_as_string, parse_arguments, parse_expr_as_comparison
|
||||
from .symbols import SourceRef, SymbolTable, DataType, SymbolDefinition, SubroutineDef, LabelDef, \
|
||||
@ -38,6 +38,12 @@ class ParseResult:
|
||||
self.blocks = [] # type: List['ParseResult.Block']
|
||||
self.subroutine_usage = defaultdict(set) # type: Dict[Tuple[str, str], Set[str]]
|
||||
|
||||
def all_blocks(self) -> Generator['ParseResult.Block', None, None]:
|
||||
for block in self.blocks:
|
||||
yield block
|
||||
for sub in block.symbols.iter_subroutines(True):
|
||||
yield sub.sub_block
|
||||
|
||||
class Block:
|
||||
_unnamed_block_labels = {} # type: Dict[ParseResult.Block, str]
|
||||
|
||||
@ -75,18 +81,12 @@ class ParseResult:
|
||||
except (SymbolError, LookupError):
|
||||
return None, None
|
||||
|
||||
def flatten_statement_list(self) -> None:
|
||||
if all(isinstance(stmt, ParseResult._AstNode) for stmt in self.statements):
|
||||
# this is the common case
|
||||
return
|
||||
statements = []
|
||||
def all_statements(self) -> Generator[Tuple['ParseResult.Block', Optional[SubroutineDef], 'ParseResult._AstNode'], None, None]:
|
||||
for stmt in self.statements:
|
||||
if isinstance(stmt, (tuple, list)):
|
||||
statements.extend(stmt)
|
||||
else:
|
||||
assert isinstance(stmt, ParseResult._AstNode)
|
||||
statements.append(stmt)
|
||||
self.statements = statements
|
||||
yield self, None, stmt
|
||||
for sub in self.symbols.iter_subroutines(True):
|
||||
for stmt in sub.sub_block.statements:
|
||||
yield sub.sub_block, sub, stmt
|
||||
|
||||
class Value:
|
||||
def __init__(self, datatype: DataType, name: str=None, constant: bool=False) -> None:
|
||||
@ -606,7 +606,10 @@ class Parser:
|
||||
if sys.stderr.isatty():
|
||||
print("\x1b[1m", file=sys.stderr)
|
||||
print("\nERROR: internal parser error: ", x, file=sys.stderr)
|
||||
print(" file:", self.sourceref.file, "block:", self.cur_block.name, "line:", self.sourceref.line, file=sys.stderr)
|
||||
if self.cur_block:
|
||||
print(" file:", self.sourceref.file, "block:", self.cur_block.name, "line:", self.sourceref.line, file=sys.stderr)
|
||||
else:
|
||||
print(" file:", self.sourceref.file, file=sys.stderr)
|
||||
if sys.stderr.isatty():
|
||||
print("\x1b[0m", file=sys.stderr)
|
||||
raise # XXX temporary solution to get stack trace info in the event of parse errors
|
||||
@ -697,37 +700,30 @@ class Parser:
|
||||
self.sourceref.line = -1
|
||||
self.sourceref.column = 0
|
||||
|
||||
def desugar_immediate_strings(stmt: ParseResult._AstNode) -> None:
|
||||
if isinstance(stmt, ParseResult.CallStmt):
|
||||
for s in stmt.desugared_call_arguments:
|
||||
self.sourceref.line = s.lineno
|
||||
self.sourceref.column = 0
|
||||
s.desugar_immediate_string(self)
|
||||
for s in stmt.desugared_output_assignments:
|
||||
self.sourceref.line = s.lineno
|
||||
self.sourceref.column = 0
|
||||
s.desugar_immediate_string(self)
|
||||
if isinstance(stmt, ParseResult.AssignmentStmt):
|
||||
self.sourceref.line = stmt.lineno
|
||||
self.sourceref.column = 0
|
||||
stmt.desugar_immediate_string(self)
|
||||
|
||||
for block in self.result.blocks:
|
||||
self.cur_block = block
|
||||
# create parameter loads for calls
|
||||
for index, stmt in enumerate(list(block.statements)):
|
||||
self.sourceref = block.sourceref.copy()
|
||||
self.sourceref.column = 0
|
||||
for block, sub, stmt in block.all_statements():
|
||||
if isinstance(stmt, ParseResult.CallStmt):
|
||||
self.sourceref.line = stmt.lineno
|
||||
self.sourceref.column = 0
|
||||
stmt.desugar_call_arguments_and_outputs(self)
|
||||
# create parameter loads for calls, in subroutine blocks
|
||||
for sub in block.symbols.iter_subroutines(True):
|
||||
for stmt in sub.sub_block.statements:
|
||||
if isinstance(stmt, ParseResult.CallStmt):
|
||||
self.sourceref.line = stmt.lineno
|
||||
self.sourceref.column = 0
|
||||
stmt.desugar_call_arguments_and_outputs(self)
|
||||
block.flatten_statement_list()
|
||||
# desugar immediate string value assignments
|
||||
for index, stmt in enumerate(list(block.statements)):
|
||||
if isinstance(stmt, ParseResult.CallStmt):
|
||||
for s in stmt.desugared_call_arguments:
|
||||
self.sourceref.line = s.lineno
|
||||
self.sourceref.column = 0
|
||||
s.desugar_immediate_string(self)
|
||||
for s in stmt.desugared_output_assignments:
|
||||
self.sourceref.line = s.lineno
|
||||
self.sourceref.column = 0
|
||||
s.desugar_immediate_string(self)
|
||||
if isinstance(stmt, ParseResult.AssignmentStmt):
|
||||
self.sourceref.line = stmt.lineno
|
||||
self.sourceref.column = 0
|
||||
stmt.desugar_immediate_string(self)
|
||||
desugar_immediate_strings(stmt)
|
||||
|
||||
def next_line(self) -> str:
|
||||
self._cur_lineidx += 1
|
||||
@ -787,38 +783,50 @@ class Parser:
|
||||
self.result.with_sys = False
|
||||
self.result.format = ProgramFormat.RAW
|
||||
output_specified = False
|
||||
zp_specified = False
|
||||
while True:
|
||||
self._parse_comments()
|
||||
line = self.next_line()
|
||||
if line.startswith("output"):
|
||||
if line.startswith(("output ", "output\t")):
|
||||
if output_specified:
|
||||
raise self.PError("multiple occurrences of 'output'")
|
||||
raise self.PError("can only specify output options once")
|
||||
output_specified = True
|
||||
_, _, arg = line.partition(" ")
|
||||
arg = arg.lstrip()
|
||||
_, _, optionstr = line.partition(" ")
|
||||
options = set(optionstr.replace(' ', '').split(','))
|
||||
self.result.with_sys = False
|
||||
self.result.format = ProgramFormat.RAW
|
||||
if arg == "raw":
|
||||
pass
|
||||
elif arg == "prg":
|
||||
if "raw" in options:
|
||||
options.remove("raw")
|
||||
if "prg" in options:
|
||||
options.remove("prg")
|
||||
self.result.format = ProgramFormat.PRG
|
||||
elif arg.replace(' ', '') == "prg,sys":
|
||||
self.result.with_sys = True
|
||||
self.result.format = ProgramFormat.PRG
|
||||
else:
|
||||
raise self.PError("invalid output format")
|
||||
elif line.startswith("clobberzp"):
|
||||
if self.result.clobberzp:
|
||||
raise self.PError("multiple occurrences of 'clobberzp'")
|
||||
self.result.clobberzp = True
|
||||
_, _, arg = line.partition(" ")
|
||||
arg = arg.lstrip()
|
||||
if arg == "restore":
|
||||
self.result.restorezp = True
|
||||
elif arg == "":
|
||||
pass
|
||||
else:
|
||||
raise self.PError("invalid arg for clobberzp")
|
||||
if "basic" in options:
|
||||
options.remove("basic")
|
||||
if self.result.format == ProgramFormat.PRG:
|
||||
self.result.with_sys = True
|
||||
else:
|
||||
raise self.PError("can only use basic output option with prg, not raw")
|
||||
if options:
|
||||
raise self.PError("invalid output option(s): " + str(options))
|
||||
elif line.startswith(("zp ", "zp\t")):
|
||||
if zp_specified:
|
||||
raise self.PError("can only specify ZP options once")
|
||||
zp_specified = True
|
||||
_, _, optionstr = line.partition(" ")
|
||||
options = set(optionstr.replace(' ', '').split(','))
|
||||
self.result.clobberzp = False
|
||||
self.result.restorezp = False
|
||||
if "clobber" in options:
|
||||
options.remove("clobber")
|
||||
self.result.clobberzp = True
|
||||
if "restore" in options:
|
||||
options.remove("restore")
|
||||
if self.result.clobberzp:
|
||||
self.result.restorezp = True
|
||||
else:
|
||||
raise self.PError("can only use restore zp option if clobber zp is used as well")
|
||||
if options:
|
||||
raise self.PError("invalid zp option(s): " + str(options))
|
||||
elif line.startswith("address"):
|
||||
if self.result.start_address:
|
||||
raise self.PError("multiple occurrences of 'address'")
|
||||
@ -1644,17 +1652,12 @@ class Optimizer:
|
||||
|
||||
def optimize(self) -> ParseResult:
|
||||
print("\noptimizing parse tree")
|
||||
for block in self.parsed.blocks:
|
||||
for block in self.parsed.all_blocks():
|
||||
self.remove_identity_assigns(block)
|
||||
self.combine_assignments_into_multi(block)
|
||||
self.optimize_multiassigns(block)
|
||||
self.remove_unused_subroutines(block)
|
||||
self.optimize_compare_with_zero(block)
|
||||
for sub in block.symbols.iter_subroutines(True):
|
||||
self.remove_identity_assigns(sub.sub_block)
|
||||
self.combine_assignments_into_multi(sub.sub_block)
|
||||
self.optimize_multiassigns(sub.sub_block)
|
||||
self.optimize_compare_with_zero(sub.sub_block)
|
||||
return self.parsed
|
||||
|
||||
def optimize_compare_with_zero(self, block: ParseResult.Block) -> None:
|
||||
|
37
reference.md
37
reference.md
@ -165,13 +165,36 @@ The default format of the generated program is a "raw" binary where code starts
|
||||
You can generate other types of programs as well, by telling IL65 via an output mode statement
|
||||
at the beginning of your program:
|
||||
|
||||
| mode declaration | meaning |
|
||||
|--------------------|------------------------------------------------------------------------------------|
|
||||
| ``output raw`` | no load address bytes |
|
||||
| ``output prg`` | include the first two load address bytes, (default is ``$0801``), no BASIC program |
|
||||
| ``output prg,sys`` | include the first two load address bytes, BASIC start program with sys call to code, default code start is immediately after the BASIC program at ``$081d``, or beyond |
|
||||
| | |
|
||||
| ``address $0801`` | override program start address (default is set to ``$c000`` for raw mode and ``$0801`` for C-64 prg mode). Cannot be used if output mode is ``prg,sys`` because BASIC programs always have to start at ``$0801``. |
|
||||
| mode declaration | meaning |
|
||||
|----------------------|------------------------------------------------------------------------------------|
|
||||
| ``output raw`` | no load address bytes |
|
||||
| ``output prg`` | include the first two load address bytes, (default is ``$0801``), no BASIC program |
|
||||
| ``output prg,basic`` | as 'prg', but include a BASIC start program with SYS call, default code start is immediately after the BASIC program at ``$081d``, or after that. |
|
||||
| | |
|
||||
| ``address $0801`` | override program start address (default is set to ``$c000`` for raw mode and ``$0801`` for C-64 prg mode). Cannot be used if output mode is ``prg,basic`` because BASIC programs always have to start at ``$0801``. |
|
||||
|
||||
|
||||
### ZeroPage Options
|
||||
|
||||
You can tell the compiler how to treat the *zero page*. Normally it is considered a 'no-go' area
|
||||
except for the frew free locations mentioned under "Memory Model".
|
||||
However you can specify some options globally in your program to change this behavior:
|
||||
|
||||
- ``zp clobber``
|
||||
Use the whole zeropage for variables. It is not possible to exit your program
|
||||
correctly back to BASIC, other than resetting the machine.
|
||||
It does make your program smaller and faster because many more variables can
|
||||
be stored in the ZP, which is more efficient.
|
||||
- ``zp clobber, restore``
|
||||
Use the whole zeropage, but make a backup copy of the original values at program start.
|
||||
When your program exits, the original ZP is restored and you drop back to the BASIC prompt.
|
||||
|
||||
If you use ``zp clobber``, you can no longer use BASIC or KERNAL routines,
|
||||
because these depend on most of the locations in the ZP. This includes most of the floating-point
|
||||
logic and several utility routines that do I/O, such as ``print_string``.
|
||||
|
||||
@todo default IRQ handling will still change certain values in ZP...
|
||||
|
||||
|
||||
|
||||
### Program Entry Point
|
||||
|
@ -1,4 +1,4 @@
|
||||
output prg,sys
|
||||
output prg,basic
|
||||
|
||||
import "c64lib"
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
; var definitions and immediate primitive data type tests
|
||||
|
||||
output raw
|
||||
clobberzp
|
||||
zp clobber
|
||||
|
||||
|
||||
import "c64lib"
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
; floating point tests
|
||||
|
||||
output prg, sys
|
||||
output prg, basic
|
||||
|
||||
import "c64lib"
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
output prg,sys
|
||||
output prg,basic
|
||||
|
||||
import "c64lib"
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
; var definitions and immediate primitive data type tests
|
||||
|
||||
output prg, sys
|
||||
clobberzp
|
||||
output prg, basic
|
||||
zp clobber
|
||||
|
||||
import "c64lib"
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
output prg,sys ; @todo basic
|
||||
output prg,basic
|
||||
;reg_preserve off ; @todo global option
|
||||
|
||||
import "c64lib"
|
||||
@ -40,7 +40,13 @@ start
|
||||
printloop
|
||||
c64util.print_string("\nYou have ")
|
||||
c64util.print_byte_decimal(attempts_left)
|
||||
c64util.print_string(" guesses left.\nWhat is your next guess? ")
|
||||
c64util.print_string(" guess")
|
||||
A = attempts_left
|
||||
A -= 1 ; @todo wrong instruction adc
|
||||
if_zero A goto ask_guess
|
||||
c64util.print_string("es")
|
||||
ask_guess
|
||||
c64util.print_string(" left.\nWhat is your next guess? ")
|
||||
A = c64util.input_chars(guess)
|
||||
c64.CHROUT('\n')
|
||||
[$22.word] = guess
|
||||
@ -54,8 +60,8 @@ printloop
|
||||
goto continue
|
||||
|
||||
correct_guess
|
||||
c64util.print_string("\nImpressive!\n")
|
||||
bye()
|
||||
c64util.print_string("\nThat's my number, impressive!\n")
|
||||
goodbye()
|
||||
return
|
||||
|
||||
too_high
|
||||
@ -71,16 +77,15 @@ game_over
|
||||
c64util.print_string("\nToo bad! It was: ")
|
||||
c64util.print_byte_decimal(secretnumber)
|
||||
c64.CHROUT('\n')
|
||||
bye()
|
||||
goodbye()
|
||||
return
|
||||
|
||||
sub bye ()->() {
|
||||
sub goodbye ()->() {
|
||||
;var x ; @todo vars in sub
|
||||
;memory y = $c000 ; @todo vars in sub
|
||||
;const q = 22 ; @todo const in sub
|
||||
|
||||
c64.CHROUT('\n')
|
||||
;c64util.print_string("Thanks for playing. Bye!\n\n") ;@todo string values should work in sub too
|
||||
c64util.print_string("\nThanks for playing. Bye!\n")
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -4,9 +4,8 @@
|
||||
; line 3 comment
|
||||
|
||||
|
||||
output prg,sys ; create a c-64 program with basic SYS call to launch it
|
||||
;clobberzp restore ; clobber over the zp memory normally used by basic/kernel rom, frees up more zp
|
||||
|
||||
output basic , prg ; create a c-64 program with basic SYS call to launch it
|
||||
zp restore , clobber ; clobber over the zp memory normally used by basic/kernel rom, frees up more zp
|
||||
|
||||
|
||||
~main $0a00
|
||||
|
@ -1,8 +1,8 @@
|
||||
; source IL file
|
||||
; these are comments
|
||||
|
||||
output prg,sys ; create a c-64 program with basic SYS call to launch it
|
||||
clobberzp restore ; clobber over the zp memory normally used by basic/kernel rom, frees up more zp
|
||||
output prg, basic ; create a c-64 program with basic SYS call to launch it
|
||||
zp clobber,restore ; clobber over the zp memory normally used by basic/kernel rom, frees up more zp
|
||||
|
||||
~main
|
||||
{
|
||||
|
@ -1,8 +1,5 @@
|
||||
; source IL file
|
||||
; these are comments
|
||||
|
||||
output prg,sys ; create a c-64 program with basic SYS call to launch it
|
||||
; clobberzp restore ; clobber over the zp memory normally used by basic/kernel rom, frees up more zp
|
||||
output prg,basic ; create a c-64 program with basic SYS call to launch it
|
||||
|
||||
|
||||
import "c64lib" ; searched in several locations and with .ill file extension added
|
||||
|
@ -1,4 +1,4 @@
|
||||
output prg,sys ; create a c-64 program with basic SYS to() launch it
|
||||
output prg,basic ; create a c-64 program with basic SYS to() launch it
|
||||
|
||||
import "c64lib.ill"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user