예제 #1
0
    def test_init(self):
        config.init()
        self.assertEqual(config.OPTIONS.Debug, 0)
        self.assertEqual(config.OPTIONS.stdin, sys.stdin)
        self.assertEqual(config.OPTIONS.stdout, sys.stdout)
        self.assertEqual(config.OPTIONS.stderr, sys.stderr)
        self.assertEqual(config.OPTIONS.optimization,
                         global_.DEFAULT_OPTIMIZATION_LEVEL)
        self.assertEqual(config.OPTIONS.case_insensitive, False)
        self.assertEqual(config.OPTIONS.array_base, 0)
        self.assertEqual(config.OPTIONS.byref, False)
        self.assertEqual(config.OPTIONS.max_syntax_errors,
                         global_.DEFAULT_MAX_SYNTAX_ERRORS)
        self.assertEqual(config.OPTIONS.string_base, 0)
        self.assertIsNone(config.OPTIONS.memory_map)
        self.assertEqual(config.OPTIONS.bracket, False)
        self.assertEqual(config.OPTIONS.use_loader, False)
        self.assertEqual(config.OPTIONS.autorun, False)
        self.assertEqual(config.OPTIONS.output_file_type, 'bin')
        self.assertEqual(config.OPTIONS.include_path, '')
        self.assertEqual(config.OPTIONS.memoryCheck, False)
        self.assertEqual(config.OPTIONS.strictBool, False)
        self.assertEqual(config.OPTIONS.arrayCheck, False)
        self.assertEqual(config.OPTIONS.enableBreak, False)
        self.assertEqual(config.OPTIONS.emitBackend, False)
        self.assertIsNone(config.OPTIONS.architecture)
        self.assertEqual(config.OPTIONS.expect_warnings, 0)

        # private options that cannot be accessed with #pragma
        self.assertEqual(config.OPTIONS['__DEFINES'].value, {})
        self.assertEqual(config.OPTIONS.explicit, False)
        self.assertEqual(config.OPTIONS.Sinclair, False)
        self.assertEqual(config.OPTIONS.strict, False)
예제 #2
0
 def test_initted_values(self):
     config.init()
     self.assertEqual(sorted(config.OPTIONS._options.keys()), [
         'Debug', 'Sinclair', 'StdErrFileName', '__DEFINES', 'architecture',
         'arrayCheck', 'array_base', 'autorun', 'bracket', 'byref',
         'case_insensitive', 'emitBackend', 'enableBreak',
         'expect_warnings', 'explicit', 'hide_warning_codes',
         'include_path', 'inputFileName', 'max_syntax_errors',
         'memoryCheck', 'memory_map', 'optimization', 'outputFileName',
         'output_file_type', 'stderr', 'stdin', 'stdout', 'strict',
         'strictBool', 'string_base', 'use_loader', 'zxnext'
     ])
예제 #3
0
def entry_point(args=None):
    if args is None:
        args = sys.argv[1:]

    config.init()
    init()
    setMode('BASIC')

    parser = argparse.ArgumentParser()
    parser.add_argument('-o', '--output', type=str, dest='output_file', default=None,
                        help='Sets output file. Default is to output to console (STDOUT)')
    parser.add_argument('-d', '--debug', dest='debug', default=config.OPTIONS.debug_level, action='count',
                        help='Enable verbosity/debugging output. Additional -d increases verbosity/debug level')
    parser.add_argument('-e', '--errmsg', type=str, dest='stderr', default=None,
                        help='Error messages file. Standard error console by default (STDERR)')
    parser.add_argument('input_file', type=str, default=None, nargs='?',
                        help="File to parse. If not specified, console input will be used (STDIN)")
    parser.add_argument('--arch', type=str, default=arch.AVAILABLE_ARCHITECTURES[0],
                        help=f"Target architecture (defaults is'{arch.AVAILABLE_ARCHITECTURES[0]}'). "
                             f"Available architectures: {','.join(arch.AVAILABLE_ARCHITECTURES)}")
    parser.add_argument('--expect-warnings', default=config.OPTIONS.expected_warnings, type=int,
                        help='Expects N warnings: first N warnings will be silenced')

    options = parser.parse_args(args=args)
    config.OPTIONS.debug_level = options.debug
    config.OPTIONS.debug_zxbpp = config.OPTIONS.debug_level > 0
    config.OPTIONS.expected_warnings = options.expect_warnings

    if options.arch not in arch.AVAILABLE_ARCHITECTURES:
        parser.error(f"Invalid architecture '{options.arch}'")
        return 2
    config.OPTIONS.architecture = options.arch

    if options.stderr:
        config.OPTIONS.stderr_filename = options.stderr
        config.OPTIONS.stderr = utils.open_file(config.OPTIONS.stderr_filename, 'wt', 'utf-8')

    result = main([options.input_file] if options.input_file else [])
    if not global_.has_errors:  # ok?
        if options.output_file:
            with utils.open_file(options.output_file, 'wt', 'utf-8') as output_file:
                output_file.write(OUTPUT)
        else:
            config.OPTIONS.stdout.write(OUTPUT)
    return result
