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 7 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 64 additions & 25 deletions vyper/cli/venom_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,46 +20,85 @@


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,
)
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",
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))

venom_source = None

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()
elif args.input_file:
venom_source = read_from_file(args.input_file)

assert venom_source is not None
process_venom_source(venom_source)
Fixed Show fixed Hide fixed


def read_from_stdin():

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()
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)


def read_from_file(input_file: str):
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)


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)


if __name__ == "__main__":
_parse_args(sys.argv[1:])
_parse_cli_args()
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
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)

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 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
Expand All @@ -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
Expand Down
Loading