Meta: Avoid match statements in generate_libwasm_spec_test.py

This commit is contained in:
Zaggy1024 2026-04-29 19:28:31 -05:00 committed by Jelle Raaijmakers
parent 8f7a4754fe
commit b5db4da668

View file

@ -181,36 +181,34 @@ class Context:
def parse_value(arg: dict[str, str]) -> WasmValue:
type_ = arg["type"]
match type_:
case "i32" | "i64" | "f32" | "f64":
return WasmPrimitiveValue(type_, arg["value"])
case "externref" | "funcref":
return WasmPrimitiveValue(type_, arg["value"] if "value" in arg else None)
case "refnull":
return WasmPrimitiveValue("externref", "null")
case "nullfuncref":
return WasmPrimitiveValue("funcref", "null")
case "v128":
if not isinstance(arg["value"], list):
raise ParseException("Got unknown type for Wasm value")
num_bits = int(arg["lane_type"][1:])
return WasmVector(arg["value"], num_bits)
case (
"arrayref"
| "structref"
| "eqref"
| "anyref"
| "i31ref"
| "exnref"
| "nullref"
| "nullexnref"
| "nullexternref"
):
return WasmGCValue(type_, arg.get("value"))
case "either":
return EitherOf([parse_value(opt) for opt in arg["values"]])
case _:
raise ParseException(f"Unknown value type: {type_}")
if type_ in ("i32", "i64", "f32", "f64"):
return WasmPrimitiveValue(type_, arg["value"])
if type_ in ("externref", "funcref"):
return WasmPrimitiveValue(type_, arg["value"] if "value" in arg else None)
if type_ == "refnull":
return WasmPrimitiveValue("externref", "null")
if type_ == "nullfuncref":
return WasmPrimitiveValue("funcref", "null")
if type_ == "v128":
if not isinstance(arg["value"], list):
raise ParseException("Got unknown type for Wasm value")
num_bits = int(arg["lane_type"][1:])
return WasmVector(arg["value"], num_bits)
if type_ in (
"arrayref",
"structref",
"eqref",
"anyref",
"i31ref",
"exnref",
"nullref",
"nullexnref",
"nullexternref",
):
return WasmGCValue(type_, arg.get("value"))
if type_ == "either":
return EitherOf([parse_value(opt) for opt in arg["values"]])
raise ParseException(f"Unknown value type: {type_}")
def parse_args(raw_args: list[dict[str, str]]) -> list[WasmValue]:
@ -218,13 +216,12 @@ def parse_args(raw_args: list[dict[str, str]]) -> list[WasmValue]:
def parse_action(action: dict[str, Any]) -> Action:
match action["type"]:
case "invoke":
return Invoke(action["field"], parse_args(action["args"]), action.get("module"))
case "get":
return Get(action["field"], action.get("module"))
case _:
raise ParseException(f"Action not implemented: {action['type']}")
action_type = action["type"]
if action_type == "invoke":
return Invoke(action["field"], parse_args(action["args"]), action.get("module"))
if action_type == "get":
return Get(action["field"], action.get("module"))
raise ParseException(f"Action not implemented: {action_type}")
def module_binary_filename(raw_cmd: dict[str, str]) -> Path:
@ -237,37 +234,36 @@ def parse(raw: dict[str, Any]) -> WastDescription:
for raw_cmd in raw["commands"]:
line = raw_cmd["line"]
cmd: Command
match raw_cmd["type"]:
case "module":
cmd = ModuleCommand(line, module_binary_filename(raw_cmd), raw_cmd.get("name"))
case "module_definition":
if "name" in raw_cmd:
defined_modules[raw_cmd["name"]] = module_binary_filename(raw_cmd)
continue
else:
cmd = ModuleCommand(line, module_binary_filename(raw_cmd), None)
case "module_instance":
cmd = ModuleCommand(line, defined_modules[raw_cmd["module"]], raw_cmd.get("instance"))
case "action":
cmd = ActionCommand(line, parse_action(raw_cmd["action"]))
case "register":
cmd = Register(line, raw_cmd.get("name"), raw_cmd["as"])
case "assert_return":
cmd = AssertReturn(
line,
parse_action(raw_cmd["action"]),
(parse_value(raw_cmd["expected"][0]) if len(raw_cmd["expected"]) == 1 else None),
)
case "assert_trap" | "assert_exhaustion":
cmd = AssertTrap(line, raw_cmd["text"], parse_action(raw_cmd["action"]))
case "assert_invalid" | "assert_malformed" | "assert_uninstantiable" | "assert_unlinkable":
if raw_cmd.get("module_type") == "text":
continue
cmd = AssertInvalid(line, module_binary_filename(raw_cmd), raw_cmd["text"])
case "assert_exception":
cmd = AssertException(line, parse_action(raw_cmd["action"]))
case _:
raise ParseException(f"Unknown command type: {raw_cmd['type']}")
cmd_type = raw_cmd["type"]
if cmd_type == "module":
cmd = ModuleCommand(line, module_binary_filename(raw_cmd), raw_cmd.get("name"))
elif cmd_type == "module_definition":
if "name" in raw_cmd:
defined_modules[raw_cmd["name"]] = module_binary_filename(raw_cmd)
continue
cmd = ModuleCommand(line, module_binary_filename(raw_cmd), None)
elif cmd_type == "module_instance":
cmd = ModuleCommand(line, defined_modules[raw_cmd["module"]], raw_cmd.get("instance"))
elif cmd_type == "action":
cmd = ActionCommand(line, parse_action(raw_cmd["action"]))
elif cmd_type == "register":
cmd = Register(line, raw_cmd.get("name"), raw_cmd["as"])
elif cmd_type == "assert_return":
cmd = AssertReturn(
line,
parse_action(raw_cmd["action"]),
(parse_value(raw_cmd["expected"][0]) if len(raw_cmd["expected"]) == 1 else None),
)
elif cmd_type in ("assert_trap", "assert_exhaustion"):
cmd = AssertTrap(line, raw_cmd["text"], parse_action(raw_cmd["action"]))
elif cmd_type in ("assert_invalid", "assert_malformed", "assert_uninstantiable", "assert_unlinkable"):
if raw_cmd.get("module_type") == "text":
continue
cmd = AssertInvalid(line, module_binary_filename(raw_cmd), raw_cmd["text"])
elif cmd_type == "assert_exception":
cmd = AssertException(line, parse_action(raw_cmd["action"]))
else:
raise ParseException(f"Unknown command type: {cmd_type}")
commands.append(cmd)
return WastDescription(raw["source_filename"], commands)
@ -355,19 +351,17 @@ def gen_value_arg(value: WasmValue) -> str:
if value.value == "-inf":
return "-Infinity"
match value.kind:
case "i32":
return str(unsigned_to_signed(int(value.value), 32))
case "i64":
return str(unsigned_to_signed(int(value.value), 64)) + "n"
case "f32":
return str(int(value.value)) + f" /* {float_to_str(int(value.value))} */"
case "f64":
return str(int(value.value)) + f"n /* {float_to_str(int(value.value), double=True)} */"
case "externref" | "funcref" | "v128":
return value.value
case _:
raise GenerateException(f"Not implemented: {value.kind}")
if value.kind == "i32":
return str(unsigned_to_signed(int(value.value), 32))
if value.kind == "i64":
return str(unsigned_to_signed(int(value.value), 64)) + "n"
if value.kind == "f32":
return str(int(value.value)) + f" /* {float_to_str(int(value.value))} */"
if value.kind == "f64":
return str(int(value.value)) + f"n /* {float_to_str(int(value.value), double=True)} */"
if value.kind in ("externref", "funcref", "v128"):
return value.value
raise GenerateException(f"Not implemented: {value.kind}")
def gen_value_result(value: WasmValue) -> GeneratedValue:
@ -382,13 +376,11 @@ def gen_value_result(value: WasmValue) -> GeneratedValue:
if (value.kind == "f32" or value.kind == "f64") and value.value.startswith("nan"):
num_bits = int(value.kind[1:])
match value.value:
case "nan:canonical":
return CanonicalNan(num_bits)
case "nan:arithmetic":
return ArithmeticNan(num_bits)
case _:
raise GenerateException(f"Unknown indeterminate nan: {value.value}")
if value.value == "nan:canonical":
return CanonicalNan(num_bits)
if value.value == "nan:arithmetic":
return ArithmeticNan(num_bits)
raise GenerateException(f"Unknown indeterminate nan: {value.value}")
return gen_value_arg(value)
@ -458,64 +450,67 @@ def gen_pretty_expect(expr: str, got: str, expect: str):
def gen_expectation(gen_result: GeneratedValue, module: str):
match gen_result:
case str():
print(f"expect(_result).toBe({gen_result});")
case GeneratedAnyFuncRef():
print(f"/* {gen_result} */ ", end="")
gen_pretty_expect(
f"isValidFuncrefIn(_result, {module})",
"_result",
"(ref.func)",
)
case ArithmeticNan():
print(f"/* {gen_result} */ ", end="")
gen_pretty_expect(
f"isArithmeticNaN{gen_result.num_bits}(_result)",
"_result",
"nan:arithmetic",
)
case CanonicalNan():
print(f"/* {gen_result} */ ", end="")
gen_pretty_expect(
f"isCanonicalNaN{gen_result.num_bits}(_result)",
"_result",
"nan:canonical",
)
case GeneratedVector():
if gen_result.num_bits == 64:
array = "new BigUint64Array(_result)"
else:
array = f"new Uint{gen_result.num_bits}Array(_result)"
print(f"/* {gen_result} */ ", end="")
gen_pretty_expect(
f"testSIMDVector({gen_result.repr}, {array})",
array,
gen_result.repr,
)
case GeneratedEitherOf():
print("let matched = false;")
print("let error_sample = null;")
expectations = []
for option in gen_result.options:
print("try {")
gen_expectation(option, module)
print("matched = true;")
print("} catch (e) { error_sample = e; }")
expectation = "unknown"
match option:
case str():
expectation = option
case ArithmeticNan():
expectation = "nan:arithmetic"
case CanonicalNan():
expectation = "nan:canonical"
case GeneratedVector():
expectation = option.repr
expectations.append(expectation)
print(
f"if (!matched) {{ expect().fail(`Expected one of {', '.join(expectations)}, got ${{_result}}: ${{error_sample}}`); }}"
)
if isinstance(gen_result, str):
print(f"expect(_result).toBe({gen_result});")
return
if isinstance(gen_result, GeneratedAnyFuncRef):
print(f"/* {gen_result} */ ", end="")
gen_pretty_expect(
f"isValidFuncrefIn(_result, {module})",
"_result",
"(ref.func)",
)
return
if isinstance(gen_result, ArithmeticNan):
print(f"/* {gen_result} */ ", end="")
gen_pretty_expect(
f"isArithmeticNaN{gen_result.num_bits}(_result)",
"_result",
"nan:arithmetic",
)
return
if isinstance(gen_result, CanonicalNan):
print(f"/* {gen_result} */ ", end="")
gen_pretty_expect(
f"isCanonicalNaN{gen_result.num_bits}(_result)",
"_result",
"nan:canonical",
)
return
if isinstance(gen_result, GeneratedVector):
if gen_result.num_bits == 64:
array = "new BigUint64Array(_result)"
else:
array = f"new Uint{gen_result.num_bits}Array(_result)"
print(f"/* {gen_result} */ ", end="")
gen_pretty_expect(
f"testSIMDVector({gen_result.repr}, {array})",
array,
gen_result.repr,
)
return
assert isinstance(gen_result, GeneratedEitherOf)
print("let matched = false;")
print("let error_sample = null;")
expectations = []
for option in gen_result.options:
print("try {")
gen_expectation(option, module)
print("matched = true;")
print("} catch (e) { error_sample = e; }")
expectation = "unknown"
if isinstance(option, str):
expectation = option
elif isinstance(option, ArithmeticNan):
expectation = "nan:arithmetic"
elif isinstance(option, CanonicalNan):
expectation = "nan:canonical"
elif isinstance(option, GeneratedVector):
expectation = option.repr
expectations.append(expectation)
print(
f"if (!matched) {{ expect().fail(`Expected one of {', '.join(expectations)}, got ${{_result}}: ${{error_sample}}`); }}"
)
def gen_invoke(
@ -570,32 +565,36 @@ def gen_register(register: Register, _: Context):
def gen_command(command: Command, ctx: Context):
match command:
case ModuleCommand():
gen_module_command(command, ctx)
case ActionCommand():
if isinstance(command.action, Invoke):
gen_invoke(command.line, command.action, None, ctx)
else:
raise GenerateException(f"Not implemented: top-level {type(command.action)}")
case AssertInvalid():
gen_invalid(command, ctx)
case Register():
gen_register(command, ctx)
case AssertReturn():
match command.action:
case Invoke():
gen_invoke(command.line, command.action, command.expected, ctx)
case Get():
gen_get(command.line, command.action, command.expected, ctx)
case AssertTrap():
if not isinstance(command.action, Invoke):
raise GenerateException(f"Not implemented: {type(command.action)}")
gen_invoke(command.line, command.action, None, ctx, fail_msg=command.messsage)
case AssertException():
if not isinstance(command.action, Invoke):
raise GenerateException(f"Not implemented: {type(command.action)}")
gen_invoke(command.line, command.action, None, ctx, fail_msg="exception")
if isinstance(command, ModuleCommand):
gen_module_command(command, ctx)
return
if isinstance(command, ActionCommand):
if isinstance(command.action, Invoke):
gen_invoke(command.line, command.action, None, ctx)
else:
raise GenerateException(f"Not implemented: top-level {type(command.action)}")
return
if isinstance(command, AssertInvalid):
gen_invalid(command, ctx)
return
if isinstance(command, Register):
gen_register(command, ctx)
return
if isinstance(command, AssertReturn):
if isinstance(command.action, Invoke):
gen_invoke(command.line, command.action, command.expected, ctx)
else:
gen_get(command.line, command.action, command.expected, ctx)
return
if isinstance(command, AssertTrap):
if not isinstance(command.action, Invoke):
raise GenerateException(f"Not implemented: {type(command.action)}")
gen_invoke(command.line, command.action, None, ctx, fail_msg=command.messsage)
return
assert isinstance(command, AssertException)
if not isinstance(command.action, Invoke):
raise GenerateException(f"Not implemented: {type(command.action)}")
gen_invoke(command.line, command.action, None, ctx, fail_msg="exception")
def generate(description: WastDescription):