net-plugin: modularize constants (consts.rs) and sockets (sockets.rs); remove legacy commented socket code; fix unused imports mir: move instruction unit tests to tests/mir_instruction_unit.rs (file lean-up); no semantic changes runner/pyvm: ensure using pre-strip; misc docs updates Build: cargo build ok; legacy cfg warnings remain as before
90 lines
2.8 KiB
Python
90 lines
2.8 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Nyash PyVM runner (scaffold)
|
|
|
|
Usage:
|
|
- python3 tools/pyvm_runner.py --in mir.json [--entry Main.main] [--args-env NYASH_SCRIPT_ARGS_JSON]
|
|
|
|
Executes MIR(JSON) using a tiny Python interpreter for a minimal opcode set:
|
|
- const/binop/compare/branch/jump/ret
|
|
- newbox (ConsoleBox, StringBox minimal)
|
|
- boxcall (String: length/substring/lastIndexOf; Console: print/println/log)
|
|
- externcall (nyash.console.println)
|
|
|
|
On success, exits with the integer return value if it is an Integer; otherwise 0.
|
|
Outputs produced by println/log are written to stdout.
|
|
"""
|
|
|
|
import argparse
|
|
import json
|
|
import sys
|
|
import os
|
|
from pathlib import Path
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
PYVM_DIR = ROOT / "src" / "llvm_py" / "pyvm"
|
|
|
|
# Ensure imports can find the package root (src)
|
|
SRC_DIR = ROOT / "src"
|
|
if str(SRC_DIR) not in sys.path:
|
|
sys.path.insert(0, str(SRC_DIR))
|
|
|
|
from llvm_py.pyvm.vm import PyVM # type: ignore
|
|
|
|
|
|
def main():
|
|
ap = argparse.ArgumentParser()
|
|
ap.add_argument("--in", dest="infile", required=True, help="MIR JSON input")
|
|
ap.add_argument("--entry", dest="entry", default="Main.main", help="Entry function (default Main.main)")
|
|
ap.add_argument("--args-env", dest="args_env", default="NYASH_SCRIPT_ARGS_JSON", help="Env var containing JSON array of argv to pass to entry")
|
|
args = ap.parse_args()
|
|
|
|
with open(args.infile, "r") as f:
|
|
program = json.load(f)
|
|
|
|
vm = PyVM(program)
|
|
# Fallbacks for entry name
|
|
entry = args.entry
|
|
fun_names = {f.get("name", "") for f in program.get("functions", [])}
|
|
if entry not in fun_names:
|
|
if "main" in fun_names:
|
|
entry = "main"
|
|
elif "Main.main" in fun_names:
|
|
entry = "Main.main"
|
|
|
|
# Load argv if present
|
|
argv: list[str] = []
|
|
if args.args_env:
|
|
js = os.environ.get(args.args_env)
|
|
if js:
|
|
try:
|
|
arr = json.loads(js)
|
|
if isinstance(arr, list):
|
|
argv = [str(x) if x is not None else "" for x in arr]
|
|
except Exception:
|
|
pass
|
|
|
|
result = vm.run_args(entry, argv) if argv else vm.run(entry)
|
|
# Exit code convention: integers propagate; bool -> 0/1; else 0
|
|
code = 0
|
|
if isinstance(result, bool):
|
|
code = 1 if result else 0
|
|
elif isinstance(result, int):
|
|
# Clamp to 32-bit exit code domain
|
|
code = int(result) & 0xFFFFFFFF
|
|
if code & 0x80000000:
|
|
code = -((~code + 1) & 0xFFFFFFFF)
|
|
# For parity comparisons, avoid emitting extra lines here.
|
|
sys.exit(code)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
try:
|
|
main()
|
|
except Exception as e:
|
|
import traceback
|
|
print(f"[pyvm] error: {e}", file=sys.stderr)
|
|
if sys.stderr and (os.environ.get('NYASH_CLI_VERBOSE') == '1' or True):
|
|
traceback.print_exc()
|
|
sys.exit(1)
|