예제 #4
0
 def test_initted_values(self):
     config.init()
     self.assertEqual(sorted(config.OPTIONS._options.keys()), [
         '__DEFINES', config.OPTION.ARCH, config.OPTION.ARRAY_BASE,
         config.OPTION.CHECK_ARRAYS, config.OPTION.AUTORUN,
         config.OPTION.CASE_INS, config.OPTION.DEBUG,
         config.OPTION.DEFAULT_BYREF, config.OPTION.EMIT_BACKEND,
         config.OPTION.ENABLE_BREAK, config.OPTION.EXPECTED_WARNINGS,
         config.OPTION.EXPLICIT, config.OPTION.FORCE_ASM_BRACKET,
         config.OPTION.HIDE_WARNING_CODES, config.OPTION.INCLUDE_PATH,
         config.OPTION.INPUT_FILENAME, config.OPTION.MAX_SYN_ERRORS,
         config.OPTION.CHECK_MEMORY, config.OPTION.MEMORY_MAP,
         config.OPTION.O_LEVEL, config.OPTION.OUTPUT_FILE_TYPE,
         config.OPTION.OUTPUT_FILENAME, 'project_filename', 'sinclair',
         config.OPTION.STDERR, config.OPTION.STDERR_FILENAME,
         config.OPTION.STDIN, config.OPTION.STDOUT, config.OPTION.STRICT,
         config.OPTION.STRICT_BOOL, config.OPTION.STR_BASE,
         config.OPTION.USE_BASIC_LOADER, config.OPTION.ASM_ZXNEXT
     ])
