@@ -12,25 +12,26 @@ import { isAbstractElement, type AbstractElement, type Grammar } from '../langua
1212import type { Linker } from '../references/linker.js' ;
1313import type { Lexer } from '../parser/lexer.js' ;
1414import type { LangiumCoreServices } from '../services.js' ;
15- import type { Reference , AstNode , CstNode , LeafCstNode , GenericAstNode , Mutable } from '../syntax-tree.js' ;
15+ import type { ParseResult } from '../parser/langium-parser.js' ;
16+ import type { Reference , AstNode , CstNode , LeafCstNode , GenericAstNode , Mutable , RootCstNode } from '../syntax-tree.js' ;
1617import { isRootCstNode , isCompositeCstNode , isLeafCstNode , isAstNode , isReference } from '../syntax-tree.js' ;
1718import { streamAst } from '../utils/ast-utils.js' ;
1819import { BiMap } from '../utils/collections.js' ;
1920import { streamCst } from '../utils/cst-utils.js' ;
2021
2122/**
22- * The hydrator service is responsible for allowing AST nodes to be sent across worker threads.
23+ * The hydrator service is responsible for allowing AST parse results to be sent across worker threads.
2324 */
2425export interface Hydrator {
2526 /**
26- * Converts an AST node to a plain object. The resulting object can be sent across worker threads.
27+ * Converts a parse result to a plain object. The resulting object can be sent across worker threads.
2728 */
28- dehydrate ( node : AstNode ) : object ;
29+ dehydrate ( result : ParseResult < AstNode > ) : ParseResult < object > ;
2930 /**
30- * Converts a plain object to an AST node . The resulting AST node can be used in the main thread.
31- * Calling this method on non-plain objects will result in undefined behavior.
31+ * Converts a plain object to a parse result . The included AST node can then be used in the main thread.
32+ * Calling this method on objects that have not been dehydrated first will result in undefined behavior.
3233 */
33- hydrate ( node : object ) : AstNode ;
34+ hydrate < T extends AstNode = AstNode > ( result : ParseResult < object > ) : ParseResult < T > ;
3435}
3536
3637export interface DehydrateContext {
@@ -58,8 +59,14 @@ export class DefaultHydrator implements Hydrator {
5859 this . linker = services . references . Linker ;
5960 }
6061
61- dehydrate ( node : AstNode ) : object {
62- return this . dehydrateAstNode ( node , this . createDehyrationContext ( node ) ) ;
62+ dehydrate ( result : ParseResult < AstNode > ) : ParseResult < object > {
63+ return {
64+ // We need to create shallow copies of the errors
65+ // The original errors inherit from the `Error` class, which is not transferable across worker threads
66+ lexerErrors : result . lexerErrors . map ( e => ( { ...e } ) ) ,
67+ parserErrors : result . parserErrors . map ( e => ( { ...e } ) ) ,
68+ value : this . dehydrateAstNode ( result . value , this . createDehyrationContext ( result . value ) )
69+ } ;
6370 }
6471
6572 protected createDehyrationContext ( node : AstNode ) : DehydrateContext {
@@ -128,6 +135,7 @@ export class DefaultHydrator implements Hydrator {
128135 if ( isRootCstNode ( node ) ) {
129136 cstNode . fullText = node . fullText ;
130137 } else {
138+ // Note: This returns undefined for hidden nodes (i.e. comments)
131139 cstNode . grammarSource = this . getGrammarElementId ( node . grammarSource ) ;
132140 }
133141 cstNode . hidden = node . hidden ;
@@ -146,12 +154,17 @@ export class DefaultHydrator implements Hydrator {
146154 return cstNode ;
147155 }
148156
149- hydrate ( node : object ) : AstNode {
157+ hydrate < T extends AstNode = AstNode > ( result : ParseResult < object > ) : ParseResult < T > {
158+ const node = result . value ;
150159 const context = this . createHydrationContext ( node ) ;
151160 if ( '$cstNode' in node ) {
152161 this . hydrateCstNode ( node . $cstNode , context ) ;
153162 }
154- return this . hydrateAstNode ( node , context ) ;
163+ return {
164+ lexerErrors : result . lexerErrors ,
165+ parserErrors : result . parserErrors ,
166+ value : this . hydrateAstNode ( node , context ) as T
167+ } ;
155168 }
156169
157170 protected createHydrationContext ( node : any ) : HydrateContext {
@@ -160,18 +173,21 @@ export class DefaultHydrator implements Hydrator {
160173 for ( const astNode of streamAst ( node ) ) {
161174 astNodes . set ( astNode , { } as AstNode ) ;
162175 }
176+ let root : RootCstNode ;
163177 if ( node . $cstNode ) {
164178 for ( const cstNode of streamCst ( node . $cstNode ) ) {
165- let cst : CstNode | undefined ;
179+ let cst : Mutable < CstNode > | undefined ;
166180 if ( 'fullText' in cstNode ) {
167181 cst = new RootCstNodeImpl ( cstNode . fullText as string ) ;
182+ root = cst as RootCstNode ;
168183 } else if ( 'content' in cstNode ) {
169184 cst = new CompositeCstNodeImpl ( ) ;
170185 } else if ( 'tokenType' in cstNode ) {
171186 cst = this . hydrateCstLeafNode ( cstNode ) ;
172187 }
173188 if ( cst ) {
174189 cstNodes . set ( cstNode , cst ) ;
190+ cst . root = root ! ;
175191 }
176192 }
177193 }
@@ -198,15 +214,15 @@ export class DefaultHydrator implements Hydrator {
198214 astNode [ name ] = arr ;
199215 for ( const item of value ) {
200216 if ( isAstNode ( item ) ) {
201- arr . push ( this . setParent ( this . hydrate ( item ) , astNode ) ) ;
217+ arr . push ( this . setParent ( this . hydrateAstNode ( item , context ) , astNode ) ) ;
202218 } else if ( isReference ( item ) ) {
203219 arr . push ( this . hydrateReference ( item , astNode , name , context ) ) ;
204220 } else {
205221 arr . push ( item ) ;
206222 }
207223 }
208224 } else if ( isAstNode ( value ) ) {
209- astNode [ name ] = this . setParent ( this . hydrate ( value ) , astNode ) ;
225+ astNode [ name ] = this . setParent ( this . hydrateAstNode ( value , context ) , astNode ) ;
210226 } else if ( isReference ( value ) ) {
211227 astNode [ name ] = this . hydrateReference ( value , astNode , name , context ) ;
212228 } else if ( value !== undefined ) {
@@ -272,11 +288,11 @@ export class DefaultHydrator implements Hydrator {
272288 return this . lexer . definition [ name ] ;
273289 }
274290
275- protected getGrammarElementId ( node : AbstractElement ) : number {
291+ protected getGrammarElementId ( node : AbstractElement ) : number | undefined {
276292 if ( this . grammarElementIdMap . size === 0 ) {
277293 this . createGrammarElementIdMap ( ) ;
278294 }
279- return this . grammarElementIdMap . get ( node ) ?? - 1 ;
295+ return this . grammarElementIdMap . get ( node ) ;
280296 }
281297
282298 protected getGrammarElement ( id : number ) : AbstractElement {
0 commit comments