1
0
Fork 0
forked from fun/fun

Tons of refactoring and some fixes and enhancements... ;)

This commit is contained in:
Johannes Findeisen 2025-09-15 06:31:32 +02:00
commit 13919ad87d
82 changed files with 1775 additions and 1382 deletions

98
scripts/check_op_includes.py Executable file
View file

@ -0,0 +1,98 @@
#!/usr/bin/env python3
import re
import sys
import argparse
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
BYTECODE = ROOT / "src" / "bytecode.h"
VM_C = ROOT / "src" / "vm.c"
OP_RE = re.compile(r'\bOP_([A-Z0-9_]+)\b')
INCLUDE_RE = re.compile(r'#include\s+"vm_case_([a-z0-9_]+)\.inc"')
def read_text(path: Path) -> str:
try:
return path.read_text(encoding="utf-8")
except Exception as e:
print(f"error: cannot read {path}: {e}", file=sys.stderr)
sys.exit(2)
def parse_opcodes_from_bytecode(text: str) -> set[str]:
# Gather OP_* from the enum definition, excluding duplicates in comments
ops = set()
# crude: find typedef enum block and extract OP_* tokens
enum_start = text.find("typedef enum")
if enum_start == -1:
enum_start = 0
enum_block = text[enum_start:]
for m in OP_RE.finditer(enum_block):
token = m.group(1)
ops.add(token)
return ops
def parse_includes_from_vm(text: str) -> set[str]:
incs = set(m.group(1) for m in INCLUDE_RE.finditer(text))
# Convert include base names back to uppercase opcode tokens heuristically.
mapping = {
"nop":"NOP","halt":"HALT",
"load_const":"LOAD_CONST","load_local":"LOAD_LOCAL","store_local":"STORE_LOCAL",
"load_global":"LOAD_GLOBAL","store_global":"STORE_GLOBAL",
"pop":"POP","dup":"DUP","swap":"SWAP",
"call":"CALL","return":"RETURN","print":"PRINT","jump":"JUMP","jump_if_false":"JUMP_IF_FALSE",
"add":"ADD","sub":"SUB","mul":"MUL","div":"DIV",
"mod":"MOD","lt":"LT","lte":"LTE","gt":"GT","gte":"GTE",
"eq":"EQ","neq":"NEQ","and":"AND","or":"OR","not":"NOT",
"make_array":"MAKE_ARRAY","len":"LEN",
"index_get":"INDEX_GET","index_set":"INDEX_SET",
"arr_push":"ARR_PUSH","arr_pop":"ARR_POP","arr_set":"ARR_SET","arr_insert":"ARR_INSERT","arr_remove":"ARR_REMOVE",
"slice":"SLICE",
"to_number":"TO_NUMBER","to_string":"TO_STRING",
"split":"SPLIT","join":"JOIN","substr":"SUBSTR","find":"FIND",
"enumerate":"ENUMERATE","zip":"ZIP",
"min":"MIN","max":"MAX","clamp":"CLAMP","abs":"ABS","pow":"POW",
"random_seed":"RANDOM_SEED","random_int":"RANDOM_INT",
"make_map":"MAKE_MAP","keys":"KEYS","values":"VALUES","has_key":"HAS_KEY",
"read_file":"READ_FILE","write_file":"WRITE_FILE",
}
tokens = set()
for inc in incs:
tokens.add(mapping.get(inc, inc.upper()))
return tokens
def main() -> int:
ap = argparse.ArgumentParser(description="Check that vm_case_*.inc includes cover OP_* opcodes.")
ap.add_argument("-v", "--verbose", action="store_true", help="print summary of ops and includes")
args = ap.parse_args()
bc_text = read_text(BYTECODE)
vm_text = read_text(VM_C)
ops = parse_opcodes_from_bytecode(bc_text)
inc_tokens = parse_includes_from_vm(vm_text)
missing_handlers = sorted([op for op in ops if op not in inc_tokens])
extra_includes = sorted([t for t in inc_tokens if t not in ops])
ok = True
if missing_handlers:
ok = False
print("Missing vm_case includes for opcodes:", ", ".join(missing_handlers))
if extra_includes:
ok = False
print("Includes without matching OP_* in bytecode.h:", ", ".join(extra_includes))
if ok:
if args.verbose:
print(f"OK: vm_case includes cover OP_* set from bytecode.h")
print(f" OP_* count : {len(ops)}")
print(f" includes count : {len(inc_tokens)}")
print(f" OP_* tokens : {', '.join(sorted(ops))}")
print(f" include tokens : {', '.join(sorted(inc_tokens))}")
else:
print("OK: vm_case includes cover OP_* set from bytecode.h")
return 0
return 1
if __name__ == "__main__":
sys.exit(main())