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)
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' ])
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
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 ])
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
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