@@ -144,13 +144,14 @@ export function schemeEval(
144144 rest = args
145145 } else if ( isImproperList ( args ) ) {
146146 ; [ argsList , rest ] = flattenImproperList ( args )
147- argsList . forEach ( arg => {
147+ argsList . forEach ( ( arg : any ) => {
148148 if ( ! ( arg instanceof _Symbol ) ) {
149149 return handleRuntimeError (
150150 context ,
151151 new errors . ExceptionError ( new Error ( 'Invalid arguments for lambda!' ) )
152152 )
153153 }
154+ return
154155 } )
155156 if ( rest !== null && ! ( rest instanceof _Symbol ) ) {
156157 return handleRuntimeError (
@@ -160,13 +161,14 @@ export function schemeEval(
160161 }
161162 } else if ( isList ( args ) ) {
162163 argsList = flattenList ( args ) as _Symbol [ ]
163- argsList . forEach ( arg => {
164+ argsList . forEach ( ( arg : any ) => {
164165 if ( ! ( arg instanceof _Symbol ) ) {
165166 return handleRuntimeError (
166167 context ,
167168 new errors . ExceptionError ( new Error ( 'Invalid arguments for lambda!' ) )
168169 )
169170 }
171+ return
170172 } )
171173 } else {
172174 return handleRuntimeError (
@@ -177,7 +179,7 @@ export function schemeEval(
177179
178180 // convert the args to estree pattern
179181 const params : ( es . Identifier | es . RestElement ) [ ] = argsList . map ( arg =>
180- makeDummyIdentifierNode ( arg . sym )
182+ makeDummyIdentifierNode ( encode ( arg . sym ) )
181183 )
182184
183185 let body_elements = parsedList . slice ( 2 )
@@ -189,7 +191,7 @@ export function schemeEval(
189191 if ( rest !== null ) {
190192 params . push ( {
191193 type : 'RestElement' ,
192- argument : makeDummyIdentifierNode ( rest . sym )
194+ argument : makeDummyIdentifierNode ( encode ( rest . sym ) )
193195 } )
194196 body = arrayToList ( [
195197 new _Symbol ( 'begin' ) ,
@@ -206,11 +208,10 @@ export function schemeEval(
206208 const lambda = {
207209 type : 'ArrowFunctionExpression' ,
208210 params : params ,
209- body : body as any ,
210- modified : true
211+ body : convertToEvalExpression ( body )
211212 }
212213
213- control . push ( lambda as unknown as es . ArrowFunctionExpression )
214+ control . push ( lambda as es . ArrowFunctionExpression )
214215 return
215216
216217 case 'define' :
@@ -269,10 +270,9 @@ export function schemeEval(
269270 {
270271 type : 'VariableDeclarator' ,
271272 id : makeDummyIdentifierNode ( encode ( variable . sym ) ) ,
272- init : value
273+ init : convertToEvalExpression ( value )
273274 }
274- ] ,
275- modified : true
275+ ]
276276 }
277277
278278 control . push ( definition as es . VariableDeclaration )
@@ -298,8 +298,7 @@ export function schemeEval(
298298 type : 'AssignmentExpression' ,
299299 operator : '=' ,
300300 left : makeDummyIdentifierNode ( encode ( set_variable . sym ) ) ,
301- right : set_value ,
302- modified : true
301+ right : convertToEvalExpression ( set_value )
303302 }
304303
305304 control . push ( assignment as es . AssignmentExpression )
@@ -328,10 +327,9 @@ export function schemeEval(
328327 // estree ConditionalExpression
329328 const conditional = {
330329 type : 'ConditionalExpression' ,
331- test : truthyCondition as any ,
332- consequent,
333- alternate,
334- modified : true
330+ test : convertToEvalExpression ( truthyCondition ) ,
331+ consequent : convertToEvalExpression ( consequent ) ,
332+ alternate : alternate ? convertToEvalExpression ( alternate ) : undefined
335333 }
336334
337335 control . push ( conditional as es . ConditionalExpression )
@@ -398,10 +396,15 @@ export function schemeEval(
398396 // at this point, we assume that syntax-rules is verified
399397 // and parsed correctly already.
400398 const syntaxRulesList = flattenList ( syntaxRules )
401- if ( ! ( syntaxRulesList [ 0 ] instanceof _Symbol ) || syntaxRulesList [ 0 ] . sym !== 'syntax-rules' ) {
399+ if (
400+ ! ( syntaxRulesList [ 0 ] instanceof _Symbol ) ||
401+ syntaxRulesList [ 0 ] . sym !== 'syntax-rules'
402+ ) {
402403 return handleRuntimeError (
403404 context ,
404- new errors . ExceptionError ( new Error ( 'define-syntax requires a syntax-rules transformer!' ) )
405+ new errors . ExceptionError (
406+ new Error ( 'define-syntax requires a syntax-rules transformer!' )
407+ )
405408 )
406409 }
407410 if ( syntaxRulesList . length < 3 ) {
@@ -441,8 +444,8 @@ export function schemeEval(
441444 const appln = {
442445 type : 'CallExpression' ,
443446 optional : false ,
444- callee : procedure ,
445- arguments : args
447+ callee : convertToEvalExpression ( procedure ) as es . Expression ,
448+ arguments : args . map ( convertToEvalExpression ) // unfortunately, each one needs to be converted.
446449 }
447450 control . push ( appln as es . CallExpression )
448451 return
@@ -510,167 +513,101 @@ export function makeDummyEvalExpression(callee: string, argument: string): es.Ca
510513}
511514
512515/**
513- * Because we have passed estree nodes with list elements
514- * to the control, if any future estree functions require
515- * the values within the nodes to be evaluated, we use this
516- * function to re-parse the modified estree nodes to avoid any errors.
516+ * Convert a scheme expression (that is meant to be evaluated)
517+ * into an estree expression, using eval.
518+ * this will let us avoid the "hack" of storing Scheme lists
519+ * in estree nodes.
520+ * @param expression
521+ * @returns estree expression
517522 */
518- export function reparseEstreeNode ( node : any ) : es . Node {
519- // if the node is an estree node, we recursively reparse it.
520- if ( node . type ) {
521- if ( ! node . modified ) {
522- return node
523- }
524- switch ( node . type ) {
525- case 'ArrowFunctionExpression' :
526- return {
527- type : 'ArrowFunctionExpression' ,
528- params : node . params . map ( ( param : any ) => reparseEstreeNode ( param ) as es . Identifier | es . RestElement ) ,
529- body : reparseEstreeNode ( node . body ) as es . BlockStatement
530- } as es . Node
531- case 'VariableDeclaration' :
532- return {
533- type : 'VariableDeclaration' ,
534- kind : node . kind ,
535- declarations : node . declarations . map ( ( decl : any ) => reparseEstreeNode ( decl ) as es . VariableDeclarator )
536- } as es . Node
537- case 'VariableDeclarator' :
538- return {
539- type : 'VariableDeclarator' ,
540- id : reparseEstreeNode ( node . id ) as es . Identifier ,
541- init : reparseEstreeNode ( node . init )
542- } as es . Node
543- case 'AssignmentExpression' :
544- return {
545- type : 'AssignmentExpression' ,
546- operator : node . operator ,
547- left : reparseEstreeNode ( node . left ) as es . Identifier ,
548- right : reparseEstreeNode ( node . right )
549- } as es . Node
550- case 'ConditionalExpression' :
551- return {
552- type : 'ConditionalExpression' ,
553- test : reparseEstreeNode ( node . test ) ,
554- consequent : reparseEstreeNode ( node . consequent ) ,
555- alternate : reparseEstreeNode ( node . alternate )
556- } as es . Node
557- case 'CallExpression' :
558- return {
559- type : 'CallExpression' ,
560- optional : false ,
561- callee : reparseEstreeNode ( node . callee ) ,
562- arguments : node . arguments . map ( ( arg : any ) => reparseEstreeNode ( arg ) )
563- } as es . Node
564- case 'Identifier' :
565- return {
523+ export function convertToEvalExpression ( expression : SchemeControlItems ) : es . CallExpression {
524+ function convertToEstreeExpression ( expression : SchemeControlItems ) : es . Expression {
525+ /*
526+ cases to consider:
527+ - list
528+ - pair/improper list
529+ - symbol
530+ - number
531+ - boolean
532+ - string
533+ */
534+ if ( isList ( expression ) ) {
535+ // make a call expression to list
536+ // with the elements of the list as its arguments.
537+ const args = flattenList ( expression ) . map ( convertToEstreeExpression )
538+ return {
539+ type : 'CallExpression' ,
540+ optional : false ,
541+ callee : {
566542 type : 'Identifier' ,
567- name : node . name
568- } as es . Node
569- case 'RestElement' :
570- return {
571- type : 'RestElement' ,
572- argument : reparseEstreeNode ( node . argument ) as es . Identifier
573- } as es . Node
574- default :
575- // no other node was touched by schemeEval.
576- // return it as is.
577- return node
578- }
579- }
580- // if the node is not an estree node, there are several possibilities:
581- // 1. it is a list/improper list
582- // 2. it is a symbol
583- // 3. it is a number
584- // 4. it is a boolean
585- // 5. it is a string
586- // we need to handle each of these cases.
587- if ( isList ( node ) ) {
588- // if it is a list, we can be lazy and reparse the list as a
589- // CallExpression to the list procedure- followed by a call to eval.
590- // this will ensure that the list is evaluated.
591-
592- // this also handles null.
593- const items = flattenList ( node )
594- const evalledItems = items . map ( ( item : any ) => reparseEstreeNode ( item ) )
595- const listCall = {
596- type : 'CallExpression' ,
597- optional : false ,
598- callee : {
599- type : 'Identifier' ,
600- name : 'list'
601- } ,
602- arguments : evalledItems
603- }
604- return {
605- type : 'CallExpression' ,
606- optional : false ,
607- callee : {
608- type : 'Identifier' ,
609- name : encode ( 'eval' )
610- } ,
611- arguments : [ listCall as es . CallExpression ]
612- }
613- } else if ( isImproperList ( node ) ) {
614- // we can treat the improper list as a recursive CallExpression of cons
615- // followed by a call to eval.
616- const pairCall = {
617- type : 'CallExpression' ,
618- optional : false ,
619- callee : {
620- type : 'Identifier' ,
621- name : 'cons'
622- } ,
623- arguments : [
624- reparseEstreeNode ( node [ 0 ] ) ,
625- reparseEstreeNode ( node [ 1 ] )
626- ]
627- }
628- return {
629- type : 'CallExpression' ,
630- optional : false ,
631- callee : {
632- type : 'Identifier' ,
633- name : encode ( 'eval' )
634- } ,
635- arguments : [ pairCall as es . CallExpression ]
636- }
637- } else if ( node instanceof _Symbol ) {
638- // if it is a symbol, we can just return an Identifier node.
639- return {
640- type : 'Identifier' ,
641- name : node . sym
642- }
643- } else if ( is_number ( node ) ) {
644- // if it is a number, we treat it as a call to
645- // the string->number function.
646- return {
647- type : 'CallExpression' ,
648- optional : false ,
649- callee : {
650- type : 'Identifier' ,
651- name : encode ( 'string->number' )
652- } ,
653- arguments : [
654- {
655- type : 'Literal' ,
656- value : node . toString ( )
657- }
658- ]
659- }
660- } else if ( typeof node === 'boolean' ) {
661- return {
662- type : 'Literal' ,
663- value : node
543+ name : 'list'
544+ } ,
545+ arguments : args
546+ }
547+ } else if ( isImproperList ( expression ) ) {
548+ // make a call to cons
549+ // with the car and cdr as its arguments.
550+ const [ car , cdr ] = expression as [ SchemeControlItems , SchemeControlItems ]
551+ return {
552+ type : 'CallExpression' ,
553+ optional : false ,
554+ callee : {
555+ type : 'Identifier' ,
556+ name : 'cons'
557+ } ,
558+ arguments : [ convertToEstreeExpression ( car ) , convertToEstreeExpression ( cdr ) ]
559+ }
560+ } else if ( expression instanceof _Symbol ) {
561+ // make a call to string->symbol
562+ // with the symbol name as its argument.
563+ return {
564+ type : 'CallExpression' ,
565+ optional : false ,
566+ callee : {
567+ type : 'Identifier' ,
568+ name : encode ( 'string->symbol' )
569+ } ,
570+ arguments : [
571+ {
572+ type : 'Literal' ,
573+ value : expression . sym
574+ }
575+ ]
576+ }
577+ } else if ( is_number ( expression ) ) {
578+ // make a call to string->number
579+ // with the number toString() as its argument.
580+ return {
581+ type : 'CallExpression' ,
582+ optional : false ,
583+ callee : {
584+ type : 'Identifier' ,
585+ name : encode ( 'string->number' )
586+ } ,
587+ arguments : [
588+ {
589+ type : 'Literal' ,
590+ value : ( expression as any ) . toString ( )
591+ }
592+ ]
593+ }
664594 }
665- } else if ( typeof node === 'string' ) {
595+ // if we're here, then it is a boolean or string.
596+ // just return the literal value.
666597 return {
667598 type : 'Literal' ,
668- value : node
599+ value : expression as boolean | string
669600 }
670601 }
671- // if we get to this point, just return undefined
602+
603+ // make a call expression to eval with the single expression as its component.
672604 return {
673- type : 'Literal' ,
674- value : "undefined"
605+ type : 'CallExpression' ,
606+ optional : false ,
607+ callee : {
608+ type : 'Identifier' ,
609+ name : encode ( 'eval' )
610+ } ,
611+ arguments : [ convertToEstreeExpression ( expression ) as es . Expression ]
675612 }
676613}
0 commit comments