11import {
22 ArrowFuncDefNode ,
33 AssignNode , AstBlock , AstNode , BinOpNode , BracketObjectAccessNode , ConstNode , CreateArrayNode ,
4- CreateObjectNode , DotObjectAccessNode , ForNode , FuncDefNode , FunctionCallNode , FunctionDefNode , GetSingleVarNode , IfNode , OperationFuncs , Primitive , SetSingleVarNode , WhileNode
4+ CreateObjectNode , DotObjectAccessNode , ForNode , FuncDefNode , FunctionCallNode , FunctionDefNode , GetSingleVarNode , IfNode , OperationFuncs , Primitive , ReturnNode , SetSingleVarNode , WhileNode
55} from '../common' ;
6- import { Scope } from './scope' ;
6+ import { BlockContext , Scope } from './scope' ;
77
88export class Evaluator {
99
10- evalBlock ( ast : AstBlock , scope : Scope ) : unknown {
10+ evalBlock ( ast : AstBlock , blockContext : BlockContext ) : unknown {
1111 let lastResult = null ;
1212
1313 for ( let node of ast ?. funcs || [ ] ) {
1414 const funcDef = node as FunctionDefNode ;
1515
1616 // a child scope needs to be created here
17- const newScope = scope ;
17+ const newScope = blockContext . blockScope ;
1818
19- scope . set ( funcDef . name ,
20- ( ...args : unknown [ ] ) : unknown => this . jspyFuncInvoker ( funcDef , scope , ...args )
19+ newScope . set ( funcDef . funcAst . name ,
20+ ( ...args : unknown [ ] ) : unknown => this . jspyFuncInvoker ( funcDef , blockContext , ...args )
2121 ) ;
2222 }
2323
2424 for ( const node of ast . body ) {
25- lastResult = this . evalNode ( node , scope ) ;
25+ if ( node . type === 'comment' ) { continue ; }
26+ lastResult = this . evalNode ( node , blockContext ) ;
27+
28+ if ( blockContext . returnCalled ) {
29+ const res = blockContext . returnObject ;
30+ blockContext . returnCalled = false ;
31+ blockContext . returnObject = null ;
32+ return res ;
33+ }
34+
35+ if ( blockContext . continueCalled ) {
36+ break ;
37+ }
38+ if ( blockContext . breakCalled ) {
39+ break ;
40+ }
2641 }
2742
2843 return lastResult ;
2944 }
3045
31- async evalBlockAsync ( ast : AstBlock , scope : Scope ) : Promise < unknown > {
46+ async evalBlockAsync ( ast : AstBlock , blockContext : BlockContext ) : Promise < unknown > {
3247 let lastResult = null ;
3348
3449 for ( let node of ast ?. funcs || [ ] ) {
3550 const funcDef = node as FunctionDefNode ;
3651
3752 // a child scope needs to be created here
38- const newScope = scope ;
53+ const newScope = blockContext . blockScope ;
3954
40- scope . set ( funcDef . name ,
41- ( ...args : unknown [ ] ) : unknown => this . jspyFuncInvoker ( funcDef , scope , ...args )
55+ newScope . set ( funcDef . funcAst . name ,
56+ ( ...args : unknown [ ] ) : unknown => this . jspyFuncInvoker ( funcDef , blockContext , ...args )
4257 ) ;
4358 }
4459
4560 for ( const node of ast . body ) {
46- lastResult = this . evalNode ( node , scope ) ;
61+ if ( node . type === 'comment' ) { continue ; }
62+
63+ lastResult = this . evalNode ( node , blockContext ) ;
64+ if ( blockContext . returnCalled ) {
65+ const res = blockContext . returnObject ;
66+ blockContext . returnCalled = false ;
67+ blockContext . returnObject = null ;
68+ return res ;
69+ }
70+
71+ if ( blockContext . continueCalled ) {
72+ break ;
73+ }
74+ if ( blockContext . breakCalled ) {
75+ break ;
76+ }
4777 }
4878
4979 return lastResult ;
5080 }
5181
52- private jspyFuncInvoker ( funcDef : FuncDefNode , newScope : Scope , ...args : unknown [ ] ) : unknown {
82+ private jspyFuncInvoker ( funcDef : FuncDefNode , context : BlockContext , ...args : unknown [ ] ) : unknown {
83+
84+ const ast = funcDef . funcAst ;
5385
54- const ast = { name : '' , type : 'func' , funcs : [ ] , body : funcDef . body } as AstBlock ;
86+ const blockContext = {
87+ namelessFuncsCount : 0 ,
88+ blockScope : context . blockScope . clone ( )
89+ } as BlockContext ;
5590
5691 // set parameters into new scope, based incomming arguments
5792 for ( let i = 0 ; i < args ?. length || 0 ; i ++ ) {
5893 if ( i >= funcDef . params . length ) {
5994 break ;
6095 // throw new Error('Too much parameters provided');
6196 }
62- newScope . set ( funcDef . params [ i ] , args [ i ] ) ;
97+ blockContext . blockScope . set ( funcDef . params [ i ] , args [ i ] ) ;
6398 }
64- return this . evalBlock ( ast , newScope ) ;
99+
100+ return this . evalBlock ( ast , blockContext ) ;
65101 }
66102
67103 private invokeFunction ( func : ( ...args : unknown [ ] ) => unknown , fps : unknown [ ] ) : unknown {
@@ -101,44 +137,72 @@ export class Evaluator {
101137 }
102138 }
103139
104- private evalNode ( node : AstNode , scope : Scope ) : unknown {
140+ private evalNode ( node : AstNode , blockContext : BlockContext ) : unknown {
105141 if ( node . type === 'import' ) {
106142 // skip this for now. As modules are implemented externally
107143 return null ;
108144 }
109145
146+ if ( node . type === 'comment' ) {
147+ return null ;
148+ }
149+
110150 if ( node . type === 'if' ) {
111151 const ifNode = node as IfNode ;
112- const newScope = scope ;
113- if ( this . evalNode ( ifNode . conditionNode , scope ) ) {
114- this . evalBlock ( { body : ifNode . ifBody } as AstBlock , newScope ) ;
152+ if ( this . evalNode ( ifNode . conditionNode , blockContext ) ) {
153+ this . evalBlock ( { body : ifNode . ifBody } as AstBlock , blockContext ) ;
115154 } else if ( ifNode . elseBody ) {
116- this . evalBlock ( { body : ifNode . elseBody } as AstBlock , newScope ) ;
155+ this . evalBlock ( { body : ifNode . elseBody } as AstBlock , blockContext ) ;
117156 }
118157
119158 return ;
120159 }
121160
161+ if ( node . type === 'return' ) {
162+ const returnNode = node as ReturnNode ;
163+ blockContext . returnCalled = true ;
164+ blockContext . returnObject = returnNode . returnValue ?
165+ this . evalNode ( returnNode . returnValue , blockContext )
166+ : null ;
167+
168+ return blockContext . returnObject ;
169+ }
170+
171+ if ( node . type === 'continue' ) {
172+ blockContext . continueCalled = true ;
173+ return ;
174+ }
175+
176+ if ( node . type === 'break' ) {
177+ blockContext . breakCalled = true ;
178+ return ;
179+ }
180+
122181 if ( node . type === 'for' ) {
123182 const forNode = node as ForNode ;
124- const newScope = scope ;
125183
126- const array = this . evalNode ( forNode . sourceArray , newScope ) as unknown [ ] | string ;
184+ const array = this . evalNode ( forNode . sourceArray , blockContext ) as unknown [ ] | string ;
127185
128186 for ( let item of array ) {
129- newScope . set ( forNode . itemVarName , item ) ;
130- this . evalBlock ( { body : forNode . body } as AstBlock , newScope ) ;
187+ blockContext . blockScope . set ( forNode . itemVarName , item ) ;
188+ this . evalBlock ( { body : forNode . body } as AstBlock , blockContext ) ;
189+ if ( blockContext . continueCalled ) { blockContext . continueCalled = false ; }
190+ if ( blockContext . breakCalled ) { break ; }
131191 }
192+ if ( blockContext . breakCalled ) { blockContext . breakCalled = false ; }
132193 return ;
133194 }
134195
135196 if ( node . type === 'while' ) {
136197 const forNode = node as WhileNode ;
137- const newScope = scope ;
138198
139- while ( this . evalNode ( forNode . condition , newScope ) ) {
140- this . evalBlock ( { body : forNode . body } as AstBlock , newScope ) ;
199+ while ( this . evalNode ( forNode . condition , blockContext ) ) {
200+ this . evalBlock ( { body : forNode . body } as AstBlock , blockContext ) ;
201+
202+ if ( blockContext . continueCalled ) { blockContext . continueCalled = false ; }
203+ if ( blockContext . breakCalled ) { break ; }
141204 }
205+ if ( blockContext . breakCalled ) { blockContext . breakCalled = false ; }
142206
143207 return ;
144208 }
@@ -148,27 +212,26 @@ export class Evaluator {
148212 }
149213
150214 if ( node . type === "getSingleVar" ) {
151- return scope . get ( ( node as GetSingleVarNode ) . name ) ;
215+ return blockContext . blockScope . get ( ( node as GetSingleVarNode ) . name ) ;
152216 }
153217
154218 if ( node . type === "binOp" ) {
155219 const binOpNode = ( node as BinOpNode ) ;
156- var left = this . evalNode ( binOpNode . left , scope ) ;
157- var right = this . evalNode ( binOpNode . right , scope ) ;
220+ var left = this . evalNode ( binOpNode . left , blockContext ) ;
221+ var right = this . evalNode ( binOpNode . right , blockContext ) ;
158222 return OperationFuncs [ binOpNode . op ] ( left as Primitive , right as Primitive ) ;
159223 }
160224
161225 if ( node . type === "arrowFuncDef" ) {
162226 const arrowFuncDef = node as ArrowFuncDefNode ;
163- const newScope = scope ;
164227
165- return ( ...args :unknown [ ] ) : unknown => this . jspyFuncInvoker ( arrowFuncDef , newScope , ...args ) ;
228+ return ( ...args : unknown [ ] ) : unknown => this . jspyFuncInvoker ( arrowFuncDef , blockContext , ...args ) ;
166229 }
167230
168231 if ( node . type === "funcCall" ) {
169232 const funcCallNode = node as FunctionCallNode ;
170- const func = scope . get ( funcCallNode . name ) as ( ...args : unknown [ ] ) => unknown ;
171- const pms = funcCallNode . paramNodes ?. map ( n => this . evalNode ( n , scope ) ) || [ ]
233+ const func = blockContext . blockScope . get ( funcCallNode . name ) as ( ...args : unknown [ ] ) => unknown ;
234+ const pms = funcCallNode . paramNodes ?. map ( n => this . evalNode ( n , blockContext ) ) || [ ]
172235
173236 return this . invokeFunction ( func , pms ) ;
174237 }
@@ -178,26 +241,26 @@ export class Evaluator {
178241
179242 if ( assignNode . target . type === 'getSingleVar' ) {
180243 const node = assignNode . target as SetSingleVarNode ;
181- scope . set ( node . name , this . evalNode ( assignNode . source , scope ) ) ;
244+ blockContext . blockScope . set ( node . name , this . evalNode ( assignNode . source , blockContext ) ) ;
182245 } else if ( assignNode . target . type === 'dotObjectAccess' ) {
183246 const targetNode = assignNode . target as DotObjectAccessNode ;
184247
185248 // create a node for all but last property token
186249 // potentially it can go to parser
187250 const targetObjectNode = new DotObjectAccessNode ( targetNode . nestedProps . slice ( 0 , targetNode . nestedProps . length - 1 ) ) ;
188- const targetObject = this . evalNode ( targetObjectNode , scope ) as Record < string , unknown > ;
251+ const targetObject = this . evalNode ( targetObjectNode , blockContext ) as Record < string , unknown > ;
189252
190253 // not sure nested properties should be GetSingleVarNode
191254 // can be factored in the parser
192255 const lastPropertyName = ( targetNode . nestedProps [ targetNode . nestedProps . length - 1 ] as GetSingleVarNode ) . name
193256
194- targetObject [ lastPropertyName ] = this . evalNode ( assignNode . source , scope ) ;
257+ targetObject [ lastPropertyName ] = this . evalNode ( assignNode . source , blockContext ) ;
195258 } else if ( assignNode . target . type === 'bracketObjectAccess' ) {
196259 const targetNode = assignNode . target as BracketObjectAccessNode ;
197- const keyValue = this . evalNode ( targetNode . bracketBody , scope ) as string | number ;
198- const targetObject = scope . get ( targetNode . propertyName as string ) as Record < string , unknown > ;
260+ const keyValue = this . evalNode ( targetNode . bracketBody , blockContext ) as string | number ;
261+ const targetObject = blockContext . blockScope . get ( targetNode . propertyName as string ) as Record < string , unknown > ;
199262
200- targetObject [ keyValue ] = this . evalNode ( assignNode . source , scope ) ;
263+ targetObject [ keyValue ] = this . evalNode ( assignNode . source , blockContext ) ;
201264 } else {
202265 throw Error ( 'Not implemented Assign operation' ) ;
203266 // get chaining calls
@@ -208,15 +271,15 @@ export class Evaluator {
208271
209272 if ( node . type === 'bracketObjectAccess' ) {
210273 const sbNode = node as BracketObjectAccessNode ;
211- const key = this . evalNode ( sbNode . bracketBody , scope ) as string ;
212- const obj = scope . get ( sbNode . propertyName as string ) as Record < string , unknown > ;
213- return ( obj [ key ] === undefined ) ? null : obj [ key ] ;
274+ const key = this . evalNode ( sbNode . bracketBody , blockContext ) as string ;
275+ const obj = blockContext . blockScope . get ( sbNode . propertyName as string ) as Record < string , unknown > ;
276+ return ( obj [ key ] === undefined ) ? null : obj [ key ] ;
214277 }
215278
216279 if ( node . type === "dotObjectAccess" ) {
217280 const dotObject = node as DotObjectAccessNode ;
218281
219- let startObject = this . evalNode ( dotObject . nestedProps [ 0 ] , scope ) as any ;
282+ let startObject = this . evalNode ( dotObject . nestedProps [ 0 ] , blockContext ) as any ;
220283 for ( let i = 1 ; i < dotObject . nestedProps . length ; i ++ ) {
221284 const nestedProp = dotObject . nestedProps [ i ] ;
222285
@@ -229,11 +292,11 @@ export class Evaluator {
229292 } else if ( nestedProp . type === 'bracketObjectAccess' ) {
230293 const node = nestedProp as BracketObjectAccessNode ;
231294 startObject = startObject [ node . propertyName ] as unknown ;
232- startObject = startObject [ this . evalNode ( node . bracketBody , scope ) as string ] as unknown ;
295+ startObject = startObject [ this . evalNode ( node . bracketBody , blockContext ) as string ] as unknown ;
233296 } else if ( nestedProp . type === 'funcCall' ) {
234297 const funcCallNode = nestedProp as FunctionCallNode ;
235298 const func = startObject [ funcCallNode . name ] as ( ...args : unknown [ ] ) => unknown ;
236- const pms = funcCallNode . paramNodes ?. map ( n => this . evalNode ( n , scope ) ) || [ ]
299+ const pms = funcCallNode . paramNodes ?. map ( n => this . evalNode ( n , blockContext ) ) || [ ]
237300
238301 startObject = this . invokeFunction ( func . bind ( startObject ) , pms ) ;
239302
@@ -251,7 +314,7 @@ export class Evaluator {
251314 const obj = { } as Record < string , unknown > ;
252315
253316 for ( const p of createObjectNode . props ) {
254- obj [ this . evalNode ( p . name , scope ) as string ] = this . evalNode ( p . value , scope ) ;
317+ obj [ this . evalNode ( p . name , blockContext ) as string ] = this . evalNode ( p . value , blockContext ) ;
255318 }
256319
257320 return obj ;
@@ -262,7 +325,7 @@ export class Evaluator {
262325 const res = [ ] as unknown [ ] ;
263326
264327 for ( const item of arrayNode . items ) {
265- res . push ( this . evalNode ( item , scope ) ) ;
328+ res . push ( this . evalNode ( item , blockContext ) ) ;
266329 }
267330
268331 return res ;
0 commit comments