Skip to content

feat(extract): Pine Script (.pine) extractor - #2480

Open
Dyrokk wants to merge 1 commit into
Graphify-Labs:v8from
Dyrokk:feat/pine-script-extractor
Open

feat(extract): Pine Script (.pine) extractor#2480
Dyrokk wants to merge 1 commit into
Graphify-Labs:v8from
Dyrokk:feat/pine-script-extractor

Conversation

@Dyrokk

@Dyrokk Dyrokk commented Aug 5, 2026

Copy link
Copy Markdown

What

Adds a Pine Script (.pine) extractor, following the "Adding a new language extractor" steps in ARCHITECTURE.md.

TradingView's Pine has no tree-sitter grammar, so this is a line-oriented extractor in the same family as the regex fallback in pascal.py. Pine is small and highly regular — declarations live at column 0, blocks are indentation scoped, there is no macro layer — so a lexer over comment/string-stripped lines recovers the structure an AST would give.

Nodes: the script declaration (indicator/strategy/library plus its title), user functions and methods, user-defined types and enums, inputs, and imported libraries.

Edges: contains, calls (user function → user function), references (function → input it reads), uses (script → notable built-in), imports.

Two decisions worth reviewing

Inputs are first-class nodes. In Pine the inputs are the tunable surface of a strategy. "Which function reads slBuf" is the question people actually ask of a trading script, and it is unanswerable if inputs are just text.

Built-ins are a curated whitelist, not every dotted call. Only the calls that change what a script does: strategy.entry/exit/close/order, alertcondition, request.security, box.new, line.new, label.new, plot*, container constructors. Edges to math.*/ta.* would hub the graph into god nodes and drown the real structure. Built-in and library nodes are unscoped so two scripts share them (that shared edge is the point: "which strategies request a higher timeframe?"), while user function IDs are file-scoped, since Pine has no cross-file symbol resolution outside explicit library imports and a calcQty() defined in two scripts is genuinely two functions.

Validation

Run against a private corpus of 9 real strategies, 3,714 lines of Pine: 82/82 function definitions detected, no false positives and none missed, checked against a ground-truth scan of the => definition lines. On the largest script (1,223 lines) it recovers 59 functions and all 35 inputs exactly.

Handles the cases that break a naive regex:

  • multi-line signatures (f(float a,\n float b) =>)
  • // inside string literals — common in Pine alert payloads carrying URLs — which is why comment stripping is character-wise rather than a regex
  • calls that look like definitions (ma = ta.sma(close, 14) must not become a node)
  • string bodies are blanked to spaces rather than removed, so column indices survive; indentation is what delimits Pine blocks

Tests

  • tests/fixtures/sample.pine + 6 tests in tests/test_languages.py, per the contributing guidelines
  • tests/test_pine.py — 13 focused cases (scoping, comment/string handling, multi-line signatures, empty file, no self-edges)

Full suite on this branch: 3910 passed. The 56 failures on my machine (test_skillgen, test_watch, test_uninstall_scope) are pre-existing on Windows — I ran the suite with -p no:randomly before and after the change and the failure set is byte-identical, so nothing here is new. Happy to have CI confirm on Linux.

Notes

  • No new dependency: the extractor is pure stdlib, so .pine needs no entry in _EXTRA_FOR_EXTENSION.
  • .pine added to CODE_EXTENSIONS in detect.py; watch.py picks it up automatically since _WATCHED_EXTENSIONS derives from it.
  • MQL5 (.mq5/.mqh) is the obvious follow-up for the same audience and is C-like enough to likely reuse the generic core rather than a bespoke extractor. Happy to send that separately if this direction looks right.

TradingView Pine has no tree-sitter grammar, so this is a line-oriented
extractor in the same family as the Pascal regex fallback. Pine is small and
regular -- declarations sit at column 0 and blocks are indentation scoped --
so a lexer over comment/string-stripped lines recovers the structure an AST
would give.

Nodes: script declaration (indicator/strategy/library + title), user functions
and methods, user-defined types and enums, inputs, imported libraries.
Edges: contains, calls (user function -> user function), references (function
-> input it reads), uses (script -> notable built-in), imports.

Two deliberate choices:

- Inputs are first-class nodes. In Pine the inputs are the tunable surface of
  a strategy, so "which function reads slBuf" is the question people actually
  ask of a trading script.
- Built-ins are a curated whitelist (strategy.entry/exit/close, alertcondition,
  request.security, box.new, line.new, plot, ...) rather than every dotted
  call. Edges to math.* / ta.* would hub the graph into god nodes; these are
  the calls that change what a script does, so they answer cross-script
  questions without that cost. Built-in and library nodes are unscoped so two
  scripts share them; user function IDs are file-scoped so a calcQty() defined
  in two strategies stays two nodes.

Validated against a 3,714-line corpus of 9 real strategies: 82/82 function
definitions detected, no false positives. Handles multi-line signatures, "//"
inside string literals (common in Pine alert payloads), and calls that
resemble definitions.

@graphify-labs graphify-labs Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).

Graphify reviewed this change.

Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).


Graphify review — findings

This PR adds a Pine Script (.pine) extractor to graphify. Since TradingView's Pine has no tree-sitter grammar, it introduces a new line-oriented/regex-based extractor (graphify/extractors/pine.py) in the same style as the existing Pascal fallback, wired into the dispatch table, the extractor registry, and the recognized code extensions in detect.py. The extractor is intended to produce nodes for the script declaration, user functions/methods, user-defined types/enums, inputs, and imported libraries, along with contains/calls/references/uses/imports edges (with uses limited to a curated built-in whitelist and node IDs scoped per file). Supporting changes include README and CHANGELOG documentation updates and a new test suite covering the Pine extractor. Surface area touched: detect.py, extract.py, extractors/__init__.py, the new extractors/pine.py, associated tests, README, and CHANGELOG.

No blocking issues surfaced. 2 lower-confidence candidates did not survive cross-model review.

Analysis details — impact, health, verification

Impact & health

Graphify review

Impact — 2136 functions depend on the 962 functions this change touches.

Health — this change adds coupling hotspots:

  • new: extract_pine() — 16 callers, 8 callees

Verification — 2136 functions in the blast radius were not formally verified this run (proofs are advisory here).

Gate & verification

graphify gate

PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.

Advisory (not blocking):

  • verification_scope: 1950 function(s) in the blast radius were not formally verified this run

· 1 grounded finding(s) anchored inline below.

return start


def extract_pine(path: Path) -> dict:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regressionextract_pine()

fans out to 8 callees (efferent coupling); 16 callers depend on it (afferent coupling).

Grounded coupling-delta finding (deterministic), not an LLM guess.

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