Skip to content

Add: R (.r/.R) language support via bespoke extract_r#1933

Open
Sirhan1 wants to merge 1 commit into
Graphify-Labs:v8from
Sirhan1:feat/r-language-support
Open

Add: R (.r/.R) language support via bespoke extract_r#1933
Sirhan1 wants to merge 1 commit into
Graphify-Labs:v8from
Sirhan1:feat/r-language-support

Conversation

@Sirhan1

@Sirhan1 Sirhan1 commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR adds initial support for R (.r/.R) source files via a dedicated extract_r extractor, following the same approach used for Julia and Elixir.

The scope is intentionally limited to R source code structure and call relationships. Package metadata (DESCRIPTION, NAMESPACE, exports), package-qualified resolution, .Rmd support, and other R-specific semantics are intentionally deferred to a follow-up PR.

Why a dedicated extractor?

Unlike languages with named function declarations, R functions are typically anonymous functions assigned to variables:

foo <- function(x) {
    ...
}

Tree-sitter represents these as assignments containing anonymous function definitions rather than named declarations, making the generic extractor a poor fit. This follows the existing pattern used for other languages that require bespoke extractors.

What this PR adds

Parser integration

  • Adds an optional r extra using tree-sitter-language-pack
  • Registers .r, .R, and Rscript
  • Integrates with the existing language dispatch system
  • Preserves the existing missing-language warning (pip install "graphifyy[r]")

R extraction

The new extractor supports:

  • Named functions created using all common assignment operators:
    • <-
    • <<-
    • =
    • ->
    • ->>
  • Nested functions with the same ownership model used by other bespoke extractors
  • Function calls attributed to the correct enclosing scope
  • Cross-file call resolution via the existing shared resolver
  • Detection of:
    • package-qualified calls (pkg::fn, pkg:::fn)
    • member-style calls ($, @)
    • pipe expressions (|>, %>%)

Nested function bodies are skipped while traversing an enclosing function to prevent double attribution.

Imports

Supports static detection of:

  • library()
  • require()
  • requireNamespace()
  • source("file.R")

source() imports are only created when the referenced file exists locally, matching Graphify's existing behaviour for Bash source.

Tests

Added:

  • 4 fixture files
  • 22 language tests covering dispatch, parsing, extraction, and optional dependency behaviour
  • 2 new extraction tests plus updates to existing warning tests

The optional grammar follows the same testing pattern as the existing optional language parsers:

  • Parser-dependent tests are skipped when the grammar isn't installed
  • Missing-dependency tests remain network-free
  • The test suite never downloads a parser automatically

The implementation was also verified on a synthetic multi-file R project covering common R idioms, including nested functions, pipes, package-qualified calls, cross-file resolution, and ambiguous definitions.

Results

  • ✅ R-specific tests: 22/22
  • ✅ Updated extraction tests: 13/13
  • ✅ Full test suite: 3250 passed, 3 skipped
  • ruff check
  • pyright
  • ✅ Local graph regenerated with graphify update .

Out of scope

The following are intentionally deferred to a future PR:

  • DESCRIPTION / NAMESPACE parsing
  • Exported symbol modelling
  • Package-qualified call resolution
  • .Rmd extraction
  • S3/S4/R6 support
  • Non-standard evaluation (NSE)
  • renv.lock integration

Adds a new bespoke R extractor under graphify/extractors/, following the
Julia/Elixir precedent. Handles the five function-assignment forms
(<-, <<-, =, ->, ->>), nested function scoping, top-level vs function-level
call attribution, member ($/@) and package-qualified (::/:::) raw_calls,
pipes (|>/%>%), static library/require/requireNamespace imports, and
static source() cross-file linkage. Unqualified bare calls route through
the shared raw_calls resolver with an R language-family guard that blocks
binding to non-R definitions.

Wires .r into _DISPATCH/_EXTRA_FOR_EXTENSION/_LANG_FAMILY_BY_EXT and
Rscript into _SHEBANG_DISPATCH; .R reuses the existing case-insensitive
suffix fallback. Ships under optional [r] extra via
tree-sitter-language-pack (no maintained tree-sitter-r wheel fits the
per-language model); the pack may fetch/cache the R grammar on first use
(no source uploaded, R not executed).

Fixes Graphify-Labs#1689 for .R (no longer a no-AST-extractor case) and reuses the
existing Graphify-Labs#1745 install-extra warning with pip install "graphifyy[r]".

Adds tests/fixtures/sample.r + 3 cross-file fixtures, 22 R tests in
test_languages.py following the DM optional-grammar skip-when-unavailable
convention, and 2 dispatch/missing-dep tests in test_extract.py.

R package metadata (DESCRIPTION/NAMESPACE/exports) and package-qualified
call resolution are deferred to a follow-up (Graphify-Labs#9).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant