mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-10-30 06:42:43 +00:00
fix(r): create an R6 field Property for every non-function value kind
The R6 public/private field queries listed only some expression kinds, so
`public = list(x = NA)` (also Inf, NaN, complex, if/for/while/repeat, a$b,
pkg::v, v[1], v[[1]], {..}, (..)) produced no Property although
RFieldExtractor accepts any value that is not a function_definition.
Reproduction: new test/unit/r-r6-field-values.test.ts failed 46 of 76 cases
on the unfixed query (expected ['x'], received []). The query now enumerates
every named grammar expression kind except function_definition, shared by the
namespaced and bare R6Class patterns.
This commit is contained in:
parent
f114d5e754
commit
3b8682f23e
2 changed files with 98 additions and 2 deletions
|
|
@ -3051,6 +3051,19 @@ export const ZIG_QUERIES = `
|
|||
function: (field_expression
|
||||
member: (identifier) @call.name)) @call
|
||||
`;
|
||||
/**
|
||||
* Every named expression kind the R grammar allows as an argument value, except
|
||||
* `function_definition` (an R6 entry holding a function is a method, not a field).
|
||||
* Mirrors RFieldExtractor, which accepts any non-function value, so the query and
|
||||
* the extractor agree on what a field is.
|
||||
*/
|
||||
const R6_FIELD_VALUE = `[
|
||||
(binary_operator) (braced_expression) (break) (call) (complex) (dot_dot_i) (dots)
|
||||
(extract_operator) (false) (float) (for_statement) (identifier) (if_statement) (inf)
|
||||
(integer) (na) (namespace_operator) (nan) (next) (null) (parenthesized_expression)
|
||||
(repeat_statement) (return) (string) (subset) (subset2) (true) (unary_operator)
|
||||
(while_statement)
|
||||
]`;
|
||||
export const R_QUERIES = `
|
||||
; ── Functions (name <- function(...) or name = function(...)) ────────────────
|
||||
(binary_operator
|
||||
|
|
@ -3199,7 +3212,7 @@ export const R_QUERIES = `
|
|||
arguments: (arguments
|
||||
(argument
|
||||
name: (identifier) @name
|
||||
value: [(string) (integer) (float) (true) (false) (null) (identifier) (call) (unary_operator) (binary_operator)] @_val) @definition.property))))))
|
||||
value: ${R6_FIELD_VALUE} @_val) @definition.property))))))
|
||||
|
||||
; ── R6 Fields via bare call ──────────────────────────────────────────────────
|
||||
(binary_operator
|
||||
|
|
@ -3217,7 +3230,7 @@ export const R_QUERIES = `
|
|||
arguments: (arguments
|
||||
(argument
|
||||
name: (identifier) @name
|
||||
value: [(string) (integer) (float) (true) (false) (null) (identifier) (call) (unary_operator) (binary_operator)] @_val2) @definition.property))))))
|
||||
value: ${R6_FIELD_VALUE} @_val2) @definition.property))))))
|
||||
|
||||
; ── S4 slots / representation ────────────────────────────────────────────────
|
||||
(call
|
||||
|
|
|
|||
83
gitnexus/test/unit/r-r6-field-values.test.ts
Normal file
83
gitnexus/test/unit/r-r6-field-values.test.ts
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/**
|
||||
* R6 `public =` / `private =` list() entries become Property definitions for every
|
||||
* value that is not a function (functions are methods). The query used to enumerate
|
||||
* only some of the grammar's expression kinds, so `list(x = NA)` or
|
||||
* `list(x = if (ready) 1 else 2)` created no Property although RFieldExtractor
|
||||
* accepts any non-function value. These tests run the query itself, for both the
|
||||
* `R6::R6Class` and the bare `R6Class` forms.
|
||||
*/
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import Parser from 'tree-sitter';
|
||||
import R from '@eagleoutice/tree-sitter-r';
|
||||
import { R_QUERIES } from '../../src/core/ingestion/tree-sitter-queries.js';
|
||||
|
||||
const language = R as Parameters<Parser['setLanguage']>[0];
|
||||
const parser = new Parser();
|
||||
parser.setLanguage(language);
|
||||
const query = new Parser.Query(language, R_QUERIES);
|
||||
|
||||
/** Names of every `definition.property` the query captures, in source order. */
|
||||
function properties(source: string): string[] {
|
||||
const tree = parser.parse(source);
|
||||
const out: string[] = [];
|
||||
for (const match of query.matches(tree.rootNode)) {
|
||||
const def = match.captures.find((c) => c.name === 'definition.property');
|
||||
const name = match.captures.find((c) => c.name === 'name');
|
||||
if (def && name) out.push(name.node.text);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const FORMS: Record<string, (body: string) => string> = {
|
||||
bare: (body) => `X <- R6Class("X", public = list(${body}))`,
|
||||
namespaced: (body) => `X <- R6::R6Class("X", public = list(${body}))`,
|
||||
private: (body) => `X <- R6Class("X", private = list(${body}))`,
|
||||
};
|
||||
|
||||
// One value per grammar expression kind that can start an R expression, except
|
||||
// function_definition (a method, not a field).
|
||||
const VALUES: Record<string, string> = {
|
||||
string: '"a"',
|
||||
integer: '1L',
|
||||
float: '1.5',
|
||||
complex: '2i',
|
||||
true: 'TRUE',
|
||||
false: 'FALSE',
|
||||
null: 'NULL',
|
||||
na: 'NA',
|
||||
na_character: 'NA_character_',
|
||||
inf: 'Inf',
|
||||
nan: 'NaN',
|
||||
identifier: 'y',
|
||||
call: 'g()',
|
||||
unary_operator: '-1',
|
||||
binary_operator: '1 + 2',
|
||||
if_statement: 'if (ready) 1 else 2',
|
||||
parenthesized_expression: '(1)',
|
||||
extract_operator: 'a$b',
|
||||
namespace_operator: 'pkg::value',
|
||||
subset: 'v[1]',
|
||||
subset2: 'v[[1]]',
|
||||
braced_expression: '{ 1 }',
|
||||
for_statement: 'for (i in 1:2) i',
|
||||
while_statement: 'while (FALSE) 1',
|
||||
repeat_statement: 'repeat break',
|
||||
};
|
||||
|
||||
describe('R6 field values of every expression kind create a Property', () => {
|
||||
for (const [formName, wrap] of Object.entries(FORMS)) {
|
||||
for (const [kind, value] of Object.entries(VALUES)) {
|
||||
it(`${formName}: ${kind} (${value})`, () => {
|
||||
expect(properties(wrap(`x = ${value}`))).toEqual(['x']);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
it('still treats function values as methods, not fields', () => {
|
||||
expect(properties('X <- R6Class("X", public = list(m = function() 1, f = 2))')).toEqual(['f']);
|
||||
expect(properties('X <- R6Class("X", public = list(m = \\() 1, f = 2))')).toEqual(['f']);
|
||||
expect(properties('X <- R6::R6Class("X", public = list(m = function() 1, f = NA))')).toEqual([
|
||||
'f',
|
||||
]);
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Reference in a new issue