@@ -28,18 +28,33 @@ const _let = Sym('let');
2828
2929
3030export function asString ( expr ) {
31+ try {
3132 if ( typeof expr === 'symbol' ) {
3233 return Symbol . keyFor ( expr ) || expr . toString ( ) ;
3334 } else if ( Array . isArray ( expr ) ) {
34- return `(${ expr . map ( asString ) . join ( ' ' ) } )` ;
35+ return `(${ expr . map ( ( ea ) => asStringSafe ( ea ) ) . join ( ' ' ) } )` ;
3536 } else if ( expr === true ) {
3637 return '#t' ;
3738 } else if ( expr === false ) {
3839 return '#f' ;
3940 } else {
4041 return expr . toString ( ) ;
4142 }
43+ } catch ( e ) {
44+ return '#<error>' ;
45+ }
46+ }
4247
48+ function asStringSafe ( expr , covered_exprs = new Set ( ) ) {
49+ if ( covered_exprs . has ( expr ) ) {
50+ return '#<circular>' ;
51+ }
52+ covered_exprs . add ( expr ) ;
53+ if ( Array . isArray ( expr ) ) {
54+ return `(${ expr . map ( ( ea ) => asStringSafe ( ea , covered_exprs ) ) . join ( ' ' ) } )` ;
55+ } else {
56+ return asString ( expr ) ;
57+ }
4358}
4459
4560// Procedure class
@@ -65,7 +80,7 @@ export class Env extends Map {
6580 this . set ( parms , Array . from ( args ) ) ;
6681 } else {
6782 if ( args . length !== parms . length ) {
68- throw new Error ( `Expected ${ parms } , given ${ args } ` ) ;
83+ throw new Error ( `Expected ${ asString ( parms ) } , given ${ asString ( args ) } ` ) ;
6984 }
7085 parms . forEach ( ( param , i ) => this . set ( param , args [ i ] ) ) ;
7186 }
@@ -271,17 +286,17 @@ function addGlobals(env) {
271286 env . set ( Sym ( "raise" ) , ( e ) => { throw e ; } ) ;
272287
273288 // String operations
274- env . set ( Sym ( 'string-append' ) , ( ...strs ) => strs . reduce ( ( acc , s ) => acc + String ( s ) , '' ) ) ;
275- env . set ( Sym ( 'string-split' ) , ( s , sep ) => String ( s ) . split ( sep ) ) ;
276- env . set ( Sym ( 'string-replace' ) , ( old , newStr , s ) => String ( s ) . replace ( old , newStr ) ) ;
289+ env . set ( Sym ( 'string-append' ) , ( ...strs ) => strs . reduce ( ( acc , s ) => acc + asString ( s ) , '' ) ) ;
290+ env . set ( Sym ( 'string-split' ) , ( s , sep ) => asString ( s ) . split ( sep ) ) ;
291+ env . set ( Sym ( 'string-replace' ) , ( old , newStr , s ) => asString ( s ) . replace ( old , newStr ) ) ;
277292 env . set ( Sym ( 'string-index' ) , ( str , substr ) => {
278293 const idx = str . indexOf ( substr ) ;
279294 return idx !== - 1 ? idx : false ;
280295 } ) ;
281- env . set ( Sym ( 'string-upcase' ) , s => String ( s ) . toUpperCase ( ) ) ;
282- env . set ( Sym ( 'string-downcase' ) , s => String ( s ) . toLowerCase ( ) ) ;
283- env . set ( Sym ( 'string-trim' ) , s => String ( s ) . trim ( ) ) ;
284- env . set ( Sym ( 'number->string' ) , x => String ( x ) ) ;
296+ env . set ( Sym ( 'string-upcase' ) , s => asString ( s ) . toUpperCase ( ) ) ;
297+ env . set ( Sym ( 'string-downcase' ) , s => asString ( s ) . toLowerCase ( ) ) ;
298+ env . set ( Sym ( 'string-trim' ) , s => asString ( s ) . trim ( ) ) ;
299+ env . set ( Sym ( 'number->string' ) , x => asString ( x ) ) ;
285300
286301 // Type checking
287302 env . set ( Sym ( 'null?' ) , x => x === null || x === undefined || ( Array . isArray ( x ) && x . length === 0 ) ) ;
0 commit comments