예제 #5
0
def main(args=None, emitter=None):
    """ Entry point when executed from command line.
    zxbc can be used as python module. If so, bear in mind this function
    won't be executed unless explicitly called.
    """
    # region [Initialization]
    config.init()
    zxbpp.init()
    zxbparser.init()
    arch.target.backend.init()
    arch.target.Translator.reset()
    asmparse.init()
    # endregion

    options = parse_options(args)
    arch.set_target_arch(OPTIONS.architecture)
    backend = arch.target.backend
    args = [
        options.PROGRAM
    ]  # Strip out other options, because they're already set in the OPTIONS container
    input_filename = options.PROGRAM

    zxbpp.setMode('basic')
    zxbpp.main(args)

    if gl.has_errors:
        debug.__DEBUG__("exiting due to errors.")
        return 1  # Exit with errors

    input_ = zxbpp.OUTPUT
    zxbparser.parser.parse(input_,
                           lexer=zxblex.lexer,
                           tracking=True,
                           debug=(OPTIONS.debug_level > 1))
    if gl.has_errors:
        debug.__DEBUG__("exiting due to errors.")
        return 1  # Exit with errors

    # Unreachable code removal
    unreachable_code_visitor = src.api.optimize.UnreachableCodeVisitor()
    unreachable_code_visitor.visit(zxbparser.ast)

    # Function calls graph
    func_call_visitor = src.api.optimize.FunctionGraphVisitor()
    func_call_visitor.visit(zxbparser.ast)

    # Optimizations
    optimizer = src.api.optimize.OptimizerVisitor()
    optimizer.visit(zxbparser.ast)

    # Emits intermediate code
    translator = arch.target.Translator()
    translator.visit(zxbparser.ast)

    if gl.DATA_IS_USED:
        gl.FUNCTIONS.extend(gl.DATA_FUNCTIONS)

    # This will fill MEMORY with pending functions
    func_visitor = arch.target.FunctionTranslator(gl.FUNCTIONS)
    func_visitor.start()

    # Emits data lines
    translator.emit_data_blocks()
    # Emits default constant strings
    translator.emit_strings()
    # Emits jump tables
    translator.emit_jump_tables()
    # Signals end of user code
    translator.ic_inline(';; --- end of user code ---')

    if OPTIONS.emit_backend:
        with open_file(OPTIONS.output_filename, 'wt', 'utf-8') as output_file:
            for quad in translator.dumpMemory(backend.MEMORY):
                output_file.write(str(quad) + '\n')

            backend.MEMORY[:] = []  # Empties memory
            # This will fill MEMORY with global declared variables
            translator = arch.target.VarTranslator()
            translator.visit(zxbparser.data_ast)

            for quad in translator.dumpMemory(backend.MEMORY):
                output_file.write(str(quad) + '\n')
        return 0  # Exit success

    # Join all lines into a single string and ensures an INTRO at end of file
    asm_output = backend.emit(backend.MEMORY,
                              optimize=OPTIONS.optimization_level > 0)
    asm_output = arch.target.optimizer.optimize(
        asm_output) + '\n'  # invoke the -O3

    asm_output = asm_output.split('\n')
    for i in range(len(asm_output)):
        tmp = backend.ASMS.get(asm_output[i], None)
        if tmp is not None:
            asm_output[i] = '\n'.join(tmp)

    asm_output = '\n'.join(asm_output)

    # Now filter them against the preprocessor again
    zxbpp.setMode('asm')
    zxbpp.OUTPUT = ''
    zxbpp.filter_(asm_output, filename=input_filename)

    # Now output the result
    asm_output = zxbpp.OUTPUT.split('\n')
    get_inits(asm_output)  # Find out remaining inits
    backend.MEMORY[:] = []

    # This will fill MEMORY with global declared variables
    var_checker = src.api.optimize.VariableVisitor()
    var_checker.visit(zxbparser.data_ast)
    translator = arch.target.VarTranslator()
    translator.visit(zxbparser.data_ast)
    if gl.has_errors:
        debug.__DEBUG__("exiting due to errors.")
        return 1  # Exit with errors

    tmp = [
        x for x in backend.emit(backend.MEMORY, optimize=False)
        if x.strip()[0] != '#'
    ]
    asm_output = backend.emit_start() + tmp \
                                      + ['%s:' % backend.DATA_END_LABEL, '%s:' % backend.MAIN_LABEL] \
                                      + asm_output + backend.emit_end()

    if OPTIONS.output_file_type == FileType.ASM:  # Only output assembler file
        with open_file(OPTIONS.output_filename, 'wt', 'utf-8') as output_file:
            output(asm_output, output_file)
    elif not options.parse_only:
        fout = StringIO()
        output(asm_output, fout)
        asmparse.assemble(fout.getvalue())
        fout.close()
        asmparse.generate_binary(
            OPTIONS.output_filename,
            OPTIONS.output_file_type,
            binary_files=options.append_binary,
            headless_binary_files=options.append_headless_binary,
            emitter=emitter)
        if gl.has_errors:
            return 5  # Error in assembly

    if OPTIONS.memory_map:
        if asmparse.MEMORY is not None:
            with open_file(OPTIONS.memory_map, 'wt', 'utf-8') as f:
                f.write(asmparse.MEMORY.memory_map)

    if not gl.has_errors and options.save_config:
        src.api.config.save_config_into_file(
            options.save_config, src.api.config.ConfigSections.ZXBC)

    return gl.has_errors  # Exit success
