11import { Effect } from 'effect'
2- import {
3- Parser ,
4- type Node as TSNode ,
5- type Tree as TSTree ,
6- } from 'web-tree-sitter'
2+ import { Parser } from 'web-tree-sitter'
73import type { Language , ParseError , ParseResult } from '../types'
84import {
95 clearGrammarCache ,
106 type GrammarLoadError ,
117 getLanguageGrammar ,
128} from './languages'
9+ import { buildParseResult } from './shared'
1310
14- // Re-export language utilities
1511export {
1612 clearGrammarCache ,
1713 detectLanguage ,
1814 GrammarLoadError ,
1915 LANGUAGE_EXTENSIONS ,
2016 loadGrammar ,
2117} from './languages'
18+ export {
19+ buildParseResult ,
20+ getParseErrorMessage ,
21+ hasParseErrors ,
22+ } from './shared'
2223
23- /**
24- * Error thrown when parser initialization fails
25- */
2624export class ParserInitError extends Error {
2725 readonly _tag = 'ParserInitError'
2826 override readonly cause ?: unknown
@@ -34,16 +32,8 @@ export class ParserInitError extends Error {
3432 }
3533}
3634
37- /**
38- * Flag to track if tree-sitter has been initialized
39- */
4035let initialized : boolean = false
4136
42- /**
43- * Initialize the tree-sitter WASM module
44- *
45- * @returns Effect that initializes tree-sitter
46- */
4737export function initParser ( ) : Effect . Effect < void , ParserInitError > {
4838 return Effect . gen ( function * ( ) {
4939 if ( initialized ) {
@@ -60,99 +50,28 @@ export function initParser(): Effect.Effect<void, ParserInitError> {
6050 } )
6151}
6252
63- /**
64- * Check if a parse tree has errors
65- */
66- function hasParseErrors ( tree : TSTree ) : boolean {
67- return tree . rootNode . hasError
68- }
69-
70- /**
71- * Get error message from a tree with errors
72- */
73- function getParseErrorMessage ( tree : TSTree ) : string {
74- const errorNodes : string [ ] = [ ]
75-
76- function findErrors ( node : TSNode ) {
77- if ( node . isError || node . isMissing ) {
78- const pos = node . startPosition
79- errorNodes . push (
80- `${ node . isError ? 'ERROR' : 'MISSING' } at line ${ pos . row + 1 } , column ${ pos . column + 1 } ` ,
81- )
82- }
83- for ( const child of node . children ) {
84- findErrors ( child )
85- }
86- }
87-
88- findErrors ( tree . rootNode )
89- return errorNodes . length > 0
90- ? errorNodes . slice ( 0 , 3 ) . join ( '; ' ) +
91- ( errorNodes . length > 3 ? `; ... and ${ errorNodes . length - 3 } more` : '' )
92- : 'Unknown parse error'
93- }
94-
95- /**
96- * Parse source code into an AST
97- *
98- * Uses Effect internally for error handling. Tree-sitter always produces a tree
99- * even with syntax errors (recoverable parsing).
100- *
101- * @param parser - The tree-sitter parser instance
102- * @param code - The source code to parse
103- * @param language - The programming language
104- * @returns Effect resolving to ParseResult
105- */
10653export function parse (
10754 parser : Parser ,
10855 code : string ,
10956 language : Language ,
11057) : Effect . Effect < ParseResult , ParseError | GrammarLoadError > {
11158 return Effect . gen ( function * ( ) {
112- // Load and set the language grammar
11359 const grammar = yield * getLanguageGrammar ( language )
11460 parser . setLanguage ( grammar )
11561
116- // Parse the code
11762 const tree = parser . parse ( code )
63+ const result = buildParseResult ( tree )
11864
119- if ( ! tree ) {
120- return yield * Effect . fail ( {
121- message : 'Parser returned null - no language set or parsing cancelled' ,
122- recoverable : false ,
123- } satisfies ParseError )
65+ if ( result . error && ! result . error . recoverable ) {
66+ return yield * Effect . fail ( result . error )
12467 }
12568
126- // Check for parse errors
127- if ( hasParseErrors ( tree ) ) {
128- return {
129- tree,
130- error : {
131- message : getParseErrorMessage ( tree ) ,
132- recoverable : true , // Tree-sitter always produces a tree
133- } ,
134- } satisfies ParseResult
135- }
136-
137- return {
138- tree,
139- error : null ,
140- } satisfies ParseResult
69+ return result
14170 } )
14271}
14372
144- // ============================================================================
145- // Public API - Unwraps Effect for consumers
146- // ============================================================================
147-
148- /**
149- * Shared parser instance for the public API
150- */
15173let sharedParser : Parser | null = null
15274
153- /**
154- * Get or create the shared parser instance
155- */
15675async function getSharedParser ( ) : Promise < Parser > {
15776 if ( sharedParser ) {
15877 return sharedParser
@@ -163,14 +82,6 @@ async function getSharedParser(): Promise<Parser> {
16382 return sharedParser
16483}
16584
166- /**
167- * Parse source code into an AST (public async API)
168- *
169- * @param code - The source code to parse
170- * @param language - The programming language
171- * @returns Promise resolving to ParseResult
172- * @throws ParseError or GrammarLoadError if parsing fails irrecoverably
173- */
17485export async function parseCode (
17586 code : string ,
17687 language : Language ,
@@ -179,24 +90,10 @@ export async function parseCode(
17990 return Effect . runPromise ( parse ( parser , code , language ) )
18091}
18192
182- /**
183- * Initialize the parser module (public async API)
184- *
185- * Call this before using other parser functions to ensure tree-sitter is ready.
186- * This is called automatically by parseCode, but can be called explicitly for
187- * early initialization.
188- *
189- * @returns Promise that resolves when initialization is complete
190- * @throws ParserInitError if initialization fails
191- */
19293export async function initializeParser ( ) : Promise < void > {
19394 await getSharedParser ( )
19495}
19596
196- /**
197- * Reset the shared parser state (useful for testing)
198- * Also clears the grammar cache to ensure clean reinitialization
199- */
20097export function resetParser ( ) : void {
20198 if ( sharedParser ) {
20299 sharedParser . delete ( )
0 commit comments