zuzu-js 0.1.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/cli.js CHANGED
@@ -254,7 +254,7 @@ function stripShebang( source ) {
254
254
  }
255
255
 
256
256
  function printVersion( runtime, verbose ) {
257
- process.stdout.write( 'zuzu-js version 0.1.2\n' );
257
+ process.stdout.write( 'zuzu-js version 0.3.0\n' );
258
258
  if ( verbose ) {
259
259
  process.stdout.write( '\nlib search paths:\n' );
260
260
  for ( const p of runtime.getModuleSearchRoots() ) {
@@ -251,6 +251,8 @@ function collectTopLevelDeclarations( source, stripPod ) {
251
251
  let inSingle = false;
252
252
  let inDouble = false;
253
253
  let inBacktick = false;
254
+ let inRegex = false;
255
+ let inRegexClass = false;
254
256
  let inLineComment = false;
255
257
  let inBlockComment = false;
256
258
  let escape = false;
@@ -280,6 +282,35 @@ function collectTopLevelDeclarations( source, stripPod ) {
280
282
  masked.push( ch === '\n' ? '\n' : ' ' );
281
283
  continue;
282
284
  }
285
+ if ( inRegex ) {
286
+ if ( escape ) {
287
+ escape = false;
288
+ masked.push( ' ' );
289
+ continue;
290
+ }
291
+ if ( ch === '\\' ) {
292
+ escape = true;
293
+ masked.push( ' ' );
294
+ continue;
295
+ }
296
+ if ( ch === '[' ) {
297
+ inRegexClass = true;
298
+ masked.push( ' ' );
299
+ continue;
300
+ }
301
+ if ( ch === ']' ) {
302
+ inRegexClass = false;
303
+ masked.push( ' ' );
304
+ continue;
305
+ }
306
+ if ( ch === '/' && !inRegexClass ) {
307
+ inRegex = false;
308
+ masked.push( ' ' );
309
+ continue;
310
+ }
311
+ masked.push( ch === '\n' ? '\n' : ' ' );
312
+ continue;
313
+ }
283
314
  if ( inSingle || inDouble || inBacktick ) {
284
315
  if ( escape ) {
285
316
  escape = false;
@@ -318,6 +349,12 @@ function collectTopLevelDeclarations( source, stripPod ) {
318
349
  masked.push( ' ' );
319
350
  continue;
320
351
  }
352
+ if ( ch === '/' && looksLikeRegexLiteralStart( stripped, i ) ) {
353
+ inRegex = true;
354
+ inRegexClass = false;
355
+ masked.push( ' ' );
356
+ continue;
357
+ }
321
358
  if ( ch === '\'' ) {
322
359
  inSingle = true;
323
360
  masked.push( ' ' );
@@ -391,6 +428,18 @@ function collectTopLevelDeclarations( source, stripPod ) {
391
428
  return [ ...names ];
392
429
  }
393
430
 
431
+ function looksLikeRegexLiteralStart( source, index ) {
432
+ let pos = index - 1;
433
+ while ( pos >= 0 && /\s/u.test( source[pos] ) ) {
434
+ pos--;
435
+ }
436
+ if ( pos < 0 ) {
437
+ return true;
438
+ }
439
+ const previous = source[pos];
440
+ return /[({[=,:;!~?&|+\-*%^<>]/u.test( previous );
441
+ }
442
+
394
443
  function collectTopLevelUnpackDeclarationNames( source, topLevelMask ) {
395
444
  const names = [];
396
445
  const rx = /(?:^|[;\n])\s*(?:export\s+)?(?:let|const)\b/gm;
package/lib/runtime.js CHANGED
@@ -53,6 +53,28 @@ const textEncoder = new TextEncoder();
53
53
  const utf8Decoder = new TextDecoder( 'utf-8', { fatal: true } );
54
54
  const ZUZU_SKIP_BUILD = Symbol.for( 'zuzu.skip_build' );
55
55
 
56
+ const runtimeVersion = (() => {
57
+ if ( typeof globalThis === 'object' && globalThis !== null ) {
58
+ const globalVersion = globalThis.__ZUZU_RUNTIME_VERSION;
59
+ if ( typeof globalVersion === 'string' && globalVersion.trim() ) {
60
+ return String( globalVersion );
61
+ }
62
+ }
63
+ if ( typeof process === 'object' && process && process.versions ) {
64
+ try {
65
+ const packageMetadata = require( '../package.json' );
66
+ const packageVersion = packageMetadata && packageMetadata.version;
67
+ if ( packageVersion ) {
68
+ return String( packageVersion );
69
+ }
70
+ }
71
+ catch ( err ) {
72
+ return 'dev';
73
+ }
74
+ }
75
+ return 'dev';
76
+ })();
77
+
56
78
  function defaultModuleSearchRoots( host, repoRoot, includePaths ) {
57
79
  if ( host.name === 'browser' ) {
58
80
  return [
@@ -1283,7 +1305,11 @@ function defineZuzuClass( name, base, spec = {} ) {
1283
1305
  zuzuStoreField( this, field, value );
1284
1306
  }
1285
1307
  let named = null;
1286
- if ( ctorArgs.length === 1 && isPairListLike( ctorArgs[0] ) ) {
1308
+ const lastArg = ctorArgs[ctorArgs.length - 1];
1309
+ if (
1310
+ ctorArgs.length === 1
1311
+ && isPairListLike( ctorArgs[0] )
1312
+ ) {
1287
1313
  named = ctorArgs[0];
1288
1314
  }
1289
1315
  else if (
@@ -1294,6 +1320,20 @@ function defineZuzuClass( name, base, spec = {} ) {
1294
1320
  ) {
1295
1321
  named = ctorArgs[0];
1296
1322
  }
1323
+ else if (
1324
+ ctorArgs.length > 1
1325
+ && isPairListLike( lastArg )
1326
+ ) {
1327
+ named = lastArg;
1328
+ }
1329
+ else if (
1330
+ ctorArgs.length > 1
1331
+ && lastArg
1332
+ && typeof lastArg === 'object'
1333
+ && !Array.isArray( lastArg )
1334
+ ) {
1335
+ named = lastArg;
1336
+ }
1297
1337
  const releaseTemporaryNamed = !skipBuild
1298
1338
  && isPairListLike( named )
1299
1339
  && named.__zuzu_temporary_call_args === true;
@@ -1459,9 +1499,6 @@ function zuzuCallMember( object, property, ...args ) {
1459
1499
  if ( dictMethod ) {
1460
1500
  return dictMethod( ...args );
1461
1501
  }
1462
- if ( args.length === 0 ) {
1463
- return value;
1464
- }
1465
1502
  throw new TypeError( `${String( property )} is not a function` );
1466
1503
  }
1467
1504
 
@@ -2421,7 +2458,7 @@ class ZuzuScript {
2421
2458
  const systemGlobalSeed = {
2422
2459
  language_version: 0,
2423
2460
  runtime: 'zuzu-js',
2424
- runtime_version: 'dev',
2461
+ runtime_version: runtimeVersion,
2425
2462
  platform: this.host.name,
2426
2463
  inc: Object.freeze( moduleSearchRoots.slice() ),
2427
2464
  deny_fs: capabilityFlags.fs ? false : true,
@@ -609,6 +609,8 @@ function extractSpecialPreludeNames( signature ) {
609
609
  return [ '__zuzu_call_args', signature.headName, signature.restName, signature.namedName, '__i', '__a' ];
610
610
  case 'scalar_pairlist':
611
611
  return [ '__zuzu_call_args', signature.headName, signature.namedName, '__i', '__a' ];
612
+ case 'scalar_pairlist_array':
613
+ return [ '__zuzu_call_args', signature.headName, signature.namedName, signature.restName, '__i', '__a' ];
612
614
  case 'variadic':
613
615
  return [ '__zuzu_call_args', signature.headName, signature.restName, '__i', '__a' ];
614
616
  default:
@@ -694,6 +696,22 @@ function analyzeSpecialSignature( params ) {
694
696
  namedName: params[1].name,
695
697
  };
696
698
  }
699
+ if (
700
+ params.length === 3
701
+ && params[0].type === 'Parameter'
702
+ && params[1].type === 'SpecialParameter'
703
+ && params[1].special === 'rest_only'
704
+ && params[1].containerType === 'PairList'
705
+ && params[2].type === 'Parameter'
706
+ && ( params[2].typeName === 'Array' || params[2].typeName == null )
707
+ ) {
708
+ return {
709
+ kind: 'scalar_pairlist_array',
710
+ headName: params[0].name,
711
+ namedName: params[1].name,
712
+ restName: params[2].name,
713
+ };
714
+ }
697
715
  if (
698
716
  params.length === 1
699
717
  && params[0].type === 'SpecialParameter'
@@ -729,7 +747,7 @@ function emitFunctionPreamble( params, signature = null ) {
729
747
  for ( const param of params ) {
730
748
  if ( param.type === 'SpecialParameter' ) {
731
749
  if ( param.special === 'lead_rest' ) {
732
- lines.push( `let ${param.leadName} = arguments[${argIndex}];` );
750
+ lines.push( `const ${param.leadName} = arguments[${argIndex}];` );
733
751
  argIndex++;
734
752
  lines.push( `const ${param.name} = Array.prototype.slice.call( arguments, ${argIndex} );` );
735
753
  continue;
@@ -757,13 +775,13 @@ function emitFunctionPreamble( params, signature = null ) {
757
775
  continue;
758
776
  }
759
777
  if ( param.defaultValue ) {
760
- lines.push( `let ${paramName} = __argc__ > ${argIndex} ? arguments[${argIndex}] : ${emitExpression( param.defaultValue )};` );
778
+ lines.push( `const ${paramName} = __argc__ > ${argIndex} ? arguments[${argIndex}] : ${emitExpression( param.defaultValue )};` );
761
779
  }
762
780
  else if ( param.optional ) {
763
- lines.push( `let ${paramName} = __argc__ > ${argIndex} ? arguments[${argIndex}] : null;` );
781
+ lines.push( `const ${paramName} = __argc__ > ${argIndex} ? arguments[${argIndex}] : null;` );
764
782
  }
765
783
  else {
766
- lines.push( `let ${paramName} = arguments[${argIndex}];` );
784
+ lines.push( `const ${paramName} = arguments[${argIndex}];` );
767
785
  }
768
786
  if ( param.typeName ) {
769
787
  lines.push(
@@ -780,62 +798,84 @@ function emitSpecialFunctionPreamble( signature ) {
780
798
  case 'pairlist_only':
781
799
  return [
782
800
  `const __zuzu_call_args = Array.prototype.slice.call( arguments );`,
783
- `let ${signature.namedName} = __zuzu_pairlist_literal( [] );`,
801
+ 'let __zuzu_named_args = __zuzu_pairlist_literal( [] );',
784
802
  'for ( const __a of __zuzu_call_args ) {',
785
- `if ( __zuzu_is_pairlist( __a ) ) { ${signature.namedName} = __a; } else { throw new Exception( "named PairList parameter only accepts named arguments" ); }`,
803
+ 'if ( __zuzu_is_pairlist( __a ) ) { __zuzu_named_args = __a; } else { throw new Exception( "named PairList parameter only accepts named arguments" ); }',
786
804
  '}',
805
+ `const ${signature.namedName} = __zuzu_named_args;`,
787
806
  ].join( '\n' );
788
807
  case 'pairlist_rest_array':
789
808
  case 'rest_array_pairlist':
790
809
  return [
791
810
  `const __zuzu_call_args = Array.prototype.slice.call( arguments );`,
792
- `let ${signature.namedName} = __zuzu_pairlist_literal( [] );`,
793
- `let ${signature.restName} = [];`,
811
+ 'let __zuzu_named_args = __zuzu_pairlist_literal( [] );',
812
+ 'const __zuzu_rest_args = [];',
794
813
  'for ( const __a of __zuzu_call_args ) {',
795
- `if ( __zuzu_is_pairlist( __a ) ) { ${signature.namedName} = __a; } else { ${signature.restName}.push( __a ); }`,
814
+ 'if ( __zuzu_is_pairlist( __a ) ) { __zuzu_named_args = __a; } else { __zuzu_rest_args.push( __a ); }',
796
815
  '}',
816
+ `const ${signature.namedName} = __zuzu_named_args;`,
817
+ `const ${signature.restName} = __zuzu_rest_args;`,
797
818
  ].join( '\n' );
798
819
  case 'lead_pairlist':
799
820
  return [
800
821
  'const __zuzu_call_args = Array.prototype.slice.call( arguments );',
801
- `let ${signature.headName} = __zuzu_call_args[0];`,
802
- `let ${signature.namedName} = __zuzu_pairlist_literal( [] );`,
803
- `let ${signature.restName} = [];`,
822
+ `const ${signature.headName} = __zuzu_call_args[0];`,
823
+ 'let __zuzu_named_args = __zuzu_pairlist_literal( [] );',
824
+ 'const __zuzu_rest_args = [];',
804
825
  'for ( let __i = 1; __i < __zuzu_call_args.length; __i++ ) {',
805
826
  'const __a = __zuzu_call_args[__i];',
806
- `if ( __zuzu_is_pairlist( __a ) ) { ${signature.namedName} = __a; } else { ${signature.restName}.push( __a ); }`,
827
+ 'if ( __zuzu_is_pairlist( __a ) ) { __zuzu_named_args = __a; } else { __zuzu_rest_args.push( __a ); }',
807
828
  '}',
829
+ `const ${signature.namedName} = __zuzu_named_args;`,
830
+ `const ${signature.restName} = __zuzu_rest_args;`,
808
831
  ].join( '\n' );
809
832
  case 'trail_pairlist':
810
833
  return [
811
834
  'const __zuzu_call_args = Array.prototype.slice.call( arguments );',
812
- `let ${signature.headName} = __zuzu_call_args[0];`,
813
- `let ${signature.restName} = [];`,
814
- `let ${signature.namedName} = __zuzu_pairlist_literal( [] );`,
835
+ `const ${signature.headName} = __zuzu_call_args[0];`,
836
+ 'const __zuzu_rest_args = [];',
837
+ 'let __zuzu_named_args = __zuzu_pairlist_literal( [] );',
815
838
  'for ( let __i = 1; __i < __zuzu_call_args.length; __i++ ) {',
816
839
  'const __a = __zuzu_call_args[__i];',
817
- `if ( __zuzu_is_pairlist( __a ) ) { ${signature.namedName} = __a; } else { ${signature.restName}.push( __a ); }`,
840
+ 'if ( __zuzu_is_pairlist( __a ) ) { __zuzu_named_args = __a; } else { __zuzu_rest_args.push( __a ); }',
818
841
  '}',
842
+ `const ${signature.restName} = __zuzu_rest_args;`,
843
+ `const ${signature.namedName} = __zuzu_named_args;`,
819
844
  ].join( '\n' );
820
845
  case 'scalar_pairlist':
821
846
  return [
822
847
  'const __zuzu_call_args = Array.prototype.slice.call( arguments );',
823
- `let ${signature.headName} = __zuzu_call_args[0];`,
824
- `let ${signature.namedName} = __zuzu_pairlist_literal( [] );`,
848
+ `const ${signature.headName} = __zuzu_call_args[0];`,
849
+ 'let __zuzu_named_args = __zuzu_pairlist_literal( [] );',
825
850
  'for ( let __i = 1; __i < __zuzu_call_args.length; __i++ ) {',
826
851
  'const __a = __zuzu_call_args[__i];',
827
- `if ( __zuzu_is_pairlist( __a ) ) { ${signature.namedName} = __a; } else { throw new Exception( "named arguments not allowed for this function" ); }`,
852
+ 'if ( __zuzu_is_pairlist( __a ) ) { __zuzu_named_args = __a; } else { throw new Exception( "named arguments not allowed for this function" ); }',
828
853
  '}',
854
+ `const ${signature.namedName} = __zuzu_named_args;`,
855
+ ].join( '\n' );
856
+ case 'scalar_pairlist_array':
857
+ return [
858
+ 'const __zuzu_call_args = Array.prototype.slice.call( arguments );',
859
+ `const ${signature.headName} = __zuzu_call_args[0];`,
860
+ 'let __zuzu_named_args = __zuzu_pairlist_literal( [] );',
861
+ 'const __zuzu_rest_args = [];',
862
+ 'for ( let __i = 1; __i < __zuzu_call_args.length; __i++ ) {',
863
+ 'const __a = __zuzu_call_args[__i];',
864
+ 'if ( __zuzu_is_pairlist( __a ) ) { __zuzu_named_args = __a; } else { __zuzu_rest_args.push( __a ); }',
865
+ '}',
866
+ `const ${signature.restName} = __zuzu_rest_args;`,
867
+ `const ${signature.namedName} = __zuzu_named_args;`,
829
868
  ].join( '\n' );
830
869
  case 'variadic':
831
870
  return [
832
871
  'const __zuzu_call_args = Array.prototype.slice.call( arguments );',
833
- `let ${signature.headName} = __zuzu_call_args[0];`,
834
- `let ${signature.restName} = [];`,
872
+ `const ${signature.headName} = __zuzu_call_args[0];`,
873
+ 'const __zuzu_rest_args = [];',
835
874
  'for ( let __i = 1; __i < __zuzu_call_args.length; __i++ ) {',
836
875
  'const __a = __zuzu_call_args[__i];',
837
- `if ( __zuzu_is_pairlist( __a ) ) { throw new Exception( "named arguments not allowed for this function" ); } ${signature.restName}.push( __a );`,
876
+ 'if ( __zuzu_is_pairlist( __a ) ) { throw new Exception( "named arguments not allowed for this function" ); } __zuzu_rest_args.push( __a );',
838
877
  '}',
878
+ `const ${signature.restName} = __zuzu_rest_args;`,
839
879
  ].join( '\n' );
840
880
  default:
841
881
  return '';
@@ -2154,6 +2194,9 @@ function emitAssignmentExpression( node ) {
2154
2194
  : `${left} = __zuzu_assign_strong( ${left}, ${right} )`;
2155
2195
  }
2156
2196
  if ( node.left && node.left.type === 'MemberExpression' ) {
2197
+ if ( !node.left.computed ) {
2198
+ throw new UnsupportedSyntaxError( 'Invalid assignment target' );
2199
+ }
2157
2200
  const object = emitExpression( node.left.object );
2158
2201
  const objectTarget = emitAssignmentTarget( node.left.object );
2159
2202
  const key = node.left.computed
@@ -55,26 +55,22 @@ function parse( tokens, options = {} ) {
55
55
  return tokens[index - 1];
56
56
  }
57
57
 
58
- function canTerminateStatement() {
58
+ function canTerminateSimpleStatement() {
59
59
  const token = current();
60
- const prev = previous();
61
- if ( !prev ) {
62
- return false;
63
- }
64
60
  if ( token.type === 'punctuation' && token.value === '}' ) {
65
61
  return true;
66
62
  }
67
63
  if ( token.type === 'eof' ) {
68
64
  return true;
69
65
  }
70
- return token.line > prev.endLine;
66
+ return false;
71
67
  }
72
68
 
73
69
  function consumeStatementTerminator(message) {
74
70
  if ( match( 'punctuation', ';' ) ) {
75
71
  return;
76
72
  }
77
- if ( canTerminateStatement() ) {
73
+ if ( canTerminateSimpleStatement() ) {
78
74
  return;
79
75
  }
80
76
  throw new TranspilerSyntaxError( message || 'Expected statement terminator', current() );
@@ -1313,7 +1309,7 @@ function parse( tokens, options = {} ) {
1313
1309
  prefix: true,
1314
1310
  }, keyword, endLocFromNode( test ) );
1315
1311
  }
1316
- expect( 'punctuation', ';', semicolonMessage );
1312
+ consumeStatementTerminator( semicolonMessage );
1317
1313
  const consequent = withLoc( {
1318
1314
  type: 'BlockStatement',
1319
1315
  body: [ stmt ],
@@ -1330,7 +1326,7 @@ function parse( tokens, options = {} ) {
1330
1326
  const keyword = current();
1331
1327
  index++;
1332
1328
  const iterable = parseExpression();
1333
- expect( 'punctuation', ';', semicolonMessage );
1329
+ consumeStatementTerminator( semicolonMessage );
1334
1330
  const body = withLoc( {
1335
1331
  type: 'BlockStatement',
1336
1332
  body: [ stmt ],
@@ -235,6 +235,7 @@ function validateExpression( node, scope ) {
235
235
  }
236
236
  return;
237
237
  case 'UpdateExpression':
238
+ requireValidAssignmentTarget( node.argument );
238
239
  if ( node.argument && node.argument.type === 'Identifier' ) {
239
240
  requireMutable( node.argument, scope );
240
241
  }
@@ -242,6 +243,23 @@ function validateExpression( node, scope ) {
242
243
  validateExpression( node.argument, scope );
243
244
  }
244
245
  return;
246
+ case 'UnaryExpression':
247
+ if ( node.operator === '++' || node.operator === '--' ) {
248
+ requireValidAssignmentTarget( node.argument );
249
+ if ( node.argument && node.argument.type === 'Identifier' ) {
250
+ requireMutable( node.argument, scope );
251
+ }
252
+ else {
253
+ validateExpression( node.argument, scope );
254
+ }
255
+ return;
256
+ }
257
+ validateChildNodes( node, scope );
258
+ return;
259
+ case 'RefExpression':
260
+ requireValidAssignmentTarget( node.argument );
261
+ validateExpression( node.argument, scope );
262
+ return;
245
263
  case 'FunctionExpression':
246
264
  validateFunctionBody( node, scope );
247
265
  return;
@@ -261,11 +279,13 @@ function requireValidAssignmentTarget( node ) {
261
279
  }
262
280
  if (
263
281
  target.type === 'Identifier'
264
- || target.type === 'MemberExpression'
265
282
  || target.type === 'SliceExpression'
266
283
  ) {
267
284
  return;
268
285
  }
286
+ if ( target.type === 'MemberExpression' && target.computed ) {
287
+ return;
288
+ }
269
289
  if (
270
290
  target.type === 'BinaryExpression'
271
291
  && [ '@', '@@', '@?' ].includes( target.operator )
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zuzu-js",
3
- "version": "0.1.2",
3
+ "version": "0.3.0",
4
4
  "description": "JavaScript runtime, compiler, and browser bundle for ZuzuScript.",
5
5
  "main": "lib/zuzu.js",
6
6
  "bin": {
@@ -158,6 +158,7 @@ function parse_lines ( Array lines ) {
158
158
  let top_level := _new_counts();
159
159
  let assertions := _new_counts();
160
160
  let planned := null;
161
+ let plan_directive := "";
161
162
  let tests := [];
162
163
 
163
164
  for ( let line in lines ) {
@@ -170,8 +171,11 @@ function parse_lines ( Array lines ) {
170
171
 
171
172
  if ( body ~ /^\d+\.\.\d+\b/ ) {
172
173
  if ( is_top_level ) {
173
- let parts := split( body, "..", 2 );
174
- planned := parts.get( 1, "" ) + 0;
174
+ let plan_parts := split( body, "#", 2 );
175
+ let range_parts := split( trim( plan_parts[0] ), "..", 2 );
176
+ planned := trim( range_parts.get( 1, "" ) ) + 0;
177
+ plan_directive := trim( plan_parts.get( 1, "" ) )
178
+ if plan_parts.length() > 1;
175
179
  }
176
180
  next;
177
181
  }
@@ -186,10 +190,17 @@ function parse_lines ( Array lines ) {
186
190
  }
187
191
  }
188
192
 
193
+ let skip_all_plan := false;
194
+ if ( planned ≡ 0 and plan_directive ~ /^skip\b/i ) {
195
+ skip_all_plan := true;
196
+ }
197
+
189
198
  return {
190
199
  top_level: top_level,
191
200
  assertions: assertions,
192
201
  planned: planned,
202
+ plan_directive: plan_directive,
203
+ skip_all: skip_all_plan,
193
204
  tests: tests,
194
205
  };
195
206
  }