Skip to content

Commit

Permalink
feat: create --output-format parameter
Browse files Browse the repository at this point in the history
  • Loading branch information
boriel committed Aug 22, 2024
1 parent cc7bdbc commit 379a426
Show file tree
Hide file tree
Showing 8 changed files with 97 additions and 50 deletions.
4 changes: 2 additions & 2 deletions src/api/decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ class classproperty:
to the class object.
"""

def __init__(self, fget: Callable):
def __init__(self, fget: Callable[[type], Callable]) -> None:
self.fget = fget

def __get__(self, owner_self, owner_cls):
def __get__(self, owner_self, owner_cls: type):
return self.fget(owner_cls)
7 changes: 7 additions & 0 deletions src/api/errmsg.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ def wrapper(*args, **kwargs):
return decorator


def warning_command_line_flag_deprecation(flag: str) -> None:
"""Warning signaling command line flag is deprecated.
This is a special warning that can't be silenced, and needs no line number nor filename.
"""
msg_output(f"WARNING: deprecated flag {flag}")


# region [Warnings]
@register_warning("100")
def warning_implicit_type(lineno: int, id_: str, type_: str = None):
Expand Down
57 changes: 30 additions & 27 deletions src/zxbc/args_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,15 @@
from src import arch
from src.api import errmsg
from src.api.config import OPTIONS
from src.api.errmsg import warning_command_line_flag_deprecation
from src.api.utils import open_file
from src.zxbc import args_parser
from src.zxbc.args_parser import FileType

if TYPE_CHECKING:
from argparse import Namespace

__all__ = "FileType", "parse_options", "set_option_defines"


class FileType:
ASM = "asm"
IC = "ic"
TAP = "tap"
TZX = "tzx"
SNA = "sna"
__all__ = "parse_options", "set_option_defines"


def parse_options(args: list[str] | None = None) -> Namespace:
Expand All @@ -49,7 +43,6 @@ def parse_options(args: list[str] | None = None) -> Namespace:
OPTIONS.memory_check = options.debug_memory
OPTIONS.strict_bool = options.strict_bool
OPTIONS.array_check = options.debug_array
OPTIONS.emit_backend = options.emit_backend
OPTIONS.enable_break = options.enable_break
OPTIONS.explicit = options.explicit
OPTIONS.memory_map = options.memory_map
Expand Down Expand Up @@ -102,32 +95,42 @@ def parse_options(args: list[str] | None = None) -> Namespace:
OPTIONS.case_insensitive = True

OPTIONS.case_insensitive = options.ignore_case

if (options.basic or options.autorun) and not (options.tzx or options.tap or options.sna):
parser.error("Options --BASIC and --autorun require --tzx, tap or sna format")

if not (options.basic and options.autorun) and options.sna:
parser.error("Options --BASIC and --autorun are both required for --sna format")

if options.append_binary and not options.tzx and not options.tap:
parser.error("Option --append-binary needs either --tap or --tzx")

if options.asm and options.memory_map:
parser.error("Option --asm and --mmap cannot be used together")

OPTIONS.use_basic_loader = options.basic
OPTIONS.autorun = options.autorun

if options.tzx:
if options.output_format:
OPTIONS.output_file_type = options.output_format
elif options.tzx:
OPTIONS.output_file_type = FileType.TZX
warning_command_line_flag_deprecation(f"--tzx (use --output-format={FileType.TZX} instead)")
elif options.tap:
OPTIONS.output_file_type = FileType.TAP
elif options.sna:
OPTIONS.output_file_type = FileType.SNA
warning_command_line_flag_deprecation(f"--tap (use --output-format={FileType.TAP} instead)")
elif options.asm:
OPTIONS.output_file_type = FileType.ASM
warning_command_line_flag_deprecation(f"--asm (use --output-format={FileType.ASM} instead)")
elif options.emit_backend:
OPTIONS.output_file_type = FileType.IC
OPTIONS.output_file_type = FileType.IR
warning_command_line_flag_deprecation(f"--emit-backend (use --output-format={FileType.IR} instead)")

if OPTIONS.output_file_type == FileType.IR:
OPTIONS.emit_backend = True

if (options.basic or options.autorun) and OPTIONS.output_file_type not in {
FileType.TAP,
FileType.TZX,
FileType.SNA,
}:
parser.error("Options --BASIC and --autorun require one of tzx, tap, or sna output format")

if not (options.basic and options.autorun) and OPTIONS.output_file_type == FileType.SNA:
parser.error("Options --BASIC and --autorun are both required for sna format")

if options.append_binary and OPTIONS.output_file_type not in {FileType.TAP, FileType.TZX}:
parser.error("Option --append-binary needs either tap or tzx output format")

if OPTIONS.output_file_type == FileType.ASM and options.memory_map:
parser.error("Option --asm and --mmap cannot be used together")

args = [options.PROGRAM]
if not os.path.exists(options.PROGRAM):
Expand Down
52 changes: 42 additions & 10 deletions src/zxbc/args_parser.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from __future__ import annotations

import argparse
from enum import StrEnum

from src import arch
from src.api import errmsg
Expand All @@ -7,9 +10,19 @@
from .version import VERSION


class FileType(StrEnum):
ASM = "asm"
BIN = "bin"
IR = "IR"
SNA = "sna"
TAP = "tap"
TZX = "tzx"


def parse_warning_option(code: str) -> str:
if not errmsg.is_valid_warning_code(code):
raise argparse.ArgumentTypeError(f"Invalid warning option 'W{code}'")

return code


Expand Down Expand Up @@ -42,18 +55,40 @@ def parser() -> argparse.ArgumentParser:

output_file_type_group = parser_.add_mutually_exclusive_group()
output_file_type_group.add_argument(
"-T", "--tzx", action="store_true", help="Sets output format to .tzx (default is .bin)"
"-T",
"--tzx",
action="store_true",
help="Sets output format to .tzx (default is .bin). DEPRECATED.",
)
output_file_type_group.add_argument(
"-t", "--tap", action="store_true", help="Sets output format to .tap (default is .bin)"
"-t",
"--tap",
action="store_true",
help="Sets output format to .tap (default is .bin). DEPRECATED.",
)
output_file_type_group.add_argument(
"--sna", action="store_true", help="Sets output format to .sna (default is .bin)"
"-A",
"--asm",
action="store_true",
help="Sets output format to .asm. DEPRECATED",
)
parser_.add_argument(
"-E",
"--emit-backend",
action="store_true",
help="Emits backend code (IR) instead of ASM or binary. DEPRECATED.",
)
output_file_type_group.add_argument("-A", "--asm", action="store_true", help="Sets output format to .asm")
output_file_type_group.add_argument(
"--parse-only", action="store_true", help="Only parses to check for syntax and semantic errors"
)
output_file_type_group.add_argument(
"-f",
"--output-format",
type=str,
choices=FileType,
required=False,
help="Output format",
)

parser_.add_argument(
"-B",
Expand Down Expand Up @@ -91,9 +126,7 @@ def parser() -> argparse.ArgumentParser:
parser_.add_argument("--debug-array", action="store_true", default=None, help="Enables array boundary checking")
parser_.add_argument("--strict-bool", action="store_true", default=None, help="Enforce boolean values to be 0 or 1")
parser_.add_argument("--enable-break", action="store_true", help="Enables program execution BREAK detection")
parser_.add_argument(
"-E", "--emit-backend", action="store_true", help="Emits backend code instead of ASM or binary"
)

parser_.add_argument(
"--explicit", action="store_true", help="Requires all variables and functions to be declared before used"
)
Expand All @@ -106,9 +139,7 @@ def parser() -> argparse.ArgumentParser:
help="Defines de given macro. Eg. -D MYDEBUG or -D NAME=Value",
)
parser_.add_argument("-M", "--mmap", type=str, dest="memory_map", default=None, help="Generate label memory map")

case_sens_group = parser_.add_mutually_exclusive_group()
case_sens_group.add_argument(
parser_.add_argument(
"-i",
"--ignore-case",
action="store_true",
Expand Down Expand Up @@ -170,4 +201,5 @@ def parser() -> argparse.ArgumentParser:
"-F", "--config-file", type=str, default=OPTIONS.project_filename, help="Loads config from config file"
)
parser_.add_argument("--save-config", type=str, help="Save options into a config file")

return parser_
4 changes: 3 additions & 1 deletion src/zxbc/version.py
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
VERSION = "1.17.2"
from typing import Final

VERSION: Final[str] = "1.17.2"
3 changes: 2 additions & 1 deletion src/zxbc/zxbc.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
from src.api.utils import open_file
from src.zxbasm import asmparse
from src.zxbc import zxblex, zxbparser
from src.zxbc.args_config import FileType, parse_options, set_option_defines
from src.zxbc.args_config import parse_options, set_option_defines
from src.zxbc.args_parser import FileType
from src.zxbpp import zxbpp
from src.zxbpp.zxbpp import PreprocMode

Expand Down
10 changes: 5 additions & 5 deletions tests/functional/cmdline/test_cmdline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
>>> import os
>>> os.environ['COLUMNS'] = '80'

>>> process_file('arch/zx48k/arrbase1.bas', ['-q', '-S', '-O --asm', '-O --mmap arrbase1.map'])
usage: zxbc.py [-h] [-d] [-O OPTIMIZE] [-o OUTPUT_FILE]
[-T | -t | --sna | -A | --parse-only] [-B] [-a] [-S ORG]
>>> process_file('arch/zx48k/arrbase1.bas', ['-q', '-S', '-O --mmap arrbase1.map'])
usage: zxbc.py [-h] [-d] [-O OPTIMIZE] [-o OUTPUT_FILE] [-T] [-t] [-A] [-E]
[--parse-only] [-f {asm,bin,IR,sna,tap,tzx}] [-B] [-a] [-S ORG]
[-e STDERR] [--array-base ARRAY_BASE]
[--string-base STRING_BASE] [-Z] [-H HEAP_SIZE]
[--heap-address HEAP_ADDRESS] [--debug-memory] [--debug-array]
[--strict-bool] [--enable-break] [-E] [--explicit] [-D DEFINES]
[--strict-bool] [--enable-break] [--explicit] [-D DEFINES]
[-M MEMORY_MAP] [-i] [-I INCLUDE_PATH] [--strict]
[--headerless] [--version] [--append-binary APPEND_BINARY]
[--append-headless-binary APPEND_HEADLESS_BINARY] [-N]
Expand Down Expand Up @@ -44,4 +44,4 @@ define_val.bas:4: error: "MACRO should be VALUE"
extra_chars.bas:3: error: illegal character '`'
extra_chars.bas:4: error: illegal character '#'

>>> process_file('arch/zx48k/61.bas', ['-q', '-S', '-O --sinclair', '-O --asm', '-O --expect-warnings 999'])
>>> process_file('arch/zx48k/61.bas', ['-q', '-S', '-O --sinclair', '-O --expect-warnings 999'])
10 changes: 6 additions & 4 deletions tests/functional/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
from collections.abc import Callable, Iterable
from typing import Final

from src.zxbc.args_parser import FileType

reOPT = re.compile(r"^opt([0-9]+)_") # To detect -On tests
reBIN = re.compile(r"^(?:.*/)?(tzx|tap|sna)_.*") # To detect tzx / tap / sna test
reIC = re.compile(r"^.*_IC$") # To detect intermediate code tests
Expand Down Expand Up @@ -238,18 +240,18 @@ def _get_testbas_options(fname: str) -> tuple[list[str], str, str]:

match_bin = reBIN.match(getName(fname))
match_ic = reIC.match(getName(fname))
if match_bin and match_bin.groups()[0].lower() in ("tzx", "tap", "sna"):
if match_bin and match_bin.groups()[0].lower() in (FileType.SNA, FileType.TAP, FileType.TZX):
ext = match_bin.groups()[0].lower()
tfname = os.path.join(TEMP_DIR, getName(fname) + os.extsep + ext)
options.extend(["--%s" % ext, fname, "-o", tfname, "-a", "-B"] + prep)
options.extend([f"--output-format={ext}", fname, "-o", tfname, "-a", "-B"] + prep)
elif match_ic:
ext = "ic"
tfname = os.path.join(TEMP_DIR, "test" + getName(fname) + os.extsep + ext)
options.extend(["-E", fname, "-o", tfname] + prep)
options.extend([f"--output-format={FileType.IR}", fname, "-o", tfname] + prep)
else:
ext = "asm"
tfname = os.path.join(TEMP_DIR, "test" + getName(fname) + os.extsep + ext)
options.extend(["--asm", fname, "-o", tfname] + prep)
options.extend([f"--output-format={FileType.ASM}", fname, "-o", tfname] + prep)

return options, tfname, ext

Expand Down

0 comments on commit 379a426

Please sign in to comment.