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
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
class BigNum {
|
|
4
4
|
constructor( value, text = null, isInt = null ) {
|
|
5
|
+
if ( typeof value === 'bigint' ) {
|
|
6
|
+
this._value = value;
|
|
7
|
+
this._text = text == null ? String( value ) : String( text );
|
|
8
|
+
this._isInt = isInt == null ? true : Boolean( isInt );
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
5
11
|
this._value = Number( value ?? 0 );
|
|
6
12
|
this._text = text == null ? String( this._value ) : String( text );
|
|
7
13
|
this._isInt = isInt == null ? Number.isInteger( this._value ) : Boolean( isInt );
|
|
@@ -19,22 +25,54 @@ class BigNum {
|
|
|
19
25
|
|
|
20
26
|
static from_dec( value ) {
|
|
21
27
|
const text = String( value ?? '0' ).trim();
|
|
22
|
-
|
|
28
|
+
const safeText = text || '0';
|
|
29
|
+
if ( BigNum._isIntegerText( safeText ) ) {
|
|
30
|
+
return new BigNum( BigInt( safeText ), safeText, true );
|
|
31
|
+
}
|
|
32
|
+
return new BigNum( Number( safeText ), safeText, !/[.eE]/.test( safeText ) );
|
|
23
33
|
}
|
|
24
34
|
|
|
25
35
|
static from_hex( value ) {
|
|
26
|
-
const text = String( value ?? '0' ).trim().
|
|
27
|
-
const
|
|
36
|
+
const text = String( value ?? '0' ).trim().replace( /^0x/u, '' ).toLowerCase() || '0';
|
|
37
|
+
const negative = text.startsWith( '-' );
|
|
38
|
+
const digits = text.replace( /^[+-]/u, '' );
|
|
39
|
+
const parsed = BigInt( `0x${digits}` ) * ( negative ? -1n : 1n );
|
|
28
40
|
return new BigNum( parsed, String( parsed ), true );
|
|
29
41
|
}
|
|
30
42
|
|
|
31
|
-
|
|
43
|
+
is_int() {
|
|
32
44
|
return this._isInt;
|
|
33
45
|
}
|
|
34
46
|
|
|
47
|
+
_toBigInt() {
|
|
48
|
+
if ( !this._isInt ) {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
if ( typeof this._value === 'bigint' ) {
|
|
52
|
+
return this._value;
|
|
53
|
+
}
|
|
54
|
+
try {
|
|
55
|
+
return BigInt( this._text );
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
_toNumber() {
|
|
63
|
+
return typeof this._value === 'bigint' ? Number( this._value ) : this._value;
|
|
64
|
+
}
|
|
65
|
+
|
|
35
66
|
bcmp( other ) {
|
|
36
|
-
const rhs = BigNum._coerce( other )
|
|
37
|
-
|
|
67
|
+
const rhs = BigNum._coerce( other );
|
|
68
|
+
const lhsBigInt = this._toBigInt();
|
|
69
|
+
const rhsBigInt = rhs._toBigInt();
|
|
70
|
+
if ( lhsBigInt !== null && rhsBigInt !== null ) {
|
|
71
|
+
return lhsBigInt < rhsBigInt ? -1 : ( lhsBigInt > rhsBigInt ? 1 : 0 );
|
|
72
|
+
}
|
|
73
|
+
return this._toNumber() < rhs._toNumber()
|
|
74
|
+
? -1
|
|
75
|
+
: ( this._toNumber() > rhs._toNumber() ? 1 : 0 );
|
|
38
76
|
}
|
|
39
77
|
|
|
40
78
|
beq( other ) { return this.bcmp( other ) === 0; }
|
|
@@ -44,45 +82,134 @@ class BigNum {
|
|
|
44
82
|
bgt( other ) { return this.bcmp( other ) > 0; }
|
|
45
83
|
bge( other ) { return this.bcmp( other ) >= 0; }
|
|
46
84
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
85
|
+
babs() {
|
|
86
|
+
const value = this._toBigInt();
|
|
87
|
+
if ( value !== null ) {
|
|
88
|
+
return new BigNum( value < 0n ? -value : value, String( value < 0n ? -value : value ), this._isInt );
|
|
89
|
+
}
|
|
90
|
+
return new BigNum( Math.abs( this._toNumber() ), String( Math.abs( this._toNumber() ) ), this._isInt );
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
bneg() {
|
|
94
|
+
const value = this._toBigInt();
|
|
95
|
+
if ( value !== null ) {
|
|
96
|
+
return new BigNum( -value, String( -value ), this._isInt );
|
|
97
|
+
}
|
|
98
|
+
return new BigNum( -this._toNumber(), String( -this._toNumber() ), this._isInt );
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
binv() { return new BigNum( 1 / this._toNumber() ); }
|
|
102
|
+
bsin() { return new BigNum( Math.sin( this._toNumber() ), String( Math.sin( this._toNumber() ) ), false ); }
|
|
103
|
+
bcos() { return new BigNum( Math.cos( this._toNumber() ) ); }
|
|
104
|
+
btan() { return new BigNum( Math.tan( this._toNumber() ), String( Math.tan( this._toNumber() ) ), false ); }
|
|
105
|
+
bsqrt() { return new BigNum( Math.sqrt( this._toNumber() ) ); }
|
|
106
|
+
bround() { return new BigNum( Math.round( this._toNumber() ) ); }
|
|
107
|
+
bfloor() { return new BigNum( Math.floor( this._toNumber() ) ); }
|
|
108
|
+
bceil() { return new BigNum( Math.ceil( this._toNumber() ) ); }
|
|
109
|
+
|
|
110
|
+
badd( other ) {
|
|
111
|
+
const rhs = BigNum._coerce( other );
|
|
112
|
+
const lhsInt = this._toBigInt();
|
|
113
|
+
const rhsInt = rhs._toBigInt();
|
|
114
|
+
if ( lhsInt !== null && rhsInt !== null ) {
|
|
115
|
+
return new BigNum( lhsInt + rhsInt, String( lhsInt + rhsInt ), false );
|
|
116
|
+
}
|
|
117
|
+
return new BigNum( this._toNumber() + rhs._toNumber(), String( this._toNumber() + rhs._toNumber() ), false );
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
bsub( other ) {
|
|
121
|
+
const rhs = BigNum._coerce( other );
|
|
122
|
+
const lhsInt = this._toBigInt();
|
|
123
|
+
const rhsInt = rhs._toBigInt();
|
|
124
|
+
if ( lhsInt !== null && rhsInt !== null ) {
|
|
125
|
+
return new BigNum( lhsInt - rhsInt, String( lhsInt - rhsInt ), false );
|
|
126
|
+
}
|
|
127
|
+
return new BigNum( this._toNumber() - rhs._toNumber(), String( this._toNumber() - rhs._toNumber() ), false );
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
bmul( other ) {
|
|
131
|
+
const rhs = BigNum._coerce( other );
|
|
132
|
+
const lhsInt = this._toBigInt();
|
|
133
|
+
const rhsInt = rhs._toBigInt();
|
|
134
|
+
if ( lhsInt !== null && rhsInt !== null ) {
|
|
135
|
+
return new BigNum( lhsInt * rhsInt, String( lhsInt * rhsInt ), false );
|
|
136
|
+
}
|
|
137
|
+
return new BigNum( this._toNumber() * rhs._toNumber(), String( this._toNumber() * rhs._toNumber() ), false );
|
|
138
|
+
}
|
|
57
139
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
140
|
+
bdiv( other ) {
|
|
141
|
+
const rhs = BigNum._coerce( other );
|
|
142
|
+
return new BigNum( this._toNumber() / rhs._toNumber() );
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
bmod( other ) {
|
|
146
|
+
const rhs = BigNum._coerce( other );
|
|
147
|
+
const lhsInt = this._toBigInt();
|
|
148
|
+
const rhsInt = rhs._toBigInt();
|
|
149
|
+
if ( lhsInt !== null && rhsInt !== null && rhsInt !== 0n ) {
|
|
150
|
+
return new BigNum( lhsInt % rhsInt, String( lhsInt % rhsInt ), true );
|
|
151
|
+
}
|
|
152
|
+
return new BigNum( this._toNumber() % rhs._toNumber() );
|
|
153
|
+
}
|
|
154
|
+
bpow( other ) {
|
|
155
|
+
const rhs = BigNum._coerce( other );
|
|
156
|
+
const lhsInt = this._toBigInt();
|
|
157
|
+
const rhsInt = rhs._toBigInt();
|
|
158
|
+
if ( lhsInt !== null && rhsInt !== null && rhsInt >= 0n ) {
|
|
159
|
+
let base = lhsInt;
|
|
160
|
+
let power = rhsInt;
|
|
161
|
+
let result = 1n;
|
|
162
|
+
while ( power > 0n ) {
|
|
163
|
+
if ( power & 1n ) {
|
|
164
|
+
result *= base;
|
|
165
|
+
}
|
|
166
|
+
power >>= 1n;
|
|
167
|
+
if ( power > 0n ) {
|
|
168
|
+
base *= base;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return new BigNum( result, String( result ), true );
|
|
172
|
+
}
|
|
173
|
+
return new BigNum( this._toNumber() ** rhs._toNumber() );
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
to_hex() {
|
|
177
|
+
const value = this._toBigInt();
|
|
178
|
+
if ( value === null ) {
|
|
179
|
+
return `0x${Math.trunc( this._toNumber() ).toString( 16 )}`;
|
|
180
|
+
}
|
|
181
|
+
if ( value === 0n ) {
|
|
182
|
+
return '0x0';
|
|
183
|
+
}
|
|
184
|
+
const absolute = value < 0n ? -value : value;
|
|
185
|
+
return value < 0n ? `-0x${ absolute.toString( 16 ) }` : `0x${ absolute.toString( 16 ) }`;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
to_dec() {
|
|
189
|
+
return this._textify();
|
|
190
|
+
}
|
|
64
191
|
|
|
65
|
-
|
|
66
|
-
return
|
|
192
|
+
to_String() {
|
|
193
|
+
return this.to_dec();
|
|
67
194
|
}
|
|
68
195
|
|
|
69
|
-
|
|
70
|
-
return
|
|
196
|
+
toString() {
|
|
197
|
+
return String( this.to_String() );
|
|
71
198
|
}
|
|
72
199
|
|
|
73
|
-
|
|
74
|
-
return this.
|
|
200
|
+
to_Number() {
|
|
201
|
+
return this._toNumber();
|
|
75
202
|
}
|
|
76
203
|
|
|
77
|
-
|
|
78
|
-
return
|
|
204
|
+
static _isIntegerText( text ) {
|
|
205
|
+
return /^[+-]?(?:\d+)$/u.test( text );
|
|
79
206
|
}
|
|
80
207
|
|
|
81
208
|
_textify() {
|
|
82
209
|
if ( this._text != null && /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)$/u.test( this._text ) ) {
|
|
83
210
|
return this._text.replace( /\.0+$/u, '' ).replace( /(\.\d*?)0+$/u, '$1' ).replace( /\.$/u, '' );
|
|
84
211
|
}
|
|
85
|
-
return String( this._value );
|
|
212
|
+
return this._toBigInt() !== null ? this._toBigInt().toString() : String( this._value );
|
|
86
213
|
}
|
|
87
214
|
}
|
|
88
215
|
|
package/modules/std/math.js
CHANGED
|
@@ -10,7 +10,7 @@ function normalizeNumberArgs( values ) {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
const ZMath = {
|
|
13
|
-
pi
|
|
13
|
+
pi() { return PI; },
|
|
14
14
|
sin( value ) { return Math.sin( Number( value ?? 0 ) ); },
|
|
15
15
|
cos( value ) { return Math.cos( Number( value ?? 0 ) ); },
|
|
16
16
|
tan( value ) { return Math.tan( Number( value ?? 0 ) ); },
|
package/modules/std/net/url.js
CHANGED
|
@@ -68,37 +68,46 @@ function parse( value ) {
|
|
|
68
68
|
return out;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
if ( values[name] == null ) {
|
|
73
|
-
return '';
|
|
74
|
-
}
|
|
75
|
-
return encodeURIComponent( _str( values[name] ) );
|
|
76
|
-
}
|
|
71
|
+
const { StdUriTemplate } = require( '@std-uritemplate/std-uritemplate' );
|
|
77
72
|
|
|
78
|
-
function
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
73
|
+
function _templateValue( value ) {
|
|
74
|
+
if ( value == null ) {
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
if ( Array.isArray( value ) ) {
|
|
78
|
+
return value.map( (item) => _str( item ) );
|
|
79
|
+
}
|
|
80
|
+
if ( typeof value === 'boolean' ) {
|
|
81
|
+
return value ? 'true' : 'false';
|
|
82
|
+
}
|
|
83
|
+
if ( typeof value === 'object' && !( value.bytes instanceof Uint8Array ) ) {
|
|
84
|
+
// Associative values: keys sorted for deterministic expansion.
|
|
85
|
+
const out = {};
|
|
86
|
+
for ( const key of Object.keys( value ).sort() ) {
|
|
87
|
+
out[key] = _str( value[key] );
|
|
86
88
|
}
|
|
87
|
-
|
|
88
|
-
const val = encodeURIComponent( _str( values[name] ) );
|
|
89
|
-
parts.push( `${key}=${val}` );
|
|
89
|
+
return out;
|
|
90
90
|
}
|
|
91
|
-
return
|
|
91
|
+
return _str( value );
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
function fill_template( template, values ) {
|
|
95
95
|
const source = _str( template );
|
|
96
|
-
const data =
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
96
|
+
const data = {};
|
|
97
|
+
if ( values && typeof values === 'object' && !Array.isArray( values ) ) {
|
|
98
|
+
for ( const key of Object.keys( values ) ) {
|
|
99
|
+
const converted = _templateValue( values[key] );
|
|
100
|
+
if ( converted != null ) {
|
|
101
|
+
data[key] = converted;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
return StdUriTemplate.expand( source, data );
|
|
107
|
+
}
|
|
108
|
+
catch ( err ) {
|
|
109
|
+
throw new Error( `Exception: invalid URL template: ${source}` );
|
|
110
|
+
}
|
|
102
111
|
}
|
|
103
112
|
|
|
104
113
|
module.exports = {
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// std/string/encode: character-encoding conversions between String and
|
|
4
|
+
// BinaryString. UTF-16 and UTF-32 encode to big-endian without a BOM (the
|
|
5
|
+
// deterministic canonical form shared by all runtimes); decode honours a
|
|
6
|
+
// leading BOM and otherwise assumes big-endian.
|
|
7
|
+
|
|
8
|
+
const { BinaryString } = require( '../../../lib/runtime-helpers' );
|
|
9
|
+
|
|
10
|
+
function typeName( value ) {
|
|
11
|
+
if ( value == null ) {
|
|
12
|
+
return 'Null';
|
|
13
|
+
}
|
|
14
|
+
if ( typeof value === 'string' || value instanceof String ) {
|
|
15
|
+
return 'String';
|
|
16
|
+
}
|
|
17
|
+
if ( value.bytes instanceof Uint8Array ) {
|
|
18
|
+
return 'BinaryString';
|
|
19
|
+
}
|
|
20
|
+
return value.constructor && value.constructor.name ? value.constructor.name : typeof value;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function canonicalEncoding( name ) {
|
|
24
|
+
const upper = String( name == null ? 'UTF-8' : name ).toUpperCase().replace( /\s+/g, '' );
|
|
25
|
+
if ( upper === 'UTF-8' || upper === 'UTF8' ) {
|
|
26
|
+
return 'utf8';
|
|
27
|
+
}
|
|
28
|
+
if ( upper === 'UTF-16' || upper === 'UTF16' || upper === 'UTF-16BE' ) {
|
|
29
|
+
return 'utf16';
|
|
30
|
+
}
|
|
31
|
+
if ( upper === 'UTF-32' || upper === 'UTF32' || upper === 'UTF-32BE' ) {
|
|
32
|
+
return 'utf32';
|
|
33
|
+
}
|
|
34
|
+
if (
|
|
35
|
+
upper === 'ISO-8859-1' || upper === 'ISO8859-1'
|
|
36
|
+
|| upper === 'LATIN-1' || upper === 'LATIN1' || upper === 'LATIN'
|
|
37
|
+
) {
|
|
38
|
+
return 'latin1';
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function codePoints( text ) {
|
|
44
|
+
return Array.from( text, (ch) => ch.codePointAt( 0 ) );
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function encode( value, encoding ) {
|
|
48
|
+
if ( typeName( value ) !== 'String' ) {
|
|
49
|
+
throw new Error( `TypeException: encode expects String, got ${typeName( value )}` );
|
|
50
|
+
}
|
|
51
|
+
const text = String( value );
|
|
52
|
+
const codec = canonicalEncoding( encoding );
|
|
53
|
+
if ( codec === 'utf8' ) {
|
|
54
|
+
return new BinaryString( new TextEncoder().encode( text ) );
|
|
55
|
+
}
|
|
56
|
+
if ( codec === 'utf16' ) {
|
|
57
|
+
const out = [];
|
|
58
|
+
for ( let i = 0; i < text.length; i++ ) {
|
|
59
|
+
const unit = text.charCodeAt( i );
|
|
60
|
+
out.push( ( unit >> 8 ) & 0xff, unit & 0xff );
|
|
61
|
+
}
|
|
62
|
+
return new BinaryString( out );
|
|
63
|
+
}
|
|
64
|
+
if ( codec === 'utf32' ) {
|
|
65
|
+
const out = [];
|
|
66
|
+
for ( const point of codePoints( text ) ) {
|
|
67
|
+
out.push(
|
|
68
|
+
( point >>> 24 ) & 0xff,
|
|
69
|
+
( point >>> 16 ) & 0xff,
|
|
70
|
+
( point >>> 8 ) & 0xff,
|
|
71
|
+
point & 0xff,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
return new BinaryString( out );
|
|
75
|
+
}
|
|
76
|
+
if ( codec === 'latin1' ) {
|
|
77
|
+
const out = [];
|
|
78
|
+
for ( const point of codePoints( text ) ) {
|
|
79
|
+
if ( point > 0xff ) {
|
|
80
|
+
const hex = point.toString( 16 ).toUpperCase().padStart( 4, '0' );
|
|
81
|
+
throw new Error( `Exception: Character U+${hex} cannot be encoded as ISO-8859-1` );
|
|
82
|
+
}
|
|
83
|
+
out.push( point );
|
|
84
|
+
}
|
|
85
|
+
return new BinaryString( out );
|
|
86
|
+
}
|
|
87
|
+
throw new Error( `Exception: Unsupported encoding: ${encoding}` );
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function decode( value, encoding ) {
|
|
91
|
+
if ( typeName( value ) !== 'BinaryString' ) {
|
|
92
|
+
throw new Error( `TypeException: decode expects BinaryString, got ${typeName( value )}` );
|
|
93
|
+
}
|
|
94
|
+
let bytes = value.bytes;
|
|
95
|
+
const codec = canonicalEncoding( encoding );
|
|
96
|
+
if ( codec === 'utf8' ) {
|
|
97
|
+
try {
|
|
98
|
+
return new TextDecoder( 'utf-8', { fatal: true } ).decode( bytes );
|
|
99
|
+
}
|
|
100
|
+
catch ( _err ) {
|
|
101
|
+
throw new Error( 'Exception: Invalid UTF-8 in BinaryString' );
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
if ( codec === 'utf16' ) {
|
|
105
|
+
let bigEndian = true;
|
|
106
|
+
if ( bytes.length >= 2 && bytes[0] === 0xfe && bytes[1] === 0xff ) {
|
|
107
|
+
bytes = bytes.subarray( 2 );
|
|
108
|
+
}
|
|
109
|
+
else if ( bytes.length >= 2 && bytes[0] === 0xff && bytes[1] === 0xfe ) {
|
|
110
|
+
bigEndian = false;
|
|
111
|
+
bytes = bytes.subarray( 2 );
|
|
112
|
+
}
|
|
113
|
+
if ( bytes.length % 2 !== 0 ) {
|
|
114
|
+
throw new Error( 'Exception: UTF-16 input length must be a multiple of 2 bytes' );
|
|
115
|
+
}
|
|
116
|
+
const units = [];
|
|
117
|
+
for ( let i = 0; i < bytes.length; i += 2 ) {
|
|
118
|
+
units.push( bigEndian
|
|
119
|
+
? ( bytes[i] << 8 ) | bytes[i + 1]
|
|
120
|
+
: ( bytes[i + 1] << 8 ) | bytes[i] );
|
|
121
|
+
}
|
|
122
|
+
let out = '';
|
|
123
|
+
for ( let i = 0; i < units.length; i++ ) {
|
|
124
|
+
const unit = units[i];
|
|
125
|
+
if ( unit >= 0xd800 && unit <= 0xdbff ) {
|
|
126
|
+
const next = units[i + 1];
|
|
127
|
+
if ( next == null || next < 0xdc00 || next > 0xdfff ) {
|
|
128
|
+
throw new Error( 'Exception: Invalid UTF-16 in BinaryString' );
|
|
129
|
+
}
|
|
130
|
+
out += String.fromCharCode( unit, next );
|
|
131
|
+
i++;
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if ( unit >= 0xdc00 && unit <= 0xdfff ) {
|
|
135
|
+
throw new Error( 'Exception: Invalid UTF-16 in BinaryString' );
|
|
136
|
+
}
|
|
137
|
+
out += String.fromCharCode( unit );
|
|
138
|
+
}
|
|
139
|
+
return out;
|
|
140
|
+
}
|
|
141
|
+
if ( codec === 'utf32' ) {
|
|
142
|
+
let bigEndian = true;
|
|
143
|
+
if (
|
|
144
|
+
bytes.length >= 4
|
|
145
|
+
&& bytes[0] === 0x00 && bytes[1] === 0x00 && bytes[2] === 0xfe && bytes[3] === 0xff
|
|
146
|
+
) {
|
|
147
|
+
bytes = bytes.subarray( 4 );
|
|
148
|
+
}
|
|
149
|
+
else if (
|
|
150
|
+
bytes.length >= 4
|
|
151
|
+
&& bytes[0] === 0xff && bytes[1] === 0xfe && bytes[2] === 0x00 && bytes[3] === 0x00
|
|
152
|
+
) {
|
|
153
|
+
bigEndian = false;
|
|
154
|
+
bytes = bytes.subarray( 4 );
|
|
155
|
+
}
|
|
156
|
+
if ( bytes.length % 4 !== 0 ) {
|
|
157
|
+
throw new Error( 'Exception: UTF-32 input length must be a multiple of 4 bytes' );
|
|
158
|
+
}
|
|
159
|
+
let out = '';
|
|
160
|
+
for ( let i = 0; i < bytes.length; i += 4 ) {
|
|
161
|
+
const point = bigEndian
|
|
162
|
+
? ( bytes[i] * 0x1000000 ) + ( bytes[i + 1] << 16 ) + ( bytes[i + 2] << 8 ) + bytes[i + 3]
|
|
163
|
+
: ( bytes[i + 3] * 0x1000000 ) + ( bytes[i + 2] << 16 ) + ( bytes[i + 1] << 8 ) + bytes[i];
|
|
164
|
+
if ( point > 0x10ffff || ( point >= 0xd800 && point <= 0xdfff ) ) {
|
|
165
|
+
throw new Error( 'Exception: Invalid UTF-32 in BinaryString' );
|
|
166
|
+
}
|
|
167
|
+
out += String.fromCodePoint( point );
|
|
168
|
+
}
|
|
169
|
+
return out;
|
|
170
|
+
}
|
|
171
|
+
if ( codec === 'latin1' ) {
|
|
172
|
+
let out = '';
|
|
173
|
+
for ( const byte of bytes ) {
|
|
174
|
+
out += String.fromCharCode( byte );
|
|
175
|
+
}
|
|
176
|
+
return out;
|
|
177
|
+
}
|
|
178
|
+
throw new Error( `Exception: Unsupported encoding: ${encoding}` );
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
module.exports = {
|
|
182
|
+
encode,
|
|
183
|
+
decode,
|
|
184
|
+
ENCODING_UTF8: 'UTF-8',
|
|
185
|
+
ENCODING_UTF16: 'UTF-16',
|
|
186
|
+
ENCODING_UTF32: 'UTF-32',
|
|
187
|
+
ENCODING_LATIN: 'ISO-8859-1',
|
|
188
|
+
};
|
package/modules/std/string.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const { BinaryString } = require( '../../lib/runtime-helpers' );
|
|
4
|
+
|
|
3
5
|
function toStringValue( value ) {
|
|
4
6
|
if ( value instanceof RegExp ) {
|
|
5
7
|
return value.source;
|
|
@@ -272,6 +274,23 @@ function camel( text ) {
|
|
|
272
274
|
return ws.map( (w, i) => ( i === 0 ? w : `${w[0].toUpperCase()}${w.slice( 1 )}` ) ).join( '' );
|
|
273
275
|
}
|
|
274
276
|
|
|
277
|
+
function to_binary( value ) {
|
|
278
|
+
if ( typeof value !== 'string' && !( value instanceof String ) ) {
|
|
279
|
+
const type = value == null
|
|
280
|
+
? 'Null'
|
|
281
|
+
: ( value && value.bytes instanceof Uint8Array ) ? 'BinaryString' : typeof value;
|
|
282
|
+
throw new Error( `TypeException: to_binary expects String, got ${type}` );
|
|
283
|
+
}
|
|
284
|
+
return new BinaryString( new TextEncoder().encode( String( value ) ) );
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function to_string( value ) {
|
|
288
|
+
if ( !( value && value.bytes instanceof Uint8Array ) ) {
|
|
289
|
+
throw new Error( 'TypeException: to_string expects BinaryString' );
|
|
290
|
+
}
|
|
291
|
+
return new TextDecoder( 'utf-8', { fatal: true } ).decode( value.bytes );
|
|
292
|
+
}
|
|
293
|
+
|
|
275
294
|
module.exports = {
|
|
276
295
|
camel,
|
|
277
296
|
chomp,
|
|
@@ -295,5 +314,7 @@ module.exports = {
|
|
|
295
314
|
sprint,
|
|
296
315
|
substr,
|
|
297
316
|
title,
|
|
317
|
+
to_binary,
|
|
318
|
+
to_string,
|
|
298
319
|
trim,
|
|
299
320
|
};
|
package/modules/std/time.js
CHANGED
|
@@ -15,6 +15,7 @@ const MONTH_BY_NAME = new Map( [
|
|
|
15
15
|
[ 'nov', 11 ], [ 'november', 11 ],
|
|
16
16
|
[ 'dec', 12 ], [ 'december', 12 ],
|
|
17
17
|
] );
|
|
18
|
+
const DAY_ABBR = [ 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun' ];
|
|
18
19
|
const WEEKDAY_BY_NAME = new Set( [
|
|
19
20
|
'sun', 'sunday',
|
|
20
21
|
'mon', 'monday',
|
|
@@ -63,14 +64,72 @@ function offsetSeconds( zone ) {
|
|
|
63
64
|
return m[1] === '-' ? -seconds : seconds;
|
|
64
65
|
}
|
|
65
66
|
|
|
66
|
-
function pad( value, width = 2 ) {
|
|
67
|
-
return String( value ).padStart( width,
|
|
67
|
+
function pad( value, width = 2, fill = '0' ) {
|
|
68
|
+
return String( value ).padStart( width, fill );
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
function stripTrailingPeriod( text ) {
|
|
71
72
|
return String( text ).replace( /\.+$/u, '' ).toLowerCase();
|
|
72
73
|
}
|
|
73
74
|
|
|
75
|
+
function weekdayIndex( parts ) {
|
|
76
|
+
const date = new Date(
|
|
77
|
+
parts.year,
|
|
78
|
+
parts.month - 1,
|
|
79
|
+
parts.day
|
|
80
|
+
);
|
|
81
|
+
return ( ( date.getDay() + 6 ) % 7 ) + 1;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function weekParts( parts ) {
|
|
85
|
+
const date = new Date(
|
|
86
|
+
parts.year,
|
|
87
|
+
parts.month - 1,
|
|
88
|
+
parts.day
|
|
89
|
+
);
|
|
90
|
+
const weekDay = date.getDay() || 7;
|
|
91
|
+
const shifted = new Date( date );
|
|
92
|
+
shifted.setDate( date.getDate() + 4 - weekDay );
|
|
93
|
+
const weekYear = shifted.getFullYear();
|
|
94
|
+
const yearStart = new Date( weekYear, 0, 1 );
|
|
95
|
+
return {
|
|
96
|
+
year: weekYear,
|
|
97
|
+
week: Math.ceil( ( shifted - yearStart ) / 86400000 / 7 + 1 ),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function dayOfMonthForDisplay( day ) {
|
|
102
|
+
return String( day ).padStart( 2, ' ' );
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function dayOfYear( parts ) {
|
|
106
|
+
const start = new Date( parts.year, 0, 1 );
|
|
107
|
+
const now = new Date( parts.year, parts.month - 1, parts.day );
|
|
108
|
+
return ( now - start ) / 86400000;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function julianDay( parts ) {
|
|
112
|
+
let year = parts.year;
|
|
113
|
+
let month = parts.month;
|
|
114
|
+
const day = parts.day;
|
|
115
|
+
if ( month <= 2 ) {
|
|
116
|
+
year -= 1;
|
|
117
|
+
month += 12;
|
|
118
|
+
}
|
|
119
|
+
const a = Math.floor( ( 14 - month ) / 12 );
|
|
120
|
+
const y = year + 4800 - a;
|
|
121
|
+
const m = month + 12 * a - 3;
|
|
122
|
+
const jd = day
|
|
123
|
+
+ Math.floor( ( 153 * m + 2 ) / 5 )
|
|
124
|
+
+ 365 * y
|
|
125
|
+
+ Math.floor( y / 4 )
|
|
126
|
+
- Math.floor( y / 100 )
|
|
127
|
+
+ Math.floor( y / 400 )
|
|
128
|
+
- 32045;
|
|
129
|
+
const fraction = ( parts.hour * 3600 + parts.minute * 60 + parts.second ) / 86400;
|
|
130
|
+
return jd + fraction - 0.5;
|
|
131
|
+
}
|
|
132
|
+
|
|
74
133
|
function isLeapYear( year ) {
|
|
75
134
|
if ( year % 4 !== 0 ) return false;
|
|
76
135
|
if ( year % 100 !== 0 ) return true;
|
|
@@ -435,6 +494,39 @@ class Time {
|
|
|
435
494
|
mon() { return this._parts().month; }
|
|
436
495
|
month() { return this.mon(); }
|
|
437
496
|
year() { return this._parts().year; }
|
|
497
|
+
yy() { return pad( this._parts().year % 100, 2 ); }
|
|
498
|
+
day_of_week() { return weekdayIndex( this._parts() ); }
|
|
499
|
+
day() { return DAY_ABBR[ this.day_of_week() - 1 ]; }
|
|
500
|
+
day_of_year() { return dayOfYear( this._parts() ); }
|
|
501
|
+
month_last_day() { return daysInMonth( this._parts().year, this._parts().month ); }
|
|
502
|
+
hms( separator = ':' ) {
|
|
503
|
+
const p = this._parts();
|
|
504
|
+
return `${pad( p.hour )}${String( separator )}${pad( p.minute )}${String( separator )}${pad( p.second )}`;
|
|
505
|
+
}
|
|
506
|
+
ymd( separator = '-' ) {
|
|
507
|
+
const p = this._parts();
|
|
508
|
+
const sep = String( separator );
|
|
509
|
+
return `${pad( p.year, 4 )}${sep}${pad( p.month )}${sep}${pad( p.day )}`;
|
|
510
|
+
}
|
|
511
|
+
mdy( separator = '-' ) {
|
|
512
|
+
const p = this._parts();
|
|
513
|
+
const sep = String( separator );
|
|
514
|
+
return `${pad( p.month )}${sep}${pad( p.day )}${sep}${pad( p.year, 4 )}`;
|
|
515
|
+
}
|
|
516
|
+
dmy( separator = '-' ) {
|
|
517
|
+
const p = this._parts();
|
|
518
|
+
const sep = String( separator );
|
|
519
|
+
return `${pad( p.day )}${sep}${pad( p.month )}${sep}${pad( p.year, 4 )}`;
|
|
520
|
+
}
|
|
521
|
+
cdate() {
|
|
522
|
+
const p = this._parts();
|
|
523
|
+
return `${DAY_ABBR[ weekdayIndex( p ) - 1 ]} ${MONTHS[p.month - 1]} ${dayOfMonthForDisplay( p.day )} ${pad( p.hour )}:${pad( p.minute )}:${pad( p.second )} ${p.year}`;
|
|
524
|
+
}
|
|
525
|
+
tzoffset() { return offsetForEpoch( this._epoch, this._timezone ); }
|
|
526
|
+
is_leap_year() { return isLeapYear( this._parts().year ); }
|
|
527
|
+
week() { return weekParts( this._parts() ).week; }
|
|
528
|
+
week_year() { return weekParts( this._parts() ).year; }
|
|
529
|
+
julian_day() { return julianDay( this._parts() ); }
|
|
438
530
|
add_seconds( n ) { return this._clone( this._epoch + Number( n ) ); }
|
|
439
531
|
add_minutes( n ) { return this.add_seconds( Number( n ) * 60 ); }
|
|
440
532
|
add_hours( n ) { return this.add_seconds( Number( n ) * 3600 ); }
|
|
@@ -509,8 +601,7 @@ class Time {
|
|
|
509
601
|
}
|
|
510
602
|
to_rfc5322( options = {} ) {
|
|
511
603
|
const p = this._parts();
|
|
512
|
-
const
|
|
513
|
-
const weekday = [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ][ d.getUTCDay() ];
|
|
604
|
+
const weekday = DAY_ABBR[ weekdayIndex( p ) - 1 ];
|
|
514
605
|
const core = `${pad( p.day )} ${MONTHS[p.month - 1]} ${pad( p.year, 4 )} ${pad( p.hour )}:${pad( p.minute )}:${pad( p.second )} ${formatOffset( offsetForEpoch( this._epoch, this._timezone ), false )}`;
|
|
515
606
|
return options.include_weekday === false ? core : `${weekday}, ${core}`;
|
|
516
607
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zuzu-js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "JavaScript runtime, compiler, and browser bundle for ZuzuScript.",
|
|
5
5
|
"main": "lib/zuzu.js",
|
|
6
6
|
"bin": {
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"node": ">=16"
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
+
"@std-uritemplate/std-uritemplate": "^2.0.10",
|
|
51
52
|
"@xmldom/xmldom": "^0.9.10",
|
|
52
53
|
"adm-zip": "^0.5.17",
|
|
53
54
|
"better-sqlite3": "^11.10.0",
|