Wl6adams's picture
Organize private Nexum release into Lite, Universal, and Expanded profiles
9a70a84
Raw
History Blame Contribute Delete
6.7 kB
"""Real parser-backed and lexical source inspection for language packs."""
from __future__ import annotations
import hashlib
import re
from pathlib import Path
from typing import Any, Callable, cast
from .catalog import (
LANGUAGE_PACKS,
LanguagePack,
configure_language_parser_cache,
resolve_language_pack,
)
_LEXICAL_TOKEN_RE = re.compile(
r"""
(?P<space>\s+)
|(?P<line_comment>//[^\n]*|\#[^\n]*|--[^\n]*)
|(?P<block_comment>/\*.*?\*/)
|(?P<string>"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*')
|(?P<number>\b(?:0x[0-9a-fA-F]+|\d+(?:\.\d+)?)\b)
|(?P<identifier>[A-Za-z_][A-Za-z0-9_]*)
|(?P<delimiter>[\(\)\[\]\{\}])
|(?P<operator>\S)
""",
re.DOTALL | re.VERBOSE,
)
def _source_digest(source: str) -> str:
return hashlib.sha256(source.encode("utf-8", errors="replace")).hexdigest()
def _lexical_analysis(pack: LanguagePack, source: str) -> dict[str, Any]:
counts = {
"line_comment": 0,
"block_comment": 0,
"string": 0,
"number": 0,
"identifier": 0,
"delimiter": 0,
"operator": 0,
}
delimiter_balance = {"(": 0, "[": 0, "{": 0}
closing = {")": "(", "]": "[", "}": "{"}
delimiter_errors = 0
for match in _LEXICAL_TOKEN_RE.finditer(source):
kind = match.lastgroup or ""
if kind in counts:
counts[kind] += 1
if kind != "delimiter":
continue
token = match.group(0)
if token in delimiter_balance:
delimiter_balance[token] += 1
else:
opening = closing[token]
if delimiter_balance[opening] <= 0:
delimiter_errors += 1
else:
delimiter_balance[opening] -= 1
delimiter_errors += sum(delimiter_balance.values())
return {
"schema": "nexum.language-inspection.v1",
"language_pack_id": pack.pack_id,
"language": pack.name,
"backend": "lexical",
"parser_language": "",
"source_sha256": _source_digest(source),
"total_bytes": len(source.encode("utf-8", errors="replace")),
"total_lines": source.count("\n") + (1 if source else 0),
"node_count": sum(counts.values()),
"error_count": delimiter_errors,
"max_depth": 0,
"root_type": "source",
"token_counts": counts,
"operational": True,
}
def _parser_analysis(
pack: LanguagePack,
source: str,
parser_language: str,
) -> dict[str, Any]:
try:
from tree_sitter_language_pack import get_parser
except ImportError as exc:
raise RuntimeError("language parser package is unavailable") from exc
try:
configure_language_parser_cache()
parser_factory = cast(Callable[[str], Any], get_parser)
parser = parser_factory(parser_language)
tree = parser.parse(source.encode("utf-8", errors="replace"))
except (LookupError, RuntimeError, ValueError) as exc:
raise RuntimeError(
f"language parser is unavailable: {parser_language}"
) from exc
root = tree.root_node
stack: list[tuple[Any, int]] = [(root, 0)]
node_count = 0
error_count = 0
missing_count = 0
max_depth = 0
while stack:
node, depth = stack.pop()
node_count += 1
max_depth = max(max_depth, depth)
if str(getattr(node, "type", "")) == "ERROR":
error_count += 1
if bool(getattr(node, "is_missing", False)):
missing_count += 1
children = getattr(node, "children", ())
stack.extend((child, depth + 1) for child in children)
return {
"schema": "nexum.language-inspection.v1",
"language_pack_id": pack.pack_id,
"language": pack.name,
"backend": "parser",
"parser_mode": pack.parser_mode,
"parser_language": parser_language,
"source_sha256": _source_digest(source),
"total_bytes": len(source.encode("utf-8", errors="replace")),
"total_lines": source.count("\n") + (1 if source else 0),
"node_count": node_count,
"error_count": error_count + missing_count,
"missing_node_count": missing_count,
"max_depth": max_depth,
"root_type": str(getattr(root, "type", "")),
"root_has_error": bool(getattr(root, "has_error", False)),
"operational": bool(node_count > 0),
}
def analyze_language_source(
source: str,
*,
language: str,
path: str = "",
) -> dict[str, Any]:
"""Inspect source through the selected release-local language pack."""
pack = resolve_language_pack(language, path=path)
if not pack.parser_languages:
return _lexical_analysis(pack, source)
return _parser_analysis(pack, source, pack.parser_languages[0])
def analyze_language_file(
path: Path,
*,
language: str = "",
) -> dict[str, Any]:
"""Inspect one existing text file without returning its content."""
source = path.read_text(encoding="utf-8", errors="replace")
result = analyze_language_source(
source,
language=language,
path=path.name,
)
return {**result, "path": path.name}
def verify_language_packs() -> dict[str, Any]:
"""Exercise every declared source-intelligence backend."""
failures: list[dict[str, str]] = []
parser_checks = 0
lexical_checks = 0
for pack in LANGUAGE_PACKS:
try:
if pack.parser_languages:
for parser_language in pack.parser_languages:
result = _parser_analysis(pack, "", parser_language)
if not bool(result["operational"]):
raise RuntimeError("parser returned no syntax tree")
parser_checks += 1
else:
result = _lexical_analysis(pack, "x\n")
if not bool(result["operational"]):
raise RuntimeError("lexical analyzer returned no structure")
lexical_checks += 1
except (OSError, RuntimeError, ValueError) as exc:
failures.append(
{
"id": pack.pack_id,
"reason": f"{type(exc).__name__}: {exc}",
}
)
return {
"schema": "nexum.language-pack-verification.v1",
"ok": not failures,
"language_pack_count": len(LANGUAGE_PACKS),
"verified_language_pack_count": len(LANGUAGE_PACKS) - len(failures),
"parser_checks": parser_checks,
"lexical_checks": lexical_checks,
"failures": failures,
}
__all__ = [
"analyze_language_file",
"analyze_language_source",
"verify_language_packs",
]