mirror of
https://github.com/norandom/OpenUltraSAST.git
synced 2026-10-16 19:59:26 +00:00
feat(learning-harness): TypeScript and TSX rows parse under the semantic extra
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
This commit is contained in:
co-authored by
Claude Opus 5
parent
6c205b728f
commit
6b58c81ba0
@@ -42,6 +42,7 @@ _KIND = {
|
||||
}
|
||||
_KIND["cpp"] = _KIND["c"]
|
||||
_KIND["typescript"] = _KIND["javascript"]
|
||||
_KIND["tsx"] = _KIND["javascript"]
|
||||
|
||||
_CONST = frozenset(
|
||||
{
|
||||
@@ -64,7 +65,7 @@ _SEQUENCE = frozenset({"list", "tuple", "array", "array_creation_expression", "i
|
||||
def parse_with_cst(path: str, text: str, language: str) -> FileIR | None:
|
||||
from .extra import grammar_for
|
||||
|
||||
grammar = grammar_for(language)
|
||||
grammar = grammar_for(_grammar_language(language, path))
|
||||
if grammar is None:
|
||||
return None
|
||||
try:
|
||||
@@ -77,6 +78,11 @@ def parse_with_cst(path: str, text: str, language: str) -> FileIR | None:
|
||||
return file_ir_from_tree(path=path, text=text, language=language, tree=tree)
|
||||
|
||||
|
||||
def _grammar_language(language: str, path: str) -> str:
|
||||
"""TypeScript ships two grammars; a `.tsx` file needs the JSX-aware one. The FileIR keeps the language it was given."""
|
||||
return "tsx" if language == "typescript" and path.endswith(".tsx") else language
|
||||
|
||||
|
||||
def file_ir_from_tree(*, path: str, text: str, language: str, tree: Any) -> FileIR:
|
||||
del text
|
||||
root = tree.root_node
|
||||
|
||||
@@ -7,12 +7,16 @@ 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",
|
||||
"javascript": "tree_sitter_javascript",
|
||||
"c": "tree_sitter_c",
|
||||
"cpp": "tree_sitter_cpp",
|
||||
"java": "tree_sitter_java",
|
||||
"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"})
|
||||
@@ -26,7 +30,7 @@ def has_semantic_extra() -> bool:
|
||||
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())
|
||||
return any(importlib.util.find_spec(module) is not None for module, _ in _GRAMMAR_MODULES.values())
|
||||
|
||||
|
||||
def grammar_for(language: str) -> Any | None:
|
||||
@@ -34,15 +38,16 @@ def grammar_for(language: str) -> Any | None:
|
||||
flag = os.environ.get(_PROBE_ENV, "").strip().lower()
|
||||
if flag in _PROBE_OFF:
|
||||
return None
|
||||
module_name = _GRAMMAR_MODULES.get(language)
|
||||
if module_name is None or importlib.util.find_spec("tree_sitter") is 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(module.language())
|
||||
return Language(getattr(module, attribute)())
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user