mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-17 12:09:27 +00:00
Task 1.4. The optional extra gains the TypeScript grammar wheel, which ships two grammars: a file with JSX needs the JSX-aware one and plain TypeScript needs the other, so the parser picks by file suffix while the parsed file keeps the language it was given. The walker already treated TypeScript as JavaScript, so nothing else changes. Eighteen agent-vfc rows and every TypeScript absence row were counted as unsupported before this and could not be scored at all. They now parse on both sides. The core install stays zero-dependency and the extra-free suite still skips these tests. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]> Claude-Session: https://claude.ai/code/session_01T7XD7x5KonMdmGRkp63EQG
54 lines
2.0 KiB
Python
54 lines
2.0 KiB
Python
"""Optional semantic extra: lazy grammar load. Never import wheels at package load."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib
|
|
import importlib.util
|
|
import os
|
|
from typing import Any
|
|
|
|
# language -> (wheel module, the module attribute that builds the grammar). TypeScript ships two grammars;
|
|
# `.tsx` needs the JSX-aware one and plain `.ts` needs the other, so the caller asks for the one it needs.
|
|
_GRAMMAR_MODULES = {
|
|
"python": ("tree_sitter_python", "language"),
|
|
"javascript": ("tree_sitter_javascript", "language"),
|
|
"typescript": ("tree_sitter_typescript", "language_typescript"),
|
|
"tsx": ("tree_sitter_typescript", "language_tsx"),
|
|
"c": ("tree_sitter_c", "language"),
|
|
"cpp": ("tree_sitter_cpp", "language"),
|
|
"java": ("tree_sitter_java", "language"),
|
|
}
|
|
_PROBE_ENV = "OPENULTRASAST_TREE_SITTER_PROBE"
|
|
_PROBE_OFF = frozenset({"0", "off", "false", "unavailable", "no"})
|
|
_PROBE_ON = frozenset({"1", "on", "true", "available", "yes"})
|
|
|
|
|
|
def has_semantic_extra() -> bool:
|
|
"""True when the parser runtime and at least one in-scope grammar are importable."""
|
|
flag = os.environ.get(_PROBE_ENV, "").strip().lower()
|
|
if flag in _PROBE_OFF:
|
|
return False
|
|
if importlib.util.find_spec("tree_sitter") is None:
|
|
return False
|
|
return any(importlib.util.find_spec(module) is not None for module, _ in _GRAMMAR_MODULES.values())
|
|
|
|
|
|
def grammar_for(language: str) -> Any | None:
|
|
"""Return a tree_sitter.Language for an in-scope language, or None. Never raises into the scan."""
|
|
flag = os.environ.get(_PROBE_ENV, "").strip().lower()
|
|
if flag in _PROBE_OFF:
|
|
return None
|
|
entry = _GRAMMAR_MODULES.get(language)
|
|
if entry is None or importlib.util.find_spec("tree_sitter") is None:
|
|
return None
|
|
module_name, attribute = entry
|
|
if importlib.util.find_spec(module_name) is None:
|
|
return None
|
|
try:
|
|
from tree_sitter import Language
|
|
|
|
module = importlib.import_module(module_name)
|
|
return Language(getattr(module, attribute)())
|
|
except Exception:
|
|
return None
|