mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-09-30 01:51:20 +00:00
add support for julia
This commit is contained in:
parent
39e9b40136
commit
56065efaa4
17 changed files with 633 additions and 0 deletions
|
|
@ -43,6 +43,7 @@ const EXTENSION_MAP: Record<SupportedLanguages, readonly string[]> = {
|
|||
[SupportedLanguages.Dart]: ['.dart'],
|
||||
[SupportedLanguages.Vue]: ['.vue'],
|
||||
[SupportedLanguages.Cobol]: ['.cbl', '.cob', '.cpy', '.cobol'],
|
||||
[SupportedLanguages.Julia]: ['.jl'],
|
||||
} satisfies Record<SupportedLanguages, readonly string[]>; // Ensure exhaustiveness
|
||||
|
||||
/** Pre-built reverse lookup: extension → language (built once at module load). */
|
||||
|
|
@ -111,6 +112,7 @@ const SYNTAX_MAP: Record<SupportedLanguages, string> = {
|
|||
[SupportedLanguages.Dart]: 'dart',
|
||||
[SupportedLanguages.Vue]: 'typescript',
|
||||
[SupportedLanguages.Cobol]: 'cobol',
|
||||
[SupportedLanguages.Julia]: 'julia',
|
||||
} satisfies Record<SupportedLanguages, string>; // Ensure exhaustiveness
|
||||
|
||||
/** Non-code file extensions → Prism-compatible syntax identifiers */
|
||||
|
|
|
|||
|
|
@ -22,4 +22,5 @@ export enum SupportedLanguages {
|
|||
Vue = 'vue',
|
||||
/** Standalone regex processor — no tree-sitter, no LanguageProvider. */
|
||||
Cobol = 'cobol',
|
||||
Julia = 'julia',
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ export const LanguageClassifications: Readonly<Record<SupportedLanguages, Langua
|
|||
[SupportedLanguages.Dart]: 'production',
|
||||
[SupportedLanguages.Vue]: 'experimental',
|
||||
[SupportedLanguages.Cobol]: 'experimental',
|
||||
[SupportedLanguages.Julia]: 'experimental',
|
||||
};
|
||||
|
||||
/** Convenience predicate: is this language gating Ring 4 retirement? */
|
||||
|
|
|
|||
20
gitnexus/package-lock.json
generated
20
gitnexus/package-lock.json
generated
|
|
@ -39,6 +39,7 @@
|
|||
"tree-sitter-go": "^0.23.0",
|
||||
"tree-sitter-java": "^0.23.5",
|
||||
"tree-sitter-javascript": "^0.23.0",
|
||||
"tree-sitter-julia": "^0.23.1",
|
||||
"tree-sitter-php": "^0.23.0",
|
||||
"tree-sitter-python": "0.23.4",
|
||||
"tree-sitter-ruby": "^0.23.1",
|
||||
|
|
@ -4908,6 +4909,25 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/tree-sitter-julia": {
|
||||
"version": "0.23.1",
|
||||
"resolved": "https://registry.npmmirror.com/tree-sitter-julia/-/tree-sitter-julia-0.23.1.tgz",
|
||||
"integrity": "sha512-3vShY0GIu8ajR6hXzE0pyUk6kkfg4pGx3Bfzm6lGmR9aC3fe+LgoBMlaFJ7JY+t0fNFccc77J8HVP67ukuDMxQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"node-addon-api": "^8.2.2",
|
||||
"node-gyp-build": "^4.8.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"tree-sitter": "^0.21.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"tree-sitter": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/tree-sitter-kotlin": {
|
||||
"version": "0.3.8",
|
||||
"resolved": "https://registry.npmjs.org/tree-sitter-kotlin/-/tree-sitter-kotlin-0.3.8.tgz",
|
||||
|
|
|
|||
|
|
@ -82,6 +82,7 @@
|
|||
"tree-sitter-go": "^0.23.0",
|
||||
"tree-sitter-java": "^0.23.5",
|
||||
"tree-sitter-javascript": "^0.23.0",
|
||||
"tree-sitter-julia": "^0.23.1",
|
||||
"tree-sitter-php": "^0.23.0",
|
||||
"tree-sitter-python": "0.23.4",
|
||||
"tree-sitter-ruby": "^0.23.1",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,6 @@
|
|||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { CallExtractionConfig } from '../../call-types.js';
|
||||
|
||||
export const juliaCallConfig: CallExtractionConfig = {
|
||||
language: SupportedLanguages.Julia,
|
||||
};
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { ClassExtractionConfig } from '../../class-types.js';
|
||||
|
||||
export const juliaClassConfig: ClassExtractionConfig = {
|
||||
language: SupportedLanguages.Julia,
|
||||
typeDeclarationNodes: ['struct_definition', 'abstract_definition'],
|
||||
ancestorScopeNodeTypes: ['struct_definition', 'abstract_definition', 'module_definition'],
|
||||
};
|
||||
|
|
@ -246,3 +246,31 @@ export const rubyExportChecker: ExportChecker = (_node, _name) => true;
|
|||
|
||||
/** Dart: public if no leading underscore (convention, same as Python). */
|
||||
export const dartExportChecker: ExportChecker = (_node, name) => !name.startsWith('_');
|
||||
|
||||
/**
|
||||
* Julia: walk ancestors looking for an export_statement.
|
||||
* Julia uses explicit `export` keyword to mark public symbols.
|
||||
* Symbols not in an export list are still accessible via qualified names
|
||||
* but are not exported — we treat un-exported symbols as non-public.
|
||||
* Fallback: if no export statement is found in the module, treat all
|
||||
* non-underscore names as public (common for scripts without explicit exports).
|
||||
*/
|
||||
export const juliaExportChecker: ExportChecker = (node, name) => {
|
||||
if (name.startsWith('_')) return false;
|
||||
let current: SyntaxNode | null = node;
|
||||
while (current) {
|
||||
if (current.type === 'module_definition') {
|
||||
// Check siblings inside module body for export_statement listing this name
|
||||
const body = current.childForFieldName('body') ?? current;
|
||||
for (let i = 0; i < body.childCount; i++) {
|
||||
const child = body.child(i);
|
||||
if (child?.type === 'export_statement' && child.text?.includes(name)) return true;
|
||||
}
|
||||
// Module found but name not in any export — treat as internal
|
||||
return false;
|
||||
}
|
||||
current = current.parent;
|
||||
}
|
||||
// Top-level (script) — treat all non-underscore names as public
|
||||
return true;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
// gitnexus/src/core/ingestion/field-extractors/configs/julia.ts
|
||||
// Verified against tree-sitter-julia grammar
|
||||
|
||||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { FieldExtractionConfig } from '../generic.js';
|
||||
|
||||
/**
|
||||
* Julia field extraction config.
|
||||
*
|
||||
* Julia struct fields appear as typed or untyped identifiers inside struct bodies:
|
||||
* struct Point
|
||||
* x::Float64
|
||||
* y::Float64
|
||||
* end
|
||||
*
|
||||
* In tree-sitter-julia the struct body contains field declarations.
|
||||
* Each field is either an identifier or a typed_parameter node.
|
||||
*/
|
||||
export const juliaConfig: FieldExtractionConfig = {
|
||||
language: SupportedLanguages.Julia,
|
||||
typeDeclarationNodes: ['struct_definition'],
|
||||
fieldNodeTypes: ['identifier', 'typed_parameter'],
|
||||
bodyNodeTypes: ['field_declaration_list'],
|
||||
defaultVisibility: 'public',
|
||||
|
||||
extractName(node) {
|
||||
if (node.type === 'identifier') return node.text;
|
||||
if (node.type === 'typed_parameter') {
|
||||
const nameNode = node.childForFieldName('name') ?? node.firstNamedChild;
|
||||
return nameNode?.type === 'identifier' ? nameNode.text : undefined;
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
extractType(node) {
|
||||
if (node.type === 'typed_parameter') {
|
||||
const typeNode = node.childForFieldName('type') ?? node.namedChild(1);
|
||||
return typeNode?.text?.trim();
|
||||
}
|
||||
return undefined;
|
||||
},
|
||||
|
||||
extractVisibility(_node) {
|
||||
// Julia struct fields are always public (no access modifiers)
|
||||
return 'public';
|
||||
},
|
||||
|
||||
isStatic(_node) {
|
||||
return false;
|
||||
},
|
||||
|
||||
isReadonly(_node) {
|
||||
// Immutable structs (struct, not mutable struct) have readonly fields.
|
||||
// The mutable keyword is on the parent struct_definition, not on individual fields.
|
||||
// We return false here; the struct-level immutability is handled elsewhere.
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* Julia import resolution config.
|
||||
*
|
||||
* Julia supports two import forms:
|
||||
* import Foo → namespace import (Foo.bar access)
|
||||
* using Foo → wildcard import (all exported names in scope)
|
||||
* using Foo: bar → named import
|
||||
* import Foo: bar → named import
|
||||
*
|
||||
* Within a package, `include("file.jl")` is used for relative file inclusion.
|
||||
* We handle include() as a special import form resolved via suffix matching.
|
||||
*/
|
||||
|
||||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { ImportResolutionConfig, ImportResolverStrategy } from '../types.js';
|
||||
import { suffixResolve } from '../utils.js';
|
||||
import { createStandardStrategy } from '../standard.js';
|
||||
|
||||
/** Julia include("file.jl") resolution strategy. */
|
||||
export const juliaIncludeStrategy: ImportResolverStrategy = (rawImportPath, _filePath, ctx) => {
|
||||
// Strip quotes and resolve as relative .jl path
|
||||
const cleaned = rawImportPath.replace(/^["']|["']$/g, '');
|
||||
if (!cleaned.endsWith('.jl')) return null;
|
||||
|
||||
const pathParts = cleaned.replace(/^\.\//, '').split('/').filter(Boolean);
|
||||
const resolved = suffixResolve(pathParts, ctx.normalizedFileList, ctx.allFileList, ctx.index);
|
||||
return resolved ? { kind: 'files', files: [resolved] } : null;
|
||||
};
|
||||
|
||||
export const juliaImportConfig: ImportResolutionConfig = {
|
||||
language: SupportedLanguages.Julia,
|
||||
strategies: [juliaIncludeStrategy, createStandardStrategy(SupportedLanguages.Julia)],
|
||||
};
|
||||
|
|
@ -25,6 +25,7 @@ import { swiftProvider } from './swift.js';
|
|||
import { dartProvider } from './dart.js';
|
||||
import { vueProvider } from './vue.js';
|
||||
import { cobolProvider } from './cobol.js';
|
||||
import { juliaProvider } from './julia.js';
|
||||
|
||||
export const providers = {
|
||||
[SupportedLanguages.JavaScript]: javascriptProvider,
|
||||
|
|
@ -43,6 +44,7 @@ export const providers = {
|
|||
[SupportedLanguages.Dart]: dartProvider,
|
||||
[SupportedLanguages.Vue]: vueProvider,
|
||||
[SupportedLanguages.Cobol]: cobolProvider,
|
||||
[SupportedLanguages.Julia]: juliaProvider,
|
||||
} satisfies Record<SupportedLanguages, LanguageProvider>;
|
||||
|
||||
/** Get provider by language enum (always succeeds for SupportedLanguages). */
|
||||
|
|
|
|||
108
gitnexus/src/core/ingestion/languages/julia.ts
Normal file
108
gitnexus/src/core/ingestion/languages/julia.ts
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/**
|
||||
* Julia language provider.
|
||||
*
|
||||
* Julia uses wildcard-leaf import semantics: `using Foo` brings all exported
|
||||
* names into scope in one hop. `import Foo` uses namespace access (Foo.bar).
|
||||
* Within packages, `include("file.jl")` handles relative file inclusion.
|
||||
*
|
||||
* Key Julia traits:
|
||||
* - importSemantics: 'wildcard-leaf' (using Foo / import Foo)
|
||||
* - No mandatory visibility modifiers — export list controls public API
|
||||
* - Multiple dispatch: functions specialize on argument types
|
||||
* - Structs are class-like (no methods in body; methods defined externally)
|
||||
*/
|
||||
|
||||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import { createClassExtractor } from '../class-extractors/generic.js';
|
||||
import { juliaClassConfig } from '../class-extractors/configs/julia.js';
|
||||
import { defineLanguage } from '../language-provider.js';
|
||||
import { typeConfig as juliaTypeConfig } from '../type-extractors/julia.js';
|
||||
import { juliaExportChecker } from '../export-detection.js';
|
||||
import { createImportResolver } from '../import-resolvers/resolver-factory.js';
|
||||
import { juliaImportConfig } from '../import-resolvers/configs/julia.js';
|
||||
import { JULIA_QUERIES } from '../tree-sitter-queries.js';
|
||||
import { createFieldExtractor } from '../field-extractors/generic.js';
|
||||
import { juliaConfig as juliaFieldConfig } from '../field-extractors/configs/julia.js';
|
||||
import { createMethodExtractor } from '../method-extractors/generic.js';
|
||||
import { juliaMethodConfig } from '../method-extractors/configs/julia.js';
|
||||
import { createVariableExtractor } from '../variable-extractors/generic.js';
|
||||
import { juliaVariableConfig } from '../variable-extractors/configs/julia.js';
|
||||
import { createCallExtractor } from '../call-extractors/generic.js';
|
||||
import { juliaCallConfig } from '../call-extractors/configs/julia.js';
|
||||
import { createHeritageExtractor } from '../heritage-extractors/generic.js';
|
||||
|
||||
const BUILT_INS: ReadonlySet<string> = new Set([
|
||||
'print',
|
||||
'println',
|
||||
'typeof',
|
||||
'length',
|
||||
'size',
|
||||
'push!',
|
||||
'pop!',
|
||||
'append!',
|
||||
'map',
|
||||
'filter',
|
||||
'reduce',
|
||||
'sum',
|
||||
'prod',
|
||||
'minimum',
|
||||
'maximum',
|
||||
'sort',
|
||||
'sort!',
|
||||
'collect',
|
||||
'enumerate',
|
||||
'zip',
|
||||
'keys',
|
||||
'values',
|
||||
'haskey',
|
||||
'get',
|
||||
'getindex',
|
||||
'setindex!',
|
||||
'error',
|
||||
'throw',
|
||||
'isa',
|
||||
'isdefined',
|
||||
'isnothing',
|
||||
'ismissing',
|
||||
'eltype',
|
||||
'zeros',
|
||||
'ones',
|
||||
'rand',
|
||||
'randn',
|
||||
'copy',
|
||||
'deepcopy',
|
||||
'string',
|
||||
'Symbol',
|
||||
'parse',
|
||||
'convert',
|
||||
'promote',
|
||||
'similar',
|
||||
'fill',
|
||||
'repeat',
|
||||
'reshape',
|
||||
'hcat',
|
||||
'vcat',
|
||||
'cat',
|
||||
'tuple',
|
||||
'nameof',
|
||||
'supertype',
|
||||
'subtypes',
|
||||
]);
|
||||
|
||||
export const juliaProvider = defineLanguage({
|
||||
id: SupportedLanguages.Julia,
|
||||
extensions: ['.jl'],
|
||||
entryPointPatterns: [/^main$/, /^run$/, /^execute$/],
|
||||
treeSitterQueries: JULIA_QUERIES,
|
||||
typeConfig: juliaTypeConfig,
|
||||
exportChecker: juliaExportChecker,
|
||||
importResolver: createImportResolver(juliaImportConfig),
|
||||
importSemantics: 'wildcard-leaf',
|
||||
callExtractor: createCallExtractor(juliaCallConfig),
|
||||
fieldExtractor: createFieldExtractor(juliaFieldConfig),
|
||||
methodExtractor: createMethodExtractor(juliaMethodConfig),
|
||||
variableExtractor: createVariableExtractor(juliaVariableConfig),
|
||||
classExtractor: createClassExtractor(juliaClassConfig),
|
||||
heritageExtractor: createHeritageExtractor(SupportedLanguages.Julia),
|
||||
builtInNames: BUILT_INS,
|
||||
});
|
||||
119
gitnexus/src/core/ingestion/method-extractors/configs/julia.ts
Normal file
119
gitnexus/src/core/ingestion/method-extractors/configs/julia.ts
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
// gitnexus/src/core/ingestion/method-extractors/configs/julia.ts
|
||||
// Verified against tree-sitter-julia grammar
|
||||
|
||||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { MethodExtractionConfig, ParameterInfo } from '../../method-types.js';
|
||||
import type { SyntaxNode } from '../../utils/ast-helpers.js';
|
||||
|
||||
/**
|
||||
* Extract parameters from a Julia function definition.
|
||||
*
|
||||
* Julia parameters can be:
|
||||
* - Plain identifier: `x`
|
||||
* - Typed: `x::Int`
|
||||
* - With default: `x=1`
|
||||
* - Typed with default: `x::Int=1`
|
||||
* - Splat: `args...`
|
||||
* - Keyword: `; kw=default`
|
||||
*/
|
||||
function extractJuliaParameters(node: SyntaxNode): ParameterInfo[] {
|
||||
const paramList = node.childForFieldName('parameters');
|
||||
if (!paramList) return [];
|
||||
|
||||
const params: ParameterInfo[] = [];
|
||||
|
||||
for (let i = 0; i < paramList.namedChildCount; i++) {
|
||||
const param = paramList.namedChild(i);
|
||||
if (!param) continue;
|
||||
|
||||
switch (param.type) {
|
||||
case 'identifier': {
|
||||
params.push({ name: param.text, type: null, rawType: null, isOptional: false, isVariadic: false });
|
||||
break;
|
||||
}
|
||||
case 'typed_parameter': {
|
||||
const nameNode = param.childForFieldName('name') ?? param.firstNamedChild;
|
||||
const typeNode = param.childForFieldName('type') ?? param.namedChild(1);
|
||||
if (nameNode) {
|
||||
params.push({
|
||||
name: nameNode.text,
|
||||
type: typeNode?.text?.trim() ?? null,
|
||||
rawType: typeNode?.text?.trim() ?? null,
|
||||
isOptional: false,
|
||||
isVariadic: false,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'optional_parameter': {
|
||||
const nameNode = param.childForFieldName('name') ?? param.firstNamedChild;
|
||||
const typeNode = param.childForFieldName('type');
|
||||
if (nameNode) {
|
||||
params.push({
|
||||
name: nameNode.text,
|
||||
type: typeNode?.text?.trim() ?? null,
|
||||
rawType: typeNode?.text?.trim() ?? null,
|
||||
isOptional: true,
|
||||
isVariadic: false,
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'splat_parameter': {
|
||||
const nameNode = param.firstNamedChild;
|
||||
if (nameNode) {
|
||||
params.push({ name: nameNode.text, type: null, rawType: null, isOptional: false, isVariadic: true });
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
|
||||
function extractJuliaReturnType(node: SyntaxNode): string | undefined {
|
||||
const retType = node.childForFieldName('return_type');
|
||||
return retType?.text?.trim();
|
||||
}
|
||||
|
||||
export const juliaMethodConfig: MethodExtractionConfig = {
|
||||
language: SupportedLanguages.Julia,
|
||||
// Julia methods are defined outside structs — no struct body method extraction
|
||||
typeDeclarationNodes: ['struct_definition'],
|
||||
methodNodeTypes: ['function_definition', 'short_function_definition'],
|
||||
bodyNodeTypes: ['field_declaration_list'],
|
||||
|
||||
extractName(node) {
|
||||
const nameNode = node.childForFieldName('name');
|
||||
return nameNode?.text;
|
||||
},
|
||||
|
||||
extractReturnType: extractJuliaReturnType,
|
||||
extractParameters: extractJuliaParameters,
|
||||
|
||||
extractVisibility(_node) {
|
||||
return 'public';
|
||||
},
|
||||
|
||||
isStatic(_node) {
|
||||
return false;
|
||||
},
|
||||
|
||||
isAbstract(_node) {
|
||||
return false;
|
||||
},
|
||||
|
||||
isFinal(_node) {
|
||||
return false;
|
||||
},
|
||||
|
||||
extractAnnotations(_node) {
|
||||
return [];
|
||||
},
|
||||
|
||||
isAsync(_node) {
|
||||
return false;
|
||||
},
|
||||
};
|
||||
|
|
@ -1512,6 +1512,61 @@ export const DART_QUERIES = `
|
|||
(type_identifier) @heritage.trait))) @heritage
|
||||
`;
|
||||
|
||||
// Julia queries - works with tree-sitter-julia
|
||||
export const JULIA_QUERIES = `
|
||||
; ── Structs (class-like types) ───────────────────────────────────────────────
|
||||
(struct_definition
|
||||
name: (identifier) @name) @definition.class
|
||||
|
||||
; ── Abstract types ────────────────────────────────────────────────────────────
|
||||
(abstract_definition
|
||||
name: (identifier) @name) @definition.class
|
||||
|
||||
; ── Modules ───────────────────────────────────────────────────────────────────
|
||||
(module_definition
|
||||
name: (identifier) @name) @definition.module
|
||||
|
||||
; ── Function definitions ──────────────────────────────────────────────────────
|
||||
(function_definition
|
||||
name: (identifier) @name) @definition.function
|
||||
|
||||
; ── Short-form function definitions: f(x) = expr ─────────────────────────────
|
||||
(short_function_definition
|
||||
name: (identifier) @name) @definition.function
|
||||
|
||||
; ── Macro definitions ─────────────────────────────────────────────────────────
|
||||
(macro_definition
|
||||
name: (identifier) @name) @definition.function
|
||||
|
||||
; ── Import statements ─────────────────────────────────────────────────────────
|
||||
(import_statement) @import
|
||||
|
||||
(using_statement) @import
|
||||
|
||||
; ── Call expressions ──────────────────────────────────────────────────────────
|
||||
(call_expression
|
||||
(identifier) @call.name) @call
|
||||
|
||||
(call_expression
|
||||
(field_expression
|
||||
(identifier) @call.name)) @call
|
||||
|
||||
; ── Heritage: struct Foo <: Bar ───────────────────────────────────────────────
|
||||
(struct_definition
|
||||
name: (identifier) @heritage.class
|
||||
supertype: (type_clause
|
||||
(identifier) @heritage.extends)) @heritage
|
||||
|
||||
(abstract_definition
|
||||
name: (identifier) @heritage.class
|
||||
supertype: (type_clause
|
||||
(identifier) @heritage.extends)) @heritage
|
||||
|
||||
; ── Variable assignments at module scope ──────────────────────────────────────
|
||||
(assignment
|
||||
left: (identifier) @name) @definition.variable
|
||||
`;
|
||||
|
||||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
|
||||
export const LANGUAGE_QUERIES: Record<SupportedLanguages, string> = {
|
||||
|
|
@ -1531,4 +1586,5 @@ export const LANGUAGE_QUERIES: Record<SupportedLanguages, string> = {
|
|||
[SupportedLanguages.Dart]: DART_QUERIES,
|
||||
[SupportedLanguages.Vue]: TYPESCRIPT_QUERIES, // Vue <script> blocks are parsed as TypeScript
|
||||
[SupportedLanguages.Cobol]: '', // Standalone regex processor — no tree-sitter queries
|
||||
[SupportedLanguages.Julia]: JULIA_QUERIES,
|
||||
};
|
||||
|
|
|
|||
123
gitnexus/src/core/ingestion/type-extractors/julia.ts
Normal file
123
gitnexus/src/core/ingestion/type-extractors/julia.ts
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
import type {
|
||||
LanguageTypeConfig,
|
||||
ParameterExtractor,
|
||||
TypeBindingExtractor,
|
||||
InitializerExtractor,
|
||||
ConstructorBindingScanner,
|
||||
PendingAssignmentExtractor,
|
||||
} from './types.js';
|
||||
import { extractSimpleTypeName, extractVarName } from './shared.js';
|
||||
import type { SyntaxNode } from '../utils/ast-helpers.js';
|
||||
|
||||
/**
|
||||
* Julia type extractor.
|
||||
*
|
||||
* Julia uses `::` for type annotations:
|
||||
* x::Int
|
||||
* function foo(x::Int, y::String)::Bool
|
||||
*
|
||||
* Constructor inference: `obj = MyType(args...)` binds `obj` → `MyType`.
|
||||
*/
|
||||
|
||||
const DECLARATION_NODE_TYPES: ReadonlySet<string> = new Set([
|
||||
'function_definition',
|
||||
'short_function_definition',
|
||||
'assignment',
|
||||
]);
|
||||
|
||||
/**
|
||||
* Extract type annotations from function parameter lists into env.
|
||||
* Julia: `function foo(x::Int, y::String)` → env.set("x", "Int")
|
||||
*/
|
||||
const extractDeclaration: TypeBindingExtractor = (node: SyntaxNode, env: Map<string, string>): void => {
|
||||
if (node.type !== 'function_definition' && node.type !== 'short_function_definition') return;
|
||||
|
||||
const paramList = node.childForFieldName('parameters');
|
||||
if (!paramList) return;
|
||||
|
||||
for (let i = 0; i < paramList.namedChildCount; i++) {
|
||||
const param = paramList.namedChild(i);
|
||||
if (!param) continue;
|
||||
|
||||
if (param.type === 'typed_parameter') {
|
||||
const nameNode = param.childForFieldName('name') ?? param.firstNamedChild;
|
||||
const typeNode = param.childForFieldName('type') ?? param.namedChild(1);
|
||||
if (nameNode && typeNode) {
|
||||
const varName = extractVarName(nameNode);
|
||||
const typeName = extractSimpleTypeName(typeNode) ?? typeNode.text?.trim();
|
||||
if (varName && typeName) env.set(varName, typeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const extractParameter: ParameterExtractor = (_node: SyntaxNode, _env: Map<string, string>): void => {
|
||||
// Parameter types are handled by extractDeclaration above
|
||||
};
|
||||
|
||||
/**
|
||||
* Julia constructor inference: obj = MyType(args...)
|
||||
* MyType() is a constructor call when MyType matches a known type name.
|
||||
*/
|
||||
const extractInitializer: InitializerExtractor = (node, env, classNames): void => {
|
||||
if (node.type !== 'assignment') return;
|
||||
const left = node.childForFieldName('left');
|
||||
const right = node.childForFieldName('right');
|
||||
if (!left || !right) return;
|
||||
|
||||
const varName = extractVarName(left);
|
||||
if (!varName || env.has(varName)) return;
|
||||
|
||||
// call_expression where the callee is an identifier matching a known type
|
||||
if (right.type === 'call_expression') {
|
||||
const callee = right.firstNamedChild ?? right.child(0);
|
||||
if (callee?.type === 'identifier' && classNames.has(callee.text)) {
|
||||
env.set(varName, callee.text);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const scanConstructorBinding: ConstructorBindingScanner = (node) => {
|
||||
if (node.type !== 'assignment') return undefined;
|
||||
const left = node.childForFieldName('left');
|
||||
const right = node.childForFieldName('right');
|
||||
if (!left || !right) return undefined;
|
||||
|
||||
const varName = extractVarName(left);
|
||||
if (!varName) return undefined;
|
||||
|
||||
if (right.type === 'call_expression') {
|
||||
const callee = right.firstNamedChild ?? right.child(0);
|
||||
if (callee?.type === 'identifier') {
|
||||
return { varName, calleeName: callee.text };
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
const extractPendingAssignment: PendingAssignmentExtractor = (node, scopeEnv) => {
|
||||
if (node.type !== 'assignment') return undefined;
|
||||
const lhsNode = node.childForFieldName('left');
|
||||
if (!lhsNode || lhsNode.type !== 'identifier') return undefined;
|
||||
const varName = lhsNode.text;
|
||||
if (scopeEnv.has(varName)) return undefined;
|
||||
const rhsNode = node.childForFieldName('right');
|
||||
if (!rhsNode) return undefined;
|
||||
if (rhsNode.type === 'identifier') return { kind: 'copy', lhs: varName, rhs: rhsNode.text };
|
||||
if (rhsNode.type === 'call_expression') {
|
||||
const callee = rhsNode.firstNamedChild ?? rhsNode.child(0);
|
||||
if (callee?.type === 'identifier') {
|
||||
return { kind: 'callResult', lhs: varName, callee: callee.text };
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
};
|
||||
|
||||
export const typeConfig: LanguageTypeConfig = {
|
||||
declarationNodeTypes: DECLARATION_NODE_TYPES,
|
||||
extractDeclaration,
|
||||
extractParameter,
|
||||
extractInitializer,
|
||||
scanConstructorBinding,
|
||||
extractPendingAssignment,
|
||||
};
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
// gitnexus/src/core/ingestion/variable-extractors/configs/julia.ts
|
||||
|
||||
import { SupportedLanguages } from 'gitnexus-shared';
|
||||
import type { VariableExtractionConfig, VariableVisibility } from '../../variable-types.js';
|
||||
import type { SyntaxNode } from '../../utils/ast-helpers.js';
|
||||
|
||||
function extractNameFromJulia(node: SyntaxNode): string | undefined {
|
||||
// assignment: x = value
|
||||
if (node.type === 'assignment') {
|
||||
const left = node.childForFieldName('left');
|
||||
if (left?.type === 'identifier') return left.text;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function extractTypeFromJulia(node: SyntaxNode): string | undefined {
|
||||
// Julia module-level assignments rarely carry inline type annotations.
|
||||
// Typed declarations would be `x::Int = 5` via typed_parameter, but that's uncommon at module scope.
|
||||
if (node.type === 'assignment') {
|
||||
const left = node.childForFieldName('left');
|
||||
if (left?.type === 'typed_parameter') {
|
||||
const typeNode = left.childForFieldName('type') ?? left.namedChild(1);
|
||||
return typeNode?.text?.trim();
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function extractVisFromJulia(node: SyntaxNode): VariableVisibility {
|
||||
const name = extractNameFromJulia(node);
|
||||
if (!name) return 'public';
|
||||
if (name.startsWith('_')) return 'private';
|
||||
return 'public';
|
||||
}
|
||||
|
||||
export const juliaVariableConfig: VariableExtractionConfig = {
|
||||
language: SupportedLanguages.Julia,
|
||||
constNodeTypes: [],
|
||||
staticNodeTypes: [],
|
||||
variableNodeTypes: ['assignment'],
|
||||
|
||||
extractName: extractNameFromJulia,
|
||||
extractType: extractTypeFromJulia,
|
||||
extractVisibility: extractVisFromJulia,
|
||||
|
||||
isConst(node) {
|
||||
// Julia convention: SCREAMING_SNAKE_CASE names are constants
|
||||
const name = extractNameFromJulia(node);
|
||||
if (!name) return false;
|
||||
return name === name.toUpperCase() && /^[A-Z][A-Z0-9_]*$/.test(name);
|
||||
},
|
||||
|
||||
isStatic(_node) {
|
||||
return false;
|
||||
},
|
||||
|
||||
isMutable(node) {
|
||||
const name = extractNameFromJulia(node);
|
||||
if (!name) return true;
|
||||
return !(name === name.toUpperCase() && /^[A-Z][A-Z0-9_]*$/.test(name));
|
||||
},
|
||||
};
|
||||
|
|
@ -161,6 +161,11 @@ const SOURCES: Record<string, GrammarSource> = {
|
|||
'Kotlin parsing disabled: `tree-sitter-kotlin` is an optionalDependency ' +
|
||||
'and is not installed (or its native binding failed to build).',
|
||||
},
|
||||
[SupportedLanguages.Julia]: {
|
||||
load: () => _require('tree-sitter-julia'),
|
||||
unavailableNote:
|
||||
'Julia parsing requires `tree-sitter-julia`. Check that the package and its native binding installed cleanly (`npm ci`).',
|
||||
},
|
||||
};
|
||||
|
||||
type LoadResult =
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue