zuzu-js 0.2.0 → 0.4.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/README.md +0 -6
- package/bin/zuzu-generate-browser-stdlib +1 -0
- package/dist/zuzu-browser-worker.js +1484 -369
- package/dist/zuzu-browser.js +1488 -373
- package/lib/cli.js +1 -1
- package/lib/collections.js +6 -2
- package/lib/runtime-helpers.js +36 -0
- package/lib/runtime.js +128 -10
- package/lib/transpiler-new/codegen.js +45 -0
- package/lib/transpiler-new/lexer.js +40 -1
- package/lib/transpiler-new/parser.js +32 -14
- package/lib/transpiler-new/validate-bindings.js +21 -1
- package/modules/std/math/bignum.js +158 -31
- package/modules/std/math.js +1 -1
- package/modules/std/net/url.js +33 -24
- package/modules/std/string/encode.js +188 -0
- package/modules/std/string.js +21 -0
- package/modules/std/time.js +95 -4
- package/package.json +2 -1
- package/stdlib/modules/std/colour.zzm +1 -0
- package/stdlib/modules/std/config.zzm +6 -5
- package/stdlib/modules/std/getopt.zzm +3 -15
- package/stdlib/modules/std/net/url.zzm +45 -3
- package/stdlib/modules/std/string/encode.zzm +95 -0
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.
|
|
257
|
+
process.stdout.write( 'zuzu-js version 0.4.0\n' );
|
|
258
258
|
if ( verbose ) {
|
|
259
259
|
process.stdout.write( '\nlib search paths:\n' );
|
|
260
260
|
for ( const p of runtime.getModuleSearchRoots() ) {
|
package/lib/collections.js
CHANGED
|
@@ -24,6 +24,10 @@ function makeSet( values ) {
|
|
|
24
24
|
return runtimeHelpers().makeSet( values );
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
function stringSortComparator( a, b ) {
|
|
28
|
+
return runtimeHelpers().stringSortComparator( a, b );
|
|
29
|
+
}
|
|
30
|
+
|
|
27
31
|
class ZuzuBag {
|
|
28
32
|
constructor( ...items ) {
|
|
29
33
|
let values;
|
|
@@ -105,7 +109,7 @@ class ZuzuBag {
|
|
|
105
109
|
sum() { return this.items.reduce( (a, b) => Number( a ) + Number( b ), 0 ); }
|
|
106
110
|
product() { return this.items.reduce( (a, b) => Number( a ) * Number( b ), 1 ); }
|
|
107
111
|
sort( fn ) { return this.to_Array().sort( fn ); }
|
|
108
|
-
sortstr() { return this.to_Array().sort(
|
|
112
|
+
sortstr() { return this.to_Array().sort( stringSortComparator ); }
|
|
109
113
|
sortnum() { return this.to_Array().map( (item) => Number( item ) ).sort( (a, b) => a - b ); }
|
|
110
114
|
clear() { releaseCollectionValues( this ); this.items = []; return this; }
|
|
111
115
|
[Symbol.iterator]() { return this.items[Symbol.iterator](); }
|
|
@@ -338,7 +342,7 @@ function withArrayMethods() {
|
|
|
338
342
|
define( 'shuffle', function _shuffle() { return this.slice(); } );
|
|
339
343
|
define( 'sample', function _sample( n ) { return this.slice( 0, n ); } );
|
|
340
344
|
define( 'for_each_value', function _for_each_value( fn ) { this.forEach( fn ); return this; } );
|
|
341
|
-
define( 'sortstr', function _sortstr() { return this.slice().sort(
|
|
345
|
+
define( 'sortstr', function _sortstr() { return this.slice().sort( stringSortComparator ); } );
|
|
342
346
|
define(
|
|
343
347
|
'sortnum',
|
|
344
348
|
function _sortnum() {
|
package/lib/runtime-helpers.js
CHANGED
|
@@ -886,6 +886,40 @@ function operatorString( value ) {
|
|
|
886
886
|
throw new Error( `TypeException: cannot coerce ${typeName( value )} to String` );
|
|
887
887
|
}
|
|
888
888
|
|
|
889
|
+
const surrogatePattern = /[\uD800-\uDFFF]/;
|
|
890
|
+
|
|
891
|
+
// Compare two native strings by Unicode code point, matching the string
|
|
892
|
+
// ordering used by zuzu-rust and zuzu-perl. Plain UTF-16 comparison only
|
|
893
|
+
// disagrees with code-point order when astral characters (surrogate
|
|
894
|
+
// pairs) meet code units in the U+E000..U+FFFF range, so the slow path is
|
|
895
|
+
// only taken when a surrogate is present.
|
|
896
|
+
function codePointStringCompare( left, right ) {
|
|
897
|
+
if ( left === right ) {
|
|
898
|
+
return 0;
|
|
899
|
+
}
|
|
900
|
+
if ( surrogatePattern.test( left ) || surrogatePattern.test( right ) ) {
|
|
901
|
+
const leftPoints = Array.from( left );
|
|
902
|
+
const rightPoints = Array.from( right );
|
|
903
|
+
const shared = Math.min( leftPoints.length, rightPoints.length );
|
|
904
|
+
for ( let i = 0; i < shared; i++ ) {
|
|
905
|
+
const a = leftPoints[i].codePointAt( 0 );
|
|
906
|
+
const b = rightPoints[i].codePointAt( 0 );
|
|
907
|
+
if ( a !== b ) {
|
|
908
|
+
return a < b ? -1 : 1;
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
if ( leftPoints.length === rightPoints.length ) {
|
|
912
|
+
return 0;
|
|
913
|
+
}
|
|
914
|
+
return leftPoints.length < rightPoints.length ? -1 : 1;
|
|
915
|
+
}
|
|
916
|
+
return left < right ? -1 : 1;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
function stringSortComparator( a, b ) {
|
|
920
|
+
return codePointStringCompare( String( a ), String( b ) );
|
|
921
|
+
}
|
|
922
|
+
|
|
889
923
|
function operatorRegexp( value ) {
|
|
890
924
|
value = resolveWeakValue( value );
|
|
891
925
|
if ( value instanceof RegExp || Object.prototype.toString.call( value ) === '[object RegExp]' ) {
|
|
@@ -915,6 +949,8 @@ module.exports = {
|
|
|
915
949
|
lengthOf,
|
|
916
950
|
operatorString,
|
|
917
951
|
operatorRegexp,
|
|
952
|
+
codePointStringCompare,
|
|
953
|
+
stringSortComparator,
|
|
918
954
|
ZuzuBinary,
|
|
919
955
|
BinaryString,
|
|
920
956
|
ZuzuWeakCell,
|
package/lib/runtime.js
CHANGED
|
@@ -46,6 +46,8 @@ const {
|
|
|
46
46
|
addSetValue,
|
|
47
47
|
assignStrongValue,
|
|
48
48
|
assignWeakValue,
|
|
49
|
+
codePointStringCompare,
|
|
50
|
+
stringSortComparator,
|
|
49
51
|
} = require( './runtime-helpers' );
|
|
50
52
|
const taskRuntime = require( '../modules/std/task' );
|
|
51
53
|
|
|
@@ -53,6 +55,28 @@ const textEncoder = new TextEncoder();
|
|
|
53
55
|
const utf8Decoder = new TextDecoder( 'utf-8', { fatal: true } );
|
|
54
56
|
const ZUZU_SKIP_BUILD = Symbol.for( 'zuzu.skip_build' );
|
|
55
57
|
|
|
58
|
+
const runtimeVersion = (() => {
|
|
59
|
+
if ( typeof globalThis === 'object' && globalThis !== null ) {
|
|
60
|
+
const globalVersion = globalThis.__ZUZU_RUNTIME_VERSION;
|
|
61
|
+
if ( typeof globalVersion === 'string' && globalVersion.trim() ) {
|
|
62
|
+
return String( globalVersion );
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if ( typeof process === 'object' && process && process.versions ) {
|
|
66
|
+
try {
|
|
67
|
+
const packageMetadata = require( '../package.json' );
|
|
68
|
+
const packageVersion = packageMetadata && packageMetadata.version;
|
|
69
|
+
if ( packageVersion ) {
|
|
70
|
+
return String( packageVersion );
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch ( err ) {
|
|
74
|
+
return 'dev';
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return 'dev';
|
|
78
|
+
})();
|
|
79
|
+
|
|
56
80
|
function defaultModuleSearchRoots( host, repoRoot, includePaths ) {
|
|
57
81
|
if ( host.name === 'browser' ) {
|
|
58
82
|
return [
|
|
@@ -226,11 +250,21 @@ function stringCompare( left, right, options = {} ) {
|
|
|
226
250
|
const r = options.insensitive
|
|
227
251
|
? zuzuOperatorString( right ).toLowerCase()
|
|
228
252
|
: zuzuOperatorString( right );
|
|
229
|
-
|
|
253
|
+
// Code-point order, matching zuzu-rust and zuzu-perl; localeCompare
|
|
254
|
+
// would give locale-dependent, case-folded ordering.
|
|
255
|
+
return codePointStringCompare( l, r );
|
|
230
256
|
}
|
|
231
257
|
|
|
232
258
|
function parseNumericString( value ) {
|
|
233
259
|
const text = String( value ).trim();
|
|
260
|
+
// Coercion is more permissive than the lexer: radix prefixes match
|
|
261
|
+
// either case.
|
|
262
|
+
const radix = text.match( /^([+-]?)0([xX][0-9a-fA-F]+|[bB][01]+|[oO][0-7]+)$/ );
|
|
263
|
+
if ( radix ) {
|
|
264
|
+
const base = 'xX'.includes( radix[2][0] ) ? 16 : 'bB'.includes( radix[2][0] ) ? 2 : 8;
|
|
265
|
+
const magnitude = parseInt( radix[2].slice( 1 ), base );
|
|
266
|
+
return radix[1] === '-' ? -magnitude : magnitude;
|
|
267
|
+
}
|
|
234
268
|
const match = text.match( /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/ );
|
|
235
269
|
if ( !match ) {
|
|
236
270
|
throw new Error( `TypeException: cannot coerce String to Number: ${JSON.stringify( String( value ) )}` );
|
|
@@ -565,6 +599,52 @@ function bitwiseNot( value ) {
|
|
|
565
599
|
return ( ~( zuzuToNumber( value ) >>> 0 ) ) >>> 0;
|
|
566
600
|
}
|
|
567
601
|
|
|
602
|
+
// BinaryStrings shift as one whole bit string: bits carry across byte
|
|
603
|
+
// boundaries, length is preserved, vacated bits are 0. Numbers shift via
|
|
604
|
+
// float multiply/divide so counts beyond 32 bits behave portably.
|
|
605
|
+
function shiftBitstream( bytes, count, left ) {
|
|
606
|
+
const len = bytes.length;
|
|
607
|
+
const out = new Uint8Array( len );
|
|
608
|
+
if ( len === 0 || count >= len * 8 ) {
|
|
609
|
+
return out;
|
|
610
|
+
}
|
|
611
|
+
const byteShift = Math.floor( count / 8 );
|
|
612
|
+
const bitShift = count % 8;
|
|
613
|
+
for ( let i = 0; i < len; i++ ) {
|
|
614
|
+
let value;
|
|
615
|
+
if ( left ) {
|
|
616
|
+
const src = i + byteShift;
|
|
617
|
+
const hi = src < len ? bytes[src] : 0;
|
|
618
|
+
const lo = src + 1 < len ? bytes[src + 1] : 0;
|
|
619
|
+
value = ( ( ( hi << 8 ) | lo ) << bitShift ) >> 8;
|
|
620
|
+
}
|
|
621
|
+
else {
|
|
622
|
+
if ( i < byteShift ) {
|
|
623
|
+
continue;
|
|
624
|
+
}
|
|
625
|
+
const lo = bytes[i - byteShift];
|
|
626
|
+
const hi = i > byteShift ? bytes[i - byteShift - 1] : 0;
|
|
627
|
+
value = ( ( hi << 8 ) | lo ) >> bitShift;
|
|
628
|
+
}
|
|
629
|
+
out[i] = value & 0xff;
|
|
630
|
+
}
|
|
631
|
+
return out;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
function shiftValue( left, right, leftward ) {
|
|
635
|
+
left = resolveWeakValue( left );
|
|
636
|
+
const count = Math.trunc( zuzuToNumber( right ) );
|
|
637
|
+
if ( !( count >= 0 ) || !Number.isFinite( count ) ) {
|
|
638
|
+
throw new Error( 'Exception: shift count must be a non-negative integer' );
|
|
639
|
+
}
|
|
640
|
+
if ( left instanceof ZuzuBinary ) {
|
|
641
|
+
return new BinaryString( shiftBitstream( left.bytes, count, leftward ) );
|
|
642
|
+
}
|
|
643
|
+
const value = Math.trunc( zuzuToNumber( left ) );
|
|
644
|
+
const factor = Math.pow( 2, count );
|
|
645
|
+
return leftward ? value * factor : Math.floor( value / factor );
|
|
646
|
+
}
|
|
647
|
+
|
|
568
648
|
function zuzuComparableValue( value ) {
|
|
569
649
|
value = resolveWeakValue( value );
|
|
570
650
|
if ( typeof value === 'function' && value.length === 0 ) {
|
|
@@ -1283,7 +1363,11 @@ function defineZuzuClass( name, base, spec = {} ) {
|
|
|
1283
1363
|
zuzuStoreField( this, field, value );
|
|
1284
1364
|
}
|
|
1285
1365
|
let named = null;
|
|
1286
|
-
|
|
1366
|
+
const lastArg = ctorArgs[ctorArgs.length - 1];
|
|
1367
|
+
if (
|
|
1368
|
+
ctorArgs.length === 1
|
|
1369
|
+
&& isPairListLike( ctorArgs[0] )
|
|
1370
|
+
) {
|
|
1287
1371
|
named = ctorArgs[0];
|
|
1288
1372
|
}
|
|
1289
1373
|
else if (
|
|
@@ -1294,6 +1378,20 @@ function defineZuzuClass( name, base, spec = {} ) {
|
|
|
1294
1378
|
) {
|
|
1295
1379
|
named = ctorArgs[0];
|
|
1296
1380
|
}
|
|
1381
|
+
else if (
|
|
1382
|
+
ctorArgs.length > 1
|
|
1383
|
+
&& isPairListLike( lastArg )
|
|
1384
|
+
) {
|
|
1385
|
+
named = lastArg;
|
|
1386
|
+
}
|
|
1387
|
+
else if (
|
|
1388
|
+
ctorArgs.length > 1
|
|
1389
|
+
&& lastArg
|
|
1390
|
+
&& typeof lastArg === 'object'
|
|
1391
|
+
&& !Array.isArray( lastArg )
|
|
1392
|
+
) {
|
|
1393
|
+
named = lastArg;
|
|
1394
|
+
}
|
|
1297
1395
|
const releaseTemporaryNamed = !skipBuild
|
|
1298
1396
|
&& isPairListLike( named )
|
|
1299
1397
|
&& named.__zuzu_temporary_call_args === true;
|
|
@@ -1459,9 +1557,6 @@ function zuzuCallMember( object, property, ...args ) {
|
|
|
1459
1557
|
if ( dictMethod ) {
|
|
1460
1558
|
return dictMethod( ...args );
|
|
1461
1559
|
}
|
|
1462
|
-
if ( args.length === 0 ) {
|
|
1463
|
-
return value;
|
|
1464
|
-
}
|
|
1465
1560
|
throw new TypeError( `${String( property )} is not a function` );
|
|
1466
1561
|
}
|
|
1467
1562
|
|
|
@@ -2128,7 +2223,13 @@ class ZuzuScript {
|
|
|
2128
2223
|
}
|
|
2129
2224
|
const moduleSearchRoots = this.getModuleSearchRoots();
|
|
2130
2225
|
const emit = ( value ) => {
|
|
2131
|
-
|
|
2226
|
+
let line = String( value );
|
|
2227
|
+
if ( line === 'Infinity' ) {
|
|
2228
|
+
line = 'Inf';
|
|
2229
|
+
}
|
|
2230
|
+
else if ( line === '-Infinity' ) {
|
|
2231
|
+
line = '-Inf';
|
|
2232
|
+
}
|
|
2132
2233
|
if ( this.outputLines ) {
|
|
2133
2234
|
this.outputLines.push( line );
|
|
2134
2235
|
}
|
|
@@ -2387,7 +2488,7 @@ class ZuzuScript {
|
|
|
2387
2488
|
defineRuntimeMethod( Set.prototype, 'is_disjoint', function _setIsDisjoint( other ) { return this.intersection( other ).size === 0 ? 1 : 0; } );
|
|
2388
2489
|
defineRuntimeMethod( Set.prototype, 'equals', function _setEquals( other ) { return collectionEquivalentOf( this, other ); } );
|
|
2389
2490
|
defineRuntimeMethod( Set.prototype, 'sort', function _setSort( fn ) { return [ ...this ].sort( fn ); } );
|
|
2390
|
-
defineRuntimeMethod( Set.prototype, 'sortstr', function _setSortstr() { return [ ...this ].sort(
|
|
2491
|
+
defineRuntimeMethod( Set.prototype, 'sortstr', function _setSortstr() { return [ ...this ].sort( stringSortComparator ); } );
|
|
2391
2492
|
defineRuntimeMethod( Set.prototype, 'sortnum', function _setSortnum() { return [ ...this ].map( (item) => Number( item ) ).sort( (a, b) => a - b ); } );
|
|
2392
2493
|
defineRuntimeMethod( Set.prototype, 'map', function _setMap( fn ) { return new Set( [ ...this ].map( fn ) ); } );
|
|
2393
2494
|
defineRuntimeMethod( Set.prototype, 'grep', function _setGrep( fn ) { return new Set( [ ...this ].filter( fn ) ); } );
|
|
@@ -2421,7 +2522,7 @@ class ZuzuScript {
|
|
|
2421
2522
|
const systemGlobalSeed = {
|
|
2422
2523
|
language_version: 0,
|
|
2423
2524
|
runtime: 'zuzu-js',
|
|
2424
|
-
runtime_version:
|
|
2525
|
+
runtime_version: runtimeVersion,
|
|
2425
2526
|
platform: this.host.name,
|
|
2426
2527
|
inc: Object.freeze( moduleSearchRoots.slice() ),
|
|
2427
2528
|
deny_fs: capabilityFlags.fs ? false : true,
|
|
@@ -2767,6 +2868,14 @@ class ZuzuScript {
|
|
|
2767
2868
|
if ( input == null ) {
|
|
2768
2869
|
return [];
|
|
2769
2870
|
}
|
|
2871
|
+
if ( input instanceof ZuzuBinary ) {
|
|
2872
|
+
// Iterate byte-by-byte, each as a 1-byte BinaryString.
|
|
2873
|
+
return Array.from( input.bytes, (byte) => new BinaryString( [ byte ] ) );
|
|
2874
|
+
}
|
|
2875
|
+
if ( typeof input === 'string' || input instanceof String ) {
|
|
2876
|
+
// Iterate character-by-character (code points).
|
|
2877
|
+
return Array.from( String( input ) );
|
|
2878
|
+
}
|
|
2770
2879
|
if ( typeof input[Symbol.iterator] === 'function' ) {
|
|
2771
2880
|
return input;
|
|
2772
2881
|
}
|
|
@@ -2880,6 +2989,15 @@ class ZuzuScript {
|
|
|
2880
2989
|
return value.isAscii();
|
|
2881
2990
|
},
|
|
2882
2991
|
__zuzu_bit_and( left, right ) { return bitwiseAnd( left, right ); },
|
|
2992
|
+
__zuzu_shift_left( left, right ) { return shiftValue( left, right, true ); },
|
|
2993
|
+
// The left operand is the divisor: a ∣ b tests b mod a.
|
|
2994
|
+
__zuzu_divides( left, right ) {
|
|
2995
|
+
return ( zuzuToNumber( right ) % zuzuToNumber( left ) ) === 0;
|
|
2996
|
+
},
|
|
2997
|
+
__zuzu_ndivides( left, right ) {
|
|
2998
|
+
return zuzuToNumber( right ) % zuzuToNumber( left );
|
|
2999
|
+
},
|
|
3000
|
+
__zuzu_shift_right( left, right ) { return shiftValue( left, right, false ); },
|
|
2883
3001
|
__zuzu_bit_or( left, right ) { return bitwiseOr( left, right ); },
|
|
2884
3002
|
__zuzu_bit_xor( left, right ) { return bitwiseXor( left, right ); },
|
|
2885
3003
|
__zuzu_bit_not( value ) { return bitwiseNot( value ); },
|
|
@@ -3169,7 +3287,7 @@ class ZuzuScript {
|
|
|
3169
3287
|
define( Array.prototype, 'shuffle', function _shuffle() { return this.slice(); } );
|
|
3170
3288
|
define( Array.prototype, 'sample', function _sample( n ) { return this.slice( 0, n ); } );
|
|
3171
3289
|
define( Array.prototype, 'for_each_value', function _for_each_value( fn ) { this.forEach( fn ); return this; } );
|
|
3172
|
-
define( Array.prototype, 'sortstr', function _sortstr() { return this.slice().sort(
|
|
3290
|
+
define( Array.prototype, 'sortstr', function _sortstr() { return this.slice().sort( stringSortComparator ); } );
|
|
3173
3291
|
define( Array.prototype, 'sortnum', function _sortnum() { return this.map( (item) => Number( item ) ).sort( (a, b) => a - b ); } );
|
|
3174
3292
|
define( Array.prototype, 'to_Array', function _to_array() { return __zuzu_array( this ); } );
|
|
3175
3293
|
define( Array.prototype, 'to_Set', function _to_set() { return __zuzu_set( this ); } );
|
|
@@ -3199,7 +3317,7 @@ class ZuzuScript {
|
|
|
3199
3317
|
define( Set.prototype, 'is_disjoint', function _is_disjoint( other ) { return this.intersection( other ).size === 0 ? 1 : 0; } );
|
|
3200
3318
|
define( Set.prototype, 'equals', function _equals( other ) { return __zuzu_equivalentof( this, other ); } );
|
|
3201
3319
|
define( Set.prototype, 'sort', function _sort( fn ) { return [ ...this ].sort( fn ); } );
|
|
3202
|
-
define( Set.prototype, 'sortstr', function _sortstr() { return [ ...this ].sort(
|
|
3320
|
+
define( Set.prototype, 'sortstr', function _sortstr() { return [ ...this ].sort( stringSortComparator ); } );
|
|
3203
3321
|
define( Set.prototype, 'sortnum', function _sortnum() { return [ ...this ].map( (item) => Number( item ) ).sort( (a, b) => a - b ); } );
|
|
3204
3322
|
define( Set.prototype, 'map', function _map( fn ) { return new Set( [ ...this ].map( fn ) ); } );
|
|
3205
3323
|
define( Set.prototype, 'grep', function _grep( fn ) { return new Set( [ ...this ].filter( fn ) ); } );
|
|
@@ -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'
|
|
@@ -835,6 +853,19 @@ function emitSpecialFunctionPreamble( signature ) {
|
|
|
835
853
|
'}',
|
|
836
854
|
`const ${signature.namedName} = __zuzu_named_args;`,
|
|
837
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;`,
|
|
868
|
+
].join( '\n' );
|
|
838
869
|
case 'variadic':
|
|
839
870
|
return [
|
|
840
871
|
'const __zuzu_call_args = Array.prototype.slice.call( arguments );',
|
|
@@ -2121,6 +2152,17 @@ function emitBinaryExpression( node ) {
|
|
|
2121
2152
|
return `__zuzu_bit_or( ${left}, ${right} )`;
|
|
2122
2153
|
case '^':
|
|
2123
2154
|
return `__zuzu_bit_xor( ${left}, ${right} )`;
|
|
2155
|
+
case '<<':
|
|
2156
|
+
case '«':
|
|
2157
|
+
return `__zuzu_shift_left( ${left}, ${right} )`;
|
|
2158
|
+
case '∣':
|
|
2159
|
+
case 'divides':
|
|
2160
|
+
return `__zuzu_divides( ${left}, ${right} )`;
|
|
2161
|
+
case '∤':
|
|
2162
|
+
return `__zuzu_ndivides( ${left}, ${right} )`;
|
|
2163
|
+
case '>>':
|
|
2164
|
+
case '»':
|
|
2165
|
+
return `__zuzu_shift_right( ${left}, ${right} )`;
|
|
2124
2166
|
default:
|
|
2125
2167
|
return `( ${left} ${node.operator} ${right} )`;
|
|
2126
2168
|
}
|
|
@@ -2163,6 +2205,9 @@ function emitAssignmentExpression( node ) {
|
|
|
2163
2205
|
: `${left} = __zuzu_assign_strong( ${left}, ${right} )`;
|
|
2164
2206
|
}
|
|
2165
2207
|
if ( node.left && node.left.type === 'MemberExpression' ) {
|
|
2208
|
+
if ( !node.left.computed ) {
|
|
2209
|
+
throw new UnsupportedSyntaxError( 'Invalid assignment target' );
|
|
2210
|
+
}
|
|
2166
2211
|
const object = emitExpression( node.left.object );
|
|
2167
2212
|
const objectTarget = emitAssignmentTarget( node.left.object );
|
|
2168
2213
|
const key = node.left.computed
|
|
@@ -103,6 +103,7 @@ const KEYWORDS = new Set( [
|
|
|
103
103
|
'throw',
|
|
104
104
|
'sub',
|
|
105
105
|
'union',
|
|
106
|
+
'divides',
|
|
106
107
|
'intersection',
|
|
107
108
|
'difference',
|
|
108
109
|
'subsetof',
|
|
@@ -772,6 +773,29 @@ function tokenize( source ) {
|
|
|
772
773
|
}
|
|
773
774
|
|
|
774
775
|
if ( isDigit( ch ) ) {
|
|
776
|
+
// Radix-prefixed integers: 0x… hex, 0b… binary, 0o… octal.
|
|
777
|
+
// Lowercase prefixes only; the token value is normalised to
|
|
778
|
+
// decimal so later stages treat it as a plain number.
|
|
779
|
+
const radixDigitOk = ch === '0'
|
|
780
|
+
? ( peek( 1 ) === 'x'
|
|
781
|
+
? (c) => /[0-9a-fA-F]/.test( c || '' )
|
|
782
|
+
: peek( 1 ) === 'b'
|
|
783
|
+
? (c) => c === '0' || c === '1'
|
|
784
|
+
: peek( 1 ) === 'o'
|
|
785
|
+
? (c) => /[0-7]/.test( c || '' )
|
|
786
|
+
: null )
|
|
787
|
+
: null;
|
|
788
|
+
if ( radixDigitOk && radixDigitOk( peek( 2 ) ) ) {
|
|
789
|
+
const radix = peek( 1 ) === 'x' ? 16 : peek( 1 ) === 'b' ? 2 : 8;
|
|
790
|
+
advance();
|
|
791
|
+
advance();
|
|
792
|
+
let digits = '';
|
|
793
|
+
while ( radixDigitOk( peek() ) ) {
|
|
794
|
+
digits += advance();
|
|
795
|
+
}
|
|
796
|
+
addToken( 'number', String( parseInt( digits, radix ) ), start, cursor() );
|
|
797
|
+
continue;
|
|
798
|
+
}
|
|
775
799
|
let value = '';
|
|
776
800
|
while ( isDigit( peek() ) ) {
|
|
777
801
|
value += advance();
|
|
@@ -782,6 +806,21 @@ function tokenize( source ) {
|
|
|
782
806
|
value += advance();
|
|
783
807
|
}
|
|
784
808
|
}
|
|
809
|
+
// Exponent: uppercase E only (lowercase e is not part of
|
|
810
|
+
// the language).
|
|
811
|
+
if (
|
|
812
|
+
peek() === 'E'
|
|
813
|
+
&& ( isDigit( peek( 1 ) )
|
|
814
|
+
|| ( ( peek( 1 ) === '+' || peek( 1 ) === '-' ) && isDigit( peek( 2 ) ) ) )
|
|
815
|
+
) {
|
|
816
|
+
value += advance();
|
|
817
|
+
if ( peek() === '+' || peek() === '-' ) {
|
|
818
|
+
value += advance();
|
|
819
|
+
}
|
|
820
|
+
while ( isDigit( peek() ) ) {
|
|
821
|
+
value += advance();
|
|
822
|
+
}
|
|
823
|
+
}
|
|
785
824
|
addToken( 'number', value, start, cursor() );
|
|
786
825
|
continue;
|
|
787
826
|
}
|
|
@@ -812,7 +851,7 @@ function tokenize( source ) {
|
|
|
812
851
|
continue;
|
|
813
852
|
}
|
|
814
853
|
|
|
815
|
-
if ( [ '≡', '≢', '≠', '≤', '≥', '→', '√', '⌊', '⌋', '⌈', '⌉', '∈', '∉', '⋃', '⋂', '∖', '⊂', '⊃', '«', '»', '≶', '≷', '▷', '◁' ].includes( ch ) ) {
|
|
854
|
+
if ( [ '≡', '≢', '≠', '≤', '≥', '→', '√', '⌊', '⌋', '⌈', '⌉', '∈', '∉', '⋃', '⋂', '∖', '⊂', '⊃', '«', '»', '≶', '≷', '▷', '◁', '∣', '∤' ].includes( ch ) ) {
|
|
816
855
|
advance();
|
|
817
856
|
addToken( 'operator', ch, start, cursor() );
|
|
818
857
|
continue;
|
|
@@ -8,6 +8,7 @@ const {
|
|
|
8
8
|
} = require( './errors' );
|
|
9
9
|
|
|
10
10
|
function parse( tokens, options = {} ) {
|
|
11
|
+
const pendingSetClosers = [];
|
|
11
12
|
let index = 0;
|
|
12
13
|
let asyncContextDepth = 1;
|
|
13
14
|
|
|
@@ -1541,20 +1542,31 @@ function parse( tokens, options = {} ) {
|
|
|
1541
1542
|
|
|
1542
1543
|
function parseBitwise() {
|
|
1543
1544
|
return parseLeftAssociative(
|
|
1544
|
-
|
|
1545
|
+
parseShift,
|
|
1545
1546
|
(token) => token.type === 'operator' && [ '&', '|', '^' ].includes( token.value )
|
|
1546
1547
|
);
|
|
1547
1548
|
}
|
|
1548
1549
|
|
|
1550
|
+
function parseShift() {
|
|
1551
|
+
return parseLeftAssociative(
|
|
1552
|
+
parseComparison,
|
|
1553
|
+
(token) => token.type === 'operator'
|
|
1554
|
+
&& [ '<<', '>>', '«', '»' ].includes( token.value )
|
|
1555
|
+
// Inside << ... >> or « ... » the pending closer ends the
|
|
1556
|
+
// set literal instead of acting as a shift operator.
|
|
1557
|
+
&& token.value !== pendingSetClosers[pendingSetClosers.length - 1]
|
|
1558
|
+
);
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1549
1561
|
function parseComparison() {
|
|
1550
1562
|
return parseLeftAssociative(
|
|
1551
1563
|
parseRange,
|
|
1552
1564
|
(token) => (
|
|
1553
1565
|
token.type === 'operator'
|
|
1554
|
-
&& [ '<', '<=', '>', '>=', '≤', '≥', '~', '<=>', '@', '@@', '@?', '\\', '∈', '∉', '⋃', '⋂', '∖', '⊂', '⊃', '⊂⊃', '≶', '≷' ].includes( token.value )
|
|
1566
|
+
&& [ '<', '<=', '>', '>=', '≤', '≥', '~', '<=>', '@', '@@', '@?', '\\', '∈', '∉', '⋃', '⋂', '∖', '⊂', '⊃', '⊂⊃', '≶', '≷', '∣', '∤' ].includes( token.value )
|
|
1555
1567
|
) || (
|
|
1556
1568
|
token.type === 'keyword'
|
|
1557
|
-
&& [ 'gt', 'ge', 'lt', 'le', 'cmp', 'eqi', 'nei', 'gti', 'gei', 'lti', 'lei', 'cmpi', 'in', 'not_in', 'union', 'intersection', 'difference', 'subsetof', 'supersetof', 'equivalentof', 'does', 'can' ].includes( token.value )
|
|
1569
|
+
&& [ 'gt', 'ge', 'lt', 'le', 'cmp', 'eqi', 'nei', 'gti', 'gei', 'lti', 'lei', 'cmpi', 'in', 'not_in', 'union', 'intersection', 'difference', 'subsetof', 'supersetof', 'equivalentof', 'does', 'can', 'divides' ].includes( token.value )
|
|
1558
1570
|
) || (
|
|
1559
1571
|
token.type === 'identifier' && token.value === 'instanceof'
|
|
1560
1572
|
)
|
|
@@ -2252,18 +2264,24 @@ function parse( tokens, options = {} ) {
|
|
|
2252
2264
|
if ( match( 'operator', closer ) ) {
|
|
2253
2265
|
return elements;
|
|
2254
2266
|
}
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2267
|
+
pendingSetClosers.push( closer );
|
|
2268
|
+
try {
|
|
2269
|
+
while ( true ) {
|
|
2270
|
+
if ( match( 'punctuation', ',' ) ) {
|
|
2271
|
+
continue;
|
|
2272
|
+
}
|
|
2273
|
+
if ( match( 'operator', closer ) ) {
|
|
2274
|
+
break;
|
|
2275
|
+
}
|
|
2276
|
+
elements.push( parseExpression() );
|
|
2277
|
+
if ( match( 'operator', closer ) ) {
|
|
2278
|
+
break;
|
|
2279
|
+
}
|
|
2280
|
+
match( 'punctuation', ',' );
|
|
2265
2281
|
}
|
|
2266
|
-
|
|
2282
|
+
}
|
|
2283
|
+
finally {
|
|
2284
|
+
pendingSetClosers.pop();
|
|
2267
2285
|
}
|
|
2268
2286
|
return elements;
|
|
2269
2287
|
}
|
|
@@ -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 )
|