noita_entangled_worlds/scripts/parse_lua_api.py

173 lines
4.9 KiB
Python
Raw Normal View History

2024-11-24 18:46:38 +03:00
import json
path = "/home/quant/.local/share/Steam/steamapps/common/Noita/tools_modding/lua_api_documentation.html"
lines = open(path).readlines()
lines_iter = iter(lines)
parsed = []
2024-11-24 20:47:58 +03:00
def maybe_map_types(name, typ):
2024-11-24 23:12:35 +03:00
if typ == "multiple types":
raise ValueError("no 'multiple types' either")
2024-11-24 20:47:58 +03:00
if name == "entity_id":
typ = "entity_id"
if name == "component_id":
typ = "component_id"
if typ == "float":
typ = "number"
if typ == "uint":
typ = "color"
if typ == "uint32":
typ = "color"
2024-11-24 23:12:35 +03:00
if typ == "name":
typ = "string"
if typ == "bool_is_new":
typ = "bool"
if typ == "boolean":
typ = "bool"
2024-11-25 22:42:26 +03:00
if typ == "item_entity_id":
typ = "entity_id"
2024-12-22 16:16:37 +03:00
#if typ == "physics_body_id":
# raise ValueError(f"{typ} not supported")
if name == "physics_body_id":
typ = "physics_body_id"
2024-11-24 20:47:58 +03:00
return typ
2024-11-24 18:46:38 +03:00
def parse_arg(arg_s):
if "|" in arg_s:
raise ValueError("multiple argument types not supported")
2024-11-24 20:47:58 +03:00
if "{" in arg_s:
raise ValueError("no table support for now")
if "multiple_types" in arg_s:
raise ValueError("no 'multiple_types' either")
other, *default = arg_s.split("=", maxsplit=1)
other = other.strip()
2024-11-24 18:46:38 +03:00
if default:
2024-11-24 20:47:58 +03:00
default = default[0].strip()
2024-11-24 18:46:38 +03:00
else:
default = None
name, typ = other.split(":", maxsplit=1)
2024-11-24 20:47:58 +03:00
typ = maybe_map_types(name, typ)
2024-11-24 18:46:38 +03:00
return {
"name": name,
"typ": typ,
"default": default,
}
def parse_ret(ret_s):
if not ret_s:
return None
optional = ret_s.endswith("|nil")
ret_s = ret_s.removesuffix("|nil")
if "|" in ret_s:
raise ValueError("multiple return types not supported")
2024-11-24 20:47:58 +03:00
if "multiple_types" in ret_s:
raise ValueError("no 'multiple_types' either")
2024-11-25 22:42:26 +03:00
returns_vec = False
if ret_s.startswith("{"):
ret_s = ret_s.removeprefix("{").removesuffix("}")
returns_vec = True
if "-" in ret_s:
raise ValueError("No support for key-value tables in returns")
2024-11-24 18:46:38 +03:00
2024-11-24 20:47:58 +03:00
typ = ret_s
2024-11-24 18:46:38 +03:00
name = None
2024-11-24 20:47:58 +03:00
if ":" in ret_s:
name, typ = ret_s.split(":", maxsplit=1)
2024-11-24 18:46:38 +03:00
2024-11-24 23:12:35 +03:00
if typ.endswith(" -"):
optional = True
typ = typ.removesuffix(" -")
2024-11-24 20:47:58 +03:00
typ = maybe_map_types(name, typ)
2024-11-24 18:46:38 +03:00
return {
"name": name,
"typ": typ,
2024-11-25 22:42:26 +03:00
"optional": optional,
"is_vec": returns_vec,
2024-11-24 18:46:38 +03:00
}
ignore = {
2024-11-24 23:12:35 +03:00
# Those have some specifics that make generic way of handling things not work on them
2024-11-24 20:47:58 +03:00
"PhysicsApplyForceOnArea",
"GetRandomActionWithType",
2024-11-24 23:12:35 +03:00
"GetParallelWorldPosition",
"EntityGetFirstHitboxCenter",
"InputGetJoystickAnalogStick",
"PhysicsAddBodyImage",
"PhysicsBodyIDGetBodyAABB",
"GuiTextInput",
2024-11-24 18:46:38 +03:00
}
skipped = 0
2024-11-24 20:47:58 +03:00
deprecated = 0
2024-11-24 18:46:38 +03:00
# 2 lazy 2 parse xml properly
try:
while True:
line = next(lines_iter)
if line.startswith('<th><span class="function">'):
fn_line = line.strip()
ret_line = next(lines_iter).strip()
desc_line = next(lines_iter).strip()
2024-11-24 20:47:58 +03:00
2024-11-24 18:46:38 +03:00
fn_name, other = fn_line.removeprefix('<th><span class="function">').split('</span>(<span class="field_name">', maxsplit=1)
args = other.removesuffix('</span><span class="func">)</span></th>').strip().split(", ")
try:
args = [parse_arg(arg) for arg in args if ":" in arg]
except ValueError as e:
skipped += 1
print(f"Skipping {fn_name}: {e}")
continue
2024-11-24 20:47:58 +03:00
rets = ret_line.removeprefix('<th><span class="field_name">').removesuffix('</span></th></th>').strip()
desc = desc_line.removeprefix('<th><span class="description">').removesuffix('</span></th></th>').strip().replace("</br>", "\n")
if "Debugish" in rets:
rets, desc = rets.split(" (", maxsplit=1)
desc = desc.removesuffix(")")
rets = rets.split(", ")
2024-11-24 18:46:38 +03:00
try:
2024-11-24 20:47:58 +03:00
rets = [parse_ret(ret) for ret in rets if ret]
2024-11-24 18:46:38 +03:00
except ValueError as e:
print(f"Skipping {fn_name}: {e}")
skipped += 1
continue
if not desc:
desc = "Nolla forgot to include a description :("
2024-11-24 20:47:58 +03:00
if "Deprecated" in desc_line:
deprecated += 1
print(f"Skipping {fn_name}: deprecated")
continue
2024-11-24 18:46:38 +03:00
#print(fn_line, ret_line, desc_line)
if fn_name not in ignore:
#print(fn_name, args, "->", ret)
parsed.append({
"fn_name": fn_name,
"args": args,
"desc": desc,
2024-11-24 20:47:58 +03:00
"rets": rets
2024-11-24 18:46:38 +03:00
})
2024-11-24 20:47:58 +03:00
else:
skipped += 1
2024-11-24 18:46:38 +03:00
except StopIteration:
pass
2024-11-24 20:47:58 +03:00
print("Total skipped:", skipped, deprecated)
print("Total parsed:", len(parsed))
json.dump(parsed, open("ewext/noita_api_macro/src/lua_api.json", "w"), indent=2)