Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor[tool]: refactor venom and fang CLI parsing #4398

Draft
wants to merge 10 commits into
base: master
Choose a base branch
from
86 changes: 61 additions & 25 deletions vyper/cli/venom_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,46 +20,82 @@


def _parse_args(argv: list[str]):
usage = (

Check warning on line 23 in vyper/cli/venom_main.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/venom_main.py#L23

Added line #L23 was not covered by tests
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,
)
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(

Check warning on line 33 in vyper/cli/venom_main.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/venom_main.py#L32-L33

Added lines #L32 - L33 were not covered by tests
"input_file",
nargs="?",
help="path to the Venom source file (required if --stdin is not used)",
)
group.add_argument(

Check warning on line 38 in vyper/cli/venom_main.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/venom_main.py#L38

Added line #L38 was not covered by tests
"--stdin", action="store_true", help="read the Venom source code from standard input"
)

parser.add_argument(

Check warning on line 42 in vyper/cli/venom_main.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/venom_main.py#L42

Added line #L42 was not covered by tests
"--version",
action="version",
version=vyper.__long_version__,
help="display the version of the Vyper compiler",
)
parser.add_argument("input_file", help="Venom sourcefile", nargs="?")
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="whether to pull venom input from stdin"
)

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:
if not sys.stdin.isatty():
venom_source = sys.stdin.read()
else:
# No input provided
print("Error: --stdin flag used but no input provided")
sys.exit(1)
venom_source = read_from_stdin()

Check warning on line 61 in vyper/cli/venom_main.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/venom_main.py#L61

Added line #L61 was not covered by tests
elif args.input_file:
venom_source = read_from_file(args.input_file)

Check warning on line 63 in vyper/cli/venom_main.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/venom_main.py#L63

Added line #L63 was not covered by tests

process_venom_source(venom_source)

Check warning on line 65 in vyper/cli/venom_main.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/venom_main.py#L65

Added line #L65 was not covered by tests


def read_from_stdin():

Check warning on line 68 in vyper/cli/venom_main.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/venom_main.py#L68

Added line #L68 was not covered by tests

Check notice

Code scanning / CodeQL

Explicit returns mixed with implicit (fall through) returns Note

Mixing implicit and explicit returns may indicate an error as implicit returns always return None.
if not sys.stdin.isatty():
return sys.stdin.read()

Check warning on line 70 in vyper/cli/venom_main.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/venom_main.py#L70

Added line #L70 was not covered by tests
else:
if args.input_file is None:
print("Error: No input file provided, either use --stdin or provide a path")
sys.exit(1)
with open(args.input_file, "r") as f:
venom_source = f.read()
print("Error: --stdin flag used but no input provided.")
sys.exit(1)

Check warning on line 73 in vyper/cli/venom_main.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/venom_main.py#L72-L73

Added lines #L72 - L73 were not covered by tests


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)

Check warning on line 85 in vyper/cli/venom_main.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/venom_main.py#L76-L85

Added lines #L76 - L85 were not covered by tests


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()}")
def process_venom_source(source: str):
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)

Check warning on line 97 in vyper/cli/venom_main.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/venom_main.py#L88-L97

Added lines #L88 - L97 were not covered by tests


if __name__ == "__main__":
_parse_args(sys.argv[1:])
_parse_cli_args()

Check warning on line 101 in vyper/cli/venom_main.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/venom_main.py#L101

Added line #L101 was not covered by tests
62 changes: 48 additions & 14 deletions vyper/cli/vyper_ir.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/usr/bin/env python3
import argparse
import os

Check warning on line 3 in vyper/cli/vyper_ir.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/vyper_ir.py#L3

Added line #L3 was not covered by tests
import sys

import vyper
Expand All @@ -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)

Check warning on line 15 in vyper/cli/vyper_ir.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/vyper_ir.py#L13-L15

Added lines #L13 - L15 were not covered by tests

for key in ("ir", "opt_ir", "asm", "bytecode"):
if key in compiler_data:
print(compiler_data[key])

Check warning on line 19 in vyper/cli/vyper_ir.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/vyper_ir.py#L19

Added line #L19 was not covered by tests


def _parse_args(argv):
parser = argparse.ArgumentParser(description="Vyper IR 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)")

Check warning on line 24 in vyper/cli/vyper_ir.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/vyper_ir.py#L23-L24

Added lines #L23 - L24 were not covered by tests
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)

Check warning on line 45 in vyper/cli/vyper_ir.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/vyper_ir.py#L45

Added line #L45 was not covered by tests

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

Check warning on line 51 in vyper/cli/vyper_ir.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/vyper_ir.py#L48-L51

Added lines #L48 - L51 were not covered by tests
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

Check warning on line 56 in vyper/cli/vyper_ir.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/vyper_ir.py#L53-L56

Added lines #L53 - L56 were not covered by tests


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)

Check warning on line 62 in vyper/cli/vyper_ir.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/vyper_ir.py#L61-L62

Added lines #L61 - L62 were not covered by tests

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)

Check warning on line 69 in vyper/cli/vyper_ir.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/vyper_ir.py#L64-L69

Added lines #L64 - L69 were not covered by tests

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

Check warning on line 73 in vyper/cli/vyper_ir.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/vyper_ir.py#L72-L73

Added lines #L72 - L73 were not covered by tests

if show_gas_estimates:
IRnode.repr_show_gas = True
Expand All @@ -50,6 +81,9 @@
if "ir" in output_formats:
compiler_data["ir"] = ir

if "opt_ir" in output_formats:
compiler_data["opt_ir"] = ir

Check warning on line 85 in vyper/cli/vyper_ir.py

View check run for this annotation

Codecov / codecov/patch

vyper/cli/vyper_ir.py#L85

Added line #L85 was not covered by tests

asm = compile_ir.compile_to_assembly(ir)
if "asm" in output_formats:
compiler_data["asm"] = asm
Expand Down
Loading