예제 #6
0
def main(args=None, emitter=None):
    """ Entry point when executed from command line.
    You can use zxbc.py as a module with import, and this
    function won't be executed.
    """
    # region [Initialization]
    config.init()
    zxbpp.init()
    zxbparser.init()
    arch.target.backend.init()
    arch.target.Translator.reset()
    asmparse.init()
    # endregion

    parser = args_parser.parser()
    options = parser.parse_args(args=args)

    # ------------------------------------------------------------
    # Setting of internal parameters according to command line
    # ------------------------------------------------------------
    OPTIONS.Debug = options.debug
    OPTIONS.optimization = options.optimize
    OPTIONS.outputFileName = options.output_file
    OPTIONS.StdErrFileName = options.stderr
    OPTIONS.array_base = options.array_base
    OPTIONS.string_base = options.string_base
    OPTIONS.Sinclair = options.sinclair
    OPTIONS.heap_size = options.heap_size
    OPTIONS.memoryCheck = options.debug_memory
    OPTIONS.strictBool = options.strict_bool or OPTIONS.Sinclair
    OPTIONS.arrayCheck = options.debug_array
    OPTIONS.emitBackend = options.emit_backend
    OPTIONS.enableBreak = options.enable_break
    OPTIONS.explicit = options.explicit
    OPTIONS.memory_map = options.memory_map
    OPTIONS.strict = options.strict
    OPTIONS.headerless = options.headerless
    OPTIONS.zxnext = options.zxnext
    OPTIONS.expect_warnings = gl.EXPECTED_WARNINGS = options.expect_warnings
    OPTIONS.hide_warning_codes = options.hide_warning_codes

    if options.arch not in arch.AVAILABLE_ARCHITECTURES:
        parser.error(f"Invalid architecture '{options.arch}'")
        return 2

    OPTIONS.architecture = options.arch
    arch.set_target_arch(options.arch)
    backend = arch.target.backend

    # region [Enable/Disable Warnings]
    enabled_warnings = set(options.enable_warning or [])
    disabled_warnings = set(options.disable_warning or [])
    duplicated_options = [f"W{x}" for x in enabled_warnings.intersection(disabled_warnings)]

    if duplicated_options:
        parser.error(f"Warning(s) {', '.join(duplicated_options)} cannot be enabled "
                     f"and disabled simultaneously")
        return 2

    for warn_code in enabled_warnings:
        errmsg.enable_warning(warn_code)

    for warn_code in disabled_warnings:
        errmsg.disable_warning(warn_code)

    # endregion

    OPTIONS.org = src.api.utils.parse_int(options.org)
    if OPTIONS.org is None:
        parser.error("Invalid --org option '{}'".format(options.org))

    if options.defines:
        for i in options.defines:
            macro = list(i.split('=', 1))
            name = macro[0]
            val = ''.join(macro[1:])
            OPTIONS.__DEFINES[name] = val
            zxbpp.ID_TABLE.define(name, value=val, lineno=0)

    if OPTIONS.Sinclair:
        OPTIONS.array_base = 1
        OPTIONS.string_base = 1
        OPTIONS.strictBool = True
        OPTIONS.case_insensitive = True

    if options.ignore_case:
        OPTIONS.case_insensitive = True

    debug.ENABLED = OPTIONS.Debug

    if int(options.tzx) + int(options.tap) + int(options.asm) + int(options.emit_backend) + \
            int(options.parse_only) > 1:
        parser.error("Options --tap, --tzx, --emit-backend, --parse-only and --asm are mutually exclusive")
        return 3

    if options.basic and not options.tzx and not options.tap:
        parser.error('Option --BASIC and --autorun requires --tzx or tap format')
        return 4

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

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

    OPTIONS.use_loader = options.basic
    OPTIONS.autorun = options.autorun

    if options.tzx:
        OPTIONS.output_file_type = 'tzx'
    elif options.tap:
        OPTIONS.output_file_type = 'tap'
    elif options.asm:
        OPTIONS.output_file_type = 'asm'
    elif options.emit_backend:
        OPTIONS.output_file_type = 'ic'

    args = [options.PROGRAM]
    if not os.path.exists(options.PROGRAM):
        parser.error("No such file or directory: '%s'" % args[0])
        return 2

    if OPTIONS.memoryCheck:
        OPTIONS.__DEFINES['__MEMORY_CHECK__'] = ''
        zxbpp.ID_TABLE.define('__MEMORY_CHECK__', lineno=0)

    if OPTIONS.arrayCheck:
        OPTIONS.__DEFINES['__CHECK_ARRAY_BOUNDARY__'] = ''
        zxbpp.ID_TABLE.define('__CHECK_ARRAY_BOUNDARY__', lineno=0)

    if OPTIONS.enableBreak:
        OPTIONS.__DEFINES['__ENABLE_BREAK__'] = ''
        zxbpp.ID_TABLE.define('__ENABLE_BREAK__', lineno=0)

    OPTIONS.include_path = options.include_path
    OPTIONS.inputFileName = zxbparser.FILENAME = \
        os.path.basename(args[0])

    if not OPTIONS.outputFileName:
        OPTIONS.outputFileName = \
            os.path.splitext(os.path.basename(OPTIONS.inputFileName))[0] + os.path.extsep + \
            OPTIONS.output_file_type

    if OPTIONS.StdErrFileName:
        OPTIONS.stderr = open_file(OPTIONS.StdErrFileName, 'wt', 'utf-8')

    zxbpp.setMode('basic')
    zxbpp.main(args)

    if gl.has_errors:
        debug.__DEBUG__("exiting due to errors.")
        return 1  # Exit with errors

    input_ = zxbpp.OUTPUT
    zxbparser.parser.parse(input_, lexer=zxblex.lexer, tracking=True,
                           debug=(OPTIONS.Debug > 1))
    if gl.has_errors:
        debug.__DEBUG__("exiting due to errors.")
        return 1  # Exit with errors

    # Unreachable code removal
    unreachable_code_visitor = src.api.optimize.UnreachableCodeVisitor()
    unreachable_code_visitor.visit(zxbparser.ast)

    # Function calls graph
    func_call_visitor = src.api.optimize.FunctionGraphVisitor()
    func_call_visitor.visit(zxbparser.ast)

    # Optimizations
    optimizer = src.api.optimize.OptimizerVisitor()
    optimizer.visit(zxbparser.ast)

    # Emits intermediate code
    translator = arch.target.Translator()
    translator.visit(zxbparser.ast)

    if gl.DATA_IS_USED:
        gl.FUNCTIONS.extend(gl.DATA_FUNCTIONS)

    # This will fill MEMORY with pending functions
    func_visitor = arch.target.FunctionTranslator(gl.FUNCTIONS)
    func_visitor.start()

    # Emits data lines
    translator.emit_data_blocks()
    # Emits default constant strings
    translator.emit_strings()
    # Emits jump tables
    translator.emit_jump_tables()
    # Signals end of user code
    translator.ic_inline(';; --- end of user code ---')

    if OPTIONS.emitBackend:
        with open_file(OPTIONS.outputFileName, 'wt', 'utf-8') as output_file:
            for quad in translator.dumpMemory(backend.MEMORY):
                output_file.write(str(quad) + '\n')

            backend.MEMORY[:] = []  # Empties memory
            # This will fill MEMORY with global declared variables
            translator = arch.target.VarTranslator()
            translator.visit(zxbparser.data_ast)

            for quad in translator.dumpMemory(backend.MEMORY):
                output_file.write(str(quad) + '\n')
        return 0  # Exit success

    # Join all lines into a single string and ensures an INTRO at end of file
    asm_output = backend.emit(backend.MEMORY, optimize=OPTIONS.optimization > 0)
    asm_output = arch.target.optimizer.optimize(asm_output) + '\n'  # invoke the -O3

    asm_output = asm_output.split('\n')
    for i in range(len(asm_output)):
        tmp = backend.ASMS.get(asm_output[i], None)
        if tmp is not None:
            asm_output[i] = '\n'.join(tmp)

    asm_output = '\n'.join(asm_output)

    # Now filter them against the preprocessor again
    zxbpp.setMode('asm')
    zxbpp.OUTPUT = ''
    zxbpp.filter_(asm_output, args[0])

    # Now output the result
    asm_output = zxbpp.OUTPUT.split('\n')
    get_inits(asm_output)  # Find out remaining inits
    backend.MEMORY[:] = []

    # This will fill MEMORY with global declared variables
    var_checker = src.api.optimize.VariableVisitor()
    var_checker.visit(zxbparser.data_ast)
    translator = arch.target.VarTranslator()
    translator.visit(zxbparser.data_ast)
    if gl.has_errors:
        debug.__DEBUG__("exiting due to errors.")
        return 1  # Exit with errors

    tmp = [x for x in backend.emit(backend.MEMORY, optimize=False) if x.strip()[0] != '#']
    asm_output = backend.emit_start() + tmp \
                                      + ['%s:' % backend.DATA_END_LABEL, '%s:' % backend.MAIN_LABEL] \
                                      + asm_output + backend.emit_end()

    if options.asm:  # Only output assembler file
        with open_file(OPTIONS.outputFileName, 'wt', 'utf-8') as output_file:
            output(asm_output, output_file)
    elif not options.parse_only:
        fout = StringIO()
        output(asm_output, fout)
        asmparse.assemble(fout.getvalue())
        fout.close()
        asmparse.generate_binary(OPTIONS.outputFileName, OPTIONS.output_file_type,
                                 binary_files=options.append_binary,
                                 headless_binary_files=options.append_headless_binary,
                                 emitter=emitter)
        if gl.has_errors:
            return 5  # Error in assembly

    if OPTIONS.memory_map:
        if asmparse.MEMORY is not None:
            with open_file(OPTIONS.memory_map, 'wt', 'utf-8') as f:
                f.write(asmparse.MEMORY.memory_map)

    return gl.has_errors  # Exit success