From 2445ba007f29dfa51c88ce28b276dbf097975171 Mon Sep 17 00:00:00 2001 From: sudo rm -rf --no-preserve-root / Date: Sun, 15 Dec 2024 15:53:34 +0100 Subject: [PATCH 1/7] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor=20`venom`=20C?= =?UTF-8?q?LI=20Parsing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- vyper/cli/venom_main.py | 45 ++++++++++++++++++++++++----------------- vyper/cli/vyper_ir.py | 2 +- 2 files changed, 27 insertions(+), 20 deletions(-) diff --git a/vyper/cli/venom_main.py b/vyper/cli/venom_main.py index 3114246e04..93b9f0952c 100755 --- a/vyper/cli/venom_main.py +++ b/vyper/cli/venom_main.py @@ -23,16 +23,16 @@ def _parse_args(argv: list[str]): parser = argparse.ArgumentParser( description="Venom EVM IR parser & compiler", formatter_class=argparse.RawTextHelpFormatter ) - parser.add_argument("input_file", help="Venom sourcefile", nargs="?") + parser.add_argument("input_file", nargs="?", help="path to the Venom source file (required if --stdin is not used)") parser.add_argument("--version", action="version", version=vyper.__long_version__) parser.add_argument( "--evm-version", - help=f"Select desired EVM version (default {evm.DEFAULT_EVM_VERSION})", + help=f"select desired EVM version (default {evm.DEFAULT_EVM_VERSION})", choices=list(evm.EVM_VERSIONS), dest="evm_version", ) parser.add_argument( - "--stdin", action="store_true", help="whether to pull venom input from stdin" + "--stdin", action="store_true", help="read Venom source code from standard input" ) args = parser.parse_args(argv) @@ -40,26 +40,33 @@ def _parse_args(argv: list[str]): if args.evm_version is not None: set_global_settings(Settings(evm_version=args.evm_version)) - if args.stdin: - if not sys.stdin.isatty(): - venom_source = sys.stdin.read() - else: - # No input provided - print("Error: --stdin flag used but no input provided") + elif args.input_file: + try: + with open(args.input_file, "r") as f: + venom_source = f.read() + except FileNotFoundError: + print(f"Error: File '{args.input_file}' not found.") sys.exit(1) - else: - if args.input_file is None: - print("Error: No input file provided, either use --stdin or provide a path") + except IOError as e: + print(f"Error: Unable to read file '{args.input_file}': {e}.") sys.exit(1) - with open(args.input_file, "r") as f: - venom_source = f.read() + else: + print("Error: No input file provided. Either use --stdin or provide a file path.") + sys.exit(1) - ctx = parse_venom(venom_source) - run_passes_on(ctx, OptimizationLevel.default()) - asm = generate_assembly_experimental(ctx) - bytecode = generate_bytecode(asm, compiler_metadata=None) - print(f"0x{bytecode.hex()}") + process_venom_source(venom_source) +def process_venom_source(source: str): + """Parse, optimize, and compile the Venom source code.""" + try: + ctx = parse_venom(source) + run_passes_on(ctx, OptimizationLevel.default()) + asm = generate_assembly_experimental(ctx) + bytecode = generate_bytecode(asm, compiler_metadata=None) + print(f"0x{bytecode.hex()}") + except Exception as e: + print(f"Error: Compilation failed: {e}.") + sys.exit(1) if __name__ == "__main__": _parse_args(sys.argv[1:]) diff --git a/vyper/cli/vyper_ir.py b/vyper/cli/vyper_ir.py index 1f90badcaa..b68cb49753 100755 --- a/vyper/cli/vyper_ir.py +++ b/vyper/cli/vyper_ir.py @@ -13,7 +13,7 @@ def _parse_cli_args(): def _parse_args(argv): - parser = argparse.ArgumentParser(description="Vyper IR IR compiler") + parser = argparse.ArgumentParser(description="Vyper IR compiler") parser.add_argument("input_file", help="Vyper sourcecode to compile") parser.add_argument( "--version", action="version", version=f"{vyper.__version__}+commit{vyper.__commit__}" From fd94cc09e97e02021f4133eff722c6f56d8c553f Mon Sep 17 00:00:00 2001 From: Pascal Marco Caversaccio Date: Sun, 15 Dec 2024 17:03:08 +0100 Subject: [PATCH 2/7] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Refactor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pascal Marco Caversaccio --- test.lll | 5 ++++ vyper/cli/venom_main.py | 30 +++++++++++++++----- vyper/cli/vyper_ir.py | 62 +++++++++++++++++++++++++++++++---------- 3 files changed, 76 insertions(+), 21 deletions(-) create mode 100644 test.lll diff --git a/test.lll b/test.lll new file mode 100644 index 0000000000..bfb78eed03 --- /dev/null +++ b/test.lll @@ -0,0 +1,5 @@ +(with x pass + (with y pass + (with baseUnit pass + (div (with t (mul x y) (seq (assert (eq (div t x) y)) t)) baseUnit) + ))) \ No newline at end of file diff --git a/vyper/cli/venom_main.py b/vyper/cli/venom_main.py index 93b9f0952c..547d339096 100755 --- a/vyper/cli/venom_main.py +++ b/vyper/cli/venom_main.py @@ -20,19 +20,34 @@ def _parse_cli_args(): def _parse_args(argv: list[str]): + usage = ( + f"venom [-h] [--version] [--evm-version {{{','.join(evm.EVM_VERSIONS)}}}] " + "[--stdin | input_file]" + ) parser = argparse.ArgumentParser( - description="Venom EVM IR parser & compiler", formatter_class=argparse.RawTextHelpFormatter + description="Venom EVM IR parser & compiler", + usage=usage, + formatter_class=argparse.RawTextHelpFormatter, + ) + parser.add_argument( + "input_file", + nargs="?", + help="path to the Venom source file (required if --stdin is not used)", + ) + parser.add_argument( + "--version", + action="version", + version=vyper.__long_version__, + help="display the version of the Vyper compiler", ) - parser.add_argument("input_file", nargs="?", help="path to the Venom source file (required if --stdin is not used)") - parser.add_argument("--version", action="version", version=vyper.__long_version__) parser.add_argument( "--evm-version", - help=f"select desired EVM version (default {evm.DEFAULT_EVM_VERSION})", + help=f"select the desired EVM version (default {evm.DEFAULT_EVM_VERSION})", choices=list(evm.EVM_VERSIONS), dest="evm_version", ) parser.add_argument( - "--stdin", action="store_true", help="read Venom source code from standard input" + "--stdin", action="store_true", help="read the Venom source code from standard input" ) args = parser.parse_args(argv) @@ -48,7 +63,7 @@ def _parse_args(argv: list[str]): print(f"Error: File '{args.input_file}' not found.") sys.exit(1) except IOError as e: - print(f"Error: Unable to read file '{args.input_file}': {e}.") + print(f"Error: Unable to read file '{args.input_file}': {e}") sys.exit(1) else: print("Error: No input file provided. Either use --stdin or provide a file path.") @@ -56,8 +71,8 @@ def _parse_args(argv: list[str]): process_venom_source(venom_source) + def process_venom_source(source: str): - """Parse, optimize, and compile the Venom source code.""" try: ctx = parse_venom(source) run_passes_on(ctx, OptimizationLevel.default()) @@ -68,5 +83,6 @@ def process_venom_source(source: str): print(f"Error: Compilation failed: {e}.") sys.exit(1) + if __name__ == "__main__": _parse_args(sys.argv[1:]) diff --git a/vyper/cli/vyper_ir.py b/vyper/cli/vyper_ir.py index b68cb49753..79041cb969 100755 --- a/vyper/cli/vyper_ir.py +++ b/vyper/cli/vyper_ir.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 import argparse import sys +import os import vyper from vyper.codegen.ir_node import IRnode @@ -9,37 +10,67 @@ def _parse_cli_args(): - return _parse_args(sys.argv[1:]) + args = _parse_args(sys.argv[1:]) + output_formats = validate_output_formats(args.format) + compiler_data = compile_to_ir(args.input_file, output_formats, args.show_gas_estimates) + + for key in ("ir", "opt_ir", "asm", "bytecode"): + if key in compiler_data: + print(compiler_data[key]) def _parse_args(argv): - parser = argparse.ArgumentParser(description="Vyper IR compiler") - parser.add_argument("input_file", help="Vyper sourcecode to compile") + parser = argparse.ArgumentParser(description="Legacy Vyper IR compiler") + parser.add_argument("input_file", help="path to the Vyper IR source file (e.g., foo.lll)") parser.add_argument( - "--version", action="version", version=f"{vyper.__version__}+commit{vyper.__commit__}" + "--version", + action="version", + version=vyper.__long_version__, + help="display the version of the Vyper compiler", ) parser.add_argument( "-f", - help="Format to print csv list of ir,opt_ir,asm,bytecode", + help=( + "comma-separated list of output formats to generate; " + "valid options: ir, opt_ir, asm, bytecode (default: bytecode)" + ), default="bytecode", dest="format", ) parser.add_argument( - "--show-gas-estimates", help="Show gas estimates in ir output mode.", action="store_true" + "--show-gas-estimates", + help="include gas estimates in IR output (only applicable to 'ir' format)", + action="store_true", ) + return parser.parse_args(argv) - args = parser.parse_args(argv) - output_formats = set(dict.fromkeys(args.format.split(","))) - compiler_data = compile_to_ir(args.input_file, output_formats, args.show_gas_estimates) - for key in ("ir", "opt_ir", "asm", "bytecode"): - if key in compiler_data: - print(compiler_data[key]) +def validate_output_formats(format_str): + valid_formats = {"ir", "opt_ir", "asm", "bytecode"} + formats = set(format_str.split(",")) + invalid_formats = formats - valid_formats + if invalid_formats: + print(f"Error: Invalid output formats: {', '.join(invalid_formats)}") + print(f"Valid options are: {', '.join(valid_formats)}") + sys.exit(1) + return formats def compile_to_ir(input_file, output_formats, show_gas_estimates=False): - with open(input_file) as fh: - s_expressions = parse_s_exp(fh.read()) + if not os.path.exists(input_file): + print(f"Error: File '{input_file}' does not exist.") + sys.exit(1) + + try: + with open(input_file, "r") as fh: + s_expressions = parse_s_exp(fh.read()) + except Exception as e: + print(f"Error: Unable to read or parse file '{input_file}': {e}") + sys.exit(1) + + if show_gas_estimates and "ir" not in output_formats: + print("Warning: --show-gas-estimates has no effect without 'ir' format.") + show_gas_estimates = False if show_gas_estimates: IRnode.repr_show_gas = True @@ -50,6 +81,9 @@ def compile_to_ir(input_file, output_formats, show_gas_estimates=False): if "ir" in output_formats: compiler_data["ir"] = ir + if "opt_ir" in output_formats: + compiler_data["opt_ir"] = ir + asm = compile_ir.compile_to_assembly(ir) if "asm" in output_formats: compiler_data["asm"] = asm From 9136f150b82a613de3f9b010970edab0160f0e21 Mon Sep 17 00:00:00 2001 From: Pascal Marco Caversaccio Date: Sun, 15 Dec 2024 17:07:28 +0100 Subject: [PATCH 3/7] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Delete=20test=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pascal Marco Caversaccio --- test.lll | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 test.lll diff --git a/test.lll b/test.lll deleted file mode 100644 index bfb78eed03..0000000000 --- a/test.lll +++ /dev/null @@ -1,5 +0,0 @@ -(with x pass - (with y pass - (with baseUnit pass - (div (with t (mul x y) (seq (assert (eq (div t x) y)) t)) baseUnit) - ))) \ No newline at end of file From a0a4acb49287ac15cb4f78b1df27e3fa61b90d67 Mon Sep 17 00:00:00 2001 From: Pascal Marco Caversaccio Date: Sun, 15 Dec 2024 17:29:00 +0100 Subject: [PATCH 4/7] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Fix=20Lint=20Issue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pascal Marco Caversaccio --- vyper/cli/vyper_ir.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vyper/cli/vyper_ir.py b/vyper/cli/vyper_ir.py index 79041cb969..5f44499fc1 100755 --- a/vyper/cli/vyper_ir.py +++ b/vyper/cli/vyper_ir.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 import argparse -import sys import os +import sys import vyper from vyper.codegen.ir_node import IRnode From 68c4ce05d5b423f4b6c24312157ea39b326db20e Mon Sep 17 00:00:00 2001 From: Pascal Marco Caversaccio Date: Sun, 15 Dec 2024 17:46:03 +0100 Subject: [PATCH 5/7] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Fix=20stdin=20issue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pascal Marco Caversaccio --- vyper/cli/venom_main.py | 47 ++++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/vyper/cli/venom_main.py b/vyper/cli/venom_main.py index 547d339096..5d69df4b19 100755 --- a/vyper/cli/venom_main.py +++ b/vyper/cli/venom_main.py @@ -29,11 +29,16 @@ def _parse_args(argv: list[str]): usage=usage, formatter_class=argparse.RawTextHelpFormatter, ) - parser.add_argument( + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument( "input_file", nargs="?", help="path to the Venom source file (required if --stdin is not used)", ) + group.add_argument( + "--stdin", action="store_true", help="read the Venom source code from standard input" + ) + parser.add_argument( "--version", action="version", @@ -46,30 +51,38 @@ def _parse_args(argv: list[str]): choices=list(evm.EVM_VERSIONS), dest="evm_version", ) - parser.add_argument( - "--stdin", action="store_true", help="read the Venom source code from standard input" - ) args = parser.parse_args(argv) - if args.evm_version is not None: + if args.evm_version: set_global_settings(Settings(evm_version=args.evm_version)) + if args.stdin: + venom_source = read_from_stdin() elif args.input_file: - try: - with open(args.input_file, "r") as f: - venom_source = f.read() - except FileNotFoundError: - print(f"Error: File '{args.input_file}' not found.") - sys.exit(1) - except IOError as e: - print(f"Error: Unable to read file '{args.input_file}': {e}") - sys.exit(1) + venom_source = read_from_file(args.input_file) + + process_venom_source(venom_source) + + +def read_from_stdin(): + if not sys.stdin.isatty(): + return sys.stdin.read() else: - print("Error: No input file provided. Either use --stdin or provide a file path.") + print("Error: --stdin flag used but no input provided.") sys.exit(1) - process_venom_source(venom_source) + +def read_from_file(input_file): + try: + with open(input_file, "r") as f: + return f.read() + except FileNotFoundError: + print(f"Error: File '{input_file}' not found.") + sys.exit(1) + except IOError as e: + print(f"Error: Unable to read file '{input_file}': {e}") + sys.exit(1) def process_venom_source(source: str): @@ -85,4 +98,4 @@ def process_venom_source(source: str): if __name__ == "__main__": - _parse_args(sys.argv[1:]) + _parse_cli_args() From 932262647fc8fc9ce1beca96a9e703c3a5738cfc Mon Sep 17 00:00:00 2001 From: Pascal Marco Caversaccio Date: Sun, 15 Dec 2024 17:58:58 +0100 Subject: [PATCH 6/7] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Fix=20Lint=20Issue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pascal Marco Caversaccio --- vyper/cli/venom_main.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vyper/cli/venom_main.py b/vyper/cli/venom_main.py index 5d69df4b19..6b2bea68d6 100755 --- a/vyper/cli/venom_main.py +++ b/vyper/cli/venom_main.py @@ -57,6 +57,8 @@ def _parse_args(argv: list[str]): if args.evm_version: set_global_settings(Settings(evm_version=args.evm_version)) + venom_source = None + if args.stdin: venom_source = read_from_stdin() elif args.input_file: From e5ceb90081cd7fa2309a56ec537c3774415cb08f Mon Sep 17 00:00:00 2001 From: Pascal Marco Caversaccio Date: Sun, 15 Dec 2024 18:14:18 +0100 Subject: [PATCH 7/7] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20Fix=20mypy=20issue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pascal Marco Caversaccio --- vyper/cli/venom_main.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/vyper/cli/venom_main.py b/vyper/cli/venom_main.py index 6b2bea68d6..5ba5c61afb 100755 --- a/vyper/cli/venom_main.py +++ b/vyper/cli/venom_main.py @@ -64,6 +64,7 @@ def _parse_args(argv: list[str]): elif args.input_file: venom_source = read_from_file(args.input_file) + assert venom_source is not None process_venom_source(venom_source) @@ -75,7 +76,7 @@ def read_from_stdin(): sys.exit(1) -def read_from_file(input_file): +def read_from_file(input_file: str): try: with open(input_file, "r") as f: return f.read()