-
Notifications
You must be signed in to change notification settings - Fork 22
Expand file tree
/
Copy pathjulia.test.ts
More file actions
167 lines (149 loc) · 6.37 KB
/
Copy pathjulia.test.ts
File metadata and controls
167 lines (149 loc) · 6.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import { beforeAll, describe, expect, it } from 'vitest';
import { createParsers, extractJuliaSymbols } from '../../src/domain/parser.js';
describe('Julia parser', () => {
let parsers: any;
beforeAll(async () => {
parsers = await createParsers();
});
function parseJulia(code) {
const parser = parsers.get('julia');
if (!parser) throw new Error('Julia parser not available');
const tree = parser.parse(code);
return extractJuliaSymbols(tree, 'test.jl');
}
it('extracts function definitions', () => {
const symbols = parseJulia(`function greet(name)
println("Hello $name")
end`);
expect(symbols.definitions).toContainEqual(expect.objectContaining({ kind: 'function' }));
});
it('extracts short function definitions', () => {
const symbols = parseJulia(`add(x, y) = x + y`);
expect(symbols.definitions).toContainEqual(expect.objectContaining({ kind: 'function' }));
});
it('extracts struct definitions', () => {
const symbols = parseJulia(`struct Point
x::Float64
y::Float64
end`);
expect(symbols.definitions).toContainEqual(expect.objectContaining({ kind: 'struct' }));
});
it('extracts module definitions', () => {
const symbols = parseJulia(`module MyModule
export greet
end`);
expect(symbols.definitions).toContainEqual(
expect.objectContaining({ name: 'MyModule', kind: 'module' }),
);
});
it('extracts import/using statements', () => {
const symbols = parseJulia(`using LinearAlgebra
import Base: show`);
expect(symbols.imports.length).toBeGreaterThanOrEqual(1);
// `import Base: show` must record `source: 'Base'` and `names: ['show']`,
// not the previously-broken `source: 'Base: show', names: ['Base: show']`.
const selected = symbols.imports.find((imp) => imp.source === 'Base');
expect(selected).toBeDefined();
expect(selected?.names).toContain('show');
expect(selected?.names).not.toContain('Base');
});
it('extracts function calls', () => {
const symbols = parseJulia(`println("Hello")
push!(arr, 1)`);
expect(symbols.calls.length).toBeGreaterThanOrEqual(1);
});
it('extracts parameterized struct base name', () => {
// Parameterized struct names (e.g. `Vec{T}`) must record the base
// identifier — not be silently dropped or include type-parameter text.
const symbols = parseJulia(`struct Vec{T} <: AbstractArray{T,1}
data::Vector{T}
end`);
const names = symbols.definitions.map((d) => d.name);
expect(names).toContain('Vec');
expect(names.every((n) => !n.includes('{') && !n.includes('<'))).toBe(true);
expect(symbols.classes).toHaveLength(1);
expect(symbols.classes[0]).toMatchObject({ name: 'Vec', extends: 'AbstractArray' });
});
it('extracts non-parameterized struct inheritance', () => {
// Simple `struct Name <: Super` must still record both the definition
// and the `extends` relationship — the grammar wraps it in a
// `binary_expression` just like the parameterized form.
const symbols = parseJulia(`struct Point <: AbstractPoint
x::Float64
y::Float64
end`);
const names = symbols.definitions.map((d) => d.name);
expect(names).toContain('Point');
expect(symbols.classes).toHaveLength(1);
expect(symbols.classes[0]).toMatchObject({ name: 'Point', extends: 'AbstractPoint' });
});
it('qualified short-form method does not double-prefix', () => {
// `Foo.bar(x, y) = x + y` inside `module Outer` must record `Foo.bar`,
// not `Outer.Foo.bar` — the scoped_identifier already carries the qualifier.
const symbols = parseJulia(`module Outer
Foo.bar(x, y) = x + y
end`);
const names = symbols.definitions.map((d) => d.name);
expect(names).toContain('Foo.bar');
expect(names).not.toContain('Outer.Foo.bar');
});
it('qualified function def does not double-prefix', () => {
// `function Base.show(io, x) ... end` inside `module Foo` must record
// `Base.show`, not `Foo.Base.show`.
const symbols = parseJulia(`module Foo
function Base.show(io, x)
println(io, x)
end
end`);
const names = symbols.definitions.map((d) => d.name);
expect(names).toContain('Base.show');
expect(names).not.toContain('Foo.Base.show');
});
it('extracts abstract type', () => {
const symbols = parseJulia(`abstract type AbstractShape end`);
const abs = symbols.definitions.find((d) => d.name === 'AbstractShape');
expect(abs).toBeDefined();
expect(abs).toMatchObject({ kind: 'type' });
});
it('extracts parameterized abstract type base name', () => {
// Parameterized generics with a supertype must record only the base
// identifier — never the raw `Name{T} <: Super{T,1}` text.
const symbols = parseJulia(`abstract type AbstractVector{T} <: AbstractArray{T,1} end`);
const names = symbols.definitions.map((d) => d.name);
expect(names).toContain('AbstractVector');
expect(names.every((n) => !n.includes('{') && !n.includes('<'))).toBe(true);
});
it('extracts macro definitions with correct name', () => {
// `findChild(node, 'identifier')` would resolve to the body's `x` here,
// recording the macro as `@x` instead of `@mymac`.
const symbols = parseJulia(`macro mymac(x)
x
end`);
const names = symbols.definitions.map((d) => d.name);
expect(names).toContain('@mymac');
expect(names).not.toContain('@x');
});
it('does not record function signature as call', () => {
// The signature's `call_expression` lives inside a `signature` node — a
// naive `parent.type === 'function_definition'` guard misses it and
// records `greet` as both a definition and a call.
const symbols = parseJulia(`function greet(name)
println(name)
end`);
const callNames = symbols.calls.map((c) => c.name);
expect(callNames).not.toContain('greet');
expect(callNames).toContain('println');
});
it('selected_import handles qualified module', () => {
// `import Foo.Bar: baz` — module is a scoped_identifier. The import
// must record `Foo.Bar` as the source and `baz` as the imported name,
// not the malformed `source="baz", names=["baz"]`.
const symbols = parseJulia(`import LinearAlgebra.BLAS: gemm`);
expect(symbols.imports).toHaveLength(1);
expect(symbols.imports[0]).toMatchObject({
source: 'LinearAlgebra.BLAS',
});
expect(symbols.imports[0].names).toContain('gemm');
expect(symbols.imports[0].names).not.toContain('LinearAlgebra.BLAS');
});
});