Compare commits
465 Commits
v0.1.0-alp
...
v0.10.0-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
0b6484e0b4
|
|||
|
07e0d0ba3b
|
|||
|
93cae33bb0
|
|||
|
b1e0db627c
|
|||
|
4fd05e9e6f
|
|||
|
de93adde8f
|
|||
|
5c68c1717c
|
|||
|
7883af7fff
|
|||
|
3d5be60fc7
|
|||
|
ba0b4e06ad
|
|||
|
58e3546459
|
|||
|
57097e46ca
|
|||
| 28e72503a6 | |||
| d0a0796e14 | |||
| 0de54e5505 | |||
| 96c9d29a6a | |||
| 37dc844cc4 | |||
|
b97e4bf163
|
|||
|
9ce0504948
|
|||
|
4c53689d9c
|
|||
|
6c8c329992
|
|||
|
74f08b7483
|
|||
|
2bf920c681
|
|||
|
1ac68425af
|
|||
|
7423e3f283
|
|||
|
afe4542682
|
|||
|
c971fde0e2
|
|||
|
8a34db833c
|
|||
|
398d88c0a5
|
|||
|
f80266618c
|
|||
|
f1997e4584
|
|||
|
e93498fed5
|
|||
|
29bea68525
|
|||
|
3a6401094f
|
|||
|
a705bf296e
|
|||
|
c3432306f7
|
|||
|
d8c18ccf17
|
|||
|
277f59e54a
|
|||
|
a1bb5bdba6
|
|||
|
c699102e56
|
|||
|
b2f5068e12
|
|||
|
05bafc7317
|
|||
|
c7c31c3859
|
|||
|
c18015b9a0
|
|||
|
5346b8917d
|
|||
|
2ed7959088
|
|||
|
239d0ae8d7
|
|||
|
6fac295b99
|
|||
|
66440a9649
|
|||
|
274a925ff8
|
|||
|
130fe17441
|
|||
|
493c54ed39
|
|||
|
75786f39ad
|
|||
|
bce8b7293c
|
|||
|
00aeef5321
|
|||
|
ff4aa3dcfd
|
|||
|
45f52ff123
|
|||
|
bed4014bef
|
|||
|
17406c6b81
|
|||
|
3abdd8d3c9
|
|||
|
a329f615cf
|
|||
|
058d20b27f
|
|||
|
ea158c1e60
|
|||
|
4e18c76bb1
|
|||
|
8d7939d85a
|
|||
|
814e0d7b7e
|
|||
|
138baebacb
|
|||
|
db241919f2
|
|||
|
db45eb529d
|
|||
| caf44a50e5 | |||
|
65f9debecc
|
|||
|
3f5e1a5fd8
|
|||
|
b0d8a072aa
|
|||
|
2e8e123ec4
|
|||
|
54d55814af
|
|||
|
28679f44d5
|
|||
|
611be55bbb
|
|||
|
4992d0211b
|
|||
|
ba7505e148
|
|||
|
3c13799c2f
|
|||
|
5c2fd00bfb
|
|||
|
39eb6ffac5
|
|||
|
7f6df49fc5
|
|||
|
61c5df5003
|
|||
|
41bb27e4dd
|
|||
|
b4e7a42975
|
|||
|
92fb0dbbfd
|
|||
|
fe8f65faf5
|
|||
|
54fe16393a
|
|||
|
14e4aade35
|
|||
|
754648fbf6
|
|||
|
20f2c3c5e9
|
|||
|
c5067cbfdd
|
|||
|
5644966dd7
|
|||
|
50eb204da7
|
|||
|
45308e6d65
|
|||
|
0ebf189c95
|
|||
|
2fe5ce607a
|
|||
|
d821a3a89b
|
|||
|
0064c74ac8
|
|||
|
bd548c813a
|
|||
|
78af69ec80
|
|||
|
98e5299f9c
|
|||
|
797452c28f
|
|||
|
fee74dcba4
|
|||
|
9845a39e84
|
|||
|
ee78bdc1cb
|
|||
|
7fcb9ed19f
|
|||
|
8dee4353d4
|
|||
|
59e6f34a01
|
|||
|
fdac93bf4d
|
|||
| 06eae2eaf2 | |||
|
d7b50fa45b
|
|||
|
077e8afff7
|
|||
|
631ef9db4a
|
|||
|
0258cc0a90
|
|||
|
0b3dca8b13
|
|||
|
6d56660136
|
|||
|
8a21873631
|
|||
|
fbbd26bf7d
|
|||
|
e2a8a382cc
|
|||
|
0670ffcdd1
|
|||
|
9b957335bf
|
|||
|
e9f889576c
|
|||
|
e11d182631
|
|||
|
30674ee463
|
|||
|
fab3e59910
|
|||
|
d7e4e7a024
|
|||
|
e4166fe450
|
|||
| b5b7781136 | |||
|
f13bf584a5
|
|||
|
4e8b110fc4
|
|||
|
cb82f4cf32
|
|||
|
23ae2b2e46
|
|||
|
55613ef2c9
|
|||
|
0b34c68139
|
|||
|
d87e53264d
|
|||
|
1f42263051
|
|||
|
3e6816cb2c
|
|||
|
2a01b3d86e
|
|||
|
8e64d13c87
|
|||
|
5f88061ee7
|
|||
|
b9268518f6
|
|||
|
63ccd5757e
|
|||
|
515011b3ef
|
|||
| b6f04aa177 | |||
|
6f548ce5ff
|
|||
|
7c74df4d36
|
|||
| 641d4d1863 | |||
|
0004839f6f
|
|||
|
07411f62c8
|
|||
|
4bfc062aaf
|
|||
|
9ec99def3f
|
|||
|
8f13ff3e0b
|
|||
|
d7353e19ed
|
|||
|
e509edc459
|
|||
|
b6a90b108b
|
|||
|
55caf1e206
|
|||
|
eac4a3af23
|
|||
|
5b4adf5286
|
|||
|
807b3918fa
|
|||
|
9265a7d813
|
|||
|
1589ef51b0
|
|||
|
|
313b20a3b3 | ||
|
ce9b419757
|
|||
|
6f8efe74c4
|
|||
|
bd503ed943
|
|||
|
5359c54694
|
|||
|
93c246ad97
|
|||
|
7c8efaf066
|
|||
|
546d663466
|
|||
|
f929015f55
|
|||
|
2a5f6aa9aa
|
|||
|
78d233a6bd
|
|||
|
3f25868f19
|
|||
|
e3f1bc0949
|
|||
|
506726fd76
|
|||
|
4cbc108686
|
|||
|
c39c06b792
|
|||
|
7ab78b9cea
|
|||
|
87e077d70d
|
|||
|
52f7c15ac8
|
|||
|
6932bea677
|
|||
|
82d6f52a26
|
|||
|
1b87c2f5a6
|
|||
|
163dfe7b6e
|
|||
|
2b6d4a87ca
|
|||
|
349d392269
|
|||
|
6481e992fa
|
|||
|
4a5e5a71af
|
|||
|
89ba921b4a
|
|||
|
a45f4109d8
|
|||
|
62aba62a41
|
|||
|
4048d6ef1c
|
|||
|
d0ccf00f14
|
|||
|
f09feae08f
|
|||
|
ef0fc5a61d
|
|||
|
bab59d454f
|
|||
|
3475e3a130
|
|||
|
49b52d90a7
|
|||
|
8a8de518a6
|
|||
|
099201e4ad
|
|||
|
f8553b79f9
|
|||
|
ba6462458c
|
|||
|
e33cdbb172
|
|||
|
fc6ddf7d3c
|
|||
|
7f275095a2
|
|||
|
90d019f92a
|
|||
|
6fb31be12c
|
|||
|
d6ede0b404
|
|||
|
71b40240c0
|
|||
|
a8775d212f
|
|||
|
71872a8d64
|
|||
|
c707f880f7
|
|||
|
0d629a6e82
|
|||
|
6eea342d04
|
|||
|
ece9cf68dc
|
|||
|
11ecd8c68f
|
|||
|
48bd304c6e
|
|||
|
d8e46c3149
|
|||
|
5fc5eb08f8
|
|||
|
8acbc003c4
|
|||
|
fda33112a7
|
|||
|
67b2413d7c
|
|||
|
3a05416510
|
|||
|
c291328cc3
|
|||
|
7cb267b0d9
|
|||
|
4e31766665
|
|||
|
b5b63c4342
|
|||
|
18f70a0d58
|
|||
|
d38b600366
|
|||
|
0ac7af2ea3
|
|||
|
5185c93663
|
|||
|
510422cab7
|
|||
|
79e1d1cfaf
|
|||
|
e0f3274a95
|
|||
| ef5d29105f | |||
|
d8ea6557df
|
|||
|
5ba858545a
|
|||
|
446ecd8f2b
|
|||
|
fbf103439a
|
|||
|
b30f94de8f
|
|||
|
47b4dd3c15
|
|||
|
0fb336373a
|
|||
|
b33325a98d
|
|||
|
ccf75d6066
|
|||
|
662dcc1ac1
|
|||
|
3e6214659b
|
|||
|
7c6622c53d
|
|||
|
70d5871091
|
|||
|
7b9bbe576b
|
|||
|
e6399c1546
|
|||
|
c8253795b2
|
|||
|
49dd725669
|
|||
|
52489ad3a8
|
|||
|
c4d44547c8
|
|||
|
c6dc031cfd
|
|||
|
285960bdd6
|
|||
|
cf99845f6b
|
|||
|
48bd1e2015
|
|||
|
304665904f
|
|||
|
56ae3a85a6
|
|||
|
0178cb2194
|
|||
|
a2cb5cd473
|
|||
|
c123427e77
|
|||
|
7ac5ded185
|
|||
|
769d6ae8fc
|
|||
|
afb99ffc70
|
|||
|
46136e77e2
|
|||
|
b460b87318
|
|||
|
e772f0b50d
|
|||
|
187ad55291
|
|||
|
8156a1733f
|
|||
|
d1937fdb63
|
|||
|
3f826cc85d
|
|||
|
af35d7f20b
|
|||
|
cfa0e001b9
|
|||
|
c10d071346
|
|||
|
89eea7d62b
|
|||
| 18d22a1282 | |||
|
72a0d39d0b
|
|||
|
d8585a20bf
|
|||
|
e4c9a8756e
|
|||
|
c6e6425c7e
|
|||
|
292ca64cb9
|
|||
|
4572db5c46
|
|||
|
0251c4689d
|
|||
|
3173919b49
|
|||
|
45f133c6b0
|
|||
|
34276d720c
|
|||
|
2c634778c3
|
|||
|
4aa757e625
|
|||
|
918f2623cd
|
|||
|
a321fc14bc
|
|||
|
07a6f18b16
|
|||
|
5f4011aa0c
|
|||
|
71f735b812
|
|||
|
e575b3287e
|
|||
|
4fa5f5a815
|
|||
|
a61c6a494e
|
|||
|
978ee8db79
|
|||
|
e372941e99
|
|||
|
c36a0db860
|
|||
|
d6ee59363f
|
|||
|
d5fd6e650e
|
|||
|
c0b895e00a
|
|||
|
9ea5cd9277
|
|||
|
aaf9a6fa45
|
|||
|
579f09c837
|
|||
|
3343262e72
|
|||
|
153a1a9a49
|
|||
|
bf38587271
|
|||
|
21534efd60
|
|||
|
802f2f3f52
|
|||
|
38acc20a6f
|
|||
|
d7f6010319
|
|||
|
87f8975275
|
|||
| 09eb6507dc | |||
|
2f58f6b245
|
|||
|
4bc363485f
|
|||
|
8e01db637b
|
|||
|
1c64912786
|
|||
|
28265a8f44
|
|||
|
e9e020512e
|
|||
|
4b0bbf5190
|
|||
|
031f78ebf1
|
|||
|
562f1f9425
|
|||
|
82a09e8865
|
|||
|
90da2db1fb
|
|||
|
3d275c52c0
|
|||
|
797585f539
|
|||
|
7a301eba8f
|
|||
|
1b2068a274
|
|||
|
078d7ed95f
|
|||
|
93973c12b1
|
|||
|
cad4f34b51
|
|||
|
d3571d6ee2
|
|||
|
caf9131cde
|
|||
| 8c6379eb24 | |||
|
380a5c720a
|
|||
|
76c3d377af
|
|||
|
42f443572a
|
|||
|
773bc72f3e
|
|||
|
0b5178e9fd
|
|||
|
8cffcff7db
|
|||
|
60bbaaccd4
|
|||
|
60b1762462
|
|||
|
34434965d2
|
|||
|
fe86123f0f
|
|||
|
d5e6edfa8b
|
|||
|
73345062ca
|
|||
|
124341969c
|
|||
|
8defd93855
|
|||
|
6c57e0e9f2
|
|||
|
f1932914ee
|
|||
|
977701e601
|
|||
|
e8a7ac8da8
|
|||
|
6b1cb852c2
|
|||
|
b59a003086
|
|||
|
1902e41f61
|
|||
|
27162ef8ac
|
|||
|
4f22e76d2b
|
|||
|
8924e7aadc
|
|||
| 1d0e31a423 | |||
|
ab56908171
|
|||
|
eb14bb080c
|
|||
|
f52f47cdb4
|
|||
|
567eaa8514
|
|||
|
2cfdd8e335
|
|||
|
4b1ec671e2
|
|||
|
b127aadcf6
|
|||
|
b6eaff65ca
|
|||
|
443dc0ffa1
|
|||
|
e107dd3507
|
|||
|
6af3c70fce
|
|||
|
8b743f49d1
|
|||
|
e1ce384815
|
|||
|
86d205e521
|
|||
|
f0ad936e5b
|
|||
|
4a1473c5be
|
|||
|
4111dbf5c4
|
|||
|
1666682dc2
|
|||
|
f2b33d0233
|
|||
|
f5a0b6eaf7
|
|||
|
829bea755d
|
|||
|
4b0dcffd13
|
|||
|
987f8b8f00
|
|||
|
55e3d46bc2
|
|||
|
3e25068219
|
|||
|
7ecb8bfabb
|
|||
|
488deea164
|
|||
|
ed08041335
|
|||
|
0a4149ba81
|
|||
|
30f5d619c3
|
|||
|
e7dbe91374
|
|||
|
455f5a613e
|
|||
|
1eeac3ae97
|
|||
|
1acd78e119
|
|||
|
df9932874d
|
|||
|
b47d1a7576
|
|||
|
fdfa8d7713
|
|||
|
f5d1287948
|
|||
|
15f4278cb1
|
|||
| df8465cb49 | |||
|
e3104c223c
|
|||
|
1d0bae3de8
|
|||
|
b66acd3089
|
|||
|
e326847287
|
|||
|
26591d6631
|
|||
|
af31b1ab79
|
|||
|
f885d4349f
|
|||
|
d57044acb7
|
|||
|
7df4e3b03f
|
|||
|
ed1009ab69
|
|||
|
f856cdf37e
|
|||
|
4f82574b8c
|
|||
|
0ae24148d8
|
|||
|
ac128d17f4
|
|||
|
6508f15bb0
|
|||
|
69f93b4f87
|
|||
|
b675411925
|
|||
|
d1e93c2088
|
|||
|
942db54546
|
|||
|
d20df66982
|
|||
| 16a9e5d761 | |||
|
dc9d84a370
|
|||
|
edb71daef4
|
|||
|
4b84309df6
|
|||
|
d2d9fa9738
|
|||
|
a4e5f7f471
|
|||
|
cc044374ba
|
|||
|
517e3e6657
|
|||
|
926b9c17d8
|
|||
|
fc705e7383
|
|||
|
a17ec737b7
|
|||
|
952a4d631d
|
|||
| 005610ca40 | |||
|
9743a6c078
|
|||
|
21a6d20ac5
|
|||
|
4d1846f082
|
|||
| 9547c86b32 | |||
|
1cccfa90a8
|
|||
|
604b752be6
|
|||
|
6c7fe6deaf
|
|||
|
68f3b6d926
|
|||
|
63b04019cf
|
|||
|
9c65bacbac
|
|||
|
0dacaaeb4c
|
|||
|
c1b84689c4
|
|||
|
bd08902196
|
|||
|
22ec95a7b5
|
|||
|
6c71911575
|
|||
|
e16c0fedb1
|
|||
|
cf36b7adc5
|
|||
|
ff9b57aeb9
|
|||
|
47c62128ab
|
|||
|
4aaf2f26db
|
|||
|
f21cdc831c
|
|||
|
86b206051d
|
|||
|
f2cd50726d
|
|||
|
d8071af480
|
|||
|
0b7442a3d8
|
|||
|
356a5a5b78
|
|||
|
da4b35f506
|
|||
|
8c049ac08f
|
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@@ -0,0 +1 @@
|
||||
* -text
|
||||
31
.github/workflows/tagged-release.yml
vendored
31
.github/workflows/tagged-release.yml
vendored
@@ -3,7 +3,7 @@ name: "tagged-release"
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "v*"
|
||||
- "*"
|
||||
|
||||
jobs:
|
||||
tagged-release:
|
||||
@@ -12,18 +12,23 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Clone repository
|
||||
uses: GuillaumeFalourd/clone-github-repo-action@main
|
||||
uses: actions/checkout@v3
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
owner: 'TopchetoEU'
|
||||
repository: 'java-jscript'
|
||||
- name: "Build"
|
||||
run: |
|
||||
cd java-jscript; node ./build.js release ${{ github.ref }}
|
||||
|
||||
- uses: "marvinpinto/action-automatic-releases@latest"
|
||||
distribution: 'adopt'
|
||||
java-version: '17'
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
with:
|
||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
prerelease: false
|
||||
cache-disabled: true
|
||||
gradle-version: "8.10"
|
||||
- name: Build
|
||||
run: gradle build
|
||||
- name: Create release
|
||||
uses: "https://gitea.com/actions/gitea-release-action@main"
|
||||
with:
|
||||
# api_key: "${{secrets.TOKEN}}"
|
||||
files: |
|
||||
java-jscript/LICENSE
|
||||
java-jscript/dst/*.jar
|
||||
LICENSE
|
||||
build/libs/*.jar
|
||||
30
.gitignore
vendored
30
.gitignore
vendored
@@ -1,11 +1,19 @@
|
||||
.vscode
|
||||
.gradle
|
||||
.ignore
|
||||
/out
|
||||
/build
|
||||
/bin
|
||||
/dst
|
||||
/*.js
|
||||
!/build.js
|
||||
/dead-code
|
||||
/Metadata.java
|
||||
/*
|
||||
|
||||
!/src
|
||||
!/doc
|
||||
!/tests
|
||||
!/.github
|
||||
|
||||
!/.gitignore
|
||||
!/.gitattributes
|
||||
!/LICENSE
|
||||
!/README.md
|
||||
|
||||
!/settings.gradle
|
||||
!/build.gradle
|
||||
!/gradle.properties
|
||||
|
||||
!/package.json
|
||||
!/rollup.config.js
|
||||
!/tsconfig.json
|
||||
|
||||
33
README.md
33
README.md
@@ -2,33 +2,24 @@
|
||||
|
||||
**NOTE: This had nothing to do with Microsoft's dialect of EcmaScript**
|
||||
|
||||
**WARNING: Currently, this code is mostly undocumented. Proceed with caution and a psychiatrist.**
|
||||
**WARNING: Currently, this code is undocumented. Proceed with caution and a psychiatrist.**
|
||||
|
||||
JScript is an engine, capable of running EcmaScript 5, written entirely in Java. This engine has been developed with the goal of being easy to integrate with your preexisting codebase, **THE GOAL OF THIS ENGINE IS NOT PERFORMANCE**. My crude experiments show that this engine is 50x-100x slower than V8, which, although bad, is acceptable for most simple scripting purposes.
|
||||
JScript is an engine, capable of running EcmaScript 5, written entirely in Java. This engine has been developed with the goal of being easy to integrate with your preexisting codebase, **THE GOAL OF THIS ENGINE IS NOT PERFORMANCE**. My crude experiments show that this engine is 50x-100x slower than V8, which, although bad, is acceptable for most simple scripting purposes. Note that although the codebase has a Main class, this isn't meant to be a standalone program, but instead a library for running JavaScript code.
|
||||
|
||||
## Example
|
||||
|
||||
The following will create a REPL using the engine as a backend. Not that this won't properly log errors. I recommend checking out the implementation in `Main.main`:
|
||||
The following is going to execute a simple javascript statement:
|
||||
|
||||
```java
|
||||
var engine = new PolyfillEngine(new File("."));
|
||||
var in = new BufferedReader(new InputStreamReader(System.in));
|
||||
engine.start();
|
||||
var engine = new Engine();
|
||||
// Initialize a standard environment, with implementations of most basic standard libraries (Object, Array, Symbol, etc.)
|
||||
var env = Internals.apply(new Environment());
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
var raw = in.readLine();
|
||||
// Queue code to load internal libraries and start engine
|
||||
var awaitable = engine.pushMsg(false, env, new Filename("tmp", "eval"), "10 + Math.sqrt(5 / 3)", null);
|
||||
// Run the engine on the same thread, until the event loop runs empty
|
||||
engine.run(true);
|
||||
|
||||
var res = engine.pushMsg(false, engine.global(), Map.of(), "<stdio>", raw, null).await();
|
||||
Values.printValue(engine.context(), res);
|
||||
System.out.println();
|
||||
}
|
||||
catch (EngineException e) {
|
||||
try {
|
||||
System.out.println("Uncaught " + e.toString(engine.context()));
|
||||
}
|
||||
catch (InterruptedException _e) { return; }
|
||||
}
|
||||
catch (IOException | InterruptedException e) { return; }
|
||||
}
|
||||
// Get our result
|
||||
System.out.println(awaitable.await());
|
||||
```
|
||||
|
||||
128
build.gradle
Normal file
128
build.gradle
Normal file
@@ -0,0 +1,128 @@
|
||||
import java.text.SimpleDateFormat
|
||||
|
||||
plugins {
|
||||
id 'application';
|
||||
id 'com.github.node-gradle.node' version '5.0.0';
|
||||
id 'net.nemerosa.versioning' version '2.15.0';
|
||||
id 'org.ajoberstar.grgit' version '5.0.0-rc.3'; // required by gradle
|
||||
|
||||
// TODO: figure out how to integrate proguard
|
||||
// id "com.github.xaverkapeller.proguard-annotations"
|
||||
}
|
||||
|
||||
base.archivesName = project.project_name;
|
||||
version = project.project_version;
|
||||
group = project.project_group;
|
||||
description = 'ES5-compliant JavaScript interpreter';
|
||||
|
||||
node {
|
||||
version = '20.0.0';
|
||||
npmVersion = '8.0.0';
|
||||
download = true;
|
||||
}
|
||||
|
||||
task compileEnv(type: NpmTask) {
|
||||
dependsOn npmInstall;
|
||||
|
||||
inputs.files('rollup.config.js');
|
||||
inputs.dir('src/lib/libs');
|
||||
outputs.files("build/js/env.js");
|
||||
|
||||
// group = 'build'
|
||||
args = ['run', 'build-env'];
|
||||
}
|
||||
task compileTypescript(type: NpmTask) {
|
||||
dependsOn npmInstall;
|
||||
|
||||
inputs.files('rollup.config.js');
|
||||
inputs.dir('src/lib/transpiler');
|
||||
outputs.files("build/js/ts.js");
|
||||
// nom nom tasty ram
|
||||
environment.put("NODE_OPTIONS", "--max-old-space-size=4096");
|
||||
|
||||
// group = 'build'
|
||||
args = ['run', 'build-ts'];
|
||||
}
|
||||
|
||||
|
||||
repositories {
|
||||
mavenCentral();
|
||||
}
|
||||
|
||||
dependencies {
|
||||
annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2';
|
||||
compileOnly 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2';
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2';
|
||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher';
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_17;
|
||||
targetCompatibility = JavaVersion.VERSION_17;
|
||||
|
||||
toolchain {
|
||||
languageVersion = JavaLanguageVersion.of(17);
|
||||
}
|
||||
}
|
||||
|
||||
configure([tasks.compileJava]) {
|
||||
options.release = 8;
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest {
|
||||
attributes(
|
||||
'Main-Class': project.main_class,
|
||||
'Build-Timestamp': new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").format(new Date()),
|
||||
'Build-Branch': versioning.info.branch,
|
||||
'Build-Revision': versioning.info.commit,
|
||||
'Build-Jdk': "${System.properties['java.version']} (${System.properties['java.vendor']} ${System.properties['java.vm.version']})",
|
||||
'Build-Author': 'TopchetoEU',
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
application {
|
||||
mainClass = project.main_class;
|
||||
applicationDefaultJvmArgs = ['-Xmx2G', '-Xms2G', '-server', '-Dfile.encoding=UTF-8'];
|
||||
}
|
||||
|
||||
distZip {
|
||||
eachFile { file ->
|
||||
if (file.path.contains('bin')) {
|
||||
file.exclude();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
distTar {
|
||||
eachFile { file ->
|
||||
if (file.path.contains('bin')) {
|
||||
file.exclude();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processResources {
|
||||
dependsOn compileEnv;
|
||||
dependsOn compileTypescript;
|
||||
|
||||
from("build/js") {
|
||||
into "lib";
|
||||
}
|
||||
|
||||
filesMatching "metadata.json", {
|
||||
expand(
|
||||
version: project.project_version,
|
||||
name: project.project_name,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
test {
|
||||
useJUnitPlatform();
|
||||
}
|
||||
|
||||
wrapper {
|
||||
gradleVersion = '8.10';
|
||||
}
|
||||
80
build.js
80
build.js
@@ -1,80 +0,0 @@
|
||||
const { spawn } = require('child_process');
|
||||
const fs = require('fs/promises');
|
||||
const pt = require('path');
|
||||
const { argv } = require('process');
|
||||
|
||||
const conf = {
|
||||
name: "java-jscript",
|
||||
author: "TopchetoEU",
|
||||
javahome: "",
|
||||
version: argv[3]
|
||||
};
|
||||
|
||||
if (conf.version.startsWith('refs/tags/')) conf.version = conf.version.substring(10);
|
||||
if (conf.version.startsWith('v')) conf.version = conf.version.substring(1);
|
||||
|
||||
async function* find(src, dst, wildcard) {
|
||||
const stat = await fs.stat(src);
|
||||
|
||||
if (stat.isDirectory()) {
|
||||
for (const el of await fs.readdir(src)) {
|
||||
for await (const res of find(pt.join(src, el), dst ? pt.join(dst, el) : undefined, wildcard)) yield res;
|
||||
}
|
||||
}
|
||||
else if (stat.isFile() && wildcard(src)) yield dst ? { src, dst } : src;
|
||||
}
|
||||
async function copy(src, dst, wildcard) {
|
||||
const promises = [];
|
||||
|
||||
for await (const el of find(src, dst, wildcard)) {
|
||||
promises.push((async () => {
|
||||
await fs.mkdir(pt.dirname(el.dst), { recursive: true });
|
||||
await fs.copyFile(el.src, el.dst);
|
||||
})());
|
||||
}
|
||||
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
function run(cmd, ...args) {
|
||||
return new Promise((res, rej) => {
|
||||
const proc = spawn(cmd, args, { stdio: 'inherit' });
|
||||
proc.once('exit', code => {
|
||||
if (code === 0) res(code);
|
||||
else rej(new Error(`Process ${cmd} exited with code ${code}.`));
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
async function compileJava() {
|
||||
try {
|
||||
await fs.writeFile('Metadata.java', (await fs.readFile('src/me/topchetoeu/jscript/Metadata.java')).toString()
|
||||
.replace('${VERSION}', conf.version)
|
||||
.replace('${NAME}', conf.name)
|
||||
.replace('${AUTHOR}', conf.author)
|
||||
);
|
||||
const args = ['--release', '11', ];
|
||||
if (argv[1] === 'debug') args.push('-g');
|
||||
args.push('-d', 'dst/classes', 'Metadata.java');
|
||||
|
||||
for await (const path of find('src', undefined, v => v.endsWith('.java') && !v.endsWith('Metadata.java'))) args.push(path);
|
||||
await run(conf.javahome + 'javac', ...args);
|
||||
}
|
||||
finally {
|
||||
await fs.rm('Metadata.java');
|
||||
}
|
||||
}
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
try { await fs.rm('dst', { recursive: true }); } catch {}
|
||||
await copy('src', 'dst/classes', v => !v.endsWith('.java'));
|
||||
await run('tsc', '-p', 'lib/tsconfig.json', '--outFile', 'dst/classes/me/topchetoeu/jscript/js/core.js'),
|
||||
await compileJava();
|
||||
await run('jar', '-c', '-f', 'dst/jscript.jar', '-e', 'me.topchetoeu.jscript.Main', '-C', 'dst/classes', '.');
|
||||
}
|
||||
catch (e) {
|
||||
if (argv[2] === 'debug') throw e;
|
||||
else console.log(e.toString());
|
||||
}
|
||||
})();
|
||||
@@ -1,154 +0,0 @@
|
||||
package me.topchetoeu.jscript.engine.debug;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.security.MessageDigest;
|
||||
import java.util.Base64;
|
||||
|
||||
import me.topchetoeu.jscript.engine.Engine;
|
||||
import me.topchetoeu.jscript.engine.debug.WebSocketMessage.Type;
|
||||
import me.topchetoeu.jscript.engine.debug.handlers.DebuggerHandles;
|
||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
||||
|
||||
public class DebugServer {
|
||||
public static String browserDisplayName = "jscript";
|
||||
public static String targetName = "target";
|
||||
|
||||
public final Engine engine;
|
||||
|
||||
private static void send(Socket socket, String val) throws IOException {
|
||||
Http.writeResponse(socket.getOutputStream(), 200, "OK", "application/json", val.getBytes());
|
||||
}
|
||||
|
||||
// SILENCE JAVA
|
||||
private MessageDigest getDigestInstance() {
|
||||
try {
|
||||
return MessageDigest.getInstance("sha1");
|
||||
}
|
||||
catch (Throwable a) { return null; }
|
||||
}
|
||||
|
||||
private static Thread runAsync(Runnable func, String name) {
|
||||
var res = new Thread(func);
|
||||
res.setName(name);
|
||||
res.start();
|
||||
return res;
|
||||
}
|
||||
|
||||
private void handle(WebSocket ws) throws InterruptedException, IOException {
|
||||
WebSocketMessage raw;
|
||||
|
||||
while ((raw = ws.receive()) != null) {
|
||||
if (raw.type != Type.Text) {
|
||||
ws.send(new V8Error("Expected a text message."));
|
||||
continue;
|
||||
}
|
||||
|
||||
V8Message msg;
|
||||
|
||||
try {
|
||||
msg = new V8Message(raw.textData());
|
||||
}
|
||||
catch (SyntaxException e) {
|
||||
ws.send(new V8Error(e.getMessage()));
|
||||
return;
|
||||
}
|
||||
|
||||
switch (msg.name) {
|
||||
case "Debugger.enable": DebuggerHandles.enable(msg, engine, ws); continue;
|
||||
case "Debugger.disable": DebuggerHandles.disable(msg, engine, ws); continue;
|
||||
case "Debugger.stepInto": DebuggerHandles.stepInto(msg, engine, ws); continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
private void onWsConnect(HttpRequest req, Socket socket) throws IOException {
|
||||
var key = req.headers.get("sec-websocket-key");
|
||||
|
||||
if (key == null) {
|
||||
Http.writeResponse(
|
||||
socket.getOutputStream(), 426, "Upgrade Required", "text/txt",
|
||||
"Expected a WS upgrade".getBytes()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
var resKey = Base64.getEncoder().encodeToString(getDigestInstance().digest(
|
||||
(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11").getBytes()
|
||||
));
|
||||
|
||||
Http.writeCode(socket.getOutputStream(), 101, "Switching Protocols");
|
||||
Http.writeHeader(socket.getOutputStream(), "Connection", "Upgrade");
|
||||
Http.writeHeader(socket.getOutputStream(), "Sec-WebSocket-Accept", resKey);
|
||||
Http.writeLastHeader(socket.getOutputStream(), "Upgrade", "WebSocket");
|
||||
|
||||
var ws = new WebSocket(socket);
|
||||
|
||||
runAsync(() -> {
|
||||
try {
|
||||
handle(ws);
|
||||
}
|
||||
catch (InterruptedException e) { return; }
|
||||
catch (IOException e) { e.printStackTrace(); }
|
||||
finally { ws.close(); }
|
||||
}, "Debug Server Message Reader");
|
||||
runAsync(() -> {
|
||||
try {
|
||||
handle(ws);
|
||||
}
|
||||
catch (InterruptedException e) { return; }
|
||||
catch (IOException e) { e.printStackTrace(); }
|
||||
finally { ws.close(); }
|
||||
}, "Debug Server Event Writer");
|
||||
}
|
||||
|
||||
public void open(InetSocketAddress address) throws IOException {
|
||||
ServerSocket server = new ServerSocket();
|
||||
server.bind(address);
|
||||
|
||||
try {
|
||||
while (true) {
|
||||
var socket = server.accept();
|
||||
var req = Http.readRequest(socket.getInputStream());
|
||||
|
||||
switch (req.path) {
|
||||
case "/json/version":
|
||||
send(socket, "{\"Browser\":\"" + browserDisplayName + "\",\"Protocol-Version\":\"1.2\"}");
|
||||
break;
|
||||
case "/json/list":
|
||||
case "/json":
|
||||
var addr = "ws://" + address.getHostString() + ":" + address.getPort() + "/devtools/page/" + targetName;
|
||||
send(socket, "{\"id\":\"" + browserDisplayName + "\",\"webSocketDebuggerUrl\":\"" + addr + "\"}");
|
||||
break;
|
||||
case "/json/new":
|
||||
case "/json/activate":
|
||||
case "/json/protocol":
|
||||
case "/json/close":
|
||||
case "/devtools/inspector.html":
|
||||
Http.writeResponse(
|
||||
socket.getOutputStream(),
|
||||
501, "Not Implemented", "text/txt",
|
||||
"This feature isn't (and won't be) implemented.".getBytes()
|
||||
);
|
||||
break;
|
||||
default:
|
||||
if (req.path.equals("/devtools/page/" + targetName)) onWsConnect(req, socket);
|
||||
else {
|
||||
Http.writeResponse(
|
||||
socket.getOutputStream(),
|
||||
404, "Not Found", "text/txt",
|
||||
"Not found :/".getBytes()
|
||||
);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally { server.close(); }
|
||||
}
|
||||
|
||||
public DebugServer(Engine engine) {
|
||||
this.engine = engine;
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package me.topchetoeu.jscript.engine.debug;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.engine.BreakpointData;
|
||||
import me.topchetoeu.jscript.engine.DebugCommand;
|
||||
import me.topchetoeu.jscript.engine.frame.CodeFrame;
|
||||
import me.topchetoeu.jscript.events.Event;
|
||||
|
||||
public class DebugState {
|
||||
private boolean paused = false;
|
||||
|
||||
public final HashSet<Location> breakpoints = new HashSet<>();
|
||||
public final List<CodeFrame> frames = new ArrayList<>();
|
||||
public final Map<String, String> sources = new HashMap<>();
|
||||
|
||||
public final Event<BreakpointData> breakpointNotifier = new Event<>();
|
||||
public final Event<DebugCommand> commandNotifier = new Event<>();
|
||||
public final Event<String> sourceAdded = new Event<>();
|
||||
|
||||
public DebugState pushFrame(CodeFrame frame) {
|
||||
frames.add(frame);
|
||||
return this;
|
||||
}
|
||||
public DebugState popFrame() {
|
||||
if (frames.size() > 0) frames.remove(frames.size() - 1);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DebugCommand pause(BreakpointData data) throws InterruptedException {
|
||||
paused = true;
|
||||
breakpointNotifier.next(data);
|
||||
return commandNotifier.toAwaitable().await();
|
||||
}
|
||||
public void resume(DebugCommand command) {
|
||||
paused = false;
|
||||
commandNotifier.next(command);
|
||||
}
|
||||
|
||||
// public void addSource()?
|
||||
|
||||
public boolean paused() { return paused; }
|
||||
|
||||
public boolean isBreakpoint(Location loc) {
|
||||
return breakpoints.contains(loc);
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
package me.topchetoeu.jscript.engine.debug;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.IllegalFormatException;
|
||||
|
||||
// We dont need no http library
|
||||
public class Http {
|
||||
public static void writeCode(OutputStream str, int code, String name) throws IOException {
|
||||
str.write(("HTTP/1.1 " + code + " " + name + "\r\n").getBytes());
|
||||
}
|
||||
public static void writeHeader(OutputStream str, String name, String value) throws IOException {
|
||||
str.write((name + ": " + value + "\r\n").getBytes());
|
||||
}
|
||||
public static void writeLastHeader(OutputStream str, String name, String value) throws IOException {
|
||||
str.write((name + ": " + value + "\r\n").getBytes());
|
||||
writeHeadersEnd(str);
|
||||
}
|
||||
public static void writeHeadersEnd(OutputStream str) throws IOException {
|
||||
str.write("\n".getBytes());
|
||||
}
|
||||
|
||||
public static void writeResponse(OutputStream str, int code, String name, String type, byte[] data) throws IOException {
|
||||
writeCode(str, code, name);
|
||||
writeHeader(str, "Content-Type", type);
|
||||
writeLastHeader(str, "Content-Length", data.length + "");
|
||||
str.write(data);
|
||||
str.close();
|
||||
}
|
||||
|
||||
public static HttpRequest readRequest(InputStream str) throws IOException {
|
||||
var lines = new BufferedReader(new InputStreamReader(str));
|
||||
var line = lines.readLine();
|
||||
var i1 = line.indexOf(" ");
|
||||
var i2 = line.lastIndexOf(" ");
|
||||
|
||||
var method = line.substring(0, i1).trim().toUpperCase();
|
||||
var path = line.substring(i1 + 1, i2).trim();
|
||||
var headers = new HashMap<String, String>();
|
||||
|
||||
while (!(line = lines.readLine()).isEmpty()) {
|
||||
var i = line.indexOf(":");
|
||||
if (i < 0) continue;
|
||||
var name = line.substring(0, i).trim().toLowerCase();
|
||||
var value = line.substring(i + 1).trim();
|
||||
|
||||
if (name.length() == 0) continue;
|
||||
headers.put(name, value);
|
||||
}
|
||||
|
||||
if (headers.containsKey("content-length")) {
|
||||
try {
|
||||
var i = Integer.parseInt(headers.get("content-length"));
|
||||
str.skip(i);
|
||||
}
|
||||
catch (IllegalFormatException e) { /* ¯\_(ツ)_/¯ */ }
|
||||
}
|
||||
|
||||
return new HttpRequest(method, path, headers);
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
package me.topchetoeu.jscript.engine.debug;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class HttpRequest {
|
||||
public final String method;
|
||||
public final String path;
|
||||
public final Map<String, String> headers;
|
||||
|
||||
public HttpRequest(String method, String path, Map<String, String> headers) {
|
||||
this.method = method;
|
||||
this.path = path;
|
||||
this.headers = headers;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
package me.topchetoeu.jscript.engine.debug.handlers;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import me.topchetoeu.jscript.engine.DebugCommand;
|
||||
import me.topchetoeu.jscript.engine.Engine;
|
||||
import me.topchetoeu.jscript.engine.debug.V8Error;
|
||||
import me.topchetoeu.jscript.engine.debug.V8Message;
|
||||
import me.topchetoeu.jscript.engine.debug.WebSocket;
|
||||
import me.topchetoeu.jscript.json.JSONMap;
|
||||
|
||||
public class DebuggerHandles {
|
||||
public static void enable(V8Message msg, Engine engine, WebSocket ws) throws IOException {
|
||||
if (engine.debugState == null) ws.send(new V8Error("Debugging is disabled for this engine."));
|
||||
else ws.send(msg.respond(new JSONMap().set("debuggerId", 1)));
|
||||
}
|
||||
public static void disable(V8Message msg, Engine engine, WebSocket ws) throws IOException {
|
||||
if (engine.debugState == null) ws.send(msg.respond());
|
||||
else ws.send(new V8Error("Debugger may not be disabled."));
|
||||
}
|
||||
public static void stepInto(V8Message msg, Engine engine, WebSocket ws) throws IOException {
|
||||
if (engine.debugState == null) ws.send(new V8Error("Debugging is disabled for this engine."));
|
||||
else if (!engine.debugState.paused()) ws.send(new V8Error("Debugger is not paused."));
|
||||
else {
|
||||
engine.debugState.resume(DebugCommand.STEP_INTO);
|
||||
ws.send(msg.respond());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package me.topchetoeu.jscript.engine.modules;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import me.topchetoeu.jscript.polyfills.PolyfillEngine;
|
||||
|
||||
public class FileModuleProvider implements ModuleProvider {
|
||||
public File root;
|
||||
public final boolean allowOutside;
|
||||
|
||||
private boolean checkInside(Path modFile) {
|
||||
return modFile.toAbsolutePath().startsWith(root.toPath().toAbsolutePath());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Module getModule(File cwd, String name) {
|
||||
var realName = getRealName(cwd, name);
|
||||
if (realName == null) return null;
|
||||
var path = Path.of(realName + ".js").normalize();
|
||||
|
||||
try {
|
||||
var res = PolyfillEngine.streamToString(new FileInputStream(path.toFile()));
|
||||
return new Module(realName, path.toString(), res);
|
||||
}
|
||||
catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public String getRealName(File cwd, String name) {
|
||||
var path = Path.of(".", Path.of(cwd.toString(), name).normalize().toString());
|
||||
var fileName = path.getFileName().toString();
|
||||
if (fileName == null) return null;
|
||||
if (!fileName.equals("index") && path.toFile().isDirectory()) return getRealName(cwd, name + "/index");
|
||||
path = Path.of(path.toString() + ".js");
|
||||
if (!allowOutside && !checkInside(path)) return null;
|
||||
if (!path.toFile().isFile() || !path.toFile().canRead()) return null;
|
||||
var res = path.toString().replace('\\', '/');
|
||||
var i = res.lastIndexOf('.');
|
||||
return res.substring(0, i);
|
||||
}
|
||||
|
||||
public FileModuleProvider(File root, boolean allowOutside) {
|
||||
this.root = root.toPath().normalize().toFile();
|
||||
this.allowOutside = allowOutside;
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
package me.topchetoeu.jscript.engine.modules;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import me.topchetoeu.jscript.engine.CallContext;
|
||||
import me.topchetoeu.jscript.engine.CallContext.DataKey;
|
||||
import me.topchetoeu.jscript.engine.scope.Variable;
|
||||
import me.topchetoeu.jscript.engine.values.ObjectValue;
|
||||
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||
import me.topchetoeu.jscript.interop.NativeSetter;
|
||||
|
||||
public class Module {
|
||||
public class ExportsVariable implements Variable {
|
||||
@Override
|
||||
public boolean readonly() { return false; }
|
||||
@Override
|
||||
public Object get(CallContext ctx) { return exports; }
|
||||
@Override
|
||||
public void set(CallContext ctx, Object val) { exports = val; }
|
||||
}
|
||||
|
||||
public static DataKey<Module> KEY = new DataKey<>();
|
||||
|
||||
public final String filename;
|
||||
public final String source;
|
||||
public final String name;
|
||||
private Object exports = new ObjectValue();
|
||||
private boolean executing = false;
|
||||
|
||||
@NativeGetter("name")
|
||||
public String name() { return name; }
|
||||
@NativeGetter("exports")
|
||||
public Object exports() { return exports; }
|
||||
@NativeSetter("exports")
|
||||
public void setExports(Object val) { exports = val; }
|
||||
|
||||
public void execute(CallContext ctx) throws InterruptedException {
|
||||
if (executing) return;
|
||||
|
||||
executing = true;
|
||||
var scope = ctx.engine().global().globalChild();
|
||||
scope.define(null, "module", true, this);
|
||||
scope.define("exports", new ExportsVariable());
|
||||
|
||||
var parent = new File(filename).getParentFile();
|
||||
if (parent == null) parent = new File(".");
|
||||
|
||||
ctx.engine().compile(scope, filename, source).call(ctx.copy().setData(KEY, this), null);
|
||||
executing = false;
|
||||
}
|
||||
|
||||
public Module(String name, String filename, String source) {
|
||||
this.name = name;
|
||||
this.filename = filename;
|
||||
this.source = source;
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
package me.topchetoeu.jscript.engine.modules;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import me.topchetoeu.jscript.engine.CallContext;
|
||||
|
||||
public class ModuleManager {
|
||||
private final List<ModuleProvider> providers = new ArrayList<>();
|
||||
private final HashMap<String, Module> cache = new HashMap<>();
|
||||
public final FileModuleProvider files;
|
||||
|
||||
public void addProvider(ModuleProvider provider) {
|
||||
this.providers.add(provider);
|
||||
}
|
||||
|
||||
public boolean isCached(File cwd, String name) {
|
||||
name = name.replace("\\", "/");
|
||||
|
||||
// Absolute paths are forbidden
|
||||
if (name.startsWith("/")) return false;
|
||||
// Look for files if we have a relative path
|
||||
if (name.startsWith("../") || name.startsWith("./")) {
|
||||
var realName = files.getRealName(cwd, name);
|
||||
if (cache.containsKey(realName)) return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
for (var provider : providers) {
|
||||
var realName = provider.getRealName(cwd, name);
|
||||
if (realName == null) continue;
|
||||
if (cache.containsKey(realName)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
public Module tryLoad(CallContext ctx, String name) throws InterruptedException {
|
||||
name = name.replace('\\', '/');
|
||||
|
||||
var pcwd = Path.of(".");
|
||||
|
||||
if (ctx.hasData(Module.KEY)) {
|
||||
pcwd = Path.of(((Module)ctx.getData(Module.KEY)).filename).getParent();
|
||||
if (pcwd == null) pcwd = Path.of(".");
|
||||
}
|
||||
|
||||
|
||||
var cwd = pcwd.toFile();
|
||||
|
||||
if (name.startsWith("/")) return null;
|
||||
if (name.startsWith("../") || name.startsWith("./")) {
|
||||
var realName = files.getRealName(cwd, name);
|
||||
if (realName == null) return null;
|
||||
if (cache.containsKey(realName)) return cache.get(realName);
|
||||
var mod = files.getModule(cwd, name);
|
||||
cache.put(mod.name(), mod);
|
||||
mod.execute(ctx);
|
||||
return mod;
|
||||
}
|
||||
|
||||
for (var provider : providers) {
|
||||
var realName = provider.getRealName(cwd, name);
|
||||
if (realName == null) continue;
|
||||
if (cache.containsKey(realName)) return cache.get(realName);
|
||||
var mod = provider.getModule(cwd, name);
|
||||
cache.put(mod.name(), mod);
|
||||
mod.execute(ctx);
|
||||
return mod;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public ModuleManager(File root) {
|
||||
files = new FileModuleProvider(root, false);
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
package me.topchetoeu.jscript.engine.modules;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public interface ModuleProvider {
|
||||
Module getModule(File cwd, String name);
|
||||
String getRealName(File cwd, String name);
|
||||
default boolean hasModule(File cwd, String name) { return getRealName(cwd, name) != null; }
|
||||
}
|
||||
4
gradle.properties
Normal file
4
gradle.properties
Normal file
@@ -0,0 +1,4 @@
|
||||
project_group = me.topchetoeu
|
||||
project_name = jscript
|
||||
project_version = 0.10.0-beta
|
||||
main_class = me.topchetoeu.jscript.repl.SimpleRepl
|
||||
71
lib/core.ts
71
lib/core.ts
@@ -1,71 +0,0 @@
|
||||
interface Environment {
|
||||
global: typeof globalThis & Record<string, any>;
|
||||
proto(name: string): object;
|
||||
setProto(name: string, val: object): void;
|
||||
}
|
||||
interface Internals {
|
||||
markSpecial(...funcs: Function[]): void;
|
||||
getEnv(func: Function): Environment | undefined;
|
||||
setEnv<T>(func: T, env: Environment): T;
|
||||
apply(func: Function, thisArg: any, args: any[]): any;
|
||||
delay(timeout: number, callback: Function): () => void;
|
||||
pushMessage(micro: boolean, func: Function, thisArg: any, args: any[]): void;
|
||||
|
||||
strlen(val: string): number;
|
||||
char(val: string): number;
|
||||
stringFromStrings(arr: string[]): string;
|
||||
stringFromChars(arr: number[]): string;
|
||||
symbol(name?: string): symbol;
|
||||
symbolToString(sym: symbol): string;
|
||||
|
||||
isArray(obj: any): boolean;
|
||||
generator(func: (_yield: <T>(val: T) => unknown) => (...args: any[]) => unknown): GeneratorFunction;
|
||||
defineField(obj: object, key: any, val: any, writable: boolean, enumerable: boolean, configurable: boolean): boolean;
|
||||
defineProp(obj: object, key: any, get: Function | undefined, set: Function | undefined, enumerable: boolean, configurable: boolean): boolean;
|
||||
keys(obj: object, onlyString: boolean): any[];
|
||||
ownProp(obj: any, key: string): PropertyDescriptor<any, any>;
|
||||
ownPropKeys(obj: any): any[];
|
||||
lock(obj: object, type: 'ext' | 'seal' | 'freeze'): void;
|
||||
extensible(obj: object): boolean;
|
||||
|
||||
sort(arr: any[], comaprator: (a: any, b: any) => number): void;
|
||||
|
||||
constructor: {
|
||||
log(...args: any[]): void;
|
||||
}
|
||||
}
|
||||
|
||||
var env: Environment = arguments[0], internals: Internals = arguments[1];
|
||||
globalThis.log = internals.constructor.log;
|
||||
|
||||
try {
|
||||
run('values/object');
|
||||
run('values/symbol');
|
||||
run('values/function');
|
||||
run('values/errors');
|
||||
run('values/string');
|
||||
run('values/number');
|
||||
run('values/boolean');
|
||||
run('values/array');
|
||||
run('promise');
|
||||
run('map');
|
||||
run('set');
|
||||
run('regex');
|
||||
run('timeout');
|
||||
|
||||
env.global.log = log;
|
||||
|
||||
log('Loaded polyfills!');
|
||||
}
|
||||
catch (e: any) {
|
||||
let err = 'Uncaught error while loading polyfills: ';
|
||||
|
||||
if (typeof Error !== 'undefined' && e instanceof Error && e.toString !== {}.toString) err += e;
|
||||
else if ('message' in e) {
|
||||
if ('name' in e) err += e.name + ": " + e.message;
|
||||
else err += 'Error: ' + e.message;
|
||||
}
|
||||
else err += e;
|
||||
|
||||
log(e);
|
||||
}
|
||||
586
lib/lib.d.ts
vendored
586
lib/lib.d.ts
vendored
@@ -1,586 +0,0 @@
|
||||
type PropertyDescriptor<T, ThisT> = {
|
||||
value: any;
|
||||
writable?: boolean;
|
||||
enumerable?: boolean;
|
||||
configurable?: boolean;
|
||||
} | {
|
||||
get?(this: ThisT): T;
|
||||
set(this: ThisT, val: T): void;
|
||||
enumerable?: boolean;
|
||||
configurable?: boolean;
|
||||
} | {
|
||||
get(this: ThisT): T;
|
||||
set?(this: ThisT, val: T): void;
|
||||
enumerable?: boolean;
|
||||
configurable?: boolean;
|
||||
};
|
||||
type Exclude<T, U> = T extends U ? never : T;
|
||||
type Extract<T, U> = T extends U ? T : never;
|
||||
type Record<KeyT extends string | number | symbol, ValT> = { [x in KeyT]: ValT }
|
||||
type ReplaceFunc = (match: string, ...args: any[]) => string;
|
||||
|
||||
type PromiseFulfillFunc<T> = (val: T) => void;
|
||||
type PromiseThenFunc<T, NextT> = (val: T) => NextT;
|
||||
type PromiseRejectFunc = (err: unknown) => void;
|
||||
type PromiseFunc<T> = (resolve: PromiseFulfillFunc<T>, reject: PromiseRejectFunc) => void;
|
||||
|
||||
type PromiseResult<T> ={ type: 'fulfilled'; value: T; } | { type: 'rejected'; reason: any; }
|
||||
|
||||
// wippidy-wine, this code is now mine :D
|
||||
type Awaited<T> =
|
||||
T extends null | undefined ? T : // special case for `null | undefined` when not in `--strictNullChecks` mode
|
||||
T extends object & { then(onfulfilled: infer F, ...args: infer _): any } ? // `await` only unwraps object types with a callable `then`. Non-object types are not unwrapped
|
||||
F extends ((value: infer V, ...args: infer _) => any) ? // if the argument to `then` is callable, extracts the first argument
|
||||
Awaited<V> : // recursively unwrap the value
|
||||
never : // the argument to `then` was not callable
|
||||
T;
|
||||
|
||||
type IteratorYieldResult<TReturn> =
|
||||
{ done?: false; } &
|
||||
(TReturn extends undefined ? { value?: undefined; } : { value: TReturn; });
|
||||
|
||||
type IteratorReturnResult<TReturn> =
|
||||
{ done: true } &
|
||||
(TReturn extends undefined ? { value?: undefined; } : { value: TReturn; });
|
||||
|
||||
type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>;
|
||||
|
||||
interface Thenable<T> {
|
||||
then<NextT>(onFulfilled: PromiseThenFunc<T, NextT>, onRejected?: PromiseRejectFunc): Promise<Awaited<NextT>>;
|
||||
then(onFulfilled: undefined, onRejected?: PromiseRejectFunc): Promise<T>;
|
||||
}
|
||||
|
||||
interface RegExpResultIndices extends Array<[number, number]> {
|
||||
groups?: { [name: string]: [number, number]; };
|
||||
}
|
||||
interface RegExpResult extends Array<string> {
|
||||
groups?: { [name: string]: string; };
|
||||
index: number;
|
||||
input: string;
|
||||
indices?: RegExpResultIndices;
|
||||
escape(raw: string, flags: string): RegExp;
|
||||
}
|
||||
|
||||
interface Matcher {
|
||||
[Symbol.match](target: string): RegExpResult | string[] | null;
|
||||
[Symbol.matchAll](target: string): IterableIterator<RegExpResult>;
|
||||
}
|
||||
interface Splitter {
|
||||
[Symbol.split](target: string, limit?: number, sensible?: boolean): string[];
|
||||
}
|
||||
interface Replacer {
|
||||
[Symbol.replace](target: string, replacement: string | ReplaceFunc): string;
|
||||
}
|
||||
interface Searcher {
|
||||
[Symbol.search](target: string, reverse?: boolean, start?: number): number;
|
||||
}
|
||||
|
||||
type FlatArray<Arr, Depth extends number> = {
|
||||
"done": Arr,
|
||||
"recur": Arr extends Array<infer InnerArr>
|
||||
? FlatArray<InnerArr, [-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20][Depth]>
|
||||
: Arr
|
||||
}[Depth extends -1 ? "done" : "recur"];
|
||||
|
||||
interface IArguments {
|
||||
[i: number]: any;
|
||||
length: number;
|
||||
}
|
||||
|
||||
interface Iterator<T, TReturn = any, TNext = undefined> {
|
||||
next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
|
||||
return?(value?: TReturn): IteratorResult<T, TReturn>;
|
||||
throw?(e?: any): IteratorResult<T, TReturn>;
|
||||
}
|
||||
interface Iterable<T> {
|
||||
[Symbol.iterator](): Iterator<T>;
|
||||
}
|
||||
interface IterableIterator<T> extends Iterator<T> {
|
||||
[Symbol.iterator](): IterableIterator<T>;
|
||||
}
|
||||
|
||||
interface AsyncIterator<T, TReturn = any, TNext = undefined> {
|
||||
next(...args: [] | [TNext]): Promise<IteratorResult<T, TReturn>>;
|
||||
return?(value?: TReturn | Thenable<TReturn>): Promise<IteratorResult<T, TReturn>>;
|
||||
throw?(e?: any): Promise<IteratorResult<T, TReturn>>;
|
||||
}
|
||||
interface AsyncIterable<T> {
|
||||
[Symbol.asyncIterator](): AsyncIterator<T>;
|
||||
}
|
||||
interface AsyncIterableIterator<T> extends AsyncIterator<T> {
|
||||
[Symbol.asyncIterator](): AsyncIterableIterator<T>;
|
||||
}
|
||||
|
||||
interface Generator<T = unknown, TReturn = unknown, TNext = unknown> extends Iterator<T, TReturn, TNext> {
|
||||
[Symbol.iterator](): Generator<T, TReturn, TNext>;
|
||||
return(value: TReturn): IteratorResult<T, TReturn>;
|
||||
throw(e: any): IteratorResult<T, TReturn>;
|
||||
}
|
||||
interface GeneratorFunction {
|
||||
new (...args: any[]): Generator;
|
||||
(...args: any[]): Generator;
|
||||
readonly length: number;
|
||||
readonly name: string;
|
||||
readonly prototype: Generator;
|
||||
}
|
||||
|
||||
interface AsyncGenerator<T = unknown, TReturn = unknown, TNext = unknown> extends AsyncIterator<T, TReturn, TNext> {
|
||||
return(value: TReturn | Thenable<TReturn>): Promise<IteratorResult<T, TReturn>>;
|
||||
throw(e: any): Promise<IteratorResult<T, TReturn>>;
|
||||
[Symbol.asyncIterator](): AsyncGenerator<T, TReturn, TNext>;
|
||||
}
|
||||
interface AsyncGeneratorFunction {
|
||||
new (...args: any[]): AsyncGenerator;
|
||||
(...args: any[]): AsyncGenerator;
|
||||
readonly length: number;
|
||||
readonly name: string;
|
||||
readonly prototype: AsyncGenerator;
|
||||
}
|
||||
|
||||
|
||||
interface MathObject {
|
||||
readonly E: number;
|
||||
readonly PI: number;
|
||||
readonly SQRT2: number;
|
||||
readonly SQRT1_2: number;
|
||||
readonly LN2: number;
|
||||
readonly LN10: number;
|
||||
readonly LOG2E: number;
|
||||
readonly LOG10E: number;
|
||||
|
||||
asin(x: number): number;
|
||||
acos(x: number): number;
|
||||
atan(x: number): number;
|
||||
atan2(y: number, x: number): number;
|
||||
asinh(x: number): number;
|
||||
acosh(x: number): number;
|
||||
atanh(x: number): number;
|
||||
sin(x: number): number;
|
||||
cos(x: number): number;
|
||||
tan(x: number): number;
|
||||
sinh(x: number): number;
|
||||
cosh(x: number): number;
|
||||
tanh(x: number): number;
|
||||
sqrt(x: number): number;
|
||||
cbrt(x: number): number;
|
||||
hypot(...vals: number[]): number;
|
||||
imul(a: number, b: number): number;
|
||||
exp(x: number): number;
|
||||
expm1(x: number): number;
|
||||
pow(x: number, y: number): number;
|
||||
log(x: number): number;
|
||||
log10(x: number): number;
|
||||
log1p(x: number): number;
|
||||
log2(x: number): number;
|
||||
ceil(x: number): number;
|
||||
floor(x: number): number;
|
||||
round(x: number): number;
|
||||
fround(x: number): number;
|
||||
trunc(x: number): number;
|
||||
abs(x: number): number;
|
||||
max(...vals: number[]): number;
|
||||
min(...vals: number[]): number;
|
||||
sign(x: number): number;
|
||||
random(): number;
|
||||
clz32(x: number): number;
|
||||
}
|
||||
|
||||
interface Array<T> extends IterableIterator<T> {
|
||||
[i: number]: T;
|
||||
|
||||
length: number;
|
||||
|
||||
toString(): string;
|
||||
// toLocaleString(): string;
|
||||
join(separator?: string): string;
|
||||
fill(val: T, start?: number, end?: number): T[];
|
||||
pop(): T | undefined;
|
||||
push(...items: T[]): number;
|
||||
concat(...items: (T | T[])[]): T[];
|
||||
concat(...items: (T | T[])[]): T[];
|
||||
join(separator?: string): string;
|
||||
reverse(): T[];
|
||||
shift(): T | undefined;
|
||||
slice(start?: number, end?: number): T[];
|
||||
sort(compareFn?: (a: T, b: T) => number): this;
|
||||
splice(start: number, deleteCount?: number | undefined, ...items: T[]): T[];
|
||||
unshift(...items: T[]): number;
|
||||
indexOf(searchElement: T, fromIndex?: number): number;
|
||||
lastIndexOf(searchElement: T, fromIndex?: number): number;
|
||||
every(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean;
|
||||
some(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): boolean;
|
||||
forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void;
|
||||
includes(el: any, start?: number): boolean;
|
||||
|
||||
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
|
||||
filter(predicate: (value: T, index: number, array: T[]) => unknown, thisArg?: any): T[];
|
||||
find(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[];
|
||||
findIndex(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): number;
|
||||
findLast(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): T[];
|
||||
findLastIndex(predicate: (value: T, index: number, array: T[]) => boolean, thisArg?: any): number;
|
||||
|
||||
flat<D extends number = 1>(depth?: D): FlatArray<T, D>;
|
||||
flatMap(func: (val: T, i: number, arr: T[]) => T | T[], thisAarg?: any): FlatArray<T[], 1>;
|
||||
sort(func?: (a: T, b: T) => number): this;
|
||||
|
||||
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T;
|
||||
reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
|
||||
reduce<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U;
|
||||
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T;
|
||||
reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T;
|
||||
reduceRight<U>(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U;
|
||||
|
||||
entries(): IterableIterator<[number, T]>;
|
||||
values(): IterableIterator<T>;
|
||||
keys(): IterableIterator<number>;
|
||||
}
|
||||
interface ArrayConstructor {
|
||||
new <T>(arrayLength?: number): T[];
|
||||
new <T>(...items: T[]): T[];
|
||||
<T>(arrayLength?: number): T[];
|
||||
<T>(...items: T[]): T[];
|
||||
isArray(arg: any): arg is any[];
|
||||
prototype: Array<any>;
|
||||
}
|
||||
|
||||
interface Boolean {
|
||||
valueOf(): boolean;
|
||||
}
|
||||
interface BooleanConstructor {
|
||||
(val: any): boolean;
|
||||
new (val: any): Boolean;
|
||||
prototype: Boolean;
|
||||
}
|
||||
|
||||
interface Error {
|
||||
name: string;
|
||||
message: string;
|
||||
stack: string[];
|
||||
toString(): string;
|
||||
}
|
||||
interface ErrorConstructor {
|
||||
(msg?: any): Error;
|
||||
new (msg?: any): Error;
|
||||
prototype: Error;
|
||||
}
|
||||
|
||||
interface TypeErrorConstructor extends ErrorConstructor {
|
||||
(msg?: any): TypeError;
|
||||
new (msg?: any): TypeError;
|
||||
prototype: Error;
|
||||
}
|
||||
interface TypeError extends Error {
|
||||
name: 'TypeError';
|
||||
}
|
||||
|
||||
interface RangeErrorConstructor extends ErrorConstructor {
|
||||
(msg?: any): RangeError;
|
||||
new (msg?: any): RangeError;
|
||||
prototype: Error;
|
||||
}
|
||||
interface RangeError extends Error {
|
||||
name: 'RangeError';
|
||||
}
|
||||
|
||||
interface SyntaxErrorConstructor extends ErrorConstructor {
|
||||
(msg?: any): RangeError;
|
||||
new (msg?: any): RangeError;
|
||||
prototype: Error;
|
||||
}
|
||||
interface SyntaxError extends Error {
|
||||
name: 'SyntaxError';
|
||||
}
|
||||
|
||||
interface Function {
|
||||
apply(this: Function, thisArg: any, argArray?: any): any;
|
||||
call(this: Function, thisArg: any, ...argArray: any[]): any;
|
||||
bind(this: Function, thisArg: any, ...argArray: any[]): Function;
|
||||
|
||||
toString(): string;
|
||||
|
||||
prototype: any;
|
||||
readonly length: number;
|
||||
name: string;
|
||||
}
|
||||
interface CallableFunction extends Function {
|
||||
(...args: any[]): any;
|
||||
apply<ThisArg, Args extends any[], RetT>(this: (this: ThisArg, ...args: Args) => RetT, thisArg: ThisArg, argArray?: Args): RetT;
|
||||
call<ThisArg, Args extends any[], RetT>(this: (this: ThisArg, ...args: Args) => RetT, thisArg: ThisArg, ...argArray: Args): RetT;
|
||||
bind<ThisArg, Args extends any[], Rest extends any[], RetT>(this: (this: ThisArg, ...args: [ ...Args, ...Rest ]) => RetT, thisArg: ThisArg, ...argArray: Args): (this: void, ...args: Rest) => RetT;
|
||||
}
|
||||
interface NewableFunction extends Function {
|
||||
new(...args: any[]): any;
|
||||
apply<Args extends any[], RetT>(this: new (...args: Args) => RetT, thisArg: any, argArray?: Args): RetT;
|
||||
call<Args extends any[], RetT>(this: new (...args: Args) => RetT, thisArg: any, ...argArray: Args): RetT;
|
||||
bind<Args extends any[], RetT>(this: new (...args: Args) => RetT, thisArg: any, ...argArray: Args): new (...args: Args) => RetT;
|
||||
}
|
||||
interface FunctionConstructor extends Function {
|
||||
(...args: string[]): (...args: any[]) => any;
|
||||
new (...args: string[]): (...args: any[]) => any;
|
||||
prototype: Function;
|
||||
async<ArgsT extends any[], RetT>(
|
||||
func: (await: <T>(val: T) => Awaited<T>) => (...args: ArgsT) => RetT
|
||||
): (...args: ArgsT) => Promise<RetT>;
|
||||
asyncGenerator<ArgsT extends any[], RetT>(
|
||||
func: (await: <T>(val: T) => Awaited<T>, _yield: <T>(val: T) => void) => (...args: ArgsT) => RetT
|
||||
): (...args: ArgsT) => AsyncGenerator<RetT>;
|
||||
generator<ArgsT extends any[], T = unknown, RetT = unknown, TNext = unknown>(
|
||||
func: (_yield: <T>(val: T) => TNext) => (...args: ArgsT) => RetT
|
||||
): (...args: ArgsT) => Generator<T, RetT, TNext>;
|
||||
}
|
||||
|
||||
interface Number {
|
||||
toString(): string;
|
||||
valueOf(): number;
|
||||
}
|
||||
interface NumberConstructor {
|
||||
(val: any): number;
|
||||
new (val: any): Number;
|
||||
prototype: Number;
|
||||
parseInt(val: unknown): number;
|
||||
parseFloat(val: unknown): number;
|
||||
}
|
||||
|
||||
interface Object {
|
||||
constructor: NewableFunction;
|
||||
[Symbol.typeName]: string;
|
||||
|
||||
valueOf(): this;
|
||||
toString(): string;
|
||||
hasOwnProperty(key: any): boolean;
|
||||
}
|
||||
interface ObjectConstructor extends Function {
|
||||
(arg: string): String;
|
||||
(arg: number): Number;
|
||||
(arg: boolean): Boolean;
|
||||
(arg?: undefined | null): {};
|
||||
<T extends object>(arg: T): T;
|
||||
|
||||
new (arg: string): String;
|
||||
new (arg: number): Number;
|
||||
new (arg: boolean): Boolean;
|
||||
new (arg?: undefined | null): {};
|
||||
new <T extends object>(arg: T): T;
|
||||
|
||||
prototype: Object;
|
||||
|
||||
assign<T extends object>(target: T, ...src: object[]): T;
|
||||
create<T extends object>(proto: T, props?: { [key: string]: PropertyDescriptor<any, T> }): T;
|
||||
|
||||
keys<T extends object>(obj: T, onlyString?: true): (keyof T)[];
|
||||
keys<T extends object>(obj: T, onlyString: false): any[];
|
||||
entries<T extends object>(obj: T, onlyString?: true): [keyof T, T[keyof T]][];
|
||||
entries<T extends object>(obj: T, onlyString: false): [any, any][];
|
||||
values<T extends object>(obj: T, onlyString?: true): (T[keyof T])[];
|
||||
values<T extends object>(obj: T, onlyString: false): any[];
|
||||
|
||||
fromEntries(entries: Iterable<[any, any]>): object;
|
||||
|
||||
defineProperty<T, ThisT extends object>(obj: ThisT, key: any, desc: PropertyDescriptor<T, ThisT>): ThisT;
|
||||
defineProperties<ThisT extends object>(obj: ThisT, desc: { [key: string]: PropertyDescriptor<any, ThisT> }): ThisT;
|
||||
|
||||
getOwnPropertyNames<T extends object>(obj: T): (keyof T)[];
|
||||
getOwnPropertySymbols<T extends object>(obj: T): (keyof T)[];
|
||||
hasOwn<T extends object, KeyT>(obj: T, key: KeyT): boolean;
|
||||
|
||||
getOwnPropertyDescriptor<T extends object, KeyT extends keyof T>(obj: T, key: KeyT): PropertyDescriptor<T[KeyT], T>;
|
||||
getOwnPropertyDescriptors<T extends object>(obj: T): { [x in keyof T]: PropertyDescriptor<T[x], T> };
|
||||
|
||||
getPrototypeOf(obj: any): object | null;
|
||||
setPrototypeOf<T>(obj: T, proto: object | null): T;
|
||||
|
||||
preventExtensions<T extends object>(obj: T): T;
|
||||
seal<T extends object>(obj: T): T;
|
||||
freeze<T extends object>(obj: T): T;
|
||||
|
||||
isExtensible(obj: object): boolean;
|
||||
isSealed(obj: object): boolean;
|
||||
isFrozen(obj: object): boolean;
|
||||
}
|
||||
|
||||
interface String {
|
||||
[i: number]: string;
|
||||
|
||||
toString(): string;
|
||||
valueOf(): string;
|
||||
|
||||
charAt(pos: number): string;
|
||||
charCodeAt(pos: number): number;
|
||||
substring(start?: number, end?: number): string;
|
||||
slice(start?: number, end?: number): string;
|
||||
substr(start?: number, length?: number): string;
|
||||
|
||||
startsWith(str: string, pos?: number): string;
|
||||
endsWith(str: string, pos?: number): string;
|
||||
|
||||
replace(pattern: string | Replacer, val: string | ReplaceFunc): string;
|
||||
replaceAll(pattern: string | Replacer, val: string | ReplaceFunc): string;
|
||||
|
||||
match(pattern: string | Matcher): RegExpResult | string[] | null;
|
||||
matchAll(pattern: string | Matcher): IterableIterator<RegExpResult>;
|
||||
|
||||
split(pattern: string | Splitter, limit?: number, sensible?: boolean): string;
|
||||
|
||||
concat(...others: string[]): string;
|
||||
indexOf(term: string | Searcher, start?: number): number;
|
||||
lastIndexOf(term: string | Searcher, start?: number): number;
|
||||
|
||||
toLowerCase(): string;
|
||||
toUpperCase(): string;
|
||||
|
||||
trim(): string;
|
||||
|
||||
includes(term: string, start?: number): boolean;
|
||||
|
||||
length: number;
|
||||
}
|
||||
interface StringConstructor {
|
||||
(val: any): string;
|
||||
new (val: any): String;
|
||||
|
||||
fromCharCode(val: number): string;
|
||||
|
||||
prototype: String;
|
||||
}
|
||||
|
||||
interface Symbol {
|
||||
valueOf(): symbol;
|
||||
}
|
||||
interface SymbolConstructor {
|
||||
(val?: any): symbol;
|
||||
new(...args: any[]): never;
|
||||
prototype: Symbol;
|
||||
for(key: string): symbol;
|
||||
keyFor(sym: symbol): string;
|
||||
|
||||
readonly typeName: unique symbol;
|
||||
readonly match: unique symbol;
|
||||
readonly matchAll: unique symbol;
|
||||
readonly split: unique symbol;
|
||||
readonly replace: unique symbol;
|
||||
readonly search: unique symbol;
|
||||
readonly iterator: unique symbol;
|
||||
readonly asyncIterator: unique symbol;
|
||||
}
|
||||
|
||||
interface Promise<T> extends Thenable<T> {
|
||||
catch(func: PromiseRejectFunc): Promise<T>;
|
||||
finally(func: () => void): Promise<T>;
|
||||
}
|
||||
interface PromiseConstructor {
|
||||
prototype: Promise<any>;
|
||||
|
||||
new <T>(func: PromiseFunc<T>): Promise<Awaited<T>>;
|
||||
resolve<T>(val: T): Promise<Awaited<T>>;
|
||||
reject(val: any): Promise<never>;
|
||||
|
||||
isAwaitable(val: unknown): val is Thenable<any>;
|
||||
any<T>(promises: T[]): Promise<Awaited<T>>;
|
||||
race<T>(promises: (Promise<T>|T)[]): Promise<T>;
|
||||
all<T extends any[]>(promises: T): Promise<{ [Key in keyof T]: Awaited<T[Key]> }>;
|
||||
allSettled<T extends any[]>(...promises: T): Promise<[...{ [P in keyof T]: PromiseResult<Awaited<T[P]>>}]>;
|
||||
}
|
||||
|
||||
declare var String: StringConstructor;
|
||||
//@ts-ignore
|
||||
declare const arguments: IArguments;
|
||||
declare var NaN: number;
|
||||
declare var Infinity: number;
|
||||
|
||||
declare var setTimeout: <T extends any[]>(handle: (...args: [ ...T, ...any[] ]) => void, delay?: number, ...args: T) => number;
|
||||
declare var setInterval: <T extends any[]>(handle: (...args: [ ...T, ...any[] ]) => void, delay?: number, ...args: T) => number;
|
||||
|
||||
declare var clearTimeout: (id: number) => void;
|
||||
declare var clearInterval: (id: number) => void;
|
||||
|
||||
declare var parseInt: typeof Number.parseInt;
|
||||
declare var parseFloat: typeof Number.parseFloat;
|
||||
|
||||
declare function log(...vals: any[]): void;
|
||||
|
||||
declare var Array: ArrayConstructor;
|
||||
declare var Boolean: BooleanConstructor;
|
||||
declare var Promise: PromiseConstructor;
|
||||
declare var Function: FunctionConstructor;
|
||||
declare var Number: NumberConstructor;
|
||||
declare var Object: ObjectConstructor;
|
||||
declare var Symbol: SymbolConstructor;
|
||||
declare var Promise: PromiseConstructor;
|
||||
declare var Math: MathObject;
|
||||
|
||||
declare var Error: ErrorConstructor;
|
||||
declare var RangeError: RangeErrorConstructor;
|
||||
declare var TypeError: TypeErrorConstructor;
|
||||
declare var SyntaxError: SyntaxErrorConstructor;
|
||||
|
||||
declare class Map<KeyT, ValueT> {
|
||||
public [Symbol.iterator](): IterableIterator<[KeyT, ValueT]>;
|
||||
|
||||
public clear(): void;
|
||||
public delete(key: KeyT): boolean;
|
||||
|
||||
public entries(): IterableIterator<[KeyT, ValueT]>;
|
||||
public keys(): IterableIterator<KeyT>;
|
||||
public values(): IterableIterator<ValueT>;
|
||||
|
||||
public get(key: KeyT): ValueT;
|
||||
public set(key: KeyT, val: ValueT): this;
|
||||
public has(key: KeyT): boolean;
|
||||
|
||||
public get size(): number;
|
||||
|
||||
public forEach(func: (key: KeyT, val: ValueT, map: Map<KeyT, ValueT>) => void, thisArg?: any): void;
|
||||
|
||||
public constructor();
|
||||
}
|
||||
declare class Set<T> {
|
||||
public [Symbol.iterator](): IterableIterator<T>;
|
||||
|
||||
public entries(): IterableIterator<[T, T]>;
|
||||
public keys(): IterableIterator<T>;
|
||||
public values(): IterableIterator<T>;
|
||||
|
||||
public clear(): void;
|
||||
|
||||
public add(val: T): this;
|
||||
public delete(val: T): boolean;
|
||||
public has(key: T): boolean;
|
||||
|
||||
public get size(): number;
|
||||
|
||||
public forEach(func: (key: T, set: Set<T>) => void, thisArg?: any): void;
|
||||
|
||||
public constructor();
|
||||
}
|
||||
|
||||
declare class RegExp implements Matcher, Splitter, Replacer, Searcher {
|
||||
static escape(raw: any, flags?: string): RegExp;
|
||||
|
||||
prototype: RegExp;
|
||||
|
||||
exec(val: string): RegExpResult | null;
|
||||
test(val: string): boolean;
|
||||
toString(): string;
|
||||
|
||||
[Symbol.match](target: string): RegExpResult | string[] | null;
|
||||
[Symbol.matchAll](target: string): IterableIterator<RegExpResult>;
|
||||
[Symbol.split](target: string, limit?: number, sensible?: boolean): string[];
|
||||
[Symbol.replace](target: string, replacement: string | ReplaceFunc): string;
|
||||
[Symbol.search](target: string, reverse?: boolean, start?: number): number;
|
||||
|
||||
readonly dotAll: boolean;
|
||||
readonly global: boolean;
|
||||
readonly hasIndices: boolean;
|
||||
readonly ignoreCase: boolean;
|
||||
readonly multiline: boolean;
|
||||
readonly sticky: boolean;
|
||||
readonly unicode: boolean;
|
||||
|
||||
readonly source: string;
|
||||
readonly flags: string;
|
||||
|
||||
lastIndex: number;
|
||||
|
||||
constructor(pattern?: string, flags?: string);
|
||||
constructor(pattern?: RegExp, flags?: string);
|
||||
}
|
||||
93
lib/map.ts
93
lib/map.ts
@@ -1,93 +0,0 @@
|
||||
define("map", () => {
|
||||
const syms = { values: internals.symbol('Map.values') } as { readonly values: unique symbol };
|
||||
const Object = env.global.Object;
|
||||
|
||||
class Map<KeyT, ValueT> {
|
||||
[syms.values]: any = {};
|
||||
|
||||
public [env.global.Symbol.iterator](): IterableIterator<[KeyT, ValueT]> {
|
||||
return this.entries();
|
||||
}
|
||||
|
||||
public clear() {
|
||||
this[syms.values] = {};
|
||||
}
|
||||
public delete(key: KeyT) {
|
||||
if ((key as any) in this[syms.values]) {
|
||||
delete this[syms.values];
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
public entries(): IterableIterator<[KeyT, ValueT]> {
|
||||
const keys = internals.ownPropKeys(this[syms.values]);
|
||||
let i = 0;
|
||||
|
||||
return {
|
||||
next: () => {
|
||||
if (i >= keys.length) return { done: true };
|
||||
else return { done: false, value: [ keys[i], this[syms.values][keys[i++]] ] }
|
||||
},
|
||||
[env.global.Symbol.iterator]() { return this; }
|
||||
}
|
||||
}
|
||||
public keys(): IterableIterator<KeyT> {
|
||||
const keys = internals.ownPropKeys(this[syms.values]);
|
||||
let i = 0;
|
||||
|
||||
return {
|
||||
next: () => {
|
||||
if (i >= keys.length) return { done: true };
|
||||
else return { done: false, value: keys[i] }
|
||||
},
|
||||
[env.global.Symbol.iterator]() { return this; }
|
||||
}
|
||||
}
|
||||
public values(): IterableIterator<ValueT> {
|
||||
const keys = internals.ownPropKeys(this[syms.values]);
|
||||
let i = 0;
|
||||
|
||||
return {
|
||||
next: () => {
|
||||
if (i >= keys.length) return { done: true };
|
||||
else return { done: false, value: this[syms.values][keys[i++]] }
|
||||
},
|
||||
[env.global.Symbol.iterator]() { return this; }
|
||||
}
|
||||
}
|
||||
|
||||
public get(key: KeyT) {
|
||||
return this[syms.values][key];
|
||||
}
|
||||
public set(key: KeyT, val: ValueT) {
|
||||
this[syms.values][key] = val;
|
||||
return this;
|
||||
}
|
||||
public has(key: KeyT) {
|
||||
return (key as any) in this[syms.values][key];
|
||||
}
|
||||
|
||||
public get size() {
|
||||
return internals.ownPropKeys(this[syms.values]).length;
|
||||
}
|
||||
|
||||
public forEach(func: (key: KeyT, val: ValueT, map: Map<KeyT, ValueT>) => void, thisArg?: any) {
|
||||
const keys = internals.ownPropKeys(this[syms.values]);
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
func(keys[i], this[syms.values][keys[i]], this);
|
||||
}
|
||||
}
|
||||
|
||||
public constructor(iterable: Iterable<[KeyT, ValueT]>) {
|
||||
const it = iterable[env.global.Symbol.iterator]();
|
||||
|
||||
for (let el = it.next(); !el.done; el = it.next()) {
|
||||
this[syms.values][el.value[0]] = el.value[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
env.global.Map = Map;
|
||||
});
|
||||
@@ -1,13 +0,0 @@
|
||||
var { define, run } = (() => {
|
||||
const modules: Record<string, Function> = {};
|
||||
|
||||
function define(name: string, func: Function) {
|
||||
modules[name] = func;
|
||||
}
|
||||
function run(name: string) {
|
||||
if (typeof modules[name] === 'function') return modules[name]();
|
||||
else throw "The module '" + name + "' doesn't exist.";
|
||||
}
|
||||
|
||||
return { define, run };
|
||||
})();
|
||||
203
lib/promise.ts
203
lib/promise.ts
@@ -1,203 +0,0 @@
|
||||
define("promise", () => {
|
||||
const syms = {
|
||||
callbacks: internals.symbol('Promise.callbacks'),
|
||||
state: internals.symbol('Promise.state'),
|
||||
value: internals.symbol('Promise.value'),
|
||||
handled: internals.symbol('Promise.handled'),
|
||||
} as {
|
||||
readonly callbacks: unique symbol,
|
||||
readonly state: unique symbol,
|
||||
readonly value: unique symbol,
|
||||
readonly handled: unique symbol,
|
||||
}
|
||||
|
||||
type Callback<T> = [ PromiseFulfillFunc<T>, PromiseRejectFunc ];
|
||||
enum State {
|
||||
Pending,
|
||||
Fulfilled,
|
||||
Rejected,
|
||||
}
|
||||
|
||||
function isAwaitable(val: unknown): val is Thenable<any> {
|
||||
return (
|
||||
typeof val === 'object' &&
|
||||
val !== null &&
|
||||
'then' in val &&
|
||||
typeof val.then === 'function'
|
||||
);
|
||||
}
|
||||
function resolve(promise: Promise<any>, v: any, state: State) {
|
||||
if (promise[syms.state] === State.Pending) {
|
||||
if (typeof v === 'object' && v !== null && 'then' in v && typeof v.then === 'function') {
|
||||
v.then(
|
||||
(res: any) => resolve(promise, res, state),
|
||||
(res: any) => resolve(promise, res, State.Rejected)
|
||||
);
|
||||
return;
|
||||
}
|
||||
promise[syms.value] = v;
|
||||
promise[syms.state] = state;
|
||||
|
||||
for (let i = 0; i < promise[syms.callbacks]!.length; i++) {
|
||||
promise[syms.handled] = true;
|
||||
promise[syms.callbacks]![i][state - 1](v);
|
||||
}
|
||||
|
||||
promise[syms.callbacks] = undefined;
|
||||
|
||||
internals.pushMessage(true, internals.setEnv(() => {
|
||||
if (!promise[syms.handled] && state === State.Rejected) {
|
||||
log('Uncaught (in promise) ' + promise[syms.value]);
|
||||
}
|
||||
}, env), undefined, []);
|
||||
}
|
||||
}
|
||||
|
||||
class Promise<T> {
|
||||
public static isAwaitable(val: unknown): val is Thenable<any> {
|
||||
return isAwaitable(val);
|
||||
}
|
||||
|
||||
public static resolve<T>(val: T): Promise<Awaited<T>> {
|
||||
return new Promise(res => res(val as any));
|
||||
}
|
||||
public static reject<T>(val: T): Promise<Awaited<T>> {
|
||||
return new Promise((_, rej) => rej(val as any));
|
||||
}
|
||||
|
||||
public static race<T>(vals: T[]): Promise<Awaited<T>> {
|
||||
if (typeof vals.length !== 'number') throw new TypeError('vals must be an array. Note that Promise.race is not variadic.');
|
||||
return new Promise((res, rej) => {
|
||||
for (let i = 0; i < vals.length; i++) {
|
||||
const val = vals[i];
|
||||
if (this.isAwaitable(val)) val.then(res, rej);
|
||||
else res(val as any);
|
||||
}
|
||||
});
|
||||
}
|
||||
public static any<T>(vals: T[]): Promise<Awaited<T>> {
|
||||
if (typeof vals.length !== 'number') throw new TypeError('vals must be an array. Note that Promise.any is not variadic.');
|
||||
return new Promise((res, rej) => {
|
||||
let n = 0;
|
||||
|
||||
for (let i = 0; i < vals.length; i++) {
|
||||
const val = vals[i];
|
||||
if (this.isAwaitable(val)) val.then(res, (err) => {
|
||||
n++;
|
||||
if (n === vals.length) throw Error('No promise resolved.');
|
||||
});
|
||||
else res(val as any);
|
||||
}
|
||||
|
||||
if (vals.length === 0) throw Error('No promise resolved.');
|
||||
});
|
||||
}
|
||||
public static all(vals: any[]): Promise<any[]> {
|
||||
if (typeof vals.length !== 'number') throw new TypeError('vals must be an array. Note that Promise.all is not variadic.');
|
||||
return new Promise((res, rej) => {
|
||||
const result: any[] = [];
|
||||
let n = 0;
|
||||
|
||||
for (let i = 0; i < vals.length; i++) {
|
||||
const val = vals[i];
|
||||
if (this.isAwaitable(val)) val.then(
|
||||
val => {
|
||||
n++;
|
||||
result[i] = val;
|
||||
if (n === vals.length) res(result);
|
||||
},
|
||||
rej
|
||||
);
|
||||
else {
|
||||
n++;
|
||||
result[i] = val;
|
||||
}
|
||||
}
|
||||
|
||||
if (vals.length === n) res(result);
|
||||
});
|
||||
}
|
||||
public static allSettled(vals: any[]): Promise<any[]> {
|
||||
if (typeof vals.length !== 'number') throw new TypeError('vals must be an array. Note that Promise.allSettled is not variadic.');
|
||||
return new Promise((res, rej) => {
|
||||
const result: any[] = [];
|
||||
let n = 0;
|
||||
|
||||
for (let i = 0; i < vals.length; i++) {
|
||||
const value = vals[i];
|
||||
if (this.isAwaitable(value)) value.then(
|
||||
value => {
|
||||
n++;
|
||||
result[i] = { status: 'fulfilled', value };
|
||||
if (n === vals.length) res(result);
|
||||
},
|
||||
reason => {
|
||||
n++;
|
||||
result[i] = { status: 'rejected', reason };
|
||||
if (n === vals.length) res(result);
|
||||
},
|
||||
);
|
||||
else {
|
||||
n++;
|
||||
result[i] = { status: 'fulfilled', value };
|
||||
}
|
||||
}
|
||||
|
||||
if (vals.length === n) res(result);
|
||||
});
|
||||
}
|
||||
|
||||
[syms.callbacks]?: Callback<T>[] = [];
|
||||
[syms.handled] = false;
|
||||
[syms.state] = State.Pending;
|
||||
[syms.value]?: T | unknown;
|
||||
|
||||
public then(onFulfil?: PromiseFulfillFunc<T>, onReject?: PromiseRejectFunc) {
|
||||
return new Promise((resolve, reject) => {
|
||||
onFulfil ??= v => v;
|
||||
onReject ??= v => v;
|
||||
|
||||
const callback = (func: (val: any) => any) => (v: any) => {
|
||||
try { resolve(func(v)); }
|
||||
catch (e) { reject(e); }
|
||||
}
|
||||
switch (this[syms.state]) {
|
||||
case State.Pending:
|
||||
this[syms.callbacks]![this[syms.callbacks]!.length] = [callback(onFulfil), callback(onReject)];
|
||||
break;
|
||||
case State.Fulfilled:
|
||||
this[syms.handled] = true;
|
||||
callback(onFulfil)(this[syms.value]);
|
||||
break;
|
||||
case State.Rejected:
|
||||
this[syms.handled] = true;
|
||||
callback(onReject)(this[syms.value]);
|
||||
break;
|
||||
}
|
||||
})
|
||||
}
|
||||
public catch(func: PromiseRejectFunc) {
|
||||
return this.then(undefined, func);
|
||||
}
|
||||
public finally(func: () => void) {
|
||||
return this.then(
|
||||
v => {
|
||||
func();
|
||||
return v;
|
||||
},
|
||||
v => {
|
||||
func();
|
||||
throw v;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public constructor(func: PromiseFunc<T>) {
|
||||
internals.pushMessage(true, func, undefined, [
|
||||
((v) => resolve(this, v, State.Fulfilled)) as PromiseFulfillFunc<T>,
|
||||
((err) => resolve(this, err, State.Rejected)) as PromiseRejectFunc
|
||||
]);
|
||||
}
|
||||
}
|
||||
env.global.Promise = Promise as any;
|
||||
});
|
||||
143
lib/regex.ts
143
lib/regex.ts
@@ -1,143 +0,0 @@
|
||||
define("regex", () => {
|
||||
// var RegExp = env.global.RegExp = env.internals.RegExp;
|
||||
|
||||
// setProps(RegExp.prototype as RegExp, env, {
|
||||
// [Symbol.typeName]: 'RegExp',
|
||||
|
||||
// test(val) {
|
||||
// return !!this.exec(val);
|
||||
// },
|
||||
// toString() {
|
||||
// return '/' + this.source + '/' + this.flags;
|
||||
// },
|
||||
|
||||
// [Symbol.match](target) {
|
||||
// if (this.global) {
|
||||
// const res: string[] = [];
|
||||
// let val;
|
||||
// while (val = this.exec(target)) {
|
||||
// res.push(val[0]);
|
||||
// }
|
||||
// this.lastIndex = 0;
|
||||
// return res;
|
||||
// }
|
||||
// else {
|
||||
// const res = this.exec(target);
|
||||
// if (!this.sticky) this.lastIndex = 0;
|
||||
// return res;
|
||||
// }
|
||||
// },
|
||||
// [Symbol.matchAll](target) {
|
||||
// let pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp;
|
||||
|
||||
// return {
|
||||
// next: (): IteratorResult<RegExpResult, undefined> => {
|
||||
// const val = pattern?.exec(target);
|
||||
|
||||
// if (val === null || val === undefined) {
|
||||
// pattern = undefined;
|
||||
// return { done: true };
|
||||
// }
|
||||
// else return { value: val };
|
||||
// },
|
||||
// [Symbol.iterator]() { return this; }
|
||||
// }
|
||||
// },
|
||||
// [Symbol.split](target, limit, sensible) {
|
||||
// const pattern = new this.constructor(this, this.flags + "g") as RegExp;
|
||||
// let match: RegExpResult | null;
|
||||
// let lastEnd = 0;
|
||||
// const res: string[] = [];
|
||||
|
||||
// while ((match = pattern.exec(target)) !== null) {
|
||||
// let added: string[] = [];
|
||||
|
||||
// if (match.index >= target.length) break;
|
||||
|
||||
// if (match[0].length === 0) {
|
||||
// added = [ target.substring(lastEnd, pattern.lastIndex), ];
|
||||
// if (pattern.lastIndex < target.length) added.push(...match.slice(1));
|
||||
// }
|
||||
// else if (match.index - lastEnd > 0) {
|
||||
// added = [ target.substring(lastEnd, match.index), ...match.slice(1) ];
|
||||
// }
|
||||
// else {
|
||||
// for (let i = 1; i < match.length; i++) {
|
||||
// res[res.length - match.length + i] = match[i];
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (sensible) {
|
||||
// if (limit !== undefined && res.length + added.length >= limit) break;
|
||||
// else res.push(...added);
|
||||
// }
|
||||
// else {
|
||||
// for (let i = 0; i < added.length; i++) {
|
||||
// if (limit !== undefined && res.length >= limit) return res;
|
||||
// else res.push(added[i]);
|
||||
// }
|
||||
// }
|
||||
|
||||
// lastEnd = pattern.lastIndex;
|
||||
// }
|
||||
|
||||
// if (lastEnd < target.length) {
|
||||
// res.push(target.substring(lastEnd));
|
||||
// }
|
||||
|
||||
// return res;
|
||||
// },
|
||||
// [Symbol.replace](target, replacement) {
|
||||
// const pattern = new this.constructor(this, this.flags + "d") as RegExp;
|
||||
// let match: RegExpResult | null;
|
||||
// let lastEnd = 0;
|
||||
// const res: string[] = [];
|
||||
|
||||
// // log(pattern.toString());
|
||||
|
||||
// while ((match = pattern.exec(target)) !== null) {
|
||||
// const indices = match.indices![0];
|
||||
// res.push(target.substring(lastEnd, indices[0]));
|
||||
// if (replacement instanceof Function) {
|
||||
// res.push(replacement(target.substring(indices[0], indices[1]), ...match.slice(1), indices[0], target));
|
||||
// }
|
||||
// else {
|
||||
// res.push(replacement);
|
||||
// }
|
||||
// lastEnd = indices[1];
|
||||
// if (!pattern.global) break;
|
||||
// }
|
||||
|
||||
// if (lastEnd < target.length) {
|
||||
// res.push(target.substring(lastEnd));
|
||||
// }
|
||||
|
||||
// return res.join('');
|
||||
// },
|
||||
// [Symbol.search](target, reverse, start) {
|
||||
// const pattern: RegExp | undefined = new this.constructor(this, this.flags + "g") as RegExp;
|
||||
|
||||
|
||||
// if (!reverse) {
|
||||
// pattern.lastIndex = (start as any) | 0;
|
||||
// const res = pattern.exec(target);
|
||||
// if (res) return res.index;
|
||||
// else return -1;
|
||||
// }
|
||||
// else {
|
||||
// start ??= target.length;
|
||||
// start |= 0;
|
||||
// let res: RegExpResult | null = null;
|
||||
|
||||
// while (true) {
|
||||
// const tmp = pattern.exec(target);
|
||||
// if (tmp === null || tmp.index > start) break;
|
||||
// res = tmp;
|
||||
// }
|
||||
|
||||
// if (res && res.index <= start) return res.index;
|
||||
// else return -1;
|
||||
// }
|
||||
// },
|
||||
// });
|
||||
});
|
||||
81
lib/set.ts
81
lib/set.ts
@@ -1,81 +0,0 @@
|
||||
define("set", () => {
|
||||
const syms = { values: internals.symbol('Map.values') } as { readonly values: unique symbol };
|
||||
const Object = env.global.Object;
|
||||
|
||||
class Set<T> {
|
||||
[syms.values]: any = {};
|
||||
|
||||
public [env.global.Symbol.iterator](): IterableIterator<[T, T]> {
|
||||
return this.entries();
|
||||
}
|
||||
|
||||
public clear() {
|
||||
this[syms.values] = {};
|
||||
}
|
||||
public delete(key: T) {
|
||||
if ((key as any) in this[syms.values]) {
|
||||
delete this[syms.values];
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
|
||||
public entries(): IterableIterator<[T, T]> {
|
||||
const keys = internals.ownPropKeys(this[syms.values]);
|
||||
let i = 0;
|
||||
|
||||
return {
|
||||
next: () => {
|
||||
if (i >= keys.length) return { done: true };
|
||||
else return { done: false, value: [ keys[i], keys[i] ] }
|
||||
},
|
||||
[env.global.Symbol.iterator]() { return this; }
|
||||
}
|
||||
}
|
||||
public keys(): IterableIterator<T> {
|
||||
const keys = internals.ownPropKeys(this[syms.values]);
|
||||
let i = 0;
|
||||
|
||||
return {
|
||||
next: () => {
|
||||
if (i >= keys.length) return { done: true };
|
||||
else return { done: false, value: keys[i] }
|
||||
},
|
||||
[env.global.Symbol.iterator]() { return this; }
|
||||
}
|
||||
}
|
||||
public values(): IterableIterator<T> {
|
||||
return this.keys();
|
||||
}
|
||||
|
||||
public add(val: T) {
|
||||
this[syms.values][val] = undefined;
|
||||
return this;
|
||||
}
|
||||
public has(key: T) {
|
||||
return (key as any) in this[syms.values][key];
|
||||
}
|
||||
|
||||
public get size() {
|
||||
return internals.ownPropKeys(this[syms.values]).length;
|
||||
}
|
||||
|
||||
public forEach(func: (key: T, val: T, map: Set<T>) => void, thisArg?: any) {
|
||||
const keys = internals.ownPropKeys(this[syms.values]);
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
func(keys[i], this[syms.values][keys[i]], this);
|
||||
}
|
||||
}
|
||||
|
||||
public constructor(iterable: Iterable<T>) {
|
||||
const it = iterable[env.global.Symbol.iterator]();
|
||||
|
||||
for (let el = it.next(); !el.done; el = it.next()) {
|
||||
this[syms.values][el.value] = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
env.global.Set = Set;
|
||||
});
|
||||
@@ -1,38 +0,0 @@
|
||||
define("timeout", () => {
|
||||
const timeouts: Record<number, () => void> = { };
|
||||
const intervals: Record<number, () => void> = { };
|
||||
let timeoutI = 0, intervalI = 0;
|
||||
|
||||
env.global.setTimeout = (func, delay, ...args) => {
|
||||
if (typeof func !== 'function') throw new TypeError("func must be a function.");
|
||||
delay = (delay ?? 0) - 0;
|
||||
const cancelFunc = internals.delay(delay, () => internals.apply(func, undefined, args));
|
||||
timeouts[++timeoutI] = cancelFunc;
|
||||
return timeoutI;
|
||||
};
|
||||
env.global.setInterval = (func, delay, ...args) => {
|
||||
if (typeof func !== 'function') throw new TypeError("func must be a function.");
|
||||
delay = (delay ?? 0) - 0;
|
||||
|
||||
const i = ++intervalI;
|
||||
intervals[i] = internals.delay(delay, callback);
|
||||
|
||||
return i;
|
||||
|
||||
function callback() {
|
||||
internals.apply(func, undefined, args);
|
||||
intervals[i] = internals.delay(delay!, callback);
|
||||
}
|
||||
};
|
||||
|
||||
env.global.clearTimeout = (id) => {
|
||||
const func = timeouts[id];
|
||||
if (func) func();
|
||||
timeouts[id] = undefined!;
|
||||
};
|
||||
env.global.clearInterval = (id) => {
|
||||
const func = intervals[id];
|
||||
if (func) func();
|
||||
intervals[id] = undefined!;
|
||||
};
|
||||
});
|
||||
@@ -1,34 +0,0 @@
|
||||
{
|
||||
"files": [
|
||||
"lib.d.ts",
|
||||
"modules.ts",
|
||||
"utils.ts",
|
||||
"values/object.ts",
|
||||
"values/symbol.ts",
|
||||
"values/function.ts",
|
||||
"values/errors.ts",
|
||||
"values/string.ts",
|
||||
"values/number.ts",
|
||||
"values/boolean.ts",
|
||||
"values/array.ts",
|
||||
"promise.ts",
|
||||
"map.ts",
|
||||
"set.ts",
|
||||
"regex.ts",
|
||||
"timeout.ts",
|
||||
"core.ts"
|
||||
],
|
||||
"compilerOptions": {
|
||||
"outFile": "../bin/me/topchetoeu/jscript/js/core.js",
|
||||
// "declarationDir": "",
|
||||
// "declarationDir": "bin/me/topchetoeu/jscript/dts",
|
||||
"target": "ES5",
|
||||
"lib": [],
|
||||
"module": "None",
|
||||
"stripInternal": true,
|
||||
"downlevelIteration": true,
|
||||
"esModuleInterop": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"strict": true,
|
||||
}
|
||||
}
|
||||
38
lib/utils.ts
38
lib/utils.ts
@@ -1,38 +0,0 @@
|
||||
function setProps<
|
||||
TargetT extends object,
|
||||
DescT extends {
|
||||
[x in Exclude<keyof TargetT, 'constructor'> ]?: TargetT[x] extends ((...args: infer ArgsT) => infer RetT) ?
|
||||
((this: TargetT, ...args: ArgsT) => RetT) :
|
||||
TargetT[x]
|
||||
}
|
||||
>(target: TargetT, desc: DescT) {
|
||||
var props = internals.keys(desc, false);
|
||||
for (var i = 0; i < props.length; i++) {
|
||||
var key = props[i];
|
||||
internals.defineField(
|
||||
target, key, (desc as any)[key],
|
||||
true, // writable
|
||||
false, // enumerable
|
||||
true // configurable
|
||||
);
|
||||
}
|
||||
}
|
||||
function setConstr(target: object, constr: Function) {
|
||||
internals.defineField(
|
||||
target, 'constructor', constr,
|
||||
true, // writable
|
||||
false, // enumerable
|
||||
true // configurable
|
||||
);
|
||||
}
|
||||
|
||||
function wrapI(max: number, i: number) {
|
||||
i |= 0;
|
||||
if (i < 0) i = max + i;
|
||||
return i;
|
||||
}
|
||||
function clampI(max: number, i: number) {
|
||||
if (i < 0) i = 0;
|
||||
if (i > max) i = max;
|
||||
return i;
|
||||
}
|
||||
@@ -1,336 +0,0 @@
|
||||
define("values/array", () => {
|
||||
var Array = env.global.Array = function(len?: number) {
|
||||
var res = [];
|
||||
|
||||
if (typeof len === 'number' && arguments.length === 1) {
|
||||
if (len < 0) throw 'Invalid array length.';
|
||||
res.length = len;
|
||||
}
|
||||
else {
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
res[i] = arguments[i];
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
} as ArrayConstructor;
|
||||
|
||||
env.setProto('array', Array.prototype);
|
||||
(Array.prototype as any)[env.global.Symbol.typeName] = "Array";
|
||||
setConstr(Array.prototype, Array);
|
||||
|
||||
setProps(Array.prototype, {
|
||||
[env.global.Symbol.iterator]: function() {
|
||||
return this.values();
|
||||
},
|
||||
[env.global.Symbol.typeName]: "Array",
|
||||
|
||||
values() {
|
||||
var i = 0;
|
||||
|
||||
return {
|
||||
next: () => {
|
||||
while (i < this.length) {
|
||||
if (i++ in this) return { done: false, value: this[i - 1] };
|
||||
}
|
||||
return { done: true, value: undefined };
|
||||
},
|
||||
[env.global.Symbol.iterator]() { return this; }
|
||||
};
|
||||
},
|
||||
keys() {
|
||||
var i = 0;
|
||||
|
||||
return {
|
||||
next: () => {
|
||||
while (i < this.length) {
|
||||
if (i++ in this) return { done: false, value: i - 1 };
|
||||
}
|
||||
return { done: true, value: undefined };
|
||||
},
|
||||
[env.global.Symbol.iterator]() { return this; }
|
||||
};
|
||||
},
|
||||
entries() {
|
||||
var i = 0;
|
||||
|
||||
return {
|
||||
next: () => {
|
||||
while (i < this.length) {
|
||||
if (i++ in this) return { done: false, value: [i - 1, this[i - 1]] };
|
||||
}
|
||||
return { done: true, value: undefined };
|
||||
},
|
||||
[env.global.Symbol.iterator]() { return this; }
|
||||
};
|
||||
},
|
||||
concat() {
|
||||
var res = [] as any[];
|
||||
res.push.apply(res, this);
|
||||
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
var arg = arguments[i];
|
||||
if (arg instanceof Array) {
|
||||
res.push.apply(res, arg);
|
||||
}
|
||||
else {
|
||||
res.push(arg);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
every(func, thisArg) {
|
||||
if (typeof func !== 'function') throw new TypeError("Given argument not a function.");
|
||||
func = func.bind(thisArg);
|
||||
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
if (!func(this[i], i, this)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
some(func, thisArg) {
|
||||
if (typeof func !== 'function') throw new TypeError("Given argument not a function.");
|
||||
func = func.bind(thisArg);
|
||||
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
if (func(this[i], i, this)) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
fill(val, start, end) {
|
||||
if (arguments.length < 3) end = this.length;
|
||||
if (arguments.length < 2) start = 0;
|
||||
|
||||
start = clampI(this.length, wrapI(this.length + 1, start ?? 0));
|
||||
end = clampI(this.length, wrapI(this.length + 1, end ?? this.length));
|
||||
|
||||
for (; start < end; start++) {
|
||||
this[start] = val;
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
filter(func, thisArg) {
|
||||
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
|
||||
|
||||
var res = [];
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
if (i in this && func.call(thisArg, this[i], i, this)) res.push(this[i]);
|
||||
}
|
||||
return res;
|
||||
},
|
||||
find(func, thisArg) {
|
||||
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
|
||||
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
if (i in this && func.call(thisArg, this[i], i, this)) return this[i];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
findIndex(func, thisArg) {
|
||||
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
|
||||
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
if (i in this && func.call(thisArg, this[i], i, this)) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
},
|
||||
findLast(func, thisArg) {
|
||||
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
|
||||
|
||||
for (var i = this.length - 1; i >= 0; i--) {
|
||||
if (i in this && func.call(thisArg, this[i], i, this)) return this[i];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
},
|
||||
findLastIndex(func, thisArg) {
|
||||
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
|
||||
|
||||
for (var i = this.length - 1; i >= 0; i--) {
|
||||
if (i in this && func.call(thisArg, this[i], i, this)) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
},
|
||||
flat(depth) {
|
||||
var res = [] as any[];
|
||||
var buff = [];
|
||||
res.push(...this);
|
||||
|
||||
for (var i = 0; i < (depth ?? 1); i++) {
|
||||
var anyArrays = false;
|
||||
for (var el of res) {
|
||||
if (el instanceof Array) {
|
||||
buff.push(...el);
|
||||
anyArrays = true;
|
||||
}
|
||||
else buff.push(el);
|
||||
}
|
||||
|
||||
res = buff;
|
||||
buff = [];
|
||||
if (!anyArrays) break;
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
flatMap(func, th) {
|
||||
return this.map(func, th).flat();
|
||||
},
|
||||
forEach(func, thisArg) {
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
if (i in this) func.call(thisArg, this[i], i, this);
|
||||
}
|
||||
},
|
||||
map(func, thisArg) {
|
||||
if (typeof func !== 'function') throw new TypeError("Given argument is not a function.");
|
||||
|
||||
var res = [];
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
if (i in this) res[i] = func.call(thisArg, this[i], i, this);
|
||||
}
|
||||
return res;
|
||||
},
|
||||
pop() {
|
||||
if (this.length === 0) return undefined;
|
||||
var val = this[this.length - 1];
|
||||
this.length--;
|
||||
return val;
|
||||
},
|
||||
push() {
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
this[this.length] = arguments[i];
|
||||
}
|
||||
return arguments.length;
|
||||
},
|
||||
shift() {
|
||||
if (this.length === 0) return undefined;
|
||||
var res = this[0];
|
||||
|
||||
for (var i = 0; i < this.length - 1; i++) {
|
||||
this[i] = this[i + 1];
|
||||
}
|
||||
|
||||
this.length--;
|
||||
|
||||
return res;
|
||||
},
|
||||
unshift() {
|
||||
for (var i = this.length - 1; i >= 0; i--) {
|
||||
this[i + arguments.length] = this[i];
|
||||
}
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
this[i] = arguments[i];
|
||||
}
|
||||
|
||||
return arguments.length;
|
||||
},
|
||||
slice(start, end) {
|
||||
start = clampI(this.length, wrapI(this.length + 1, start ?? 0));
|
||||
end = clampI(this.length, wrapI(this.length + 1, end ?? this.length));
|
||||
|
||||
var res: any[] = [];
|
||||
var n = end - start;
|
||||
if (n <= 0) return res;
|
||||
|
||||
for (var i = 0; i < n; i++) {
|
||||
res[i] = this[start + i];
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
toString() {
|
||||
let res = '';
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
if (i > 0) res += ',';
|
||||
if (i in this && this[i] !== undefined && this[i] !== null) res += this[i];
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
indexOf(el, start) {
|
||||
start = start! | 0;
|
||||
for (var i = Math.max(0, start); i < this.length; i++) {
|
||||
if (i in this && this[i] == el) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
},
|
||||
lastIndexOf(el, start) {
|
||||
start = start! | 0;
|
||||
for (var i = this.length; i >= start; i--) {
|
||||
if (i in this && this[i] == el) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
},
|
||||
includes(el, start) {
|
||||
return this.indexOf(el, start) >= 0;
|
||||
},
|
||||
join(val = ',') {
|
||||
let res = '', first = true;
|
||||
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
if (!(i in this)) continue;
|
||||
if (!first) res += val;
|
||||
first = false;
|
||||
res += this[i];
|
||||
}
|
||||
return res;
|
||||
},
|
||||
sort(func) {
|
||||
func ??= (a, b) => {
|
||||
const _a = a + '';
|
||||
const _b = b + '';
|
||||
|
||||
if (_a > _b) return 1;
|
||||
if (_a < _b) return -1;
|
||||
return 0;
|
||||
};
|
||||
|
||||
if (typeof func !== 'function') throw new TypeError('Expected func to be undefined or a function.');
|
||||
|
||||
internals.sort(this, func);
|
||||
return this;
|
||||
},
|
||||
splice(start, deleteCount, ...items) {
|
||||
start = clampI(this.length, wrapI(this.length, start ?? 0));
|
||||
deleteCount = (deleteCount ?? Infinity | 0);
|
||||
if (start + deleteCount >= this.length) deleteCount = this.length - start;
|
||||
|
||||
const res = this.slice(start, start + deleteCount);
|
||||
const moveN = items.length - deleteCount;
|
||||
const len = this.length;
|
||||
|
||||
if (moveN < 0) {
|
||||
for (let i = start - moveN; i < len; i++) {
|
||||
this[i + moveN] = this[i];
|
||||
}
|
||||
}
|
||||
else if (moveN > 0) {
|
||||
for (let i = len - 1; i >= start; i--) {
|
||||
this[i + moveN] = this[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
this[i + start] = items[i];
|
||||
}
|
||||
|
||||
this.length = len + moveN;
|
||||
|
||||
return res;
|
||||
}
|
||||
});
|
||||
|
||||
setProps(Array, {
|
||||
isArray(val: any) { return internals.isArray(val); }
|
||||
});
|
||||
internals.markSpecial(Array);
|
||||
});
|
||||
@@ -1,12 +0,0 @@
|
||||
define("values/boolean", () => {
|
||||
var Boolean = env.global.Boolean = function (this: Boolean | undefined, arg) {
|
||||
var val;
|
||||
if (arguments.length === 0) val = false;
|
||||
else val = !!arg;
|
||||
if (this === undefined || this === null) return val;
|
||||
else (this as any).value = val;
|
||||
} as BooleanConstructor;
|
||||
|
||||
env.setProto('bool', Boolean.prototype);
|
||||
setConstr(Boolean.prototype, Boolean);
|
||||
});
|
||||
@@ -1,46 +0,0 @@
|
||||
define("values/errors", () => {
|
||||
var Error = env.global.Error = function Error(msg: string) {
|
||||
if (msg === undefined) msg = '';
|
||||
else msg += '';
|
||||
|
||||
return Object.setPrototypeOf({
|
||||
message: msg,
|
||||
stack: [] as string[],
|
||||
}, Error.prototype);
|
||||
} as ErrorConstructor;
|
||||
|
||||
setConstr(Error.prototype, Error);
|
||||
setProps(Error.prototype, {
|
||||
name: 'Error',
|
||||
toString: internals.setEnv(function(this: Error) {
|
||||
if (!(this instanceof Error)) return '';
|
||||
|
||||
if (this.message === '') return this.name;
|
||||
else return this.name + ': ' + this.message;
|
||||
}, env)
|
||||
});
|
||||
env.setProto('error', Error.prototype);
|
||||
internals.markSpecial(Error);
|
||||
|
||||
function makeError<T1 extends ErrorConstructor>(name: string, proto: string): T1 {
|
||||
function constr (msg: string) {
|
||||
var res = new Error(msg);
|
||||
(res as any).__proto__ = constr.prototype;
|
||||
return res;
|
||||
}
|
||||
|
||||
(constr as any).__proto__ = Error;
|
||||
(constr.prototype as any).__proto__ = env.proto('error');
|
||||
setConstr(constr.prototype, constr as ErrorConstructor);
|
||||
setProps(constr.prototype, { name: name });
|
||||
|
||||
internals.markSpecial(constr);
|
||||
env.setProto(proto, constr.prototype);
|
||||
|
||||
return constr as T1;
|
||||
}
|
||||
|
||||
env.global.RangeError = makeError('RangeError', 'rangeErr');
|
||||
env.global.TypeError = makeError('TypeError', 'typeErr');
|
||||
env.global.SyntaxError = makeError('SyntaxError', 'syntaxErr');
|
||||
});
|
||||
@@ -1,140 +0,0 @@
|
||||
define("values/function", () => {
|
||||
var Function = env.global.Function = function() {
|
||||
throw 'Using the constructor Function() is forbidden.';
|
||||
} as unknown as FunctionConstructor;
|
||||
|
||||
env.setProto('function', Function.prototype);
|
||||
setConstr(Function.prototype, Function);
|
||||
|
||||
setProps(Function.prototype, {
|
||||
apply(thisArg, args) {
|
||||
if (typeof args !== 'object') throw 'Expected arguments to be an array-like object.';
|
||||
var len = args.length - 0;
|
||||
let newArgs: any[];
|
||||
if (internals.isArray(args)) newArgs = args;
|
||||
else {
|
||||
newArgs = [];
|
||||
|
||||
while (len >= 0) {
|
||||
len--;
|
||||
newArgs[len] = args[len];
|
||||
}
|
||||
}
|
||||
|
||||
return internals.apply(this, thisArg, newArgs);
|
||||
},
|
||||
call(thisArg, ...args) {
|
||||
return this.apply(thisArg, args);
|
||||
},
|
||||
bind(thisArg, ...args) {
|
||||
const func = this;
|
||||
const res = function() {
|
||||
const resArgs = [];
|
||||
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
resArgs[i] = args[i];
|
||||
}
|
||||
for (let i = 0; i < arguments.length; i++) {
|
||||
resArgs[i + args.length] = arguments[i];
|
||||
}
|
||||
|
||||
return func.apply(thisArg, resArgs);
|
||||
};
|
||||
res.name = "<bound> " + func.name;
|
||||
return res;
|
||||
},
|
||||
toString() {
|
||||
return 'function (...) { ... }';
|
||||
},
|
||||
});
|
||||
setProps(Function, {
|
||||
async(func) {
|
||||
if (typeof func !== 'function') throw new TypeError('Expected func to be function.');
|
||||
|
||||
return function (this: any) {
|
||||
const args = arguments;
|
||||
|
||||
return new Promise((res, rej) => {
|
||||
const gen = internals.apply(internals.generator(func as any), this, args as any);
|
||||
|
||||
(function next(type: 'none' | 'err' | 'ret', val?: any) {
|
||||
try {
|
||||
let result;
|
||||
|
||||
switch (type) {
|
||||
case 'err': result = gen.throw(val); break;
|
||||
case 'ret': result = gen.next(val); break;
|
||||
case 'none': result = gen.next(); break;
|
||||
}
|
||||
if (result.done) res(result.value);
|
||||
else Promise.resolve(result.value).then(
|
||||
v => next('ret', v),
|
||||
v => next('err', v)
|
||||
)
|
||||
}
|
||||
catch (e) {
|
||||
rej(e);
|
||||
}
|
||||
})('none');
|
||||
});
|
||||
};
|
||||
},
|
||||
asyncGenerator(func) {
|
||||
if (typeof func !== 'function') throw new TypeError('Expected func to be function.');
|
||||
|
||||
return function(this: any, ...args: any[]) {
|
||||
const gen = internals.apply(internals.generator((_yield) => func(
|
||||
val => _yield(['await', val]) as any,
|
||||
val => _yield(['yield', val])
|
||||
)), this, args) as Generator<['await' | 'yield', any]>;
|
||||
|
||||
const next = (resolve: Function, reject: Function, type: 'none' | 'val' | 'ret' | 'err', val?: any) => {
|
||||
let res;
|
||||
|
||||
try {
|
||||
switch (type) {
|
||||
case 'val': res = gen.next(val); break;
|
||||
case 'ret': res = gen.return(val); break;
|
||||
case 'err': res = gen.throw(val); break;
|
||||
default: res = gen.next(); break;
|
||||
}
|
||||
}
|
||||
catch (e) { return reject(e); }
|
||||
|
||||
if (res.done) return { done: true, res: <any>res };
|
||||
else if (res.value[0] === 'await') Promise.resolve(res.value[1]).then(
|
||||
v => next(resolve, reject, 'val', v),
|
||||
v => next(resolve, reject, 'err', v),
|
||||
)
|
||||
else resolve({ done: false, value: res.value[1] });
|
||||
};
|
||||
|
||||
return {
|
||||
next() {
|
||||
const args = arguments;
|
||||
if (arguments.length === 0) return new Promise((res, rej) => next(res, rej, 'none'));
|
||||
else return new Promise((res, rej) => next(res, rej, 'val', args[0]));
|
||||
},
|
||||
return: (value) => new Promise((res, rej) => next(res, rej, 'ret', value)),
|
||||
throw: (value) => new Promise((res, rej) => next(res, rej, 'err', value)),
|
||||
[env.global.Symbol.asyncIterator]() { return this; }
|
||||
}
|
||||
}
|
||||
},
|
||||
generator(func) {
|
||||
if (typeof func !== 'function') throw new TypeError('Expected func to be function.');
|
||||
const gen = internals.generator(func);
|
||||
return function(this: any, ...args: any[]) {
|
||||
const it = internals.apply(gen, this, args);
|
||||
|
||||
return {
|
||||
next: (...args) => internals.apply(it.next, it, args),
|
||||
return: (val) => internals.apply(it.next, it, [val]),
|
||||
throw: (val) => internals.apply(it.next, it, [val]),
|
||||
[env.global.Symbol.iterator]() { return this; }
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
internals.markSpecial(Function);
|
||||
});
|
||||
@@ -1,33 +0,0 @@
|
||||
define("values/number", () => {
|
||||
var Number = env.global.Number = function(this: Number | undefined, arg: any) {
|
||||
var val;
|
||||
if (arguments.length === 0) val = 0;
|
||||
else val = arg - 0;
|
||||
if (this === undefined || this === null) return val;
|
||||
else (this as any).value = val;
|
||||
} as NumberConstructor;
|
||||
|
||||
env.setProto('number', Number.prototype);
|
||||
setConstr(Number.prototype, Number);
|
||||
|
||||
setProps(Number.prototype, {
|
||||
valueOf() {
|
||||
if (typeof this === 'number') return this;
|
||||
else return (this as any).value;
|
||||
},
|
||||
toString() {
|
||||
if (typeof this === 'number') return this + '';
|
||||
else return (this as any).value + '';
|
||||
}
|
||||
});
|
||||
|
||||
setProps(Number, {
|
||||
parseInt(val) { return Math.trunc(val as any - 0); },
|
||||
parseFloat(val) { return val as any - 0; },
|
||||
});
|
||||
|
||||
env.global.parseInt = Number.parseInt;
|
||||
env.global.parseFloat = Number.parseFloat;
|
||||
env.global.Object.defineProperty(env.global, 'NaN', { value: 0 / 0, writable: false });
|
||||
env.global.Object.defineProperty(env.global, 'Infinity', { value: 1 / 0, writable: false });
|
||||
});
|
||||
@@ -1,226 +0,0 @@
|
||||
define("values/object", () => {
|
||||
var Object = env.global.Object = function(arg: any) {
|
||||
if (arg === undefined || arg === null) return {};
|
||||
else if (typeof arg === 'boolean') return new Boolean(arg);
|
||||
else if (typeof arg === 'number') return new Number(arg);
|
||||
else if (typeof arg === 'string') return new String(arg);
|
||||
return arg;
|
||||
} as ObjectConstructor;
|
||||
|
||||
env.setProto('object', Object.prototype);
|
||||
(Object.prototype as any).__proto__ = null;
|
||||
setConstr(Object.prototype, Object as any);
|
||||
|
||||
function throwNotObject(obj: any, name: string) {
|
||||
if (obj === null || typeof obj !== 'object' && typeof obj !== 'function') {
|
||||
throw new TypeError(`Object.${name} may only be used for objects.`);
|
||||
}
|
||||
}
|
||||
function check(obj: any) {
|
||||
return typeof obj === 'object' && obj !== null || typeof obj === 'function';
|
||||
}
|
||||
|
||||
setProps(Object, {
|
||||
assign(dst, ...src) {
|
||||
throwNotObject(dst, 'assign');
|
||||
for (let i = 0; i < src.length; i++) {
|
||||
const obj = src[i];
|
||||
throwNotObject(obj, 'assign');
|
||||
for (const key of Object.keys(obj)) {
|
||||
(dst as any)[key] = (obj as any)[key];
|
||||
}
|
||||
}
|
||||
return dst;
|
||||
},
|
||||
create(obj, props) {
|
||||
props ??= {};
|
||||
return Object.defineProperties({ __proto__: obj }, props as any) as any;
|
||||
},
|
||||
|
||||
defineProperty(obj, key, attrib) {
|
||||
throwNotObject(obj, 'defineProperty');
|
||||
if (typeof attrib !== 'object') throw new TypeError('Expected attributes to be an object.');
|
||||
|
||||
if ('value' in attrib) {
|
||||
if ('get' in attrib || 'set' in attrib) throw new TypeError('Cannot specify a value and accessors for a property.');
|
||||
if (!internals.defineField(
|
||||
obj, key,
|
||||
attrib.value,
|
||||
!!attrib.writable,
|
||||
!!attrib.enumerable,
|
||||
!!attrib.configurable
|
||||
)) throw new TypeError('Can\'t define property \'' + key + '\'.');
|
||||
}
|
||||
else {
|
||||
if (typeof attrib.get !== 'function' && attrib.get !== undefined) throw new TypeError('Get accessor must be a function.');
|
||||
if (typeof attrib.set !== 'function' && attrib.set !== undefined) throw new TypeError('Set accessor must be a function.');
|
||||
|
||||
if (!internals.defineProp(
|
||||
obj, key,
|
||||
attrib.get,
|
||||
attrib.set,
|
||||
!!attrib.enumerable,
|
||||
!!attrib.configurable
|
||||
)) throw new TypeError('Can\'t define property \'' + key + '\'.');
|
||||
}
|
||||
|
||||
return obj;
|
||||
},
|
||||
defineProperties(obj, attrib) {
|
||||
throwNotObject(obj, 'defineProperties');
|
||||
if (typeof attrib !== 'object' && typeof attrib !== 'function') throw 'Expected second argument to be an object.';
|
||||
|
||||
for (var key in attrib) {
|
||||
Object.defineProperty(obj, key, attrib[key]);
|
||||
}
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
||||
keys(obj, onlyString) {
|
||||
return internals.keys(obj, !!(onlyString ?? true));
|
||||
},
|
||||
entries(obj, onlyString) {
|
||||
const res = [];
|
||||
const keys = internals.keys(obj, !!(onlyString ?? true));
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
res[i] = [ keys[i], (obj as any)[keys[i]] ];
|
||||
}
|
||||
|
||||
return keys;
|
||||
},
|
||||
values(obj, onlyString) {
|
||||
const res = [];
|
||||
const keys = internals.keys(obj, !!(onlyString ?? true));
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
res[i] = (obj as any)[keys[i]];
|
||||
}
|
||||
|
||||
return keys;
|
||||
},
|
||||
|
||||
getOwnPropertyDescriptor(obj, key) {
|
||||
return internals.ownProp(obj, key) as any;
|
||||
},
|
||||
getOwnPropertyDescriptors(obj) {
|
||||
const res = [];
|
||||
const keys = internals.ownPropKeys(obj);
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
res[i] = internals.ownProp(obj, keys[i]);
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
|
||||
getOwnPropertyNames(obj) {
|
||||
const arr = internals.ownPropKeys(obj);
|
||||
const res = [];
|
||||
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (typeof arr[i] === 'symbol') continue;
|
||||
res[res.length] = arr[i];
|
||||
}
|
||||
|
||||
return res as any;
|
||||
},
|
||||
getOwnPropertySymbols(obj) {
|
||||
const arr = internals.ownPropKeys(obj);
|
||||
const res = [];
|
||||
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (typeof arr[i] !== 'symbol') continue;
|
||||
res[res.length] = arr[i];
|
||||
}
|
||||
|
||||
return res as any;
|
||||
},
|
||||
hasOwn(obj, key) {
|
||||
const keys = internals.ownPropKeys(obj);
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
if (keys[i] === key) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
|
||||
getPrototypeOf(obj) {
|
||||
return obj.__proto__;
|
||||
},
|
||||
setPrototypeOf(obj, proto) {
|
||||
(obj as any).__proto__ = proto;
|
||||
return obj;
|
||||
},
|
||||
|
||||
fromEntries(iterable) {
|
||||
const res = {} as any;
|
||||
|
||||
for (const el of iterable) {
|
||||
res[el[0]] = el[1];
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
|
||||
preventExtensions(obj) {
|
||||
throwNotObject(obj, 'preventExtensions');
|
||||
internals.lock(obj, 'ext');
|
||||
return obj;
|
||||
},
|
||||
seal(obj) {
|
||||
throwNotObject(obj, 'seal');
|
||||
internals.lock(obj, 'seal');
|
||||
return obj;
|
||||
},
|
||||
freeze(obj) {
|
||||
throwNotObject(obj, 'freeze');
|
||||
internals.lock(obj, 'freeze');
|
||||
return obj;
|
||||
},
|
||||
|
||||
isExtensible(obj) {
|
||||
if (!check(obj)) return false;
|
||||
return internals.extensible(obj);
|
||||
},
|
||||
isSealed(obj) {
|
||||
if (!check(obj)) return true;
|
||||
if (internals.extensible(obj)) return false;
|
||||
const keys = internals.ownPropKeys(obj);
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
if (internals.ownProp(obj, keys[i]).configurable) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
isFrozen(obj) {
|
||||
if (!check(obj)) return true;
|
||||
if (internals.extensible(obj)) return false;
|
||||
const keys = internals.ownPropKeys(obj);
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
const prop = internals.ownProp(obj, keys[i]);
|
||||
if (prop.configurable) return false;
|
||||
if ('writable' in prop && prop.writable) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
setProps(Object.prototype, {
|
||||
valueOf() {
|
||||
return this;
|
||||
},
|
||||
toString() {
|
||||
return '[object ' + (this[env.global.Symbol.typeName] ?? 'Unknown') + ']';
|
||||
},
|
||||
hasOwnProperty(key) {
|
||||
return Object.hasOwn(this, key);
|
||||
},
|
||||
});
|
||||
internals.markSpecial(Object);
|
||||
});
|
||||
@@ -1,267 +0,0 @@
|
||||
define("values/string", () => {
|
||||
var String = env.global.String = function(this: String | undefined, arg: any) {
|
||||
var val;
|
||||
if (arguments.length === 0) val = '';
|
||||
else val = arg + '';
|
||||
if (this === undefined || this === null) return val;
|
||||
else (this as any).value = val;
|
||||
} as StringConstructor;
|
||||
|
||||
env.setProto('string', String.prototype);
|
||||
setConstr(String.prototype, String);
|
||||
|
||||
setProps(String.prototype, {
|
||||
toString() {
|
||||
if (typeof this === 'string') return this;
|
||||
else return (this as any).value;
|
||||
},
|
||||
valueOf() {
|
||||
if (typeof this === 'string') return this;
|
||||
else return (this as any).value;
|
||||
},
|
||||
|
||||
substring(start, end) {
|
||||
if (typeof this !== 'string') {
|
||||
if (this instanceof String) return (this as any).value.substring(start, end);
|
||||
else throw new Error('This function may be used only with primitive or object strings.');
|
||||
}
|
||||
start = start ?? 0 | 0;
|
||||
end = (end ?? this.length) | 0;
|
||||
|
||||
const res = [];
|
||||
|
||||
for (let i = start; i < end; i++) {
|
||||
if (i >= 0 && i < this.length) res[res.length] = this[i];
|
||||
}
|
||||
|
||||
return internals.stringFromStrings(res);
|
||||
},
|
||||
substr(start, length) {
|
||||
start = start ?? 0 | 0;
|
||||
|
||||
if (start >= this.length) start = this.length - 1;
|
||||
if (start < 0) start = 0;
|
||||
|
||||
length = (length ?? this.length - start) | 0;
|
||||
const end = length + start;
|
||||
const res = [];
|
||||
|
||||
for (let i = start; i < end; i++) {
|
||||
if (i >= 0 && i < this.length) res[res.length] = this[i];
|
||||
}
|
||||
|
||||
return internals.stringFromStrings(res);
|
||||
},
|
||||
|
||||
toLowerCase() {
|
||||
// TODO: Implement localization
|
||||
const res = [];
|
||||
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
const c = internals.char(this[i]);
|
||||
|
||||
if (c >= 65 && c <= 90) res[i] = c - 65 + 97;
|
||||
else res[i] = c;
|
||||
}
|
||||
|
||||
return internals.stringFromChars(res);
|
||||
},
|
||||
toUpperCase() {
|
||||
// TODO: Implement localization
|
||||
const res = [];
|
||||
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
const c = internals.char(this[i]);
|
||||
|
||||
if (c >= 97 && c <= 122) res[i] = c - 97 + 65;
|
||||
else res[i] = c;
|
||||
}
|
||||
|
||||
return internals.stringFromChars(res);
|
||||
},
|
||||
|
||||
charAt(pos) {
|
||||
if (typeof this !== 'string') {
|
||||
if (this instanceof String) return (this as any).value.charAt(pos);
|
||||
else throw new Error('This function may be used only with primitive or object strings.');
|
||||
}
|
||||
|
||||
pos = pos | 0;
|
||||
if (pos < 0 || pos >= this.length) return '';
|
||||
return this[pos];
|
||||
},
|
||||
charCodeAt(pos) {
|
||||
if (typeof this !== 'string') {
|
||||
if (this instanceof String) return (this as any).value.charAt(pos);
|
||||
else throw new Error('This function may be used only with primitive or object strings.');
|
||||
}
|
||||
|
||||
pos = pos | 0;
|
||||
if (pos < 0 || pos >= this.length) return 0 / 0;
|
||||
return internals.char(this[pos]);
|
||||
},
|
||||
|
||||
startsWith(term, pos) {
|
||||
if (typeof this !== 'string') {
|
||||
if (this instanceof String) return (this as any).value.startsWith(term, pos);
|
||||
else throw new Error('This function may be used only with primitive or object strings.');
|
||||
}
|
||||
pos = pos! | 0;
|
||||
term = term + "";
|
||||
|
||||
if (pos < 0 || this.length < term.length + pos) return false;
|
||||
|
||||
for (let i = 0; i < term.length; i++) {
|
||||
if (this[i + pos] !== term[i]) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
endsWith(term, pos) {
|
||||
if (typeof this !== 'string') {
|
||||
if (this instanceof String) return (this as any).value.endsWith(term, pos);
|
||||
else throw new Error('This function may be used only with primitive or object strings.');
|
||||
}
|
||||
pos = (pos ?? this.length) | 0;
|
||||
term = term + "";
|
||||
|
||||
const start = pos - term.length;
|
||||
|
||||
if (start < 0 || this.length < term.length + start) return false;
|
||||
|
||||
for (let i = 0; i < term.length; i++) {
|
||||
if (this[i + start] !== term[i]) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
indexOf(term: any, start) {
|
||||
if (typeof this !== 'string') {
|
||||
if (this instanceof String) return (this as any).value.indexOf(term, start);
|
||||
else throw new Error('This function may be used only with primitive or object strings.');
|
||||
}
|
||||
|
||||
if (typeof term[env.global.Symbol.search] !== 'function') term = RegExp.escape(term);
|
||||
|
||||
return term[env.global.Symbol.search](this, false, start);
|
||||
},
|
||||
lastIndexOf(term: any, start) {
|
||||
if (typeof this !== 'string') {
|
||||
if (this instanceof String) return (this as any).value.indexOf(term, start);
|
||||
else throw new Error('This function may be used only with primitive or object strings.');
|
||||
}
|
||||
|
||||
if (typeof term[env.global.Symbol.search] !== 'function') term = RegExp.escape(term);
|
||||
|
||||
return term[env.global.Symbol.search](this, true, start);
|
||||
},
|
||||
includes(term, start) {
|
||||
return this.indexOf(term, start) >= 0;
|
||||
},
|
||||
|
||||
replace(pattern: any, val) {
|
||||
if (typeof this !== 'string') {
|
||||
if (this instanceof String) return (this as any).value.replace(pattern, val);
|
||||
else throw new Error('This function may be used only with primitive or object strings.');
|
||||
}
|
||||
|
||||
if (typeof pattern[env.global.Symbol.replace] !== 'function') pattern = RegExp.escape(pattern);
|
||||
|
||||
return pattern[env.global.Symbol.replace](this, val);
|
||||
},
|
||||
replaceAll(pattern: any, val) {
|
||||
if (typeof this !== 'string') {
|
||||
if (this instanceof String) return (this as any).value.replace(pattern, val);
|
||||
else throw new Error('This function may be used only with primitive or object strings.');
|
||||
}
|
||||
|
||||
if (typeof pattern[env.global.Symbol.replace] !== 'function') pattern = RegExp.escape(pattern, "g");
|
||||
if (pattern instanceof RegExp && !pattern.global) pattern = new pattern.constructor(pattern.source, pattern.flags + "g");
|
||||
|
||||
return pattern[env.global.Symbol.replace](this, val);
|
||||
},
|
||||
|
||||
match(pattern: any) {
|
||||
if (typeof this !== 'string') {
|
||||
if (this instanceof String) return (this as any).value.match(pattern);
|
||||
else throw new Error('This function may be used only with primitive or object strings.');
|
||||
}
|
||||
|
||||
if (typeof pattern[env.global.Symbol.match] !== 'function') pattern = RegExp.escape(pattern);
|
||||
|
||||
return pattern[env.global.Symbol.match](this);
|
||||
},
|
||||
matchAll(pattern: any) {
|
||||
if (typeof this !== 'string') {
|
||||
if (this instanceof String) return (this as any).value.matchAll(pattern);
|
||||
else throw new Error('This function may be used only with primitive or object strings.');
|
||||
}
|
||||
|
||||
if (typeof pattern[env.global.Symbol.match] !== 'function') pattern = RegExp.escape(pattern, "g");
|
||||
if (pattern instanceof RegExp && !pattern.global) pattern = new pattern.constructor(pattern.source, pattern.flags + "g");
|
||||
|
||||
return pattern[env.global.Symbol.match](this);
|
||||
},
|
||||
|
||||
split(pattern: any, lim, sensible) {
|
||||
if (typeof this !== 'string') {
|
||||
if (this instanceof String) return (this as any).value.split(pattern, lim, sensible);
|
||||
else throw new Error('This function may be used only with primitive or object strings.');
|
||||
}
|
||||
|
||||
if (typeof pattern[env.global.Symbol.split] !== 'function') pattern = RegExp.escape(pattern, "g");
|
||||
|
||||
return pattern[env.global.Symbol.split](this, lim, sensible);
|
||||
},
|
||||
slice(start, end) {
|
||||
if (typeof this !== 'string') {
|
||||
if (this instanceof String) return (this as any).value.slice(start, end);
|
||||
else throw new Error('This function may be used only with primitive or object strings.');
|
||||
}
|
||||
|
||||
start = wrapI(this.length, start ?? 0 | 0);
|
||||
end = wrapI(this.length, end ?? this.length | 0);
|
||||
|
||||
if (start > end) return '';
|
||||
|
||||
return this.substring(start, end);
|
||||
},
|
||||
|
||||
concat(...args) {
|
||||
if (typeof this !== 'string') {
|
||||
if (this instanceof String) return (this as any).value.concat(...args);
|
||||
else throw new Error('This function may be used only with primitive or object strings.');
|
||||
}
|
||||
|
||||
var res = this;
|
||||
for (var arg of args) res += arg;
|
||||
return res;
|
||||
},
|
||||
|
||||
trim() {
|
||||
return this
|
||||
.replace(/^\s+/g, '')
|
||||
.replace(/\s+$/g, '');
|
||||
}
|
||||
});
|
||||
|
||||
setProps(String, {
|
||||
fromCharCode(val) {
|
||||
return internals.stringFromChars([val | 0]);
|
||||
},
|
||||
})
|
||||
|
||||
env.global.Object.defineProperty(String.prototype, 'length', {
|
||||
get() {
|
||||
if (typeof this !== 'string') {
|
||||
if (this instanceof String) return (this as any).value.length;
|
||||
else throw new Error('This function may be used only with primitive or object strings.');
|
||||
}
|
||||
|
||||
return internals.strlen(this);
|
||||
},
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
});
|
||||
});
|
||||
@@ -1,36 +0,0 @@
|
||||
define("values/symbol", () => {
|
||||
const symbols: Record<string, symbol> = { };
|
||||
|
||||
var Symbol = env.global.Symbol = function(this: any, val?: string) {
|
||||
if (this !== undefined && this !== null) throw new TypeError("Symbol may not be called with 'new'.");
|
||||
if (typeof val !== 'string' && val !== undefined) throw new TypeError('val must be a string or undefined.');
|
||||
return internals.symbol(val);
|
||||
} as SymbolConstructor;
|
||||
|
||||
env.setProto('symbol', Symbol.prototype);
|
||||
setConstr(Symbol.prototype, Symbol);
|
||||
|
||||
setProps(Symbol, {
|
||||
for(key) {
|
||||
if (typeof key !== 'string' && key !== undefined) throw new TypeError('key must be a string or undefined.');
|
||||
if (key in symbols) return symbols[key];
|
||||
else return symbols[key] = internals.symbol(key);
|
||||
},
|
||||
keyFor(sym) {
|
||||
if (typeof sym !== 'symbol') throw new TypeError('sym must be a symbol.');
|
||||
return internals.symbolToString(sym);
|
||||
},
|
||||
|
||||
typeName: Symbol("Symbol.name") as any,
|
||||
replace: Symbol('Symbol.replace') as any,
|
||||
match: Symbol('Symbol.match') as any,
|
||||
matchAll: Symbol('Symbol.matchAll') as any,
|
||||
split: Symbol('Symbol.split') as any,
|
||||
search: Symbol('Symbol.search') as any,
|
||||
iterator: Symbol('Symbol.iterator') as any,
|
||||
asyncIterator: Symbol('Symbol.asyncIterator') as any,
|
||||
});
|
||||
|
||||
internals.defineField(env.global.Object.prototype, Symbol.typeName, 'Object', false, false, false);
|
||||
internals.defineField(env.global, Symbol.typeName, 'Window', false, false, false);
|
||||
});
|
||||
6
package-lock.json
generated
6
package-lock.json
generated
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"name": "java-jscript",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {}
|
||||
}
|
||||
32
package.json
32
package.json
@@ -1 +1,31 @@
|
||||
{}
|
||||
{
|
||||
"scripts": {
|
||||
"build-env": "rollup -c --environment INPUT:src/lib/libs/_entry.ts,OUTPUT:build/js/index.js,POLYFILLS:src/lib/libs/polyfills",
|
||||
"build-ts": "rollup -c --environment INPUT:src/lib/transpiler/_entry.ts,OUTPUT:build/js/ts.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/core": "^7.26.0",
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@babel/standalone": "^7.26.4",
|
||||
"@rollup/plugin-json": "^6.1.0",
|
||||
"@types/babel__preset-env": "^7.9.7",
|
||||
"@types/babel__standalone": "^7.1.9",
|
||||
"@types/coffeescript": "^2.5.7",
|
||||
"coffeescript": "^2.7.0",
|
||||
"typescript": "^5.7.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/plugin-transform-class-properties": "^7.25.9",
|
||||
"@babel/plugin-transform-runtime": "^7.25.9",
|
||||
"@babel/plugin-transform-typescript": "^7.25.9",
|
||||
"@babel/preset-env": "^7.26.0",
|
||||
"@rollup/plugin-babel": "^6.0.4",
|
||||
"@rollup/plugin-commonjs": "^28.0.1",
|
||||
"@rollup/plugin-node-resolve": "^15.3.0",
|
||||
"@rollup/plugin-terser": "^0.4.4",
|
||||
"@rollup/plugin-typescript": "^12.1.1",
|
||||
"@types/node": "^22.10.1",
|
||||
"rollup": "^4.24.0",
|
||||
"tslib": "^2.8.0"
|
||||
}
|
||||
}
|
||||
|
||||
130
rollup.config.js
Normal file
130
rollup.config.js
Normal file
@@ -0,0 +1,130 @@
|
||||
const { defineConfig } = require("rollup");
|
||||
const terser = require("@rollup/plugin-terser");
|
||||
const typescript = require("@rollup/plugin-typescript");
|
||||
const babel = require("@rollup/plugin-babel");
|
||||
const commonjs = require("@rollup/plugin-commonjs");
|
||||
const nodeResolve = require("@rollup/plugin-node-resolve");
|
||||
const json = require("@rollup/plugin-json");
|
||||
const { resolve } = require("path");
|
||||
|
||||
const shouldMinify = () => false;
|
||||
const shouldEmitSourcemaps = () => true;
|
||||
const shouldPolyfill = () => !!process.env.POLYFILLS;
|
||||
|
||||
const construct = (input, output) => defineConfig({
|
||||
input,
|
||||
plugins: [
|
||||
shouldPolyfill() && {
|
||||
name: "babel-fake-runtime",
|
||||
resolveId(source) {
|
||||
if (source.startsWith("!polyfills:/helpers")) return resolve(process.env.POLYFILLS) + source.slice(19) + ".js";
|
||||
}
|
||||
},
|
||||
commonjs(),
|
||||
nodeResolve(),
|
||||
json(),
|
||||
babel({
|
||||
extensions: [],
|
||||
exclude: ["node_modules/**"],
|
||||
|
||||
babelHelpers: "runtime",
|
||||
plugins: [
|
||||
["@babel/plugin-transform-typescript", {
|
||||
onlyRemoveTypeImports: true,
|
||||
optimizeConstEnums: true,
|
||||
allowDeclareFields: true,
|
||||
}],
|
||||
["@babel/plugin-transform-class-properties"],
|
||||
["@babel/plugin-transform-runtime", {
|
||||
moduleName: shouldPolyfill() ? "!polyfills:" : undefined,
|
||||
version: "^7.24.0",
|
||||
}],
|
||||
]
|
||||
}),
|
||||
babel({
|
||||
extensions: [],
|
||||
exclude: shouldPolyfill() ? [process.env.POLYFILLS + "/**"] : [],
|
||||
|
||||
assumptions: {
|
||||
ignoreToPrimitiveHint: true,
|
||||
noClassCalls: true,
|
||||
},
|
||||
|
||||
env: {
|
||||
development: { compact: false },
|
||||
},
|
||||
|
||||
babelHelpers: "runtime",
|
||||
plugins: [
|
||||
"@babel/plugin-transform-arrow-functions",
|
||||
"@babel/plugin-transform-block-scoping",
|
||||
"@babel/plugin-transform-classes",
|
||||
"@babel/plugin-transform-computed-properties",
|
||||
"@babel/plugin-transform-destructuring",
|
||||
"@babel/plugin-transform-for-of",
|
||||
"@babel/plugin-transform-object-super",
|
||||
"@babel/plugin-transform-parameters",
|
||||
"@babel/plugin-transform-shorthand-properties",
|
||||
"@babel/plugin-transform-spread",
|
||||
"@babel/plugin-transform-object-rest-spread",
|
||||
"@babel/plugin-transform-template-literals",
|
||||
"@babel/plugin-transform-unicode-escapes",
|
||||
"@babel/plugin-transform-unicode-regex",
|
||||
"@babel/plugin-transform-exponentiation-operator",
|
||||
"@babel/plugin-transform-async-to-generator",
|
||||
"@babel/plugin-transform-async-generator-functions",
|
||||
"@babel/plugin-transform-nullish-coalescing-operator",
|
||||
"@babel/plugin-transform-optional-chaining",
|
||||
"@babel/plugin-transform-logical-assignment-operators",
|
||||
"@babel/plugin-transform-numeric-separator",
|
||||
"@babel/plugin-transform-class-properties",
|
||||
"@babel/plugin-transform-class-static-block",
|
||||
"@babel/plugin-transform-regenerator",
|
||||
|
||||
["@babel/plugin-transform-runtime", {
|
||||
moduleName: shouldPolyfill() ? "!polyfills:" : undefined,
|
||||
version: "^7.24.0",
|
||||
}],
|
||||
],
|
||||
}),
|
||||
typescript({
|
||||
exclude: ["node_modules/**", "*.js"],
|
||||
compilerOptions: {
|
||||
allowImportingTsExtensions: true,
|
||||
noEmit: true,
|
||||
},
|
||||
noForceEmit: true,
|
||||
noEmitOnError: true,
|
||||
}),
|
||||
shouldMinify() && terser({
|
||||
sourceMap: shouldEmitSourcemaps(),
|
||||
keep_classnames: true,
|
||||
}),
|
||||
],
|
||||
output: {
|
||||
file: output,
|
||||
format: "iife",
|
||||
globals: {
|
||||
fs: "null",
|
||||
path: "null",
|
||||
os: "null",
|
||||
inspector: "null",
|
||||
tty: "null",
|
||||
util: "null",
|
||||
assert: "null",
|
||||
url: "null",
|
||||
"@babel/preset-typescript/package.json": "null",
|
||||
module: "null",
|
||||
process: "null",
|
||||
v8: "null",
|
||||
},
|
||||
// plugins: [babel.getBabelOutputPlugin({
|
||||
// allowAllFormats: true,
|
||||
// })],
|
||||
|
||||
sourcemap: shouldEmitSourcemaps(),
|
||||
inlineDynamicImports: true,
|
||||
},
|
||||
});
|
||||
|
||||
module.exports = construct(process.env.INPUT, process.env.OUTPUT);
|
||||
12
settings.gradle
Normal file
12
settings.gradle
Normal file
@@ -0,0 +1,12 @@
|
||||
pluginManagement {
|
||||
repositories {
|
||||
mavenCentral();
|
||||
gradlePluginPortal();
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0';
|
||||
}
|
||||
|
||||
rootProject.name = properties.project_name;
|
||||
91
src/lib/libs/_entry.ts
Normal file
91
src/lib/libs/_entry.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { object, setGlobalPrototypes, target } from "./primordials.ts";
|
||||
import { Error, RangeError, SyntaxError, TypeError } from "./values/errors.ts";
|
||||
import { Boolean } from "./values/boolean.ts";
|
||||
import { Function } from "./values/function.ts";
|
||||
import { Number } from "./values/number.ts";
|
||||
import { Object } from "./values/object.ts";
|
||||
import { String } from "./values/string.ts";
|
||||
import { Symbol } from "./values/symbol.ts";
|
||||
import { Array } from "./values/array.ts";
|
||||
import { Map, WeakMap } from "./classes/map.ts";
|
||||
import { RegExp } from "./values/regex.ts";
|
||||
import { Date } from "./classes/date.ts";
|
||||
import { Math as _Math } from "./namespaces/math.ts";
|
||||
import { Set, WeakSet } from "./classes/set.ts";
|
||||
import { JSON } from "./namespaces/json.ts";
|
||||
import { console } from "./namespaces/console.ts";
|
||||
import { encodeURI, encodeURIComponent } from "./url.ts";
|
||||
import { Promise } from "./classes/promise.ts";
|
||||
import { ArrayBuffer } from "./arrays/ArrayBuffer.ts";
|
||||
import { Uint8Array } from "./arrays/Uint8Array.ts";
|
||||
import { Int32Array } from "./arrays/Int32Array.ts";
|
||||
import { TypedArray } from "./arrays/TypedArray.ts";
|
||||
|
||||
declare global {
|
||||
function print(...args: any[]): void;
|
||||
function measure(func: Function): void;
|
||||
}
|
||||
|
||||
function fixup<T extends Function>(clazz: T) {
|
||||
object.setPrototype(clazz, Function.prototype);
|
||||
object.setPrototype(clazz.prototype as any, Object.prototype);
|
||||
return clazz;
|
||||
}
|
||||
|
||||
object.setPrototype(target, Object.prototype);
|
||||
|
||||
object.defineField(target, "undefined", { e: false, c: false, w: false, v: void 0 });
|
||||
|
||||
target.Symbol = fixup(Symbol);
|
||||
target.Number = fixup(Number);
|
||||
target.String = fixup(String);
|
||||
target.Boolean = fixup(Boolean);
|
||||
|
||||
target.Object = Object;
|
||||
target.Function = fixup(Function);
|
||||
target.Array = fixup(Array);
|
||||
|
||||
target.Error = fixup(Error);
|
||||
target.RangeError = RangeError;
|
||||
target.SyntaxError = SyntaxError;
|
||||
target.TypeError = TypeError;
|
||||
|
||||
fixup(TypedArray);
|
||||
target.ArrayBuffer = fixup(ArrayBuffer);
|
||||
target.Uint8Array = Uint8Array;
|
||||
target.Int32Array = Int32Array;
|
||||
|
||||
target.Map = fixup(Map);
|
||||
target.WeakMap = fixup(WeakMap);
|
||||
target.Set = fixup(Set);
|
||||
target.WeakSet = fixup(WeakSet);
|
||||
target.RegExp = fixup(RegExp);
|
||||
target.Date = fixup(Date);
|
||||
target.Promise = fixup(Promise);
|
||||
target.Math = object.setPrototype(_Math, Object.prototype);
|
||||
target.JSON = object.setPrototype(JSON, Object.prototype);
|
||||
target.console = object.setPrototype(console, Object.prototype);
|
||||
|
||||
target.parseInt = Number.parseInt;
|
||||
target.parseFloat = Number.parseFloat;
|
||||
target.NaN = Number.NaN;
|
||||
target.Infinity = Number.POSITIVE_INFINITY;
|
||||
target.encodeURI = encodeURI;
|
||||
target.encodeURIComponent = encodeURIComponent;
|
||||
|
||||
setGlobalPrototypes({
|
||||
string: String.prototype,
|
||||
number: Number.prototype,
|
||||
boolean: Boolean.prototype,
|
||||
symbol: Symbol.prototype,
|
||||
object: Object.prototype,
|
||||
array: Array.prototype,
|
||||
function: Function.prototype,
|
||||
error: Error.prototype,
|
||||
syntax: SyntaxError.prototype,
|
||||
range: RangeError.prototype,
|
||||
type: TypeError.prototype,
|
||||
uint8: Uint8Array.prototype,
|
||||
int32: Int32Array.prototype,
|
||||
regex: RegExp,
|
||||
});
|
||||
30
src/lib/libs/arrays/ArrayBuffer.ts
Normal file
30
src/lib/libs/arrays/ArrayBuffer.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { buffer, type InternalBuffer, map, symbol } from "../primordials.ts";
|
||||
|
||||
export const abs = new map(true);
|
||||
export const abKey: unique symbol = symbol.getSymbol("ArrayBuffer.impl") as any;
|
||||
|
||||
export class ArrayBuffer {
|
||||
public [abKey]!: InternalBuffer;
|
||||
|
||||
public get byteLength() {
|
||||
return this[abKey].length;
|
||||
}
|
||||
public get byteOffset() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public constructor(val: unknown) {
|
||||
if (buffer.isBuff(val)) this[abKey] = val;
|
||||
else this[abKey] = buffer.buff(Number(val));
|
||||
}
|
||||
}
|
||||
|
||||
export function getAB(buff: InternalBuffer): ArrayBuffer {
|
||||
let res = abs.get(buff);
|
||||
if (res == null) {
|
||||
res = new ArrayBuffer(buff);
|
||||
abs.set(buff, res);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
25
src/lib/libs/arrays/Int32Array.ts
Normal file
25
src/lib/libs/arrays/Int32Array.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { buffer } from "../primordials.ts";
|
||||
import { abstractIgnore, TypedArray, typedArrayFuncs } from "./TypedArray.ts";
|
||||
|
||||
const factory = buffer.int32;
|
||||
const funcs = typedArrayFuncs(4, factory);
|
||||
|
||||
export class Int32Array extends TypedArray {
|
||||
public subarray(this: number[], start?: number, end?: number) {
|
||||
return funcs.subarray(this, start, end);
|
||||
}
|
||||
public slice(this: any[], start?: number, end?: number) {
|
||||
return funcs.slice(this, start, end);
|
||||
}
|
||||
public map(this: any[], cb: (val: number, i: number, self: any) => number, self?: any) {
|
||||
return funcs.map(this, cb, self);
|
||||
}
|
||||
public filter(this: any[], cb: (val: number, i: number, self: any) => boolean, self?: any) {
|
||||
return funcs.filter(this, cb, self);
|
||||
}
|
||||
|
||||
public constructor(obj: any, start?: number, end?: number) {
|
||||
super(abstractIgnore);
|
||||
return funcs.construct(obj, start, end) as any;
|
||||
}
|
||||
}
|
||||
220
src/lib/libs/arrays/TypedArray.ts
Normal file
220
src/lib/libs/arrays/TypedArray.ts
Normal file
@@ -0,0 +1,220 @@
|
||||
import { buffer, func, type InternalBuffer, object, string, symbol } from "../primordials.ts";
|
||||
import { symbols, wrapI } from "../utils.ts";
|
||||
import { Error, TypeError } from "../values/errors.ts";
|
||||
import { abKey, ArrayBuffer, getAB } from "./ArrayBuffer.ts";
|
||||
|
||||
export const abstractIgnore = symbol.getSymbol("TypedArray.abstractIgnore");
|
||||
|
||||
export function typedArrayFuncs(perEl: number, constructor: (buff: InternalBuffer, start: number, end: number) => number[]) {
|
||||
return {
|
||||
map(self: number[], cb: (val: number, i: number, self: number[]) => number, fnSelf: any) {
|
||||
const res = constructor(buffer.buff(self.length * perEl), 0, self.length);
|
||||
|
||||
for (let i = 0; i < self.length; i++) {
|
||||
res[i] = func.invoke(cb, fnSelf, [self[i], i, self]);
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
filter(self: number[], cb: (val: number, i: number, self: number[]) => boolean, fnSelf: any) {
|
||||
const bigger = constructor(buffer.buff(self.length * perEl), 0, self.length);
|
||||
let j = 0;
|
||||
|
||||
for (let i = 0; i < self.length; i++) {
|
||||
if (func.invoke(cb, self, [self[i], i, fnSelf])) bigger[j++] = self[i];
|
||||
}
|
||||
|
||||
const res = constructor(buffer.buff(j * perEl), 0, j);
|
||||
for (let i = 0; i < j; i++) res[i] = bigger[i];
|
||||
return res;
|
||||
},
|
||||
slice(self: number[], start = 0, end = self.length) {
|
||||
start = wrapI(start, self.length);
|
||||
end = wrapI(end, self.length);
|
||||
|
||||
if (end <= start) return constructor(buffer.buff(0), 0, 0);
|
||||
|
||||
const res = constructor(buffer.buff((end - start) * perEl), 0, end - start);
|
||||
|
||||
for (let i = 0; i < end - start; i++) {
|
||||
res[i] = self[start + i];
|
||||
}
|
||||
|
||||
return res;
|
||||
},
|
||||
subarray(self: number[], start = 0, end = self.length) {
|
||||
start = wrapI(start, self.length);
|
||||
end = wrapI(end, self.length);
|
||||
|
||||
if (end <= start) return constructor(buffer.buff(0), 0, 0);
|
||||
|
||||
const offset = buffer.start(self);
|
||||
return constructor(buffer.backer(self), offset + start, offset + end);
|
||||
},
|
||||
construct(self: ArrayBuffer | number[] | number | Iterable<number>, start?: number, end?: number) {
|
||||
if (typeof self === "number") {
|
||||
return constructor(buffer.buff(self * perEl), 0, self);
|
||||
}
|
||||
if (self instanceof ArrayBuffer) {
|
||||
const internal = self[abKey];
|
||||
if (start === undefined) start = 0;
|
||||
if (end === undefined) end = (internal.length / perEl) | 0;
|
||||
return constructor(internal, start, end);
|
||||
}
|
||||
if (symbols.iterator in self && typeof self[symbols.iterator] === "function") {
|
||||
const arr: number[] = [];
|
||||
let i = 0;
|
||||
const gen: Iterator<number> = self[symbols.iterator]();
|
||||
|
||||
for (let it = gen.next(); !it.done; it = gen.next()) {
|
||||
arr[i++] = Number(it.value);
|
||||
}
|
||||
|
||||
const res = constructor(buffer.buff(i * perEl), 0, i);
|
||||
for (let j = 0; j < i; j++) res[j] = arr[j];
|
||||
return res;
|
||||
}
|
||||
|
||||
const res = constructor(buffer.buff((self as number[]).length * perEl), 0, (self as number[]).length);
|
||||
for (let i = 0; i < (self as number[]).length; i++) res[i] = (self as number[])[i];
|
||||
return res;
|
||||
},
|
||||
byteOffset(self: number[]) {
|
||||
return buffer.start(self) * perEl;
|
||||
},
|
||||
byteLength(self: number[]) {
|
||||
return (buffer.end(self) - buffer.start(self)) * perEl;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export class TypedArray {
|
||||
public get buffer() {
|
||||
return getAB(buffer.backer(this as any));
|
||||
}
|
||||
public get byteOffset(): number {
|
||||
throw new Error("abstract");
|
||||
}
|
||||
public get byteLength(): number {
|
||||
throw new Error("abstract");
|
||||
}
|
||||
|
||||
public forEach(this: number[], cb: (val: number, i: number, self: this) => void, self?: any) {
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
if (i in this) func.invoke(cb, self, [this[i], i, this]);
|
||||
}
|
||||
}
|
||||
public join(this: number[], delim = ",") {
|
||||
delim = String(delim);
|
||||
const parts = [];
|
||||
if (delim) {
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
if (i) parts[parts.length] = delim;
|
||||
parts[parts.length] = (i in this) ? String(this[i]) : "";
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
parts[i] = (i in this) ? String(this[i]) : "";
|
||||
}
|
||||
}
|
||||
|
||||
return string.stringBuild(parts);
|
||||
}
|
||||
|
||||
public subarray(this: number[], start = 0, end = this.length) {
|
||||
throw new Error("'slice' is an abstract method");
|
||||
}
|
||||
|
||||
public slice(this: number[], start = 0, end = this.length) {
|
||||
throw new Error("'slice' is an abstract method");
|
||||
}
|
||||
|
||||
public map(this: number[], cb: (val: number, i: number, self: this) => number, self?: any) {
|
||||
throw new Error("'map' is an abstract method");
|
||||
}
|
||||
public filter(this: number[], cb: (val: number, i: number, self: this) => boolean, self?: any) {
|
||||
throw new Error("'filter' is an abstract method");
|
||||
}
|
||||
public reduce(this: number[], cb: (a: number, b: number, i: number, self: number[]) => number, initial?: number) {
|
||||
let i = 0;
|
||||
if (arguments.length <= 1) initial = this[i++];
|
||||
|
||||
for (; i < this.length; i++) {
|
||||
initial = cb(initial!, this[i], i, this);
|
||||
}
|
||||
|
||||
return initial;
|
||||
}
|
||||
public some(this: number[], cb: (val: number, i: number, self: number[]) => boolean, self?: any) {
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
if (func.invoke(cb, self, [this[i], i, this])) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
public every(this: number[], cb: (val: number, i: number, self: number[]) => boolean, self?: any) {
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
if (!func.invoke(cb, self, [this[i], i, this])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
public find(this: number[], cb: (val: number, i: number, self: number[]) => boolean, self?: any) {
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
if (func.invoke(cb, self, [this[i], i, this])) return this[i];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
public indexOf(this: number[], val: number, start = 0) {
|
||||
start |= 0;
|
||||
if (start < 0) start = 0;
|
||||
for (let i = start; i < this.length; i++) {
|
||||
if (this[i] === val) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
public lastIndexOf(this: number[], val: number, start = 0) {
|
||||
start |= 0;
|
||||
if (start < 0) start = 0;
|
||||
|
||||
for (let i = this.length - 1; i >= start; i--) {
|
||||
if (this[i] === val) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
public includes(this: number[], val: number) {
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
if (this[i] === val) return i;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public sort(this: number[], cb?: (a: number, b: number) => number) {
|
||||
cb ||= (a, b) => a - b;
|
||||
|
||||
return object.sort(this, cb);
|
||||
}
|
||||
public reverse(this: number[]) {
|
||||
const mid = this.length >> 1;
|
||||
const end = this.length - 1;
|
||||
|
||||
for (let i = 0; i < mid; i++) {
|
||||
const tmp = this[i];
|
||||
this[i] = this[end - i];
|
||||
this[end - i] = tmp;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public constructor(token?: typeof abstractIgnore) {
|
||||
if (token !== abstractIgnore) {
|
||||
throw new TypeError("TypedArray constructor can't be called");
|
||||
}
|
||||
}
|
||||
}
|
||||
32
src/lib/libs/arrays/Uint8Array.ts
Normal file
32
src/lib/libs/arrays/Uint8Array.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { buffer } from "../primordials.ts";
|
||||
import { abstractIgnore, TypedArray, typedArrayFuncs } from "./TypedArray.ts";
|
||||
|
||||
const factory = buffer.uint8;
|
||||
const funcs = typedArrayFuncs(1, factory);
|
||||
|
||||
export class Uint8Array extends TypedArray {
|
||||
public override get byteOffset() {
|
||||
return funcs.byteOffset(this as any);
|
||||
}
|
||||
public override get byteLength() {
|
||||
return funcs.byteLength(this as any);
|
||||
}
|
||||
|
||||
public override subarray(this: number[], start?: number, end?: number) {
|
||||
return funcs.subarray(this, start, end);
|
||||
}
|
||||
public override slice(this: any[], start?: number, end?: number) {
|
||||
return funcs.slice(this, start, end);
|
||||
}
|
||||
public override map(this: any[], cb: (val: number, i: number, self: any) => number, self?: any) {
|
||||
return funcs.map(this, cb, self);
|
||||
}
|
||||
public override filter(this: any[], cb: (val: number, i: number, self: any) => boolean, self?: any) {
|
||||
return funcs.filter(this, cb, self);
|
||||
}
|
||||
|
||||
public constructor(obj: any, start?: number, end?: number) {
|
||||
super(abstractIgnore);
|
||||
return funcs.construct(obj, start, end) as any;
|
||||
}
|
||||
}
|
||||
20
src/lib/libs/classes/date.ts
Normal file
20
src/lib/libs/classes/date.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { now, symbol } from "../primordials.ts";
|
||||
|
||||
const timeKey: unique symbol = symbol.makeSymbol("") as any;
|
||||
|
||||
export const Date = (() => {
|
||||
class Date {
|
||||
[timeKey]!: number;
|
||||
|
||||
public constructor() {
|
||||
|
||||
}
|
||||
|
||||
public static now() {
|
||||
return now();
|
||||
}
|
||||
};
|
||||
|
||||
return Date as any as typeof Date & ((val?: unknown) => string);
|
||||
})();
|
||||
export type Date = InstanceType<typeof Date>;
|
||||
128
src/lib/libs/classes/map.ts
Normal file
128
src/lib/libs/classes/map.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import { Array } from "../values/array.ts";
|
||||
import { func, map, symbol } from "../primordials.ts";
|
||||
import { symbols } from "../utils.ts";
|
||||
|
||||
const mapKey: unique symbol = symbol.makeSymbol("Map.impl") as any;
|
||||
|
||||
export class Map<K, V> {
|
||||
private [mapKey]: InstanceType<typeof map>;
|
||||
|
||||
public get size() {
|
||||
return this[mapKey].size();
|
||||
}
|
||||
|
||||
public get(key: K): V {
|
||||
return this[mapKey].get(key);
|
||||
}
|
||||
public has(key: K): boolean {
|
||||
return this[mapKey].has(key);
|
||||
}
|
||||
public set(key: K, val: V) {
|
||||
this[mapKey].set(key, val);
|
||||
return this;
|
||||
}
|
||||
public delete(key: K): boolean {
|
||||
if (!this[mapKey].has(key)) return false;
|
||||
else {
|
||||
this[mapKey].delete(key);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public clear() {
|
||||
this[mapKey].clear();
|
||||
}
|
||||
|
||||
public keys(): K[] {
|
||||
return this[mapKey].keys();
|
||||
}
|
||||
public values(): V[] {
|
||||
const res = this[mapKey].keys();
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
res[i] = this[mapKey].get(res[i]);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
public entries(): [K, V][] {
|
||||
const res = this[mapKey].keys();
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
res[i] = [res[i], this[mapKey].get(res[i])];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public forEach(cb: Function, self?: any) {
|
||||
const entries = this.entries();
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
func.invoke(cb, self, [entries[i][1], entries[i][0], this]);
|
||||
}
|
||||
}
|
||||
|
||||
public [symbols.iterator](): Iterator<[K, V]> {
|
||||
return func.invoke(Array.prototype[symbols.iterator], this.entries(), []) as any;
|
||||
}
|
||||
|
||||
public constructor(iterable?: Iterable<[K, V]>) {
|
||||
const _map = this[mapKey] = new map();
|
||||
|
||||
if (iterable != null) {
|
||||
if (Array.isArray(iterable)) {
|
||||
for (let i = 0; i < iterable.length; i++) {
|
||||
if (!(i in iterable)) continue;
|
||||
_map.set(iterable[i][0], iterable[i][1]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const it = (iterable as any)[symbols.iterator]();
|
||||
for (let val = it.next(); !val.done; val = it.next()) {
|
||||
_map.set(val.value[0], val.value[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
export class WeakMap<K, V> {
|
||||
private [mapKey]: InstanceType<typeof map>;
|
||||
|
||||
public get(key: K): V {
|
||||
return this[mapKey].get(key);
|
||||
}
|
||||
public has(key: K): boolean {
|
||||
return this[mapKey].has(key);
|
||||
}
|
||||
public set(key: K, val: V) {
|
||||
this[mapKey].set(key, val);
|
||||
return this;
|
||||
}
|
||||
public delete(key: K): boolean {
|
||||
if (!this[mapKey].has(key)) return false;
|
||||
else {
|
||||
this[mapKey].delete(key);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public clear() {
|
||||
this[mapKey].clear();
|
||||
}
|
||||
|
||||
public constructor(iterable?: Iterable<[K, V]>) {
|
||||
const _map = this[mapKey] = new map(true);
|
||||
|
||||
if (iterable != null) {
|
||||
if (Array.isArray(iterable)) {
|
||||
for (let i = 0; i < iterable.length; i++) {
|
||||
if (!(i in iterable)) continue;
|
||||
_map.set(iterable[i][0], iterable[i][1]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const it = (iterable as any)[symbols.iterator]();
|
||||
for (let val = it.next(); !val.done; val = it.next()) {
|
||||
_map.set(val.value[0], val.value[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func.setCallable(Map, false);
|
||||
func.setCallable(WeakMap, false);
|
||||
154
src/lib/libs/classes/promise.ts
Normal file
154
src/lib/libs/classes/promise.ts
Normal file
@@ -0,0 +1,154 @@
|
||||
import { func, next, object, symbol } from "../primordials.ts";
|
||||
|
||||
enum PromiseState {
|
||||
Pending = "pend",
|
||||
Fulfilled = "ful",
|
||||
Rejected = "rej",
|
||||
}
|
||||
|
||||
const pState: unique symbol = symbol.makeSymbol("Promise.state") as any;
|
||||
const pValue: unique symbol = symbol.makeSymbol("Promise.value") as any;
|
||||
const pFulHandles: unique symbol = symbol.makeSymbol("Promise.fulfillHandles") as any;
|
||||
const pRejHandles: unique symbol = symbol.makeSymbol("Promise.rejectHandles") as any;
|
||||
|
||||
function makePromise<T>(): Promise<T> {
|
||||
return object.setPrototype({
|
||||
[pState]: PromiseState.Pending,
|
||||
[pFulHandles]: [],
|
||||
[pRejHandles]: [],
|
||||
}, Promise.prototype) as Promise<T>;
|
||||
}
|
||||
|
||||
function fulfill(self: Promise<any>, val: any) {
|
||||
if (self[pState] !== PromiseState.Pending) return;
|
||||
if (self === val) throw new Error("A promise may not be fulfilled with itself");
|
||||
|
||||
if (val != null && typeof val.then === "function") {
|
||||
val.then(
|
||||
(val: any) => fulfill(self, val),
|
||||
(err: any) => reject(self, err),
|
||||
);
|
||||
}
|
||||
else {
|
||||
self[pValue] = val;
|
||||
self[pState] = PromiseState.Fulfilled;
|
||||
|
||||
const handles = self[pFulHandles]!;
|
||||
|
||||
for (let i = 0; i < handles.length; i++) {
|
||||
handles[i](val);
|
||||
}
|
||||
|
||||
self[pFulHandles] = undefined;
|
||||
self[pRejHandles] = undefined;
|
||||
}
|
||||
}
|
||||
function reject(self: Promise<any>, val: any) {
|
||||
if (self[pState] !== PromiseState.Pending) return;
|
||||
if (self === val) throw new Error("A promise may not be rejected with itself");
|
||||
|
||||
if (val != null && typeof val.then === "function") {
|
||||
val.then(
|
||||
(val: any) => reject(self, val),
|
||||
(err: any) => reject(self, err),
|
||||
);
|
||||
}
|
||||
else {
|
||||
self[pValue] = val;
|
||||
self[pState] = PromiseState.Rejected;
|
||||
|
||||
const handles = self[pRejHandles]!;
|
||||
|
||||
for (let i = 0; i < handles.length; i++) {
|
||||
handles[i](val);
|
||||
}
|
||||
|
||||
self[pFulHandles] = undefined;
|
||||
self[pRejHandles] = undefined;
|
||||
}
|
||||
}
|
||||
function handle<T>(self: Promise<T>, ful?: (val: T) => void, rej?: (err: any) => void) {
|
||||
if (self[pState] === PromiseState.Pending) {
|
||||
if (ful != null) {
|
||||
self[pFulHandles]![self[pFulHandles]!.length] = ful;
|
||||
}
|
||||
if (rej != null) {
|
||||
self[pRejHandles]![self[pRejHandles]!.length] = rej;
|
||||
}
|
||||
}
|
||||
else if (self[pState] === PromiseState.Fulfilled) {
|
||||
if (ful != null) ful(self[pValue] as T);
|
||||
}
|
||||
else if (self[pState] === PromiseState.Rejected) {
|
||||
if (rej != null) rej(self[pValue]);
|
||||
}
|
||||
}
|
||||
|
||||
export class Promise<T> {
|
||||
public [pState]: PromiseState;
|
||||
public [pValue]?: T | unknown;
|
||||
public [pFulHandles]?: ((val: T) => void)[] = [];
|
||||
public [pRejHandles]?: ((val: T) => void)[] = [];
|
||||
|
||||
public then<Res>(ful?: (val: T) => Res, rej?: (err: any) => Res) {
|
||||
if (typeof ful !== "function") ful = undefined;
|
||||
if (typeof rej !== "function") rej = undefined;
|
||||
|
||||
const promise = makePromise<Res>();
|
||||
|
||||
handle(this,
|
||||
val => next(() => {
|
||||
if (ful == null) fulfill(promise, val);
|
||||
else {
|
||||
try { fulfill(promise, ful(val)); }
|
||||
catch (e) { reject(promise, e); }
|
||||
}
|
||||
}),
|
||||
err => next(() => {
|
||||
if (rej == null) reject(promise, err);
|
||||
else {
|
||||
try { fulfill(promise, rej(err)); }
|
||||
catch (e) { reject(promise, e); }
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
return promise;
|
||||
}
|
||||
public catch<Res>(rej?: (err: any) => Res) {
|
||||
return this.then(undefined, rej);
|
||||
}
|
||||
public finally(fn?: () => void) {
|
||||
if (typeof fn !== "function") return this["then"]();
|
||||
|
||||
return this.then(
|
||||
v => {
|
||||
fn();
|
||||
return v;
|
||||
},
|
||||
v => {
|
||||
fn();
|
||||
throw v;
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
public constructor(fn: (fulfil: (val: T) => void, reject: (err: unknown) => void) => void) {
|
||||
this[pState] = PromiseState.Pending;
|
||||
|
||||
fn(val => fulfill(this, val), err => reject(this, err));
|
||||
}
|
||||
|
||||
public static resolve(val: any) {
|
||||
const res = makePromise();
|
||||
fulfill(res, val);
|
||||
return res;
|
||||
}
|
||||
public static reject(val: any) {
|
||||
const res = makePromise();
|
||||
reject(res, val);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
func.setCallable(Promise, false);
|
||||
121
src/lib/libs/classes/set.ts
Normal file
121
src/lib/libs/classes/set.ts
Normal file
@@ -0,0 +1,121 @@
|
||||
import { Array } from "../values/array.ts";
|
||||
import { func, map, symbol } from "../primordials.ts";
|
||||
import { symbols } from "../utils.ts";
|
||||
|
||||
const mapKey: unique symbol = symbol.makeSymbol("Set.impl") as any;
|
||||
|
||||
export class Set<T> {
|
||||
private [mapKey]: InstanceType<typeof map>;
|
||||
|
||||
public get size() {
|
||||
return this[mapKey].size();
|
||||
}
|
||||
|
||||
public has(key: T): boolean {
|
||||
return this[mapKey].has(key);
|
||||
}
|
||||
public add(val: T) {
|
||||
this[mapKey].set(val, true);
|
||||
return this;
|
||||
}
|
||||
public delete(val: T): boolean {
|
||||
if (!this[mapKey].has(val)) return false;
|
||||
else {
|
||||
this[mapKey].delete(val);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public clear() {
|
||||
this[mapKey].clear();
|
||||
}
|
||||
|
||||
public keys(): T[] {
|
||||
return this[mapKey].keys();
|
||||
}
|
||||
public values(): T[] {
|
||||
return this[mapKey].keys();
|
||||
}
|
||||
public entries(): [T, T][] {
|
||||
const res = this[mapKey].keys();
|
||||
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
res[i] = [res[i], res[i]];
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
public forEach(cb: Function, self?: any) {
|
||||
const vals = this.values();
|
||||
|
||||
for (let i = 0; i < vals.length; i++) {
|
||||
func.invoke(cb, self, [vals[i], vals[i], this]);
|
||||
}
|
||||
}
|
||||
|
||||
public [symbols.iterator](): Iterator<T> {
|
||||
return func.invoke(Array.prototype[symbols.iterator], this.values(), []) as any;
|
||||
}
|
||||
|
||||
public constructor(iterable?: Iterable<T>) {
|
||||
const _map = this[mapKey] = new map();
|
||||
|
||||
if (iterable != null) {
|
||||
if (Array.isArray(iterable)) {
|
||||
for (let i = 0; i < iterable.length; i++) {
|
||||
if (!(i in iterable)) continue;
|
||||
_map.set(iterable[i], true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const it = (iterable as any)[symbols.iterator]();
|
||||
for (let val = it.next(); !val.done; val = it.next()) {
|
||||
_map.set(val.value, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export class WeakSet<T> {
|
||||
private [mapKey]: InstanceType<typeof map>;
|
||||
|
||||
public has(key: T): boolean {
|
||||
return this[mapKey].has(key);
|
||||
}
|
||||
public add(val: T) {
|
||||
this[mapKey].set(val, true);
|
||||
return this;
|
||||
}
|
||||
public delete(val: T): boolean {
|
||||
if (!this[mapKey].has(val)) return false;
|
||||
else {
|
||||
this[mapKey].delete(val);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
public clear() {
|
||||
this[mapKey].clear();
|
||||
}
|
||||
|
||||
public constructor(iterable?: Iterable<T>) {
|
||||
const _map = this[mapKey] = new map(true);
|
||||
|
||||
if (iterable != null) {
|
||||
if (Array.isArray(iterable)) {
|
||||
for (let i = 0; i < iterable.length; i++) {
|
||||
if (!(i in iterable)) continue;
|
||||
_map.set(iterable[i], true);
|
||||
}
|
||||
}
|
||||
else {
|
||||
const it = (iterable as any)[symbols.iterator]();
|
||||
for (let val = it.next(); !val.done; val = it.next()) {
|
||||
_map.set(val.value, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func.setCallable(Set, false);
|
||||
func.setCallable(WeakSet, false);
|
||||
11
src/lib/libs/namespaces/console.ts
Normal file
11
src/lib/libs/namespaces/console.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { func, json, object } from "../primordials.ts";
|
||||
|
||||
export const console = {};
|
||||
|
||||
function method(name: string, func: Function) {
|
||||
object.defineField(console, name, { c: true, e: false, w: true, v: func });
|
||||
}
|
||||
|
||||
method("log", function log() {
|
||||
func.invoke(print, null, arguments as any);
|
||||
});
|
||||
15
src/lib/libs/namespaces/json.ts
Normal file
15
src/lib/libs/namespaces/json.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { json, object } from "../primordials.ts";
|
||||
|
||||
export const JSON = {};
|
||||
|
||||
function method(name: string, func: Function) {
|
||||
object.defineField(JSON, name, { c: true, e: false, w: true, v: func });
|
||||
}
|
||||
|
||||
method("parse", function parse(val: string) {
|
||||
return json.parse(val);
|
||||
});
|
||||
|
||||
method("stringify", function stringify(val: string) {
|
||||
return json.stringify(val);
|
||||
});
|
||||
60
src/lib/libs/namespaces/math.ts
Normal file
60
src/lib/libs/namespaces/math.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { number, object } from "../primordials.ts";
|
||||
|
||||
export const Math = {};
|
||||
|
||||
function method(name: string, func: Function) {
|
||||
object.defineField(Math, name, { c: true, e: false, w: true, v: func });
|
||||
}
|
||||
|
||||
method("max", function max() {
|
||||
let res = -number.Infinity;
|
||||
|
||||
for (let i = 0; i < arguments.length; i++) {
|
||||
if (res < arguments[i]) res = arguments[i];
|
||||
}
|
||||
|
||||
return res;
|
||||
});
|
||||
|
||||
method("min", function min() {
|
||||
let res = +number.Infinity;
|
||||
|
||||
for (let i = 0; i < arguments.length; i++) {
|
||||
if (res > arguments[i]) res = arguments[i];
|
||||
}
|
||||
|
||||
return res;
|
||||
});
|
||||
|
||||
method("abs", function abs(val: number) {
|
||||
val = +val;
|
||||
if (val < 0) return -val;
|
||||
else return val;
|
||||
});
|
||||
|
||||
method("floor", function floor(val: number) {
|
||||
val = val - 0;
|
||||
if (number.isNaN(val)) return number.NaN;
|
||||
|
||||
let rem = val % 1;
|
||||
if (rem < 0) rem += 1;
|
||||
|
||||
return val - rem;
|
||||
});
|
||||
method("ceil", function floor(val: number) {
|
||||
val = val - 0;
|
||||
if (number.isNaN(val)) return number.NaN;
|
||||
|
||||
let rem = val % 1;
|
||||
if (rem === 0) return val;
|
||||
if (rem < 0) rem += 1;
|
||||
|
||||
return val + (1 - rem);
|
||||
});
|
||||
|
||||
method("pow", function pow(a: number, b: number) {
|
||||
return number.pow(a, b);
|
||||
});
|
||||
method("log", function log(val: number) {
|
||||
return number.log(val);
|
||||
});
|
||||
5
src/lib/libs/polyfills/callSuper.js
Normal file
5
src/lib/libs/polyfills/callSuper.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import { func, object } from "../primordials.ts";
|
||||
|
||||
export default function _callSuper(self, constr, args) {
|
||||
return func.construct(object.getPrototype(constr), func.target(1), args || []);
|
||||
}
|
||||
5
src/lib/libs/polyfills/classCallCheck.js
Normal file
5
src/lib/libs/polyfills/classCallCheck.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import { func } from "../primordials.ts";
|
||||
|
||||
export default function _classCallCheck() {
|
||||
if (func.invokeTypeInfer() !== "new") throw new TypeError("Cannot call a class as a function");
|
||||
}
|
||||
35
src/lib/libs/polyfills/createClass.js
Normal file
35
src/lib/libs/polyfills/createClass.js
Normal file
@@ -0,0 +1,35 @@
|
||||
import { object } from "../primordials.ts";
|
||||
|
||||
function _defineProperties(target, arr) {
|
||||
if (!arr) return;
|
||||
for (var i = 0; i < arr.length; i++) {
|
||||
var desc = arr[i];
|
||||
var res;
|
||||
var w, e, c;
|
||||
|
||||
c = desc.configurable;
|
||||
if (c == null) c = true;
|
||||
e = desc.enumerable;
|
||||
if (e == null) e = false;
|
||||
|
||||
if ("value" in desc) {
|
||||
w = desc.writable;
|
||||
if (w == null) w = true;
|
||||
|
||||
if (desc.writable == null)
|
||||
res = object.defineField(target, desc.key, { w: !!w, e: !!e, c: !!c, v: desc.value });
|
||||
}
|
||||
else {
|
||||
res = object.defineProperty(target, desc.key, { e: !!e, c: !!c, g: desc.get, s: desc.set });
|
||||
}
|
||||
|
||||
if (!res) throw "Couldn't set property";
|
||||
}
|
||||
}
|
||||
|
||||
export default function _createClass(clazz, instance, nonInstance) {
|
||||
_defineProperties(clazz.prototype, instance);
|
||||
_defineProperties(clazz, nonInstance);
|
||||
|
||||
return clazz;
|
||||
}
|
||||
7
src/lib/libs/polyfills/defineProperty.js
Normal file
7
src/lib/libs/polyfills/defineProperty.js
Normal file
@@ -0,0 +1,7 @@
|
||||
import { object } from "../primordials.ts";
|
||||
|
||||
export default function _defineProperty(obj, key, val) {
|
||||
if (obj == null) return;
|
||||
object.defineField(obj, key, { c: true, e: true, w: true, v: val });
|
||||
return obj;
|
||||
}
|
||||
5
src/lib/libs/polyfills/getPrototypeOf.js
Normal file
5
src/lib/libs/polyfills/getPrototypeOf.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import { object } from "../primordials.ts";
|
||||
|
||||
export default function _getPrototypeOf(obj) {
|
||||
return object.getPrototype(obj) || null;
|
||||
}
|
||||
11
src/lib/libs/polyfills/inherits.js
Normal file
11
src/lib/libs/polyfills/inherits.js
Normal file
@@ -0,0 +1,11 @@
|
||||
import { object } from "../primordials.ts";
|
||||
|
||||
export default function _inherits(t, e) {
|
||||
if (e == null) {
|
||||
object.setPrototype(t.prototype, undefined);
|
||||
}
|
||||
else {
|
||||
object.setPrototype(t.prototype, e.prototype);
|
||||
object.setPrototype(t, e);
|
||||
}
|
||||
}
|
||||
1
src/lib/libs/polyfills/possibleConstructorReturn.js
Normal file
1
src/lib/libs/polyfills/possibleConstructorReturn.js
Normal file
@@ -0,0 +1 @@
|
||||
export default function _possibleConstructorReturn(_, res) { return res; }
|
||||
3
src/lib/libs/polyfills/readOnlyError.js
Normal file
3
src/lib/libs/polyfills/readOnlyError.js
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function _readOnlyError(name) {
|
||||
throw name;
|
||||
}
|
||||
5
src/lib/libs/polyfills/setPrototypeOf.js
Normal file
5
src/lib/libs/polyfills/setPrototypeOf.js
Normal file
@@ -0,0 +1,5 @@
|
||||
import { object } from "../primordials";
|
||||
|
||||
export default function _setPrototypeOf(obj, proto) {
|
||||
object.setPrototype(obj, proto);
|
||||
}
|
||||
3
src/lib/libs/polyfills/typeof.js
Normal file
3
src/lib/libs/polyfills/typeof.js
Normal file
@@ -0,0 +1,3 @@
|
||||
export default function _typeof(val) {
|
||||
return typeof val;
|
||||
}
|
||||
136
src/lib/libs/primordials.ts
Normal file
136
src/lib/libs/primordials.ts
Normal file
@@ -0,0 +1,136 @@
|
||||
const buffSymbol: unique symbol = undefined as any;
|
||||
|
||||
export interface InternalBuffer {
|
||||
length: number;
|
||||
[buffSymbol]: "buffer";
|
||||
}
|
||||
|
||||
export interface SymbolPrimordials {
|
||||
makeSymbol(name: string): symbol;
|
||||
getSymbol(name: string): symbol;
|
||||
getSymbolKey(symbol: symbol): string | undefined;
|
||||
getSymbolDescription(symbol: symbol): string;
|
||||
}
|
||||
export interface NumberPrimordials {
|
||||
NaN: number;
|
||||
Infinity: number;
|
||||
PI: number;
|
||||
E: number;
|
||||
|
||||
parseInt(raw: string | number, radix?: number): number;
|
||||
parseFloat(raw: string | number): number;
|
||||
isNaN(num: number): boolean;
|
||||
|
||||
pow(a: number, b: number): number;
|
||||
log(val: number): number;
|
||||
}
|
||||
export interface StringPrimordials {
|
||||
stringBuild(parts: string[]): string;
|
||||
fromCharCode(char: number): string;
|
||||
fromCodePoint(char: number): string;
|
||||
toCharCode(char: string): number;
|
||||
toCodePoint(char: string, i: number): number;
|
||||
indexOf(str: string, search: string, start: number, reverse?: boolean): number;
|
||||
substring(str: string, start: number, end: number): string;
|
||||
lower(str: string): string;
|
||||
upper(str: string): string;
|
||||
}
|
||||
export interface ObjectPrimordials {
|
||||
defineProperty(obj: object, key: string | number | symbol, conf: { g?: Function, s?: Function, e?: boolean, c?: boolean }): boolean;
|
||||
defineField(obj: object, key: string | number | symbol, conf: { v?: any, e?: boolean, c?: boolean, w?: boolean }): boolean;
|
||||
getOwnMember(obj: object, key: any): PropertyDescriptor | undefined;
|
||||
getOwnMembers(obj: object, onlyEnumerable: boolean): string[];
|
||||
getOwnSymbolMembers(obj: object, onlyEnumerable: boolean): symbol[];
|
||||
getPrototype(obj: object): object | undefined;
|
||||
setPrototype(obj: object, proto?: object): object;
|
||||
preventExt(obj: object): void;
|
||||
seal(obj: object): void;
|
||||
freeze(obj: object): void;
|
||||
|
||||
isArray(obj: any): obj is any[];
|
||||
subarray(arr: any[], start: number, end: number): any[];
|
||||
|
||||
memcpy(src: any[], dst: any[], srcI: number, dstI: number, n: number): void;
|
||||
sort(arr: any[], cb: Function): any[];
|
||||
}
|
||||
export interface BufferPrimordials {
|
||||
buff(n: number): InternalBuffer;
|
||||
backer(arr: number[]): InternalBuffer;
|
||||
start(arr: number[]): number;
|
||||
end(arr: number[]): number;
|
||||
|
||||
uint8(buff: InternalBuffer, start: number, end: number): number[];
|
||||
int8(buff: InternalBuffer, start: number, end: number): number[];
|
||||
int32(buff: InternalBuffer, start: number, end: number): number[];
|
||||
|
||||
isUint8(val: any): val is number[];
|
||||
isInt8(val: any): val is number[];
|
||||
isInt32(val: any): val is number[];
|
||||
is(val: any): val is number[];
|
||||
isBuff(val: any): val is InternalBuffer;
|
||||
}
|
||||
export interface FunctionPrimordials {
|
||||
invokeType(args: IArguments, self: any): "new" | "call";
|
||||
invokeTypeInfer(): "new" | "call";
|
||||
target(): Function | null | undefined;
|
||||
setConstructable(func: Function, flag: boolean): void;
|
||||
setCallable(func: Function, flag: boolean): void;
|
||||
invoke(func: Function, self: any, args: any[]): any;
|
||||
construct(func: Function, self: any, args: any[]): any;
|
||||
}
|
||||
export interface JSONPrimordials {
|
||||
parse(data: string): any;
|
||||
stringify(data: any): string;
|
||||
}
|
||||
|
||||
export interface Primordials {
|
||||
symbol: SymbolPrimordials;
|
||||
number: NumberPrimordials;
|
||||
string: StringPrimordials;
|
||||
object: ObjectPrimordials;
|
||||
function: FunctionPrimordials;
|
||||
json: JSONPrimordials;
|
||||
buffer: BufferPrimordials;
|
||||
map: new (weak?: boolean) => {
|
||||
get(key: any): any;
|
||||
has(key: any): boolean;
|
||||
set(key: any, val: any): void;
|
||||
delete(key: any): void;
|
||||
keys(): any[];
|
||||
clear(): void;
|
||||
size(): number;
|
||||
};
|
||||
|
||||
regex: new (source: string, multiline?: boolean, noCase?: boolean, dotall?: boolean, unicode?: boolean, unicodeClass?: boolean) => {
|
||||
exec(target: string, offset: number, indices: boolean): { matches: RegExpMatchArray, end: number } | null;
|
||||
groupCount(): number;
|
||||
};
|
||||
compile(src: string): Function;
|
||||
setGlobalPrototypes(prototype: Record<string, any>): void;
|
||||
now(): number;
|
||||
next(func: () => void): void;
|
||||
schedule(func: () => void, delay: number): () => void;
|
||||
}
|
||||
|
||||
globalThis.undefined = void 0;
|
||||
export const target = (globalThis as any).target;
|
||||
export const primordials: Primordials = (globalThis as any).primordials;
|
||||
|
||||
export const {
|
||||
symbol,
|
||||
number,
|
||||
string,
|
||||
object,
|
||||
buffer,
|
||||
function: func,
|
||||
json,
|
||||
map,
|
||||
regex,
|
||||
setGlobalPrototypes,
|
||||
compile,
|
||||
now,
|
||||
next,
|
||||
schedule,
|
||||
} = primordials;
|
||||
|
||||
export type regex = InstanceType<typeof regex>;
|
||||
26
src/lib/libs/url.ts
Normal file
26
src/lib/libs/url.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import { regex, string } from "./primordials";
|
||||
|
||||
function escaper(matcher: regex) {
|
||||
return (text: string) => {
|
||||
const parts: string[] = [];
|
||||
let i = 0;
|
||||
|
||||
while (true) {
|
||||
const match = matcher.exec(text, i, false);
|
||||
if (match == null) break;
|
||||
|
||||
const char = match.matches[0];
|
||||
const code = string.toCharCode(char);
|
||||
parts[parts.length] = string.substring(text, i, match.matches.index!);
|
||||
parts[parts.length] = "%" + code;
|
||||
i = match.end;
|
||||
}
|
||||
|
||||
parts[parts.length] = string.substring(text, i, text.length);
|
||||
|
||||
return string.stringBuild(parts);
|
||||
};
|
||||
}
|
||||
|
||||
export const encodeURI = escaper(new regex("[^A-Za-z0-9\\-+.!~*'()]"));
|
||||
export const encodeURIComponent = escaper(new regex("[^A-Za-z0-9\\-+.!~*'();/?:@&=+$,#]"));
|
||||
218
src/lib/libs/utils.ts
Normal file
218
src/lib/libs/utils.ts
Normal file
@@ -0,0 +1,218 @@
|
||||
import { func, string, symbol } from "./primordials.ts";
|
||||
|
||||
export const valueKey: unique symbol = symbol.makeSymbol("Primitive.value") as any;
|
||||
export namespace symbols {
|
||||
export const asyncIterator: unique symbol = symbol.makeSymbol("Symbol.asyncIterator") as any;
|
||||
export const iterator: unique symbol = symbol.makeSymbol("Symbol.iterator") as any;
|
||||
export const match: unique symbol = symbol.makeSymbol("Symbol.match") as any;
|
||||
export const matchAll: unique symbol = symbol.makeSymbol("Symbol.matchAll") as any;
|
||||
export const replace: unique symbol = symbol.makeSymbol("Symbol.replace") as any;
|
||||
export const search: unique symbol = symbol.makeSymbol("Symbol.search") as any;
|
||||
export const split: unique symbol = symbol.makeSymbol("Symbol.split") as any;
|
||||
export const toStringTag: unique symbol = symbol.makeSymbol("Symbol.toStringTag") as any;
|
||||
export const isConcatSpreadable: unique symbol = symbol.makeSymbol("Symbol.isConcatSpreadable") as any;
|
||||
}
|
||||
|
||||
export interface TypeMap {
|
||||
undefined: undefined;
|
||||
boolean: boolean;
|
||||
string: string;
|
||||
number: number;
|
||||
symbol: symbol;
|
||||
object: null | object;
|
||||
function: Function;
|
||||
}
|
||||
|
||||
export function unwrapThis<T extends keyof TypeMap>(self: any, type: T, constr: Function, name: string, arg = "this", defaultVal?: TypeMap[T]): TypeMap[T] {
|
||||
if (typeof self === type) return self;
|
||||
if (self instanceof constr && valueKey in self) self = self[valueKey];
|
||||
if (typeof self === type) return self;
|
||||
if (defaultVal !== undefined) return defaultVal;
|
||||
throw new TypeError(name + " requires that '" + arg + "' be a " + constr.name);
|
||||
}
|
||||
export function wrapI(i: number, length: number) {
|
||||
if (i < 0) return (i + length) | 0;
|
||||
else return i | 0;
|
||||
}
|
||||
export function limitI(i: number, max: number) {
|
||||
i |= 0;
|
||||
if (i < 0) return 0;
|
||||
else if (i > max) return max;
|
||||
else return i;
|
||||
}
|
||||
|
||||
export type ReplaceRange = { start: number; end: number; matches: string[]; groups?: Record<string, string>; };
|
||||
type ReplaceLiteral = (string | ((_: { groups: string[]; prev: () => string; next: () => string; }) => string))[];
|
||||
|
||||
function parseReplacer(replacer: string, groupN: number) {
|
||||
const parts: ReplaceLiteral = [];
|
||||
let lastI = 0;
|
||||
let lastSlice = 0;
|
||||
|
||||
while (true) {
|
||||
const i = string.indexOf(replacer, "$", lastI);
|
||||
if (i < 0 || i + 1 >= replacer.length) break;
|
||||
lastI = i + 1;
|
||||
|
||||
switch (replacer[i + 1]) {
|
||||
case "$":
|
||||
parts[parts.length] = string.substring(replacer, lastSlice, i);
|
||||
parts[parts.length] = "$";
|
||||
lastSlice = i + 2;
|
||||
continue;
|
||||
case "&":
|
||||
parts[parts.length] = string.substring(replacer, lastSlice, i);
|
||||
parts[parts.length] = ({ groups }) => groups[0];
|
||||
lastSlice = i + 2;
|
||||
continue;
|
||||
case "`":
|
||||
parts[parts.length] = string.substring(replacer, lastSlice, i);
|
||||
parts[parts.length] = ({ prev }) => prev();
|
||||
lastSlice = i + 2;
|
||||
continue;
|
||||
case "'":
|
||||
parts[parts.length] = string.substring(replacer, lastSlice, i);
|
||||
parts[parts.length] = ({ next }) => next();
|
||||
lastSlice = i + 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
let groupI = 0;
|
||||
let hasGroup = false;
|
||||
let consumedN = 1;
|
||||
|
||||
while (i + consumedN < replacer.length) {
|
||||
const code = string.toCharCode(replacer[i + consumedN]);
|
||||
if (code >= 48 && code <= 57) {
|
||||
const newGroupI = groupI * 10 + code - 48;
|
||||
if (newGroupI < 1 || newGroupI >= groupN) break;
|
||||
|
||||
groupI = newGroupI;
|
||||
hasGroup = true;
|
||||
}
|
||||
consumedN++;
|
||||
}
|
||||
|
||||
if (hasGroup) {
|
||||
parts[parts.length] = string.substring(replacer, lastSlice, i);
|
||||
parts[parts.length] = ({ groups }) => groups[groupI];
|
||||
lastSlice = i + consumedN;
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (lastSlice === 0) return [replacer];
|
||||
else parts[parts.length] = string.substring(replacer, lastSlice, replacer.length);
|
||||
|
||||
return parts;
|
||||
}
|
||||
function executeReplacer(text: string, match: ReplaceRange, literal: ReplaceLiteral, prevEnd?: number, nextStart?: number) {
|
||||
const res = [];
|
||||
|
||||
for (let i = 0; i < literal.length; i++) {
|
||||
const curr = literal[i];
|
||||
if (typeof curr === "function") res[i] = curr({
|
||||
groups: match.matches,
|
||||
next: () => string.substring(text, prevEnd ?? 0, match.start),
|
||||
prev: () => string.substring(text, match.end, nextStart ?? 0),
|
||||
});
|
||||
else res[i] = curr;
|
||||
}
|
||||
|
||||
return string.stringBuild(res);
|
||||
}
|
||||
export function applyReplaces(text: string, ranges: ReplaceRange[], replace: any, groupN?: number | false) {
|
||||
if (ranges.length === 0) return text;
|
||||
|
||||
const res: string[] = [];
|
||||
let offset = 0;
|
||||
|
||||
if (groupN !== false && typeof replace === "string") {
|
||||
if (groupN == null) {
|
||||
for (let i = 0; i < ranges.length; i++) {
|
||||
const prevEnd = i - 1 >= 0 ? ranges[i - 1].end : undefined;
|
||||
const nextStart = i + 1 < ranges.length ? ranges[i + 1].start : undefined;
|
||||
const range = ranges[i];
|
||||
res[res.length] = string.substring(text, offset, range.start);
|
||||
res[res.length] = executeReplacer(text, range, parseReplacer(replace, range.matches.length), prevEnd, nextStart);
|
||||
offset = range.end;
|
||||
}
|
||||
|
||||
res[res.length] = string.substring(text, offset, text.length);
|
||||
}
|
||||
else {
|
||||
const literal = parseReplacer(replace, groupN);
|
||||
|
||||
for (let i = 0; i < ranges.length; i++) {
|
||||
const prevEnd = i - 1 >= 0 ? ranges[i - 1].end : undefined;
|
||||
const nextStart = i + 1 < ranges.length ? ranges[i + 1].start : undefined;
|
||||
const range = ranges[i];
|
||||
res[res.length] = string.substring(text, offset, range.start);
|
||||
res[res.length] = executeReplacer(text, range, literal, prevEnd, nextStart);
|
||||
offset = range.end;
|
||||
}
|
||||
|
||||
res[res.length] = string.substring(text, offset, text.length);
|
||||
}
|
||||
return string.stringBuild(res);
|
||||
}
|
||||
|
||||
if (typeof replace === "string") {
|
||||
for (let i = 0; i < ranges.length; i++) {
|
||||
const range = ranges[i];
|
||||
res[res.length] = string.substring(text, offset, range.start);
|
||||
res[res.length] = replace;
|
||||
offset = range.end;
|
||||
}
|
||||
|
||||
res[res.length] = string.substring(text, offset, text.length);
|
||||
}
|
||||
else {
|
||||
for (let i = 0; i < ranges.length; i++) {
|
||||
const range = ranges[i];
|
||||
const args: any[] = range.matches;
|
||||
args[args.length] = range.start;
|
||||
args[args.length] = text;
|
||||
args[args.length] = range.groups;
|
||||
|
||||
res[res.length] = string.substring(text, offset, range.start);
|
||||
res[res.length] = func.invoke(replace, undefined, args);
|
||||
offset = range.end;
|
||||
}
|
||||
|
||||
res[res.length] = string.substring(text, offset, text.length);
|
||||
}
|
||||
|
||||
return string.stringBuild(res);
|
||||
}
|
||||
|
||||
export function applySplits(text: string, limit: number | undefined, next: (offset: number) => { start: number; end: number; } | undefined) {
|
||||
let lastEnd = 0;
|
||||
let lastEmpty = true;
|
||||
let offset = 0;
|
||||
|
||||
const res: string[] = [];
|
||||
|
||||
while (true) {
|
||||
if (limit != null && limit >= 0 && res.length >= limit) break;
|
||||
|
||||
const curr = next(offset);
|
||||
|
||||
if (curr == null) {
|
||||
if (!lastEmpty || !res.length) res[res.length] = string.substring(text, lastEnd, text.length);
|
||||
break;
|
||||
}
|
||||
|
||||
const { start, end } = curr;
|
||||
const empty = start === end;
|
||||
|
||||
if (offset > 0 || !empty) res[res.length] = string.substring(text, lastEnd, start);
|
||||
|
||||
lastEnd = end;
|
||||
offset = empty ? end + 1 : end;
|
||||
lastEmpty = empty;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
330
src/lib/libs/values/array.ts
Normal file
330
src/lib/libs/values/array.ts
Normal file
@@ -0,0 +1,330 @@
|
||||
import { Error } from "../values/errors.ts";
|
||||
import { func, object, string } from "../primordials.ts";
|
||||
import { String } from "../values/string.ts";
|
||||
import { limitI, symbols, wrapI } from "../utils.ts";
|
||||
|
||||
export const Array = (() => {
|
||||
class Array {
|
||||
public forEach(this: any[], cb: (val: any, i: number, self: this) => void, self?: any) {
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
if (i in this) func.invoke(cb, self, [this[i], i, this]);
|
||||
}
|
||||
}
|
||||
public join(this: any[], delim = ",") {
|
||||
delim = String(delim);
|
||||
const parts = [];
|
||||
if (delim) {
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
if (i) parts[parts.length] = delim;
|
||||
parts[parts.length] = (i in this) ? String(this[i]) : "";
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
parts[i] = (i in this) ? String(this[i]) : "";
|
||||
}
|
||||
}
|
||||
|
||||
return string.stringBuild(parts);
|
||||
}
|
||||
|
||||
public push(this: any[]) {
|
||||
const start = this.length;
|
||||
for (let i = arguments.length - 1; i >= 0; i--) {
|
||||
this[start + i] = arguments[i];
|
||||
}
|
||||
return arguments.length;
|
||||
}
|
||||
public pop(this: any[]) {
|
||||
if (this.length === 0) return undefined;
|
||||
else {
|
||||
const res = this[this.length - 1];
|
||||
this.length--;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
public unshift(this: any[]) {
|
||||
for (let i = this.length + arguments.length - 1; i >= arguments.length; i--) {
|
||||
this[i] = this[i - arguments.length];
|
||||
}
|
||||
for (let i = 0; i < arguments.length; i++) {
|
||||
this[i] = arguments[i];
|
||||
}
|
||||
return arguments.length;
|
||||
}
|
||||
public shift(this: any[]) {
|
||||
if (this.length === 0) return undefined;
|
||||
|
||||
const tmp = this[0];
|
||||
|
||||
for (let i = 1; i < this.length; i++) {
|
||||
this[i - 1] = this[i];
|
||||
}
|
||||
|
||||
this.length--;
|
||||
|
||||
return tmp;
|
||||
}
|
||||
|
||||
public concat(this: any[]) {
|
||||
const res: any[] = [];
|
||||
|
||||
function add(arr: any) {
|
||||
if (Array.isArray(arr) || arr != null && typeof arr === "object" && symbols.isConcatSpreadable in arr) {
|
||||
const start = res.length;
|
||||
res.length += arr.length;
|
||||
|
||||
for (let i = 0; i < res.length; i++) {
|
||||
if (i in arr) res[start + i] = arr[i];
|
||||
}
|
||||
}
|
||||
else res[res.length] = arr;
|
||||
}
|
||||
|
||||
add(this);
|
||||
|
||||
for (let i = 0; i < arguments.length; i++) {
|
||||
add(arguments[i]);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
public slice(this: any[], start = 0, end = this.length) {
|
||||
start = wrapI(start, this.length);
|
||||
end = wrapI(end, this.length);
|
||||
|
||||
if (end <= start) return [];
|
||||
|
||||
const res: any[] = [];
|
||||
res.length = end - start;
|
||||
|
||||
for (let i = 0; i < end - start; i++) {
|
||||
res[i] = this[start + i];
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
public splice(this: any[], start = 0, count = this.length - start) {
|
||||
const vals: any[] = []
|
||||
for (let i = 0; i < arguments.length - 2; i++) vals[i] = arguments[i + 2];
|
||||
|
||||
start = limitI(wrapI(start, this.length), this.length);
|
||||
count = limitI(wrapI(count, this.length), this.length - start);
|
||||
|
||||
const res: any[] = [];
|
||||
const change = vals.length - count;
|
||||
|
||||
for (let i = start; i < start + count; i++) {
|
||||
res[i - start] = this[i];
|
||||
}
|
||||
|
||||
if (change < 0) {
|
||||
for (let i = start - change; i < this.length; i++) {
|
||||
this[i + change] = this[i];
|
||||
}
|
||||
this.length = this.length + change;
|
||||
}
|
||||
else {
|
||||
for (let i = this.length - 1; i >= start - change; i--) {
|
||||
this[i + change] = this[i];
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < vals.length; i++) {
|
||||
this[i + start] = vals[i];
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public map(this: any[], cb: Function, self?: any) {
|
||||
const res = [];
|
||||
res.length = this.length;
|
||||
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
if (i in this) res[i] = func.invoke(cb, self, [this[i], i, this]);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
public filter(this: any[], cb: Function, self?: any) {
|
||||
const res = [];
|
||||
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
if (i in this && func.invoke(cb, self, [this[i], i, this])) res[res.length] = this[i];
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
public reduce(this: any[], cb: Function, initial: any) {
|
||||
let i = 0;
|
||||
if (arguments.length <= 1) initial = this[i++];
|
||||
|
||||
for (; i < this.length; i++) {
|
||||
initial = cb(initial, this[i], i, this);
|
||||
}
|
||||
|
||||
return initial;
|
||||
}
|
||||
public some(this: any[], cb: Function, self?: any) {
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
if (i in this && func.invoke(cb, self, [this[i], i, this])) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
public every(this: any[], cb: Function, self?: any) {
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
if (i in this && !func.invoke(cb, self, [this[i], i, this])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
public find(this: any[], cb: Function, self?: any) {
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
if (i in this && func.invoke(cb, self, [this[i], i, this])) return this[i];
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
public indexOf(this: any[], val: any, start = 0) {
|
||||
start |= 0;
|
||||
if (start < 0) start = 0;
|
||||
for (let i = start; i < this.length; i++) {
|
||||
if (i in this && this[i] === val) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
public lastIndexOf(this: any[], val: any, start = 0) {
|
||||
start |= 0;
|
||||
if (start < 0) start = 0;
|
||||
|
||||
for (let i = this.length - 1; i >= start; i--) {
|
||||
if (i in this && this[i] === val) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
public includes(this: any[], val: any) {
|
||||
for (let i = 0; i < this.length; i++) {
|
||||
if (i in this && this[i] === val) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public sort(this: any[], cb?: Function) {
|
||||
cb ||= (a: any, b: any) => {
|
||||
if (String(a) < String(b)) return -1;
|
||||
if (String(a) === String(b)) return 0;
|
||||
return 1;
|
||||
};
|
||||
|
||||
return object.sort(this, cb);
|
||||
}
|
||||
public reverse(this: any[]) {
|
||||
const mid = this.length >> 1;
|
||||
const end = this.length - 1;
|
||||
|
||||
for (let i = 0; i < mid; i++) {
|
||||
const tmp = this[i];
|
||||
this[i] = this[end - i];
|
||||
this[end - i] = tmp;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public [symbols.iterator](this: any[]) {
|
||||
let i = 0;
|
||||
let arr: any[] | undefined = func.invoke(Array.prototype.slice, this, []);
|
||||
|
||||
return {
|
||||
next() {
|
||||
if (arr == null) return { done: true, value: undefined };
|
||||
|
||||
if (i >= arr.length) {
|
||||
arr = undefined;
|
||||
return { done: true, value: undefined };
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const res = arr![i];
|
||||
|
||||
if (i in arr!) {
|
||||
i++;
|
||||
return { done: false, value: res };
|
||||
}
|
||||
else i++;
|
||||
}
|
||||
},
|
||||
[symbols.iterator]() { return this; }
|
||||
};
|
||||
}
|
||||
|
||||
public constructor (len: unknown) {
|
||||
if (arguments.length === 1 && typeof len === "number") {
|
||||
const res: any[] = [];
|
||||
res.length = len;
|
||||
return res as any;
|
||||
}
|
||||
else {
|
||||
const res: any[] = [];
|
||||
res.length = arguments.length;
|
||||
for (let i = 0; i < arguments.length; i++) {
|
||||
res[i] = arguments[i];
|
||||
}
|
||||
return res as any;
|
||||
}
|
||||
}
|
||||
|
||||
public static isArray(val: any): val is any[] {
|
||||
return object.isArray(val);
|
||||
}
|
||||
public static from(val: any, cb?: Function, self?: any): any[] {
|
||||
if (symbols.iterator in val) {
|
||||
const res = [];
|
||||
const it = val[symbols.iterator]();
|
||||
|
||||
if (cb) {
|
||||
for (let val = it.next(); !val.done; val = it.next()) {
|
||||
res[res.length] = func.invoke(cb, self, [val.value]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (let val = it.next(); !val.done; val = it.next()) {
|
||||
res[res.length] = val.value;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
else if ("length" in val) {
|
||||
const res = [];
|
||||
|
||||
if (cb) {
|
||||
for (let i = 0; i < val.length; i++) {
|
||||
if (i in val) res[i] = func.invoke(cb, self, [val[i]]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (let i = 0; i < val.length; i++) {
|
||||
if (i in val) res[i] = val[i];
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
else if (val == null) throw new Error("Illegal argument");
|
||||
else return [];
|
||||
}
|
||||
}
|
||||
|
||||
func.setCallable(Array, true);
|
||||
func.setConstructable(Array, true);
|
||||
|
||||
return Array as any as typeof Array & ((value?: unknown) => object);
|
||||
})();
|
||||
export type Array = InstanceType<typeof Array>;
|
||||
29
src/lib/libs/values/boolean.ts
Normal file
29
src/lib/libs/values/boolean.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
import { func } from "../primordials.ts";
|
||||
import { unwrapThis, valueKey } from "../utils.ts";
|
||||
|
||||
export const Boolean = (() => {
|
||||
class Boolean {
|
||||
[valueKey]!: boolean;
|
||||
|
||||
public toString() {
|
||||
return "" + unwrapThis(this, "boolean", Boolean, "Boolean.prototype.toString");
|
||||
}
|
||||
public valueOf() {
|
||||
return unwrapThis(this, "boolean", Boolean, "Boolean.prototype.valueOf");
|
||||
}
|
||||
|
||||
public constructor(value?: unknown) {
|
||||
if (func.invokeType(arguments, this) === "call") {
|
||||
if (arguments.length === 0) return false as any;
|
||||
else return !!value as any;
|
||||
}
|
||||
this[valueKey] = (Boolean as any)(value);
|
||||
}
|
||||
};
|
||||
|
||||
func.setCallable(Boolean, true);
|
||||
func.setConstructable(Boolean, true);
|
||||
|
||||
return Boolean as any as typeof Boolean & ((value?: unknown) => symbol);
|
||||
})();
|
||||
export type Boolean = InstanceType<typeof Boolean>;
|
||||
39
src/lib/libs/values/errors.ts
Normal file
39
src/lib/libs/values/errors.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import { func, object } from "../primordials.ts";
|
||||
import { String } from "./string.ts";
|
||||
|
||||
export class Error {
|
||||
public declare name: string;
|
||||
public declare message: string;
|
||||
|
||||
public toString() {
|
||||
let res = this.name || "Error";
|
||||
const msg = this.message;
|
||||
|
||||
if (msg) res += ": " + msg;
|
||||
return res;
|
||||
}
|
||||
|
||||
public constructor (msg = "") {
|
||||
if (func.invokeType(arguments, this) === "call") return new Error(msg);
|
||||
this.message = String(msg);
|
||||
}
|
||||
}
|
||||
object.defineField(Error.prototype, "name", { c: true, e: false, w: true, v: "Error" });
|
||||
object.defineField(Error.prototype, "message", { c: true, e: false, w: true, v: "" });
|
||||
func.setCallable(Error, true);
|
||||
func.setConstructable(Error, true);
|
||||
|
||||
export class SyntaxError extends Error { }
|
||||
object.defineField(SyntaxError.prototype, "name", { c: true, e: false, w: true, v: "SyntaxError" });
|
||||
func.setCallable(SyntaxError, true);
|
||||
func.setConstructable(SyntaxError, true);
|
||||
|
||||
export class TypeError extends Error { }
|
||||
object.defineField(TypeError.prototype, "name", { c: true, e: false, w: true, v: "TypeError" });
|
||||
func.setCallable(TypeError, true);
|
||||
func.setConstructable(TypeError, true);
|
||||
|
||||
export class RangeError extends Error { }
|
||||
object.defineField(RangeError.prototype, "name", { c: true, e: false, w: true, v: "RangeError" });
|
||||
func.setCallable(RangeError, true);
|
||||
func.setConstructable(RangeError, true);
|
||||
82
src/lib/libs/values/function.ts
Normal file
82
src/lib/libs/values/function.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
import { compile, func, string } from "../primordials.ts";
|
||||
import { String } from "./string.ts";
|
||||
|
||||
export const Function = (() => {
|
||||
class Function {
|
||||
declare public readonly name: string;
|
||||
declare public readonly length: number;
|
||||
|
||||
public toString(this: Function) {
|
||||
if (this.name !== "") return "function " + this.name + "(...) { ... }";
|
||||
else return "function (...) { ... }";
|
||||
}
|
||||
public valueOf() {
|
||||
return this;
|
||||
}
|
||||
|
||||
public apply(this: (...args: any) => any, self: any, args: any[]) {
|
||||
return func.invoke(this, self, args);
|
||||
}
|
||||
public call(this: (...args: any) => any, self: any) {
|
||||
const args: any[] = [];
|
||||
for (let i = arguments.length - 1; i >= 1; i--) args[i - 1] = arguments[i];
|
||||
return func.invoke(this, self, args);
|
||||
}
|
||||
public bind(this: (...args: any) => any, self: any) {
|
||||
const cb = this;
|
||||
if (arguments.length === 0) return function (this: any) { return func.invoke(cb, this, arguments as any) };
|
||||
if (arguments.length <= 1) return function () { return func.invoke(cb, self, arguments as any); }
|
||||
|
||||
const base: any[] = [];
|
||||
const offset = arguments.length - 1;
|
||||
base.length = offset;
|
||||
|
||||
for (let i = 0; i < offset; i++) base[i] = arguments[i + 1];
|
||||
|
||||
return function () {
|
||||
for (let i = 0; i < arguments.length; i++) {
|
||||
base[offset + i] = arguments[i];
|
||||
}
|
||||
|
||||
return func.invoke(cb, self, base);
|
||||
};
|
||||
}
|
||||
|
||||
public constructor () {
|
||||
const parts = ["(function anonymous("];
|
||||
for (let i = 0; i < arguments.length - 1; i++) {
|
||||
if (i > 0) parts[parts.length] = ",";
|
||||
parts[parts.length] = arguments[i];
|
||||
}
|
||||
parts[parts.length] = "){\n";
|
||||
parts[parts.length] = String(arguments[arguments.length - 1]);
|
||||
parts[parts.length] = "\n})";
|
||||
var res = compile(string.stringBuild(parts))();
|
||||
return res;
|
||||
}
|
||||
|
||||
public static compile(src = "", { globals = [], wrap = false }: { globals?: string[], wrap?: boolean } = {}) {
|
||||
const parts = [];
|
||||
|
||||
if (wrap) parts[parts.length] = "return (function() {\n";
|
||||
if (globals.length > 0) {
|
||||
parts[parts.length] = "let {";
|
||||
for (let i = 0; i < globals.length; i++) {
|
||||
if (i > 0) parts[parts.length] = ",";
|
||||
parts[parts.length] = globals[i];
|
||||
}
|
||||
parts[parts.length] = "} = arguments[0];";
|
||||
}
|
||||
parts[parts.length] = src;
|
||||
if (wrap) parts[parts.length] = "\n})(arguments[0])";
|
||||
|
||||
const res = compile(string.stringBuild(parts));
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
func.setCallable(Function, true);
|
||||
func.setConstructable(Function, true);
|
||||
|
||||
return Function as any as typeof Function & ((value?: unknown) => (...args: any[]) => any);
|
||||
})();
|
||||
77
src/lib/libs/values/number.ts
Normal file
77
src/lib/libs/values/number.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import { func, number, object } from "../primordials.ts";
|
||||
import { unwrapThis, valueKey } from "../utils.ts";
|
||||
|
||||
export const Number = (() => {
|
||||
class Number {
|
||||
[valueKey]!: number;
|
||||
|
||||
public toString() {
|
||||
return "" + unwrapThis(this, "number", Number, "Number.prototype.toString");
|
||||
}
|
||||
public valueOf() {
|
||||
return unwrapThis(this, "number", Number, "Number.prototype.toString");
|
||||
}
|
||||
|
||||
public constructor (value?: unknown) {
|
||||
if (func.invokeType(arguments, this) === "call") {
|
||||
if (arguments.length === 0) return 0 as any;
|
||||
else return +(value as any) as any;
|
||||
}
|
||||
this[valueKey] = (Number as any)(value);
|
||||
}
|
||||
|
||||
public static isFinite(value: number) {
|
||||
value = unwrapThis(value, "number", Number, "Number.isFinite", "value");
|
||||
if (value === undefined || value !== value) return false;
|
||||
if (value === number.Infinity || value === -number.Infinity) return false;
|
||||
return true;
|
||||
}
|
||||
public static isInteger(value: number) {
|
||||
value = unwrapThis(value, "number", Number, "Number.isInteger", "value");
|
||||
if (value === undefined) return false;
|
||||
return number.parseInt(value) === value;
|
||||
}
|
||||
public static isNaN(value: number) {
|
||||
return number.isNaN(value);
|
||||
}
|
||||
public static isSafeInteger(value: number) {
|
||||
value = unwrapThis(value, "number", Number, "Number.isSafeInteger", "value");
|
||||
if (value === undefined || number.parseInt(value) !== value) return false;
|
||||
return value >= -9007199254740991 && value <= 9007199254740991;
|
||||
}
|
||||
public static parseFloat(value: unknown) {
|
||||
if (typeof value === "number") return value;
|
||||
else return number.parseFloat(value + "");
|
||||
}
|
||||
public static parseInt(value: unknown, radix = 10) {
|
||||
radix = +radix;
|
||||
if (number.isNaN(radix)) radix = 10;
|
||||
|
||||
if (typeof value === "number") return number.parseInt(value, radix);
|
||||
else return number.parseInt(value + "", radix);
|
||||
}
|
||||
|
||||
declare public static readonly EPSILON: number;
|
||||
declare public static readonly MIN_SAFE_INTEGER: number;
|
||||
declare public static readonly MAX_SAFE_INTEGER: number;
|
||||
declare public static readonly POSITIVE_INFINITY: number;
|
||||
declare public static readonly NEGATIVE_INFINITY: number;
|
||||
declare public static readonly NaN: number;
|
||||
declare public static readonly MAX_VALUE: number;
|
||||
declare public static readonly MIN_VALUE: number;
|
||||
}
|
||||
|
||||
object.defineField(Number, "EPSILON", { c: false, e: false, w: false, v: 2.220446049250313e-16 });
|
||||
object.defineField(Number, "MIN_SAFE_INTEGER", { c: false, e: false, w: false, v: -9007199254740991 });
|
||||
object.defineField(Number, "MAX_SAFE_INTEGER", { c: false, e: false, w: false, v: 9007199254740991 });
|
||||
object.defineField(Number, "POSITIVE_INFINITY", { c: false, e: false, w: false, v: +number.Infinity });
|
||||
object.defineField(Number, "NEGATIVE_INFINITY", { c: false, e: false, w: false, v: -number.Infinity });
|
||||
object.defineField(Number, "NaN", { c: false, e: false, w: false, v: number.NaN });
|
||||
object.defineField(Number, "MAX_VALUE", { c: false, e: false, w: false, v: 1.7976931348623157e+308 });
|
||||
object.defineField(Number, "MIN_VALUE", { c: false, e: false, w: false, v: 5e-324 });
|
||||
func.setCallable(Number, true);
|
||||
func.setConstructable(Number, true);
|
||||
|
||||
return Number as any as typeof Number & ((value?: unknown) => number);
|
||||
})();
|
||||
export type Number = InstanceType<typeof Number>;
|
||||
208
src/lib/libs/values/object.ts
Normal file
208
src/lib/libs/values/object.ts
Normal file
@@ -0,0 +1,208 @@
|
||||
import { Boolean } from "./boolean.ts";
|
||||
import { TypeError } from "./errors.ts";
|
||||
import { Number } from "./number.ts";
|
||||
import { func, object } from "../primordials.ts";
|
||||
import { String } from "./string.ts";
|
||||
import { symbols, valueKey } from "../utils.ts";
|
||||
import { Symbol } from "../values/symbol.ts";
|
||||
|
||||
export const Object = (() => {
|
||||
class Object {
|
||||
public toString(this: unknown) {
|
||||
if (this === undefined) return "[object Undefined]";
|
||||
else if (this === null) return "[object Null]";
|
||||
else if (typeof this === "object") {
|
||||
if (symbols.toStringTag in this) return "[object " + (this as any)[symbols.toStringTag] + "]";
|
||||
else if (object.isArray(this)) return "[object Array]";
|
||||
else return "[object Object]";
|
||||
}
|
||||
else if (typeof this === "number" || this instanceof Number) return "[object Number]";
|
||||
else if (typeof this === "symbol" || this instanceof Symbol) return "[object Symbol]";
|
||||
else if (typeof this === "string" || this instanceof String) return "[object String]";
|
||||
else if (typeof this === "boolean" || this instanceof Boolean) return "[object Boolean]";
|
||||
else if (typeof this === "function") return "[object Function]";
|
||||
}
|
||||
public valueOf() {
|
||||
return this;
|
||||
}
|
||||
public hasOwnProperty(key: string) {
|
||||
return object.getOwnMember(this, key) != null;
|
||||
}
|
||||
|
||||
public get __proto__() {
|
||||
return object.getPrototype(this);
|
||||
}
|
||||
public set __proto__(val) {
|
||||
object.setPrototype(this, val);
|
||||
}
|
||||
|
||||
public constructor (value?: unknown) {
|
||||
if (typeof value === 'object' && value !== null) return value as any;
|
||||
if (typeof value === 'string') return new String(value) as any;
|
||||
if (typeof value === 'number') return new Number(value) as any;
|
||||
if (typeof value === 'boolean') return new Boolean(value) as any;
|
||||
if (typeof value === 'symbol') {
|
||||
const res: Symbol = {} as any;
|
||||
object.setPrototype(res, Symbol.prototype);
|
||||
res[valueKey] = value;
|
||||
return res as any;
|
||||
}
|
||||
|
||||
return {} as any;
|
||||
}
|
||||
|
||||
public static getOwnPropertyDescriptor(obj: object, key: any) {
|
||||
return object.getOwnMember(obj, key);
|
||||
}
|
||||
public static getOwnPropertyNames(obj: object): string[] {
|
||||
return object.getOwnMembers(obj, false);
|
||||
}
|
||||
public static getOwnPropertySymbols(obj: object): symbol[] {
|
||||
return object.getOwnSymbolMembers(obj, false);
|
||||
}
|
||||
|
||||
public static defineProperty(obj: object, key: string | symbol, desc: PropertyDescriptor) {
|
||||
if (obj === null || typeof obj !== "function" && typeof obj !== "object") {
|
||||
throw new TypeError("Object.defineProperty called on non-object");
|
||||
}
|
||||
if (desc === null || typeof desc !== "function" && typeof desc !== "object") {
|
||||
throw new TypeError("Property description must be an object: " + desc);
|
||||
}
|
||||
const res: any = {};
|
||||
|
||||
if ("get" in desc || "set" in desc) {
|
||||
if ("get" in desc) {
|
||||
const get = desc.get;
|
||||
if (get !== undefined && typeof get !== "function") throw new TypeError("Getter must be a function: " + get);
|
||||
res.g = get;
|
||||
}
|
||||
if ("set" in desc) {
|
||||
const set = desc.set;
|
||||
if (set !== undefined && typeof set !== "function") throw new TypeError("Setter must be a function: " + set);
|
||||
res.s = set;
|
||||
}
|
||||
if ("enumerable" in desc) res.e = !!desc.enumerable;
|
||||
if ("configurable" in desc) res.e = !!desc.configurable;
|
||||
|
||||
if (!object.defineProperty(obj, key, res)) throw new TypeError("Cannot redefine property: " + String(key));
|
||||
}
|
||||
else {
|
||||
if ("enumerable" in desc) res.e = !!desc.enumerable;
|
||||
if ("configurable" in desc) res.e = !!desc.configurable;
|
||||
if ("writable" in desc) res.w = !!desc.writable;
|
||||
if ("value" in desc) res.v = desc.value;
|
||||
|
||||
if (!object.defineField(obj, key, res)) throw new TypeError("Cannot redefine property: " + String(key));
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
public static defineProperties(obj: object, desc: PropertyDescriptorMap) {
|
||||
const keys = object.getOwnMembers(desc, true) as ((keyof typeof obj) & string)[];
|
||||
const symbols = object.getOwnSymbolMembers(desc, true) as ((keyof typeof obj) & symbol)[];
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
Object.defineProperty(obj, keys[i], desc[keys[i]]);
|
||||
}
|
||||
for (let i = 0; i < symbols.length; i++) {
|
||||
Object.defineProperty(obj, symbols[i], desc[symbols[i]]);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
public static create(proto: object, desc?: PropertyDescriptorMap) {
|
||||
let res = object.setPrototype({}, proto);
|
||||
if (desc != null) this.defineProperties(res, desc);
|
||||
|
||||
return res;
|
||||
}
|
||||
public static assign(target: any) {
|
||||
for (let i = 1; i < arguments.length; i++) {
|
||||
const obj = arguments[i];
|
||||
const keys = object.getOwnMembers(obj, false);
|
||||
const symbols = object.getOwnSymbolMembers(obj, false);
|
||||
|
||||
for (let j = 0; j < keys.length; j++) {
|
||||
target[keys[j]] = obj[keys[j]];
|
||||
}
|
||||
for (let j = 0; j < symbols.length; j++) {
|
||||
target[symbols[j]] = obj[symbols[j]];
|
||||
}
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
public static setPrototypeOf(obj: object, proto: object | null) {
|
||||
object.setPrototype(obj, proto!);
|
||||
}
|
||||
public static getPrototypeOf(obj: object) {
|
||||
return object.getPrototype(obj) || null;
|
||||
}
|
||||
|
||||
public static keys(obj: any) {
|
||||
const res: any[] = [];
|
||||
const keys = object.getOwnMembers(obj, true);
|
||||
const symbols = object.getOwnSymbolMembers(obj, true);
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
res[res.length] = keys[i];
|
||||
}
|
||||
for (let i = 0; i < symbols.length; i++) {
|
||||
res[res.length] = symbols[i];
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
public static values(obj: any) {
|
||||
const res: any[] = [];
|
||||
const keys = object.getOwnMembers(obj, true);
|
||||
const symbols = object.getOwnSymbolMembers(obj, true);
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
res[res.length] = obj[keys[i]];
|
||||
}
|
||||
for (let i = 0; i < symbols.length; i++) {
|
||||
res[res.length] = obj[symbols[i]];
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
public static entries(obj: any) {
|
||||
const res: [any, any][] = [];
|
||||
const keys = object.getOwnMembers(obj, true);
|
||||
const symbols = object.getOwnSymbolMembers(obj, true);
|
||||
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
res[res.length] = [keys[i], obj[keys[i]]];
|
||||
}
|
||||
for (let i = 0; i < symbols.length; i++) {
|
||||
res[res.length] = [symbols[i], obj[symbols[i]]];
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public static preventExtensions(obj: object) {
|
||||
object.preventExt(obj);
|
||||
return obj;
|
||||
}
|
||||
public static seal(obj: object) {
|
||||
object.seal(obj);
|
||||
return obj;
|
||||
}
|
||||
public static freeze(obj: object) {
|
||||
object.freeze(obj);
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
object.defineProperty(Object.prototype, "__proto__", { e: false });
|
||||
object.setPrototype(Object.prototype, undefined);
|
||||
|
||||
func.setCallable(Object, true);
|
||||
func.setConstructable(Object, true);
|
||||
|
||||
return Object as any as typeof Object & ((value?: unknown) => object);
|
||||
})();
|
||||
export type Object = InstanceType<typeof Object>;
|
||||
138
src/lib/libs/values/regex.ts
Normal file
138
src/lib/libs/values/regex.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import { func, regex, symbol } from "../primordials.ts";
|
||||
import { String } from "./string.ts";
|
||||
import { type ReplaceRange } from "../utils.ts";
|
||||
import { applyReplaces } from "../utils.ts";
|
||||
import { applySplits } from "../utils.ts";
|
||||
import { symbols } from "../utils.ts";
|
||||
|
||||
const regexKey: unique symbol = symbol.makeSymbol("RegExp.impl") as any;
|
||||
|
||||
export class RegExp {
|
||||
private [regexKey]!: InstanceType<typeof regex>;
|
||||
|
||||
public readonly source!: string;
|
||||
public readonly flags!: string;
|
||||
public lastIndex = 0;
|
||||
|
||||
public readonly indices!: boolean;
|
||||
public readonly global!: boolean;
|
||||
public readonly ignoreCase!: boolean;
|
||||
public readonly multiline!: boolean;
|
||||
public readonly dotall!: boolean;
|
||||
public readonly unicode!: boolean;
|
||||
public readonly unicodeSets!: boolean;
|
||||
public readonly sticky!: boolean;
|
||||
|
||||
public constructor(source: any, flags = "") {
|
||||
if (func.invokeType(arguments, this) === "call") return new RegExp(source, flags);
|
||||
|
||||
source = this.source = String(typeof source === "object" && "source" in source ? source.source : source);
|
||||
flags = String(flags);
|
||||
|
||||
let indices = false;
|
||||
let global = false;
|
||||
let ignoreCase = false;
|
||||
let multiline = false;
|
||||
let dotall = false;
|
||||
let unicode = false;
|
||||
let unicodeSets = false;
|
||||
let sticky = false;
|
||||
|
||||
for (let i = 0; i < flags.length; i++) {
|
||||
switch (flags[i]) {
|
||||
case "d": indices = true; break;
|
||||
case "g": global = true; break;
|
||||
case "i": ignoreCase = true; break;
|
||||
case "m": multiline = true; break;
|
||||
case "s": dotall = true; break;
|
||||
case "u": unicode = true; break;
|
||||
case "v": unicodeSets = true; break;
|
||||
case "y": sticky = true; break;
|
||||
}
|
||||
}
|
||||
|
||||
flags = "";
|
||||
if (indices) flags += "d";
|
||||
if (global) flags += "g";
|
||||
if (ignoreCase) flags += "i";
|
||||
if (multiline) flags += "m";
|
||||
if (dotall) flags += "s";
|
||||
if (unicode) flags += "u";
|
||||
if (unicodeSets) flags += "v";
|
||||
if (sticky) flags += "y";
|
||||
this.flags = flags;
|
||||
this.indices = indices;
|
||||
this.global = global;
|
||||
this.ignoreCase = ignoreCase;
|
||||
this.multiline = multiline;
|
||||
this.dotall = dotall;
|
||||
this.unicode = unicode;
|
||||
this.unicodeSets = unicodeSets;
|
||||
this.sticky = sticky;
|
||||
|
||||
this[regexKey] = new regex(source, multiline, ignoreCase, dotall, unicode, unicodeSets);
|
||||
}
|
||||
|
||||
public exec(target: string) {
|
||||
const useLast = this.global || this.sticky;
|
||||
const start = useLast ? this.lastIndex : 0;
|
||||
|
||||
const match = this[regexKey].exec(target, start, this.indices);
|
||||
if (match != null && !(this.sticky && match.matches.index !== start)) {
|
||||
if (useLast) this.lastIndex = match.end;
|
||||
return match.matches;
|
||||
}
|
||||
|
||||
if (useLast) this.lastIndex = 0;
|
||||
return null;
|
||||
}
|
||||
public test(target: string) {
|
||||
return this.exec(target) != null;
|
||||
}
|
||||
|
||||
public [symbols.split](target: string, limit?: number) {
|
||||
return applySplits(target, limit, offset => {
|
||||
const val = this[regexKey].exec(target, offset, false);
|
||||
if (val == null) return undefined;
|
||||
|
||||
return { start: val.matches.index!, end: val.end };
|
||||
});
|
||||
}
|
||||
public [symbols.replace](target: string, replacer: any) {
|
||||
const matches: ReplaceRange[] = [];
|
||||
const regex = this[regexKey];
|
||||
|
||||
if (this.global) {
|
||||
let offset = 0;
|
||||
|
||||
while (true) {
|
||||
const match = regex.exec(target, offset, false);
|
||||
if (match == null) break;
|
||||
|
||||
const start = match.matches.index;
|
||||
const end = match.end;
|
||||
const arr: string[] = [];
|
||||
for (let i = 0; i < match.matches.length; i++) {
|
||||
arr[i] = match.matches[i];
|
||||
}
|
||||
|
||||
matches[matches.length] = { start: match.matches.index!, end: match.end, matches: arr };
|
||||
|
||||
if (start === end) offset = start + 1;
|
||||
else offset = end;
|
||||
}
|
||||
|
||||
return applyReplaces(target, matches, replacer, regex.groupCount() + 1);
|
||||
}
|
||||
else {
|
||||
const match = this.exec(target);
|
||||
if (match != null) matches[0] = {
|
||||
start: match.index!,
|
||||
end: match.index! + match[0].length,
|
||||
matches: match,
|
||||
}
|
||||
}
|
||||
|
||||
return applyReplaces(target, matches, replacer, regex.groupCount() + 1);
|
||||
}
|
||||
}
|
||||
276
src/lib/libs/values/string.ts
Normal file
276
src/lib/libs/values/string.ts
Normal file
@@ -0,0 +1,276 @@
|
||||
import { TypeError } from "./errors.ts";
|
||||
import { func, number, regex, string } from "../primordials.ts";
|
||||
import { RegExp } from "./regex.ts";
|
||||
import { applyReplaces, applySplits, limitI, type ReplaceRange, symbols, unwrapThis, valueKey, wrapI } from "../utils.ts";
|
||||
|
||||
const trimStartRegex = new regex("^\\s+", false, false, false, false, false);
|
||||
const trimEndRegex = new regex("\\s+$", false, false, false, false, false);
|
||||
|
||||
export const String = (() => {
|
||||
class String {
|
||||
[valueKey]!: string;
|
||||
|
||||
public at(index: number) {
|
||||
throw "Not implemented :/";
|
||||
return unwrapThis(this, "string", String, "String.prototype.at")[index];
|
||||
}
|
||||
public toString() {
|
||||
return unwrapThis(this, "string", String, "String.prototype.toString");
|
||||
}
|
||||
public valueOf() {
|
||||
return unwrapThis(this, "string", String, "String.prototype.valueOf");
|
||||
}
|
||||
|
||||
public includes(search: string, offset = 0) {
|
||||
const self = unwrapThis(this, "string", String, "String.prototype.indexOf");
|
||||
return string.indexOf(self, (String as any)(search), +offset, false) >= 0;
|
||||
}
|
||||
public startsWith(search: string) {
|
||||
const self = unwrapThis(this, "string", String, "String.prototype.indexOf");
|
||||
if (self.length < search.length) return false;
|
||||
return string.substring(self, 0, search.length) === search;
|
||||
}
|
||||
public endsWith(search: string) {
|
||||
const self = unwrapThis(this, "string", String, "String.prototype.indexOf");
|
||||
if (self.length < search.length) return false;
|
||||
return string.substring(self, self.length - search.length, self.length) === search;
|
||||
}
|
||||
|
||||
public indexOf(search: string, offset = 0) {
|
||||
const self = unwrapThis(this, "string", String, "String.prototype.indexOf");
|
||||
offset = +offset;
|
||||
return string.indexOf(self, search, offset, false);
|
||||
}
|
||||
public lastIndexOf(search: string, offset = 0) {
|
||||
const self = unwrapThis(this, "string", String, "String.prototype.lastIndexOf");
|
||||
offset = +offset;
|
||||
return string.indexOf(self, search, offset, true);
|
||||
}
|
||||
|
||||
public trim() {
|
||||
const self = unwrapThis(this, "string", String, "String.prototype.trim");
|
||||
const start = trimStartRegex.exec(self, 0, false);
|
||||
const end = trimEndRegex.exec(self, 0, false);
|
||||
|
||||
const startI = start == null ? 0 : start.end;
|
||||
const endI = end == null ? self.length : end.matches.index!;
|
||||
|
||||
return string.substring(self, startI, endI);
|
||||
}
|
||||
public trimStart() {
|
||||
const self = unwrapThis(this, "string", String, "String.prototype.trim");
|
||||
const start = trimStartRegex.exec(self, 0, false);
|
||||
const startI = start == null ? 0 : start.end;
|
||||
|
||||
return string.substring(self, startI, self.length);
|
||||
}
|
||||
public trimEnd() {
|
||||
const self = unwrapThis(this, "string", String, "String.prototype.trim");
|
||||
const end = trimEndRegex.exec(self, 0, false);
|
||||
const endI = end == null ? self.length : end.matches.index!;
|
||||
|
||||
return string.substring(self, 0, endI);
|
||||
}
|
||||
|
||||
public trimLeft() {
|
||||
return func.invoke(String.prototype.trimStart, this, []);
|
||||
}
|
||||
public trimRight() {
|
||||
return func.invoke(String.prototype.trimEnd, this, []);
|
||||
}
|
||||
|
||||
public charAt(i: number) {
|
||||
const self = unwrapThis(this, "string", String, "String.prototype.charAt");
|
||||
return self[i];
|
||||
}
|
||||
public charCodeAt(i: number) {
|
||||
const self = unwrapThis(this, "string", String, "String.prototype.charCodeAt");
|
||||
return self[i] ? string.toCharCode(self[i]) : number.NaN;
|
||||
}
|
||||
public codePointAt(i: number) {
|
||||
const self = unwrapThis(this, "string", String, "String.prototype.charCodeAt");
|
||||
return i >= 0 && i < self.length ? string.toCodePoint(self, i) : number.NaN;
|
||||
}
|
||||
|
||||
public split(val?: any, limit?: number) {
|
||||
const self = unwrapThis(this, "string", String, "String.prototype.split");
|
||||
if (val === undefined) return [self];
|
||||
if (val !== null && typeof val === "object" && symbols.split in val) {
|
||||
return val[symbols.split](self, limit);
|
||||
}
|
||||
|
||||
val = (String as any)(val);
|
||||
|
||||
return applySplits(self, limit, offset => {
|
||||
const start = string.indexOf(self, val, offset, false);
|
||||
if (start < 0) return undefined;
|
||||
else return { start, end: start + val.length };
|
||||
});
|
||||
}
|
||||
public replace(val: any, replacer: any) {
|
||||
const self = unwrapThis(this, "string", String, "String.prototype.replace");
|
||||
if (val !== null && typeof val === "object" && symbols.replace in val) {
|
||||
return val[symbols.replace](self, replacer);
|
||||
}
|
||||
else val = (String as any)(val);
|
||||
|
||||
const i = string.indexOf(self, val, 0);
|
||||
return applyReplaces(self, [{ start: i, end: i + val.length, matches: [val] }], replacer, false);
|
||||
}
|
||||
public replaceAll(val: any, replacer: any) {
|
||||
const self = unwrapThis(this, "string", String, "String.prototype.replaceAll");
|
||||
if (val !== null && typeof val === "object" && symbols.replace in val) {
|
||||
if (val instanceof RegExp && !val.global) throw new TypeError("replaceAll must be called with a global RegExp");
|
||||
return val[symbols.replace](self, replacer);
|
||||
}
|
||||
else val = (String as any)(val);
|
||||
|
||||
let offset = 0;
|
||||
const matches: ReplaceRange[] = [];
|
||||
const add = val.length === 0 ? 1 : val.length;
|
||||
|
||||
while (true) {
|
||||
const i = string.indexOf(self, val, offset);
|
||||
if (i < 0) break;
|
||||
|
||||
matches[matches.length] = { start: i, end: i + val.length, matches: [val] };
|
||||
if (val.length === 0)
|
||||
offset = i + add;
|
||||
}
|
||||
|
||||
return applyReplaces(self, matches, replacer, false);
|
||||
}
|
||||
public repeat(n: number) {
|
||||
const self = unwrapThis(this, "string", String, "String.prototype.replaceAll");
|
||||
const res: string[] = [];
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
res[i] = self;
|
||||
}
|
||||
|
||||
return string.stringBuild(res);
|
||||
}
|
||||
|
||||
public slice(start = 0, end?: number) {
|
||||
const self = unwrapThis(this, "string", String, "String.prototype.slice");
|
||||
if (end === undefined) end = self.length;
|
||||
start = limitI(wrapI(start, self.length), self.length);
|
||||
end = limitI(wrapI(end, self.length), self.length);
|
||||
|
||||
if (end <= start) return "";
|
||||
return string.substring(self, start, end);
|
||||
}
|
||||
public substring(start = 0, end?: number) {
|
||||
const self = unwrapThis(this, "string", String, "String.prototype.substring");
|
||||
if (end === undefined) end = self.length;
|
||||
start = limitI(start, self.length);
|
||||
end = limitI(end, self.length);
|
||||
|
||||
if (end <= start) return "";
|
||||
return string.substring(self, start, end);
|
||||
}
|
||||
public substr(start = 0, count?: number) {
|
||||
const self = unwrapThis(this, "string", String, "String.prototype.substr");
|
||||
count = self.length - start;
|
||||
start = limitI(start, self.length);
|
||||
count = limitI(count, self.length - start);
|
||||
|
||||
if (count <= 0) return "";
|
||||
return string.substring(self, start, count + start);
|
||||
}
|
||||
public concat() {
|
||||
const self = unwrapThis(this, "string", String, "String.prototype.concat");
|
||||
const parts = [self];
|
||||
for (let i = 0; i < arguments.length; i++) {
|
||||
parts[i + 1] = (String as any)(arguments[i]);
|
||||
}
|
||||
|
||||
return string.stringBuild(parts);
|
||||
}
|
||||
|
||||
public toLowerCase() {
|
||||
const self = unwrapThis(this, "string", String, "String.prototype.toLowerCase");
|
||||
return string.lower(self);
|
||||
}
|
||||
public toUpperCase() {
|
||||
const self = unwrapThis(this, "string", String, "String.prototype.toLowerCase");
|
||||
return string.upper(self);
|
||||
}
|
||||
|
||||
public match(regex: RegExp) {
|
||||
const self = unwrapThis(this, "string", String, "String.prototype.match");
|
||||
if (!(regex instanceof RegExp)) throw new TypeError("Regexp expected for String.prototype.match");
|
||||
|
||||
if (regex.global) {
|
||||
let matches: string[] | null = null;
|
||||
|
||||
while (true) {
|
||||
const match = regex.exec(self);
|
||||
if (match == null) break;
|
||||
|
||||
matches ||= [];
|
||||
matches[matches.length] = match[0];
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
else return regex.exec(self);
|
||||
}
|
||||
|
||||
public [symbols.iterator]() {
|
||||
var i = 0;
|
||||
var arr: string | undefined = unwrapThis(this, "string", String, "String.prototype[Symbol.iterator]");
|
||||
|
||||
return {
|
||||
next () {
|
||||
if (arr == null) return { done: true, value: undefined };
|
||||
if (i > arr.length) {
|
||||
arr = undefined;
|
||||
return { done: true, value: undefined };
|
||||
}
|
||||
else {
|
||||
var val = arr[i++];
|
||||
if (i >= arr.length) arr = undefined;
|
||||
return { done: false, value: val };
|
||||
}
|
||||
},
|
||||
[symbols.iterator]() { return this; }
|
||||
};
|
||||
}
|
||||
|
||||
public constructor (value?: unknown) {
|
||||
if (func.invokeType(arguments, this) === "call") {
|
||||
if (arguments.length === 0) return "" as any;
|
||||
else if (typeof value === "symbol") return value.toString() as any;
|
||||
else return (value as any) + "" as any;
|
||||
}
|
||||
this[valueKey] = (String as any)(value);
|
||||
}
|
||||
|
||||
public static fromCharCode() {
|
||||
const res: string[] = [];
|
||||
res[arguments.length] = "";
|
||||
|
||||
for (let i = 0; i < arguments.length; i++) {
|
||||
res[i] = string.fromCharCode(+arguments[i]);
|
||||
}
|
||||
|
||||
return string.stringBuild(res);
|
||||
}
|
||||
public static fromCodePoint() {
|
||||
const res: string[] = [];
|
||||
res[arguments.length] = "";
|
||||
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
res[i] = string.fromCodePoint(+arguments[i]);
|
||||
}
|
||||
return string.stringBuild(res);
|
||||
}
|
||||
}
|
||||
|
||||
func.setCallable(String, true);
|
||||
func.setConstructable(String, true);
|
||||
|
||||
return String as any as typeof String & ((value?: unknown) => string);
|
||||
})();
|
||||
export type String = InstanceType<typeof String>;
|
||||
53
src/lib/libs/values/symbol.ts
Normal file
53
src/lib/libs/values/symbol.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { func, object, symbol } from "../primordials.ts";
|
||||
import { symbols, unwrapThis, valueKey } from "../utils.ts";
|
||||
|
||||
export const Symbol = (() => {
|
||||
class Symbol {
|
||||
[valueKey]!: symbol;
|
||||
|
||||
get description() {
|
||||
return symbol.getSymbolDescription(unwrapThis(this, "symbol", Symbol, "Symbol.prototype.description"));
|
||||
}
|
||||
|
||||
public toString() {
|
||||
return "Symbol(" + unwrapThis(this, "symbol", Symbol, "Symbol.prototype.toString").description + ")";
|
||||
}
|
||||
public valueOf() {
|
||||
return unwrapThis(this, "symbol", Symbol, "Symbol.prototype.valueOf");
|
||||
}
|
||||
|
||||
public constructor(name = "") {
|
||||
return symbol.makeSymbol(name + "") as any;
|
||||
}
|
||||
|
||||
public static for(name: string) {
|
||||
return symbol.getSymbol(name + "");
|
||||
}
|
||||
|
||||
declare public static readonly asyncIterator: unique symbol;
|
||||
declare public static readonly iterator: unique symbol;
|
||||
declare public static readonly match: unique symbol;
|
||||
declare public static readonly matchAll: unique symbol;
|
||||
declare public static readonly replace: unique symbol;
|
||||
declare public static readonly search: unique symbol;
|
||||
declare public static readonly split: unique symbol;
|
||||
declare public static readonly toStringTag: unique symbol;
|
||||
};
|
||||
|
||||
func.setCallable(Symbol, true);
|
||||
func.setConstructable(Symbol, false);
|
||||
|
||||
object.defineField(Symbol, "asyncIterator", { c: false, e: false, w: false, v: symbols.asyncIterator });
|
||||
object.defineField(Symbol, "iterator", { c: false, e: false, w: false, v: symbols.iterator });
|
||||
object.defineField(Symbol, "match", { c: false, e: false, w: false, v: symbols.match });
|
||||
object.defineField(Symbol, "matchAll", { c: false, e: false, w: false, v: symbols.matchAll });
|
||||
object.defineField(Symbol, "replace", { c: false, e: false, w: false, v: symbols.replace });
|
||||
object.defineField(Symbol, "search", { c: false, e: false, w: false, v: symbols.search });
|
||||
object.defineField(Symbol, "split", { c: false, e: false, w: false, v: symbols.split });
|
||||
object.defineField(Symbol, "toStringTag", { c: false, e: false, w: false, v: symbols.toStringTag });
|
||||
object.defineField(Symbol, "isConcatSpreadable", { c: false, e: false, w: false, v: symbols.isConcatSpreadable });
|
||||
|
||||
return Symbol as any as typeof Symbol & ((name?: string) => ReturnType<SymbolConstructor>);
|
||||
})();
|
||||
export type Symbol = InstanceType<typeof Symbol>;
|
||||
|
||||
7
src/lib/transpiler/_entry.ts
Normal file
7
src/lib/transpiler/_entry.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
// import coffeescript from "./coffeescript.ts";
|
||||
// import babel from "./babel.ts";
|
||||
|
||||
// register(v => coffeescript(babel(v)));
|
||||
import typescript from "./typescript.ts";
|
||||
|
||||
register(typescript);
|
||||
27
src/lib/transpiler/babel.ts
Normal file
27
src/lib/transpiler/babel.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { SourceMap } from "./map.ts";
|
||||
import { transform, availablePresets } from "@babel/standalone";
|
||||
|
||||
export default function babel(next: Compiler): Compiler {
|
||||
print("Loaded babel!");
|
||||
|
||||
return (filename, code, prevMap) => {
|
||||
const res = transform(code, {
|
||||
filename,
|
||||
sourceMaps: true,
|
||||
presets: [availablePresets.env],
|
||||
});
|
||||
|
||||
print(res.map!.mappings);
|
||||
|
||||
const map = SourceMap.parse({
|
||||
file: "babel-internal://" + filename,
|
||||
mappings: res.map!.mappings,
|
||||
sources: [filename],
|
||||
});
|
||||
|
||||
const compiled = next("babel-internal://" + filename, res.code!, SourceMap.chain(map, prevMap));
|
||||
registerSource(filename, code);
|
||||
return compiled;
|
||||
};
|
||||
}
|
||||
|
||||
28
src/lib/transpiler/coffeescript.ts
Normal file
28
src/lib/transpiler/coffeescript.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { compile } from "coffeescript";
|
||||
import { SourceMap } from "./map.ts";
|
||||
|
||||
export default function coffee(next: Compiler): Compiler {
|
||||
print("Loaded coffeescript!");
|
||||
|
||||
return (filename, code, prevMap) => {
|
||||
const {
|
||||
js: result,
|
||||
v3SourceMap: rawMap,
|
||||
} = compile(code, {
|
||||
filename,
|
||||
sourceMap: true,
|
||||
bare: true,
|
||||
});
|
||||
|
||||
const map = SourceMap.parse({
|
||||
file: "coffee-internal://" + filename,
|
||||
mappings: JSON.parse(rawMap).mappings,
|
||||
sources: [filename],
|
||||
});
|
||||
|
||||
const compiled = next("coffee-internal://" + filename, result, SourceMap.chain(map, prevMap));
|
||||
registerSource(filename, code);
|
||||
return compiled;
|
||||
};
|
||||
}
|
||||
|
||||
190
src/lib/transpiler/map.ts
Normal file
190
src/lib/transpiler/map.ts
Normal file
@@ -0,0 +1,190 @@
|
||||
const map: number[] = [];
|
||||
let j = 0;
|
||||
|
||||
for (let i = 65; i <= 90; i++) map[i] = j++;
|
||||
for (let i = 97; i <= 122; i++) map[i] = j++;
|
||||
map[43] = j++;
|
||||
map[47] = j++;
|
||||
|
||||
export function decodeVLQ(val: string): number[][][] {
|
||||
const lines: number[][][] = [];
|
||||
|
||||
const fileParts = val.split(";", -1);
|
||||
|
||||
for (let i = 0; i < fileParts.length; i++) {
|
||||
const line = fileParts[i];
|
||||
if (line.length === 0) {
|
||||
lines.push([]);
|
||||
continue;
|
||||
}
|
||||
|
||||
const elements: number[][] = [];
|
||||
const lineParts = line.split(",", -1);
|
||||
|
||||
for (let i = 0; i < lineParts.length; i++) {
|
||||
const el = lineParts[i];
|
||||
|
||||
if (el.length === 0) elements.push([]);
|
||||
else {
|
||||
const list: number[] = [];
|
||||
|
||||
for (let i = 0; i < el.length;) {
|
||||
let sign = 1;
|
||||
let curr = map[el.charCodeAt(i++)];
|
||||
let cont = (curr & 0x20) === 0x20;
|
||||
if ((curr & 1) === 1) sign = -1;
|
||||
let res = (curr & 0b11110) >> 1;
|
||||
let n = 4;
|
||||
|
||||
for (; i < el.length && cont;) {
|
||||
curr = map[el.charCodeAt(i++)];
|
||||
cont = (curr & 0x20) == 0x20;
|
||||
res |= (curr & 0b11111) << n;
|
||||
n += 5;
|
||||
if (!cont) break;
|
||||
}
|
||||
|
||||
list.push(res * sign);
|
||||
}
|
||||
|
||||
elements.push(list);
|
||||
}
|
||||
}
|
||||
|
||||
lines.push(elements);
|
||||
}
|
||||
|
||||
return lines;
|
||||
}
|
||||
|
||||
export namespace Location {
|
||||
export function compare(a: Location, b: Location) {
|
||||
const { 0: filenameA, 1: lineA, 2: startA } = a;
|
||||
const { 0: filenameB, 1: lineB, 2: startB } = b;
|
||||
|
||||
if (filenameA < filenameB) return -1;
|
||||
if (filenameA > filenameB) return 1;
|
||||
|
||||
const lineI = lineA - lineB;
|
||||
if (lineI !== 0) return lineI;
|
||||
|
||||
return startA - startB;
|
||||
}
|
||||
export function comparePoints(a: Location, b: Location) {
|
||||
const { 1: lineA, 2: startA } = a;
|
||||
const { 1: lineB, 2: startB } = b;
|
||||
|
||||
const lineI = lineA - lineB;
|
||||
if (lineI !== 0) return lineI;
|
||||
|
||||
return startA - startB;
|
||||
}
|
||||
}
|
||||
|
||||
export class VLQSourceMap {
|
||||
public constructor(
|
||||
public readonly array: Map<string, [start: number, dst: Location][][]>,
|
||||
) { }
|
||||
|
||||
public converter() {
|
||||
return (src: Location) => {
|
||||
const file = this.array.get(src[0]);
|
||||
if (file == null) return src;
|
||||
|
||||
const line = file[src[1]];
|
||||
if (line == null || line.length === 0) return undefined;
|
||||
|
||||
let a = 0;
|
||||
let b = line.length;
|
||||
|
||||
while (true) {
|
||||
const done = b - a <= 1;
|
||||
|
||||
const mid = (a + b) >> 1;
|
||||
const el = line[mid];
|
||||
|
||||
const cmp = el[0] - src[2];
|
||||
|
||||
if (cmp < 0) {
|
||||
if (done) {
|
||||
if (b >= line.length) return undefined;
|
||||
break;
|
||||
}
|
||||
a = mid;
|
||||
}
|
||||
else if (cmp > 0) {
|
||||
if (done) {
|
||||
if (a <= 0) return undefined;
|
||||
break;
|
||||
}
|
||||
b = mid;
|
||||
}
|
||||
else return el[1];
|
||||
}
|
||||
|
||||
return line[b][1];
|
||||
};
|
||||
}
|
||||
|
||||
public static parseVLQ(compiled: string, filenames: string[], raw: string): VLQSourceMap {
|
||||
const mapping = decodeVLQ(raw);
|
||||
const file: [start: number, dst: Location][][] = [];
|
||||
const res = new Map<string, [start: number, dst: Location][][]>([[compiled, file]]);
|
||||
|
||||
let originalRow = 0;
|
||||
let originalCol = 0;
|
||||
let originalFile = 0;
|
||||
const lastCols = new Set<number>();
|
||||
|
||||
for (let compiledRow = 0; compiledRow < mapping.length; compiledRow++) {
|
||||
const line: [start: number, dst: Location][] = file[compiledRow] = [];
|
||||
let compiledCol = 0;
|
||||
|
||||
const rowData = mapping[compiledRow];
|
||||
for (let i = 0; i < rowData.length; i++) {
|
||||
const rawSeg = rowData[i];
|
||||
|
||||
compiledCol += rawSeg.length > 0 ? rawSeg[0] : 0;
|
||||
originalFile += rawSeg.length > 1 ? rawSeg[1] : 0;
|
||||
originalRow += rawSeg.length > 2 ? rawSeg[2] : 0;
|
||||
originalCol += rawSeg.length > 3 ? rawSeg[3] : 0;
|
||||
|
||||
if (!lastCols.has(compiledCol)) {
|
||||
line[line.length] = [compiledCol, [filenames[originalFile], originalRow, originalCol]];
|
||||
}
|
||||
lastCols.add(compiledCol);
|
||||
}
|
||||
|
||||
line.sort((a, b) => a[0] - b[0]);
|
||||
}
|
||||
|
||||
return new VLQSourceMap(res);
|
||||
}
|
||||
public static parse(raw: string | { file: string, mappings: string, sources: string[] }) {
|
||||
if (typeof raw === "string") raw = JSON.parse(raw) as { file: string, mappings: string, sources: string[] };
|
||||
|
||||
const compiled = raw.file;
|
||||
const mapping = raw.mappings;
|
||||
let filenames = raw.sources;
|
||||
if (filenames.length === 0 || filenames.length === 1 && filenames[0] === "") filenames = [compiled];
|
||||
|
||||
return this.parseVLQ(compiled, filenames, mapping);
|
||||
}
|
||||
}
|
||||
|
||||
export namespace SourceMap {
|
||||
export function parse(raw: string | { file: string, mappings: string, sources: string[] }) {
|
||||
return VLQSourceMap.parse(raw).converter();
|
||||
}
|
||||
export function chain(...maps: SourceMap[]): SourceMap {
|
||||
return loc => {
|
||||
for (const el of maps) {
|
||||
const tmp = el(loc);
|
||||
if (tmp == null) return undefined;
|
||||
else loc = tmp;
|
||||
}
|
||||
|
||||
return loc;
|
||||
};
|
||||
}
|
||||
}
|
||||
10
src/lib/transpiler/transpiler.d.ts
vendored
Normal file
10
src/lib/transpiler/transpiler.d.ts
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
type Location = readonly [file: string, line: number, start: number];
|
||||
type SourceMap = (loc: Location) => Location | undefined;
|
||||
|
||||
type CompilerFactory = (next: Compiler) => Compiler;
|
||||
type Compiler = (filename: string, src: string, mapper: SourceMap) => (this: any, ...args: any[]) => any;
|
||||
|
||||
declare function getResource(name: string): string | undefined;
|
||||
declare function print(...args: any[]): void;
|
||||
declare function register(factory: CompilerFactory): void;
|
||||
declare function registerSource(filename: string, src: string): void;
|
||||
118
src/lib/transpiler/typescript.ts
Normal file
118
src/lib/transpiler/typescript.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
import { createDocumentRegistry, createLanguageService, ModuleKind, ScriptSnapshot, ScriptTarget, type Diagnostic, type CompilerOptions, type IScriptSnapshot, flattenDiagnosticMessageText, CompilerHost, LanguageService } from "typescript";
|
||||
import { SourceMap } from "./map.ts";
|
||||
|
||||
const resources: Record<string, string | undefined> = {};
|
||||
|
||||
function resource(name: string) {
|
||||
if (name in resources) return resources[name];
|
||||
else return resources[name] = getResource(name);
|
||||
}
|
||||
|
||||
export default function typescript(next: Compiler): Compiler {
|
||||
const files: Record<string, IScriptSnapshot> = {};
|
||||
const versions: Record<string, number> = {};
|
||||
let declI = 0;
|
||||
|
||||
const settings: CompilerOptions = {
|
||||
target: ScriptTarget.ES5,
|
||||
module: ModuleKind.Preserve,
|
||||
|
||||
allowImportingTsExtensions: true,
|
||||
verbatimModuleSyntax: true,
|
||||
|
||||
strict: false,
|
||||
skipLibCheck: true,
|
||||
forceConsistentCasingInFileNames: true,
|
||||
declaration: true,
|
||||
sourceMap: true,
|
||||
downlevelIteration: true,
|
||||
};
|
||||
|
||||
let service: LanguageService;
|
||||
|
||||
measure(() => {
|
||||
service = createLanguageService({
|
||||
getCurrentDirectory: () => "/",
|
||||
getDefaultLibFileName: () => "/lib.d.ts",
|
||||
getScriptFileNames: () => {
|
||||
const res = ["/src.ts", "/lib.d.ts"];
|
||||
for (let i = 0; i < declI; i++) res.push("/src." + i + ".d.ts");
|
||||
return res;
|
||||
},
|
||||
getCompilationSettings: () => settings,
|
||||
log: print,
|
||||
fileExists: filename => filename in files || resource(filename) != null,
|
||||
|
||||
getScriptSnapshot: (filename) => {
|
||||
if (filename in files) return files[filename];
|
||||
else {
|
||||
const src = resource(filename);
|
||||
if (src == null) return undefined;
|
||||
return files[filename] = ScriptSnapshot.fromString(src);
|
||||
}
|
||||
},
|
||||
getScriptVersion: (filename) => String(versions[filename] || 0),
|
||||
|
||||
readFile: () => { throw "no"; },
|
||||
writeFile: () => { throw "no"; },
|
||||
}, createDocumentRegistry());
|
||||
});
|
||||
measure(() => {
|
||||
service.getEmitOutput("/lib.d.ts");
|
||||
});
|
||||
print("Loaded typescript!");
|
||||
|
||||
return (filename, code, prevMap) => {
|
||||
files["/src.ts"] = ScriptSnapshot.fromString(code);
|
||||
versions["/src.ts"] ??= 0;
|
||||
versions["/src.ts"]++;
|
||||
|
||||
const emit = service.getEmitOutput("/src.ts");
|
||||
|
||||
const diagnostics = new Array<Diagnostic>()
|
||||
.concat(service.getSyntacticDiagnostics("/src.ts"))
|
||||
.concat(service.getSemanticDiagnostics("/src.ts"))
|
||||
.map(diagnostic => {
|
||||
const message = flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
||||
|
||||
if (diagnostic.file != null) {
|
||||
let file = diagnostic.file.fileName.substring(1);
|
||||
if (file === "src.ts") file = filename;
|
||||
|
||||
if (diagnostic.start == null) return file;
|
||||
|
||||
const pos = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
|
||||
return file + ":" + (pos.line + 1) + ":" + (pos.character + 1) + ": " + message;
|
||||
}
|
||||
else return message;
|
||||
});
|
||||
|
||||
if (diagnostics.length > 0) {
|
||||
throw new SyntaxError(diagnostics.join("\n"));
|
||||
}
|
||||
|
||||
const outputs: Record<string, string> = {};
|
||||
|
||||
for (const el of emit.outputFiles) {
|
||||
outputs[el.name] = el.text;
|
||||
}
|
||||
|
||||
const rawMap = JSON.parse(outputs["/src.js.map"]);
|
||||
const map = SourceMap.parse({
|
||||
file: "ts-internal://" + filename,
|
||||
mappings: rawMap.mappings,
|
||||
sources: [filename],
|
||||
});
|
||||
const result = outputs["/src.js"];
|
||||
const declaration = outputs["/src.d.ts"];
|
||||
|
||||
const compiled = next("ts-internal://" + filename, result, SourceMap.chain(map, prevMap));
|
||||
registerSource(filename, code);
|
||||
|
||||
return function (this: any) {
|
||||
const res = compiled.apply(this, arguments);
|
||||
if (declaration !== '') files["/src." + declI++ + ".d.ts"] = ScriptSnapshot.fromString(declaration);
|
||||
return res;
|
||||
};
|
||||
};
|
||||
}
|
||||
16
src/main/java/me/topchetoeu/jscript/common/FunctionBody.java
Normal file
16
src/main/java/me/topchetoeu/jscript/common/FunctionBody.java
Normal file
@@ -0,0 +1,16 @@
|
||||
package me.topchetoeu.jscript.common;
|
||||
|
||||
public class FunctionBody {
|
||||
public final FunctionBody[] children;
|
||||
public final Instruction[] instructions;
|
||||
public final int localsN, capturablesN, capturesN, length;
|
||||
|
||||
public FunctionBody(int localsN, int capturablesN, int capturesN, int length, Instruction[] instructions, FunctionBody[] children) {
|
||||
this.children = children;
|
||||
this.length = length;
|
||||
this.localsN = localsN;
|
||||
this.capturablesN = capturablesN;
|
||||
this.capturesN = capturesN;
|
||||
this.instructions = instructions;
|
||||
}
|
||||
}
|
||||
376
src/main/java/me/topchetoeu/jscript/common/Instruction.java
Normal file
376
src/main/java/me/topchetoeu/jscript/common/Instruction.java
Normal file
@@ -0,0 +1,376 @@
|
||||
package me.topchetoeu.jscript.common;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.function.IntFunction;
|
||||
import java.util.function.IntSupplier;
|
||||
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
|
||||
public class Instruction {
|
||||
public static enum Type {
|
||||
RETURN(0x00),
|
||||
NOP(0x01),
|
||||
THROW(0x02),
|
||||
THROW_SYNTAX(0x03),
|
||||
DELETE(0x04),
|
||||
TRY_START(0x05),
|
||||
TRY_END(0x06),
|
||||
|
||||
CALL(0x10),
|
||||
CALL_NEW(0x12),
|
||||
JMP_IF(0x18),
|
||||
JMP_IFN(0x19),
|
||||
JMP(0x1A),
|
||||
|
||||
PUSH_UNDEFINED(0x20),
|
||||
PUSH_NULL(0x21),
|
||||
PUSH_BOOL(0x22),
|
||||
PUSH_NUMBER(0x23),
|
||||
PUSH_STRING(0x24),
|
||||
DUP(0x25),
|
||||
DISCARD(0x26),
|
||||
|
||||
LOAD_FUNC(0x30),
|
||||
LOAD_ARR(0x31),
|
||||
LOAD_OBJ(0x32),
|
||||
LOAD_REGEX(0x33),
|
||||
|
||||
LOAD_GLOB(0x38),
|
||||
LOAD_INTRINSICS(0x39),
|
||||
LOAD_ARG(0x3A),
|
||||
LOAD_ARGS_N(0x3B),
|
||||
LOAD_ARGS(0x3C),
|
||||
LOAD_CALLED(0x3D),
|
||||
LOAD_THIS(0x3E),
|
||||
LOAD_ERROR(0x3F),
|
||||
|
||||
LOAD_VAR(0x40),
|
||||
LOAD_MEMBER(0x41),
|
||||
LOAD_MEMBER_INT(0x42),
|
||||
LOAD_MEMBER_STR(0x43),
|
||||
|
||||
STORE_VAR(0x48),
|
||||
STORE_MEMBER(0x49),
|
||||
STORE_MEMBER_INT(0x4A),
|
||||
STORE_MEMBER_STR(0x4B),
|
||||
|
||||
DEF_PROP(0x50),
|
||||
DEF_FIELD(0x51),
|
||||
KEYS(0x52),
|
||||
TYPEOF(0x53),
|
||||
OPERATION(0x54),
|
||||
|
||||
GLOB_GET(0x60),
|
||||
GLOB_SET(0x61),
|
||||
GLOB_DEF(0x62);
|
||||
|
||||
private static final HashMap<Integer, Type> types = new HashMap<>();
|
||||
public final int numeric;
|
||||
|
||||
static {
|
||||
for (var val : Type.values()) types.put(val.numeric, val);
|
||||
}
|
||||
|
||||
private Type(int numeric) {
|
||||
this.numeric = numeric;
|
||||
}
|
||||
|
||||
public static Type fromNumeric(int i) {
|
||||
return types.get(i);
|
||||
}
|
||||
}
|
||||
public static enum BreakpointType {
|
||||
/**
|
||||
* A debugger should never stop at such instruction, unless a breakpoint has been set on it
|
||||
*/
|
||||
NONE,
|
||||
/**
|
||||
* Debuggers should pause at instructions marked with this breakpoint type
|
||||
* after any step command
|
||||
*/
|
||||
STEP_OVER,
|
||||
/**
|
||||
* Debuggers should pause at instructions marked with this breakpoint type
|
||||
* only after a step-in command
|
||||
*/
|
||||
STEP_IN;
|
||||
|
||||
public boolean shouldStepIn() {
|
||||
return this != NONE;
|
||||
}
|
||||
public boolean shouldStepOver() {
|
||||
return this == STEP_OVER;
|
||||
}
|
||||
}
|
||||
|
||||
public final Type type;
|
||||
public final Object[] params;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(int i) {
|
||||
if (i >= params.length || i < 0) return null;
|
||||
return (T)params[i];
|
||||
}
|
||||
|
||||
private Instruction(Type type, Object ...params) {
|
||||
this.type = type;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
/**
|
||||
* Signals the start of a protected context
|
||||
* @param catchStart The point to witch to jump if an error has been caught
|
||||
* @param finallyStart The point to witch to jump after either the try or catch bodies have exited
|
||||
* @param end The point to which to jump after exiting the whole protected context
|
||||
*/
|
||||
public static Instruction tryStart(int catchStart, int finallyStart, int end) {
|
||||
return new Instruction(Type.TRY_START, catchStart, finallyStart, end);
|
||||
}
|
||||
/**
|
||||
* Signifies that the current protected section (try, catch or finally) has ended
|
||||
*/
|
||||
public static Instruction tryEnd() {
|
||||
return new Instruction(Type.TRY_END);
|
||||
}
|
||||
/**
|
||||
* Throws the top stack value
|
||||
*/
|
||||
public static Instruction throwInstr() {
|
||||
return new Instruction(Type.THROW);
|
||||
}
|
||||
/**
|
||||
* Converts the given exception to a runtime syntax error and throws it
|
||||
*/
|
||||
public static Instruction throwSyntax(SyntaxException err) {
|
||||
return new Instruction(Type.THROW_SYNTAX, err.getMessage());
|
||||
}
|
||||
/**
|
||||
* Converts the given exception to a runtime syntax error and throws it
|
||||
*/
|
||||
public static Instruction throwSyntax(String err) {
|
||||
return new Instruction(Type.THROW_SYNTAX, err);
|
||||
}
|
||||
/**
|
||||
* Converts the given exception to a runtime syntax error and throws it
|
||||
*/
|
||||
public static Instruction throwSyntax(Location loc, String err) {
|
||||
return new Instruction(Type.THROW_SYNTAX, new SyntaxException(loc, err).getMessage());
|
||||
}
|
||||
/**
|
||||
* Performs a JS object property deletion.
|
||||
* Operands:
|
||||
* 1. Object to manipulate
|
||||
* 2. Key to delete
|
||||
*/
|
||||
public static Instruction delete() {
|
||||
return new Instruction(Type.DELETE);
|
||||
}
|
||||
/**
|
||||
* Returns the top stack value
|
||||
*/
|
||||
public static Instruction ret() {
|
||||
return new Instruction(Type.RETURN);
|
||||
}
|
||||
/**
|
||||
* A special NOP instruction telling any debugger to pause
|
||||
*/
|
||||
public static Instruction debug() {
|
||||
return new Instruction(Type.NOP, "debug");
|
||||
}
|
||||
|
||||
/**
|
||||
* Does nothing. May be used for metadata or implementation-specific instructions that don't alter the behavior
|
||||
*/
|
||||
public static Instruction nop(Object ...params) {
|
||||
return new Instruction(Type.NOP, params);
|
||||
}
|
||||
|
||||
public static Instruction call(int argn, boolean hasSelf) {
|
||||
return new Instruction(Type.CALL, argn, hasSelf);
|
||||
}
|
||||
public static Instruction callNew(int argn) {
|
||||
return new Instruction(Type.CALL_NEW, argn);
|
||||
}
|
||||
|
||||
public static Instruction jmp(int offset) {
|
||||
return new Instruction(Type.JMP, offset);
|
||||
}
|
||||
public static Instruction jmpIf(int offset) {
|
||||
return new Instruction(Type.JMP_IF, offset);
|
||||
}
|
||||
public static Instruction jmpIfNot(int offset) {
|
||||
return new Instruction(Type.JMP_IFN, offset);
|
||||
}
|
||||
|
||||
public static IntFunction<Instruction> jmp(IntSupplier pos) {
|
||||
return i -> new Instruction(Type.JMP, pos.getAsInt() - i);
|
||||
}
|
||||
public static IntFunction<Instruction> jmpIf(IntSupplier pos) {
|
||||
return i -> new Instruction(Type.JMP_IF, pos.getAsInt() - i);
|
||||
}
|
||||
public static IntFunction<Instruction> jmpIfNot(IntSupplier pos) {
|
||||
return i -> new Instruction(Type.JMP_IFN, pos.getAsInt() - i);
|
||||
}
|
||||
|
||||
public static Instruction pushUndefined() {
|
||||
return new Instruction(Type.PUSH_UNDEFINED);
|
||||
}
|
||||
public static Instruction pushNull() {
|
||||
return new Instruction(Type.PUSH_NULL);
|
||||
}
|
||||
public static Instruction pushValue(boolean val) {
|
||||
return new Instruction(Type.PUSH_BOOL, val);
|
||||
}
|
||||
public static Instruction pushValue(double val) {
|
||||
return new Instruction(Type.PUSH_NUMBER, val);
|
||||
}
|
||||
public static Instruction pushValue(String val) {
|
||||
return new Instruction(Type.PUSH_STRING, val);
|
||||
}
|
||||
|
||||
public static Instruction globDef(String name) {
|
||||
return new Instruction(Type.GLOB_DEF, name);
|
||||
}
|
||||
|
||||
public static Instruction globGet(String name, boolean force) {
|
||||
return new Instruction(Type.GLOB_GET, name, force);
|
||||
}
|
||||
public static Instruction globSet(String name, boolean keep, boolean define) {
|
||||
return new Instruction(Type.GLOB_SET, name, keep, define);
|
||||
}
|
||||
|
||||
public static Instruction loadVar(int i) {
|
||||
return new Instruction(Type.LOAD_VAR, i);
|
||||
}
|
||||
public static Instruction loadThis() {
|
||||
return new Instruction(Type.LOAD_THIS);
|
||||
}
|
||||
/**
|
||||
* Loads the given argument
|
||||
* @param i The index of the argument to load. If -1, will get the index from the stack instead
|
||||
*/
|
||||
public static Instruction loadArg(int i) {
|
||||
return new Instruction(Type.LOAD_ARG, i);
|
||||
}
|
||||
/**
|
||||
* Pushes the amount of arguments to the stack
|
||||
*/
|
||||
public static Instruction loadArgsN() {
|
||||
return new Instruction(Type.LOAD_ARGS_N);
|
||||
}
|
||||
/**
|
||||
* Pushes the arguments object to the stack
|
||||
*/
|
||||
public static Instruction loadArgs() {
|
||||
return new Instruction(Type.LOAD_ARGS);
|
||||
}
|
||||
/**
|
||||
* Loads a reference to the function being called
|
||||
*/
|
||||
public static Instruction loadCalled() {
|
||||
return new Instruction(Type.LOAD_CALLED);
|
||||
}
|
||||
public static Instruction loadGlob() {
|
||||
return new Instruction(Type.LOAD_GLOB);
|
||||
}
|
||||
public static Instruction loadIntrinsics(String key) {
|
||||
return new Instruction(Type.LOAD_INTRINSICS, key);
|
||||
}
|
||||
public static Instruction loadError() {
|
||||
return new Instruction(Type.LOAD_ERROR);
|
||||
}
|
||||
public static Instruction loadMember() {
|
||||
return new Instruction(Type.LOAD_MEMBER);
|
||||
}
|
||||
public static Instruction loadMember(int member) {
|
||||
return new Instruction(Type.LOAD_MEMBER_INT, member);
|
||||
}
|
||||
public static Instruction loadMember(String member) {
|
||||
return new Instruction(Type.LOAD_MEMBER_STR, member);
|
||||
}
|
||||
|
||||
public static Instruction loadRegex(String pattern, String flags) {
|
||||
return new Instruction(Type.LOAD_REGEX, pattern, flags);
|
||||
}
|
||||
// TODO: make this capturing a concern of the compiler
|
||||
public static Instruction loadFunc(int id, String name, int[] captures) {
|
||||
var args = new Object[2 + captures.length];
|
||||
args[0] = id;
|
||||
args[1] = name;
|
||||
for (var i = 0; i < captures.length; i++) args[i + 2] = captures[i];
|
||||
return new Instruction(Type.LOAD_FUNC, args);
|
||||
}
|
||||
public static Instruction loadObj() {
|
||||
return new Instruction(Type.LOAD_OBJ);
|
||||
}
|
||||
public static Instruction loadArr(int count) {
|
||||
return new Instruction(Type.LOAD_ARR, count);
|
||||
}
|
||||
public static Instruction dup() {
|
||||
return new Instruction(Type.DUP, 1, 0);
|
||||
}
|
||||
public static Instruction dup(int count, int offset) {
|
||||
return new Instruction(Type.DUP, count, offset);
|
||||
}
|
||||
|
||||
public static Instruction storeVar(int i, boolean keep, boolean initialize) {
|
||||
return new Instruction(Type.STORE_VAR, i, keep, initialize);
|
||||
}
|
||||
|
||||
public static Instruction storeMember() {
|
||||
return new Instruction(Type.STORE_MEMBER, false);
|
||||
}
|
||||
public static Instruction storeMember(boolean keep) {
|
||||
return new Instruction(Type.STORE_MEMBER, keep);
|
||||
}
|
||||
|
||||
public static Instruction storeMember(String key) {
|
||||
return new Instruction(Type.STORE_MEMBER_STR, key, false);
|
||||
}
|
||||
public static Instruction storeMember(String key, boolean keep) {
|
||||
return new Instruction(Type.STORE_MEMBER_STR, key, keep);
|
||||
}
|
||||
|
||||
public static Instruction storeMember(int key) {
|
||||
return new Instruction(Type.STORE_MEMBER_INT, key, false);
|
||||
}
|
||||
public static Instruction storeMember(int key, boolean keep) {
|
||||
return new Instruction(Type.STORE_MEMBER_INT, key, keep);
|
||||
}
|
||||
|
||||
public static Instruction discard() {
|
||||
return new Instruction(Type.DISCARD);
|
||||
}
|
||||
|
||||
public static Instruction typeof() {
|
||||
return new Instruction(Type.TYPEOF);
|
||||
}
|
||||
public static Instruction typeof(String varName) {
|
||||
return new Instruction(Type.TYPEOF, varName);
|
||||
}
|
||||
|
||||
public static Instruction keys(boolean own, boolean onlyEnumerable) {
|
||||
return new Instruction(Type.KEYS, own, onlyEnumerable);
|
||||
}
|
||||
|
||||
public static Instruction defProp(boolean setter) {
|
||||
return new Instruction(Type.DEF_PROP, setter);
|
||||
}
|
||||
public static Instruction defField() {
|
||||
return new Instruction(Type.DEF_FIELD);
|
||||
}
|
||||
|
||||
public static Instruction operation(Operation op) {
|
||||
return new Instruction(Type.OPERATION, op);
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
var res = type.toString();
|
||||
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
res += " " + params[i];
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
29
src/main/java/me/topchetoeu/jscript/common/Metadata.java
Normal file
29
src/main/java/me/topchetoeu/jscript/common/Metadata.java
Normal file
@@ -0,0 +1,29 @@
|
||||
package me.topchetoeu.jscript.common;
|
||||
|
||||
import me.topchetoeu.jscript.common.json.JSON;
|
||||
|
||||
public class Metadata {
|
||||
private static final String VERSION;
|
||||
private static final String AUTHOR;
|
||||
private static final String NAME;
|
||||
|
||||
static {
|
||||
var data = JSON.parse(null, Reading.resourceToString("metadata.json")).map();
|
||||
VERSION = data.string("version");
|
||||
AUTHOR = data.string("author");
|
||||
NAME = data.string("name");
|
||||
}
|
||||
|
||||
public static String version() {
|
||||
if (VERSION.equals("$" + "{VERSION}")) return "1337-devel";
|
||||
else return VERSION;
|
||||
}
|
||||
public static String author() {
|
||||
if (AUTHOR.equals("$" + "{AUTHOR}")) return "anonymous";
|
||||
else return AUTHOR;
|
||||
}
|
||||
public static String name() {
|
||||
if (NAME.equals("$" + "{NAME}")) return "some-product";
|
||||
else return NAME;
|
||||
}
|
||||
}
|
||||
54
src/main/java/me/topchetoeu/jscript/common/Operation.java
Normal file
54
src/main/java/me/topchetoeu/jscript/common/Operation.java
Normal file
@@ -0,0 +1,54 @@
|
||||
package me.topchetoeu.jscript.common;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public enum Operation {
|
||||
INSTANCEOF(1, 2),
|
||||
IN(2, 2),
|
||||
|
||||
MULTIPLY(3, 2),
|
||||
DIVIDE(4, 2),
|
||||
MODULO(5, 2),
|
||||
ADD(6, 2),
|
||||
SUBTRACT(7, 2),
|
||||
|
||||
USHIFT_RIGHT(8, 2),
|
||||
SHIFT_RIGHT(9, 2),
|
||||
SHIFT_LEFT(10, 2),
|
||||
|
||||
GREATER(11, 2),
|
||||
LESS(12, 2),
|
||||
GREATER_EQUALS(13, 2),
|
||||
LESS_EQUALS(14, 2),
|
||||
LOOSE_EQUALS(15, 2),
|
||||
LOOSE_NOT_EQUALS(16, 2),
|
||||
EQUALS(17, 2),
|
||||
NOT_EQUALS(18, 2),
|
||||
|
||||
AND(19, 2),
|
||||
OR(20, 2),
|
||||
XOR(21, 2),
|
||||
|
||||
NEG(23, 1),
|
||||
POS(24, 1),
|
||||
NOT(25, 1),
|
||||
INVERSE(26, 1);
|
||||
|
||||
private static final HashMap<Integer, Operation> operations = new HashMap<>();
|
||||
|
||||
static {
|
||||
for (var val : Operation.values()) operations.put(val.numeric, val);
|
||||
}
|
||||
|
||||
public final int numeric;
|
||||
public final int operands;
|
||||
|
||||
private Operation(int numeric, int n) {
|
||||
this.numeric = numeric;
|
||||
this.operands = n;
|
||||
}
|
||||
|
||||
public static Operation fromNumeric(int i) {
|
||||
return operations.get(i);
|
||||
}
|
||||
}
|
||||
92
src/main/java/me/topchetoeu/jscript/common/Reading.java
Normal file
92
src/main/java/me/topchetoeu/jscript/common/Reading.java
Normal file
@@ -0,0 +1,92 @@
|
||||
package me.topchetoeu.jscript.common;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Reading {
|
||||
private static final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
|
||||
|
||||
public static synchronized String readline() throws IOException {
|
||||
return reader.readLine();
|
||||
}
|
||||
|
||||
public static byte[] streamToBytes(InputStream in) {
|
||||
if (in == null) return null;
|
||||
|
||||
try {
|
||||
List<byte[]> bufs = null;
|
||||
byte[] result = null;
|
||||
int total = 0;
|
||||
int n;
|
||||
|
||||
do {
|
||||
var buf = new byte[8192];
|
||||
int nread = 0;
|
||||
|
||||
// read to EOF which may read more or less than buffer size
|
||||
while ((n = in.read(buf, nread, buf.length - nread)) > 0) {
|
||||
nread += n;
|
||||
}
|
||||
|
||||
if (nread > 0) {
|
||||
if (Integer.MAX_VALUE - 8 - total < nread) throw new OutOfMemoryError("Required array size too large");
|
||||
if (nread < buf.length) buf = Arrays.copyOfRange(buf, 0, nread);
|
||||
total += nread;
|
||||
|
||||
if (result == null) result = buf;
|
||||
else {
|
||||
if (bufs == null) {
|
||||
bufs = new ArrayList<>();
|
||||
bufs.add(result);
|
||||
}
|
||||
|
||||
bufs.add(buf);
|
||||
}
|
||||
}
|
||||
// if the last call to read returned -1 or the number of bytes
|
||||
// requested have been read then break
|
||||
} while (n >= 0);
|
||||
|
||||
if (bufs == null) {
|
||||
if (result == null) return new byte[0];
|
||||
return result.length == total ? result : Arrays.copyOf(result, total);
|
||||
}
|
||||
|
||||
result = new byte[total];
|
||||
|
||||
int offset = 0;
|
||||
int remaining = total;
|
||||
|
||||
for (byte[] b : bufs) {
|
||||
int count = Math.min(b.length, remaining);
|
||||
System.arraycopy(b, 0, result, offset, count);
|
||||
offset += count;
|
||||
remaining -= count;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (IOException e) { throw new UncheckedIOException(e); }
|
||||
}
|
||||
public static String streamToString(InputStream in) {
|
||||
var bytes = streamToBytes(in);
|
||||
if (bytes == null) return null;
|
||||
else return new String(bytes);
|
||||
}
|
||||
|
||||
public static InputStream resourceToStream(String name) {
|
||||
return Reading.class.getResourceAsStream("/" + name);
|
||||
}
|
||||
public static String resourceToString(String name) {
|
||||
return streamToString(resourceToStream(name));
|
||||
}
|
||||
public static byte[] resourceToBytes(String name) {
|
||||
return streamToBytes(resourceToStream(name));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package me.topchetoeu.jscript.common;
|
||||
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
|
||||
public class SyntaxException extends RuntimeException {
|
||||
public final Location loc;
|
||||
public final String msg;
|
||||
|
||||
public SyntaxException(Location loc, String msg) {
|
||||
super(String.format("Syntax error %s: %s", loc, msg));
|
||||
this.loc = loc;
|
||||
this.msg = msg;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package me.topchetoeu.jscript.common.environment;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class Environment {
|
||||
public final Environment parent;
|
||||
private final Map<Key<Object>, Object> map = new HashMap<>();
|
||||
private final Set<Key<Object>> hidden = new HashSet<>();
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(Key<T> key) {
|
||||
if (map.containsKey(key)) return (T)map.get(key);
|
||||
else if (!hidden.contains(key) && parent != null) return parent.get(key);
|
||||
else return null;
|
||||
}
|
||||
public boolean has(Key<?> key) {
|
||||
if (map.containsKey(key)) return true;
|
||||
else if (!hidden.contains(key) && parent != null) return parent.has(key);
|
||||
else return false;
|
||||
}
|
||||
|
||||
public boolean hasNotNull(Key<?> key) {
|
||||
return get(key) != null;
|
||||
}
|
||||
|
||||
public <T> T get(Key<T> key, T defaultVal) {
|
||||
if (has(key)) return get(key);
|
||||
else return defaultVal;
|
||||
}
|
||||
public <T> T getWith(Key<T> key, Supplier<T> defaultVal) {
|
||||
if (has(key)) return get(key);
|
||||
else return defaultVal.get();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> Environment add(Key<T> key, T val) {
|
||||
map.put((Key<Object>)key, val);
|
||||
hidden.remove(key);
|
||||
return this;
|
||||
}
|
||||
public Environment add(Key<Void> key) {
|
||||
return add(key, null);
|
||||
}
|
||||
@SuppressWarnings("all")
|
||||
public Environment addAll(Map<Key<?>, ?> map, boolean iterableAsMulti) {
|
||||
map.putAll((Map)map);
|
||||
hidden.removeAll(map.keySet());
|
||||
return this;
|
||||
}
|
||||
public Environment addAll(Map<Key<?>, ?> map) {
|
||||
return addAll(map, true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Environment remove(Key<?> key) {
|
||||
map.remove(key);
|
||||
hidden.add((Key<Object>)key);
|
||||
return this;
|
||||
}
|
||||
|
||||
public <T> T init(Key<T> key, T val) {
|
||||
if (!has(key)) this.add(key, val);
|
||||
return val;
|
||||
}
|
||||
public <T> T initFrom(Key<T> key, Supplier<T> val) {
|
||||
if (!has(key)) {
|
||||
var res = val.get();
|
||||
this.add(key, res);
|
||||
return res;
|
||||
}
|
||||
else return get(key);
|
||||
}
|
||||
|
||||
public Environment child() {
|
||||
return new Environment(this);
|
||||
}
|
||||
|
||||
public Environment(Environment parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
public Environment() {
|
||||
this.parent = null;
|
||||
}
|
||||
|
||||
public static Environment wrap(Environment env) {
|
||||
if (env == null) return empty();
|
||||
else return env;
|
||||
}
|
||||
|
||||
public static Environment empty() {
|
||||
return new Environment();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
package me.topchetoeu.jscript.common.environment;
|
||||
|
||||
public final class Key<T> { }
|
||||
173
src/main/java/me/topchetoeu/jscript/common/json/JSON.java
Normal file
173
src/main/java/me/topchetoeu/jscript/common/json/JSON.java
Normal file
@@ -0,0 +1,173 @@
|
||||
package me.topchetoeu.jscript.common.json;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.HashMap;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import me.topchetoeu.jscript.common.SyntaxException;
|
||||
import me.topchetoeu.jscript.common.parsing.Filename;
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||
import me.topchetoeu.jscript.common.parsing.Source;
|
||||
|
||||
public class JSON {
|
||||
public static ParseRes<JSONElement> parseString(Source src, int i) {
|
||||
var res = Parsing.parseString(src, i);
|
||||
if (!res.isSuccess()) return res.chainError();
|
||||
return ParseRes.res(JSONElement.string(res.result), res.n);
|
||||
}
|
||||
public static ParseRes<JSONElement> parseNumber(Source src, int i) {
|
||||
var res = Parsing.parseNumber(src, i, true);
|
||||
if (!res.isSuccess()) return res.chainError();
|
||||
else return ParseRes.res(JSONElement.number(res.result), res.n);
|
||||
}
|
||||
public static ParseRes<JSONElement> parseLiteral(Source src, int i) {
|
||||
var id = Parsing.parseIdentifier(src, i);
|
||||
|
||||
if (!id.isSuccess()) return ParseRes.failed();
|
||||
else if (id.result.equals("true")) return ParseRes.res(JSONElement.bool(true), id.n);
|
||||
else if (id.result.equals("false")) return ParseRes.res(JSONElement.bool(false), id.n);
|
||||
else if (id.result.equals("null")) return ParseRes.res(JSONElement.NULL, id.n);
|
||||
else return ParseRes.failed();
|
||||
}
|
||||
|
||||
public static ParseRes<JSONElement> parseValue(Source src, int i) {
|
||||
return ParseRes.first(src, i,
|
||||
JSON::parseString,
|
||||
JSON::parseNumber,
|
||||
JSON::parseLiteral,
|
||||
JSON::parseMap,
|
||||
JSON::parseList
|
||||
);
|
||||
}
|
||||
|
||||
public static ParseRes<JSONElement> parseMap(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
|
||||
if (!src.is(i + n, "{")) return ParseRes.failed();
|
||||
n++;
|
||||
|
||||
var values = new JSONMap();
|
||||
|
||||
if (src.is(i + n, "}")) return ParseRes.res(JSONElement.map(new JSONMap(new HashMap<>())), n + 1);
|
||||
while (true) {
|
||||
var name = parseString(src, i + n);
|
||||
if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected an index");
|
||||
n += name.n;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
if (!src.is(i + n, ":")) return name.chainError(src.loc(i + n), "Expected a colon");
|
||||
n++;
|
||||
|
||||
var res = parseValue(src, i + n);
|
||||
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a list element");
|
||||
values.put(name.result.toString(), res.result);
|
||||
n += res.n;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
if (src.is(i + n, ",")) n++;
|
||||
else if (src.is(i + n, "}")) {
|
||||
n++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ParseRes.res(JSONElement.map(values), n);
|
||||
}
|
||||
public static ParseRes<JSONElement> parseList(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
|
||||
if (!src.is(i + n++, "[")) return ParseRes.failed();
|
||||
|
||||
var values = new JSONList();
|
||||
|
||||
if (src.is(i + n, "]")) return ParseRes.res(JSONElement.list(new JSONList()), n + 1);
|
||||
while (true) {
|
||||
var res = parseValue(src, i + n);
|
||||
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a list element");
|
||||
values.add(res.result);
|
||||
n += res.n;
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
if (src.is(i + n, ",")) n++;
|
||||
else if (src.is(i + n, "]")) {
|
||||
n++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ParseRes.res(JSONElement.list(values), n);
|
||||
}
|
||||
public static JSONElement parse(Filename filename, String raw) {
|
||||
if (filename == null) filename = new Filename("jscript", "json");
|
||||
|
||||
var res = parseValue(new Source(null, filename, raw), 0);
|
||||
if (res.isFailed()) throw new SyntaxException(Location.of(filename, 0, 0), "Invalid JSON given");
|
||||
else if (res.isError()) throw new SyntaxException(res.errorLocation, res.error);
|
||||
else return JSONElement.of(res.result);
|
||||
}
|
||||
|
||||
public static String stringify(JSONElement el) {
|
||||
if (el.isNumber()) {
|
||||
var d = el.number();
|
||||
if (d == Double.NEGATIVE_INFINITY) return "-Infinity";
|
||||
if (d == Double.POSITIVE_INFINITY) return "Infinity";
|
||||
if (Double.isNaN(d)) return "NaN";
|
||||
return BigDecimal.valueOf(d).stripTrailingZeros().toPlainString();
|
||||
}
|
||||
if (el.isBoolean()) return el.bool() ? "true" : "false";
|
||||
if (el.isNull()) return "null";
|
||||
if (el.isString()) {
|
||||
var res = new StringBuilder("\"");
|
||||
var alphabet = "0123456789ABCDEF".toCharArray();
|
||||
|
||||
for (var c : el.string().toCharArray()) {
|
||||
if (c < 32 || c >= 127) {
|
||||
res
|
||||
.append("\\u")
|
||||
.append(alphabet[(c >> 12) & 0xF])
|
||||
.append(alphabet[(c >> 8) & 0xF])
|
||||
.append(alphabet[(c >> 4) & 0xF])
|
||||
.append(alphabet[(c >> 0) & 0xF]);
|
||||
}
|
||||
else if (c == '\\')
|
||||
res.append("\\\\");
|
||||
else if (c == '"')
|
||||
res.append("\\\"");
|
||||
else res.append(c);
|
||||
}
|
||||
|
||||
return res.append('"').toString();
|
||||
}
|
||||
if (el.isList()) {
|
||||
var res = new StringBuilder().append("[");
|
||||
for (int i = 0; i < el.list().size(); i++) {
|
||||
if (i != 0) res.append(",");
|
||||
res.append(stringify(el.list().get(i)));
|
||||
}
|
||||
res.append("]");
|
||||
return res.toString();
|
||||
}
|
||||
if (el.isMap()) {
|
||||
var res = new StringBuilder().append("{");
|
||||
var entries = el.map().entrySet().stream().collect(Collectors.toList());
|
||||
|
||||
for (int i = 0; i < entries.size(); i++) {
|
||||
if (i != 0) res.append(",");
|
||||
res.append(stringify(JSONElement.string(entries.get(i).getKey())));
|
||||
res.append(":");
|
||||
res.append(stringify(entries.get(i).getValue()));
|
||||
}
|
||||
res.append("}");
|
||||
return res.toString();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
public static String stringify(JSONMap map) {
|
||||
return stringify(JSONElement.of(map));
|
||||
}
|
||||
public static String stringify(JSONList list) {
|
||||
return stringify(JSONElement.of(list));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package me.topchetoeu.jscript.common.json;
|
||||
|
||||
public class JSONElement {
|
||||
public static enum Type {
|
||||
STRING,
|
||||
NUMBER,
|
||||
BOOLEAN,
|
||||
NULL,
|
||||
LIST,
|
||||
MAP,
|
||||
}
|
||||
|
||||
public static final JSONElement NULL = new JSONElement(Type.NULL, null);
|
||||
|
||||
public static JSONElement map(JSONMap val) {
|
||||
return new JSONElement(Type.MAP, val);
|
||||
}
|
||||
public static JSONElement list(JSONList val) {
|
||||
return new JSONElement(Type.LIST, val);
|
||||
}
|
||||
public static JSONElement string(String val) {
|
||||
return new JSONElement(Type.STRING, val);
|
||||
}
|
||||
public static JSONElement number(double val) {
|
||||
return new JSONElement(Type.NUMBER, val);
|
||||
}
|
||||
public static JSONElement bool(boolean val) {
|
||||
return new JSONElement(Type.BOOLEAN, val);
|
||||
}
|
||||
|
||||
public static JSONElement of(Object val) {
|
||||
if (val instanceof JSONElement el) return el;
|
||||
else if (val instanceof JSONMap map) return map(map);
|
||||
else if (val instanceof JSONList list) return list(list);
|
||||
else if (val instanceof String str) return string(str);
|
||||
else if (val instanceof Boolean bool) return bool(bool);
|
||||
else if (val instanceof Number num) return number(num.doubleValue());
|
||||
else if (val == null) return NULL;
|
||||
else throw new IllegalArgumentException("val must be: String, Boolean, Number, JSONList or JSONMap");
|
||||
}
|
||||
|
||||
public final Type type;
|
||||
private final Object value;
|
||||
|
||||
public boolean isMap() { return type == Type.MAP; }
|
||||
public boolean isList() { return type == Type.LIST; }
|
||||
public boolean isString() { return type == Type.STRING; }
|
||||
public boolean isNumber() { return type == Type.NUMBER; }
|
||||
public boolean isBoolean() { return type == Type.BOOLEAN; }
|
||||
public boolean isNull() { return type == Type.NULL; }
|
||||
|
||||
public JSONMap map() {
|
||||
if (!isMap()) throw new IllegalStateException("Element is not a map");
|
||||
return (JSONMap)value;
|
||||
}
|
||||
public JSONList list() {
|
||||
if (!isList()) throw new IllegalStateException("Element is not a map");
|
||||
return (JSONList)value;
|
||||
}
|
||||
public String string() {
|
||||
if (!isString()) throw new IllegalStateException("Element is not a string");
|
||||
return (String)value;
|
||||
}
|
||||
public double number() {
|
||||
if (!isNumber()) throw new IllegalStateException("Element is not a number");
|
||||
return (double)value;
|
||||
}
|
||||
public boolean bool() {
|
||||
if (!isBoolean()) throw new IllegalStateException("Element is not a boolean");
|
||||
return (boolean)value;
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
if (isMap()) return "{...}";
|
||||
if (isList()) return "[...]";
|
||||
if (isString()) return (String)value;
|
||||
if (isString()) return (String)value;
|
||||
if (isNumber()) return (double)value + "";
|
||||
if (isBoolean()) return (boolean)value + "";
|
||||
if (isNull()) return "null";
|
||||
return "";
|
||||
}
|
||||
|
||||
private JSONElement(Type type, Object val) {
|
||||
this.type = type;
|
||||
this.value = val;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package me.topchetoeu.jscript.common.json;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
|
||||
public class JSONList extends ArrayList<JSONElement> {
|
||||
public JSONList() {}
|
||||
public JSONList(JSONElement ...els) {
|
||||
super(Arrays.asList(els));
|
||||
}
|
||||
public JSONList(Collection<JSONElement> els) {
|
||||
super(els);
|
||||
}
|
||||
|
||||
public JSONList addNull() { this.add(JSONElement.NULL); return this; }
|
||||
public JSONList add(String val) { this.add(JSONElement.of(val)); return this; }
|
||||
public JSONList add(double val) { this.add(JSONElement.of(val)); return this; }
|
||||
public JSONList add(boolean val) { this.add(JSONElement.of(val)); return this; }
|
||||
public JSONList add(Map<String, JSONElement> val) { this.add(JSONElement.of(val)); return this; }
|
||||
public JSONList add(Collection<JSONElement> val) { this.add(JSONElement.of(val)); return this; }
|
||||
public JSONList add(JSONMap val) { this.add(JSONElement.of(val)); return this; }
|
||||
public JSONList add(JSONList val) { this.add(JSONElement.of(val)); return this; }
|
||||
|
||||
}
|
||||
136
src/main/java/me/topchetoeu/jscript/common/json/JSONMap.java
Normal file
136
src/main/java/me/topchetoeu/jscript/common/json/JSONMap.java
Normal file
@@ -0,0 +1,136 @@
|
||||
package me.topchetoeu.jscript.common.json;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class JSONMap implements Map<String, JSONElement> {
|
||||
private Map<String, JSONElement> elements = new HashMap<>();
|
||||
|
||||
public JSONElement get(String path) {
|
||||
JSONElement curr = JSONElement.map(this);
|
||||
|
||||
for (var seg : path.split("\\.")) {
|
||||
if (!curr.isMap()) return null;
|
||||
curr = curr.map().elements.get(seg);
|
||||
}
|
||||
|
||||
return curr;
|
||||
}
|
||||
|
||||
public boolean isMap(String path) {
|
||||
var el = get(path);
|
||||
return el != null && el.isMap();
|
||||
}
|
||||
public boolean isList(String path) {
|
||||
var el = get(path);
|
||||
return el != null && el.isList();
|
||||
}
|
||||
public boolean isString(String path) {
|
||||
var el = get(path);
|
||||
return el != null && el.isString();
|
||||
}
|
||||
public boolean isNumber(String path) {
|
||||
var el = get(path);
|
||||
return el != null && el.isNumber();
|
||||
}
|
||||
public boolean isBoolean(String path) {
|
||||
var el = get(path);
|
||||
return el != null && el.isBoolean();
|
||||
}
|
||||
public boolean isNull(String path) {
|
||||
var el = get(path);
|
||||
return el != null && el.isNull();
|
||||
}
|
||||
public boolean contains(String path) {
|
||||
return get(path) != null;
|
||||
}
|
||||
|
||||
public JSONMap map(String path) {
|
||||
var el = get(path);
|
||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path));
|
||||
return el.map();
|
||||
}
|
||||
public JSONMap map(String path, JSONMap defaultVal) {
|
||||
var el = get(path);
|
||||
if (el == null) return defaultVal;
|
||||
if (el.isMap()) return el.map();
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
public JSONList list(String path) {
|
||||
var el = get(path);
|
||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path));
|
||||
return el.list();
|
||||
}
|
||||
public JSONList list(String path, JSONList defaultVal) {
|
||||
var el = get(path);
|
||||
if (el == null) return defaultVal;
|
||||
if (el.isList()) return el.list();
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
public String string(String path) {
|
||||
var el = get(path);
|
||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path));
|
||||
return el.string();
|
||||
}
|
||||
public String string(String path, String defaultVal) {
|
||||
var el = get(path);
|
||||
if (el == null) return defaultVal;
|
||||
if (el.isString()) return el.string();
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
public double number(String path) {
|
||||
var el = get(path);
|
||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path));
|
||||
return el.number();
|
||||
}
|
||||
public double number(String path, double defaultVal) {
|
||||
var el = get(path);
|
||||
if (el == null) return defaultVal;
|
||||
if (el.isNumber()) return el.number();
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
public boolean bool(String path) {
|
||||
var el = get(path);
|
||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path));
|
||||
return el.bool();
|
||||
}
|
||||
public boolean bool(String path, boolean defaultVal) {
|
||||
var el = get(path);
|
||||
if (el == null) return defaultVal;
|
||||
if (el.isBoolean()) return el.bool();
|
||||
return defaultVal;
|
||||
}
|
||||
|
||||
public JSONMap setNull(String key) { elements.put(key, JSONElement.NULL); return this; }
|
||||
public JSONMap set(String key, String val) { elements.put(key, JSONElement.of(val)); return this; }
|
||||
public JSONMap set(String key, double val) { elements.put(key, JSONElement.of(val)); return this; }
|
||||
public JSONMap set(String key, boolean val) { elements.put(key, JSONElement.of(val)); return this; }
|
||||
public JSONMap set(String key, Map<String, JSONElement> val) { elements.put(key, JSONElement.of(val)); return this; }
|
||||
public JSONMap set(String key, Collection<JSONElement> val) { elements.put(key, JSONElement.of(val)); return this; }
|
||||
|
||||
@Override public int size() { return elements.size(); }
|
||||
@Override public boolean isEmpty() { return elements.isEmpty(); }
|
||||
@Override public boolean containsKey(Object key) { return elements.containsKey(key); }
|
||||
@Override public boolean containsValue(Object value) { return elements.containsValue(value); }
|
||||
@Override public JSONElement get(Object key) { return elements.get(key); }
|
||||
@Override public JSONElement put(String key, JSONElement value) { return elements.put(key, value); }
|
||||
@Override public JSONElement remove(Object key) { return elements.remove(key); }
|
||||
@Override public void putAll(Map<? extends String, ? extends JSONElement> m) { elements.putAll(m); }
|
||||
|
||||
@Override public void clear() { elements.clear(); }
|
||||
|
||||
@Override public Set<String> keySet() { return elements.keySet(); }
|
||||
@Override public Collection<JSONElement> values() { return elements.values(); }
|
||||
@Override public Set<Entry<String, JSONElement>> entrySet() { return elements.entrySet(); }
|
||||
|
||||
public JSONMap() { }
|
||||
public JSONMap(Map<String, JSONElement> els) {
|
||||
this.elements = new HashMap<>(els);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package me.topchetoeu.jscript.common.mapping;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.Objects;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.common.parsing.Filename;
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
|
||||
public class FunctionMap {
|
||||
public static class FunctionMapBuilder {
|
||||
private final TreeMap<Integer, Location> sourceMap = new TreeMap<>();
|
||||
private final HashMap<Location, BreakpointType> breakpoints = new HashMap<>();
|
||||
|
||||
public Location toLocation(int pc) {
|
||||
return sourceMap.headMap(pc, true).firstEntry().getValue();
|
||||
}
|
||||
|
||||
public FunctionMapBuilder setDebug(Location loc, BreakpointType type) {
|
||||
if (loc == null || type == null || type == BreakpointType.NONE) return this;
|
||||
breakpoints.put(loc, type);
|
||||
return this;
|
||||
}
|
||||
public FunctionMapBuilder setLocation(int i, Location loc) {
|
||||
if (loc == null || i < 0) return this;
|
||||
sourceMap.put(i, loc);
|
||||
return this;
|
||||
}
|
||||
public FunctionMapBuilder setLocationAndDebug(int i, Location loc, BreakpointType type) {
|
||||
setDebug(loc, type);
|
||||
setLocation(i, loc);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Location first() {
|
||||
if (sourceMap.size() == 0) return null;
|
||||
return sourceMap.firstEntry().getValue();
|
||||
}
|
||||
public Location last() {
|
||||
if (sourceMap.size() == 0) return null;
|
||||
return sourceMap.lastEntry().getValue();
|
||||
}
|
||||
|
||||
public FunctionMapBuilder map(Function<Location, Location> mapper) {
|
||||
var newSourceMaps = new HashMap<Integer, Location>();
|
||||
var newBreakpoints = new HashMap<Location, BreakpointType>();
|
||||
|
||||
for (var key : sourceMap.keySet()) {
|
||||
var mapped = mapper.apply(sourceMap.get(key));
|
||||
if (mapped == null) continue;
|
||||
newSourceMaps.put(key, mapped);
|
||||
}
|
||||
for (var key : breakpoints.keySet()) {
|
||||
var mapped = mapper.apply(key);
|
||||
if (mapped == null) continue;
|
||||
newBreakpoints.put(mapped, breakpoints.get(key));
|
||||
}
|
||||
|
||||
sourceMap.clear();
|
||||
sourceMap.putAll(newSourceMaps);
|
||||
|
||||
breakpoints.clear();
|
||||
breakpoints.putAll(newBreakpoints);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public FunctionMap build(String[] localNames, String[] capturableNames, String[] captureNames) {
|
||||
return new FunctionMap(sourceMap, breakpoints, localNames, capturableNames, captureNames);
|
||||
}
|
||||
public FunctionMap build(Function<Location, Location> mapper) {
|
||||
return new FunctionMap(sourceMap, breakpoints, new String[0], new String[0], new String[0]);
|
||||
}
|
||||
|
||||
private FunctionMapBuilder() { }
|
||||
}
|
||||
|
||||
public static final FunctionMap EMPTY = new FunctionMap();
|
||||
|
||||
private final HashMap<Integer, BreakpointType> bps = new HashMap<>();
|
||||
private final HashMap<Filename, TreeSet<Location>> bpLocs = new HashMap<>();
|
||||
|
||||
private final TreeMap<Integer, Location> pcToLoc = new TreeMap<>();
|
||||
|
||||
public final String[] localNames, capturableNames, captureNames;
|
||||
|
||||
public Location toLocation(int pc, boolean approxiamte) {
|
||||
if (pcToLoc.size() == 0 || pc < 0 || pc > pcToLoc.lastKey()) return null;
|
||||
var res = pcToLoc.get(pc);
|
||||
if (!approxiamte || res != null) return res;
|
||||
var entry = pcToLoc.headMap(pc, true).lastEntry();
|
||||
if (entry == null) return null;
|
||||
else return entry.getValue();
|
||||
}
|
||||
public Location toLocation(int pc) {
|
||||
return toLocation(pc, false);
|
||||
}
|
||||
|
||||
public BreakpointType getBreakpoint(int pc) {
|
||||
return bps.getOrDefault(pc, BreakpointType.NONE);
|
||||
}
|
||||
public Location correctBreakpoint(Location loc) {
|
||||
var set = bpLocs.get(loc.filename());
|
||||
if (set == null) return null;
|
||||
else return set.ceiling(loc);
|
||||
}
|
||||
public List<Location> correctBreakpoint(Pattern filename, int line, int column) {
|
||||
var candidates = new HashMap<Filename, TreeSet<Location>>();
|
||||
|
||||
for (var name : bpLocs.keySet()) {
|
||||
if (filename.matcher(name.toString()).matches()) {
|
||||
candidates.put(name, bpLocs.get(name));
|
||||
}
|
||||
}
|
||||
|
||||
var res = new ArrayList<Location>(candidates.size());
|
||||
for (var candidate : candidates.entrySet()) {
|
||||
var val = correctBreakpoint(Location.of(candidate.getKey(), line, column));
|
||||
if (val == null) continue;
|
||||
res.add(val);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
public List<Location> breakpoints(Location start, Location end) {
|
||||
if (!Objects.equals(start.filename(), end.filename())) return Arrays.asList();
|
||||
NavigableSet<Location> set = bpLocs.get(start.filename());
|
||||
if (set == null) return Arrays.asList();
|
||||
|
||||
if (start != null) set = set.tailSet(start, true);
|
||||
if (end != null) set = set.headSet(end, true);
|
||||
|
||||
return set.stream().collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public Location start() {
|
||||
if (pcToLoc.size() == 0) return null;
|
||||
return pcToLoc.firstEntry().getValue();
|
||||
}
|
||||
public Location end() {
|
||||
if (pcToLoc.size() == 0) return null;
|
||||
return pcToLoc.lastEntry().getValue();
|
||||
}
|
||||
|
||||
public FunctionMap clone() {
|
||||
var res = new FunctionMap(new HashMap<>(), new HashMap<>(), localNames, capturableNames, captureNames);
|
||||
res.pcToLoc.putAll(this.pcToLoc);
|
||||
res.bps.putAll(bps);
|
||||
res.bpLocs.putAll(bpLocs);
|
||||
res.pcToLoc.putAll(pcToLoc);
|
||||
return res;
|
||||
}
|
||||
|
||||
public FunctionMap(Map<Integer, Location> map, Map<Location, BreakpointType> breakpoints, String[] localNames, String[] capturableNames, String[] captureNames) {
|
||||
var locToPc = new HashMap<Location, Integer>();
|
||||
|
||||
for (var el : map.entrySet()) {
|
||||
pcToLoc.put(el.getKey(), el.getValue());
|
||||
locToPc.putIfAbsent(el.getValue(), el.getKey());
|
||||
}
|
||||
|
||||
for (var el : breakpoints.entrySet()) {
|
||||
if (el.getValue() == null || el.getValue() == BreakpointType.NONE) continue;
|
||||
bps.put(locToPc.get(el.getKey()), el.getValue());
|
||||
|
||||
if (!bpLocs.containsKey(el.getKey().filename())) bpLocs.put(el.getKey().filename(), new TreeSet<>());
|
||||
bpLocs.get(el.getKey().filename()).add(el.getKey());
|
||||
}
|
||||
|
||||
this.localNames = localNames;
|
||||
this.captureNames = captureNames;
|
||||
this.capturableNames = capturableNames;
|
||||
}
|
||||
private FunctionMap() {
|
||||
localNames = new String[0];
|
||||
captureNames = new String[0];
|
||||
capturableNames = new String[0];
|
||||
}
|
||||
|
||||
public static FunctionMapBuilder builder() {
|
||||
return new FunctionMapBuilder();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package me.topchetoeu.jscript.common.parsing;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class Filename {
|
||||
public final String protocol;
|
||||
public final String path;
|
||||
|
||||
@Override public String toString() {
|
||||
return protocol + "://" + path;
|
||||
}
|
||||
@Override public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + protocol.hashCode();
|
||||
result = prime * result + path.hashCode();
|
||||
return result;
|
||||
}
|
||||
@Override public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (obj == null) return false;
|
||||
if (getClass() != obj.getClass()) return false;
|
||||
|
||||
var other = (Filename) obj;
|
||||
|
||||
if (protocol == null) {
|
||||
if (other.protocol != null) return false;
|
||||
}
|
||||
else if (!protocol.equals(other.protocol)) return false;
|
||||
|
||||
if (path == null) {
|
||||
if (other.path != null) return false;
|
||||
}
|
||||
else if (!path.equals(other.path)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
public Filename(String protocol, String path) {
|
||||
path = path.trim();
|
||||
protocol = protocol.trim();
|
||||
this.protocol = protocol;
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public static Filename parse(String val) {
|
||||
var i = val.indexOf("://");
|
||||
if (i >= 0) return new Filename(val.substring(0, i).trim(), val.substring(i + 3).trim());
|
||||
else return new Filename("file", val.trim());
|
||||
}
|
||||
public static Filename fromFile(File file) {
|
||||
return new Filename("file", file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
111
src/main/java/me/topchetoeu/jscript/common/parsing/Location.java
Normal file
111
src/main/java/me/topchetoeu/jscript/common/parsing/Location.java
Normal file
@@ -0,0 +1,111 @@
|
||||
package me.topchetoeu.jscript.common.parsing;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class Location implements Comparable<Location> {
|
||||
public static final Location INTERNAL = Location.of(new Filename("jscript", "native"), -1, -1);
|
||||
|
||||
public abstract int line();
|
||||
public abstract int start();
|
||||
public abstract Filename filename();
|
||||
|
||||
public final String toString() {
|
||||
var res = new ArrayList<String>();
|
||||
|
||||
if (filename() != null) res.add(filename().toString());
|
||||
if (line() >= 0) res.add(line() + 1 + "");
|
||||
if (start() >= 0) res.add(start() + 1 + "");
|
||||
|
||||
return String.join(":", res);
|
||||
}
|
||||
|
||||
public final Location add(int n) {
|
||||
var self = this;
|
||||
|
||||
return new Location() {
|
||||
@Override public Filename filename() { return self.filename(); }
|
||||
@Override public int start() { return self.start() + n; }
|
||||
@Override public int line() { return self.line(); }
|
||||
};
|
||||
}
|
||||
public final Location nextLine() {
|
||||
return changeLine(1);
|
||||
}
|
||||
public final Location changeLine(int offset) {
|
||||
var self = this;
|
||||
|
||||
return new Location() {
|
||||
@Override public Filename filename() { return self.filename(); }
|
||||
@Override public int start() { return 0; }
|
||||
@Override public int line() { return self.line() + offset; }
|
||||
};
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
return Objects.hash(line(), start(), filename());
|
||||
}
|
||||
@Override public boolean equals(Object obj) {
|
||||
if (this == obj) return true;
|
||||
if (!(obj instanceof Location)) return false;
|
||||
var other = (Location)obj;
|
||||
|
||||
if (!Objects.equals(this.start(), other.start())) return false;
|
||||
if (!Objects.equals(this.line(), other.line())) return false;
|
||||
if (!Objects.equals(this.filename(), other.filename())) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public int compareTo(Location other) {
|
||||
int a = filename().toString().compareTo(other.filename().toString());
|
||||
int b = Integer.compare(line(), other.line());
|
||||
int c = Integer.compare(start(), other.start());
|
||||
|
||||
if (a != 0) return a;
|
||||
if (b != 0) return b;
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
public static Location of(Filename filename, int line, int start) {
|
||||
return new Location() {
|
||||
@Override public Filename filename() { return filename; }
|
||||
@Override public int start() { return start; }
|
||||
@Override public int line() { return line; }
|
||||
};
|
||||
}
|
||||
|
||||
public static Location of(String raw) {
|
||||
var i0 = raw.lastIndexOf(':');
|
||||
if (i0 < 0) return Location.of(Filename.parse(raw), -1, -1);
|
||||
|
||||
var i1 = raw.lastIndexOf(':', i0);
|
||||
if (i0 < 0) {
|
||||
try {
|
||||
return Location.of(Filename.parse(raw.substring(0, i0)), Integer.parseInt(raw.substring(i0 + 1)), -1);
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
return Location.of(Filename.parse(raw), -1, -1);
|
||||
}
|
||||
}
|
||||
|
||||
int start, line;
|
||||
|
||||
try {
|
||||
start = Integer.parseInt(raw.substring(i1 + 1));
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
return Location.of(Filename.parse(raw), -1, -1);
|
||||
}
|
||||
|
||||
try {
|
||||
line = Integer.parseInt(raw.substring(i0 + 1, i1));
|
||||
}
|
||||
catch (NumberFormatException e) {
|
||||
return Location.of(Filename.parse(raw.substring(i1 + 1)), start, -1);
|
||||
}
|
||||
|
||||
return Location.of(Filename.parse(raw.substring(0, i0)), start, line);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package me.topchetoeu.jscript.common.parsing;
|
||||
|
||||
public class ParseRes<T> {
|
||||
public static enum State {
|
||||
SUCCESS,
|
||||
FAILED,
|
||||
ERROR;
|
||||
|
||||
public boolean isSuccess() { return this == SUCCESS; }
|
||||
public boolean isFailed() { return this == FAILED; }
|
||||
public boolean isError() { return this == ERROR; }
|
||||
}
|
||||
|
||||
public final ParseRes.State state;
|
||||
public final Location errorLocation;
|
||||
public final String error;
|
||||
public final T result;
|
||||
public final int n;
|
||||
|
||||
private ParseRes(ParseRes.State state, Location errorLocation, String error, T result, int readN) {
|
||||
this.result = result;
|
||||
this.n = readN;
|
||||
this.state = state;
|
||||
this.error = error;
|
||||
this.errorLocation = errorLocation;
|
||||
}
|
||||
|
||||
public ParseRes<T> setN(int i) {
|
||||
if (!state.isSuccess()) return this;
|
||||
return new ParseRes<>(state, null, null, result, i);
|
||||
}
|
||||
public ParseRes<T> addN(int n) {
|
||||
if (!state.isSuccess()) return this;
|
||||
return new ParseRes<>(state, null, null, result, this.n + n);
|
||||
}
|
||||
public <T2> ParseRes<T2> chainError() {
|
||||
if (isSuccess()) throw new RuntimeException("Can't transform a ParseRes that hasn't failed");
|
||||
return new ParseRes<>(state, errorLocation, error, null, 0);
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T2> ParseRes<T2> chainError(ParseRes<?> other) {
|
||||
if (!this.isError()) return other.chainError();
|
||||
return (ParseRes<T2>) this;
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T2> ParseRes<T2> chainError(Location loc, String error) {
|
||||
if (!this.isError()) return new ParseRes<>(State.ERROR, loc, error, null, 0);
|
||||
return (ParseRes<T2>) this;
|
||||
}
|
||||
|
||||
public boolean isSuccess() { return state.isSuccess(); }
|
||||
public boolean isFailed() { return state.isFailed(); }
|
||||
public boolean isError() { return state.isError(); }
|
||||
|
||||
public static <T> ParseRes<T> failed() {
|
||||
return new ParseRes<T>(State.FAILED, null, null, null, 0);
|
||||
}
|
||||
public static <T> ParseRes<T> error(Location loc, String error) {
|
||||
// TODO: differentiate definitive and probable errors
|
||||
return new ParseRes<>(State.ERROR, loc, error, null, 0);
|
||||
}
|
||||
public static <T> ParseRes<T> res(T val, int i) {
|
||||
return new ParseRes<>(State.SUCCESS, null, null, val, i);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
@SuppressWarnings("all")
|
||||
public static <T> ParseRes<T> first(Source src, int i, Parser ...parsers) {
|
||||
int n = Parsing.skipEmpty(src, i);
|
||||
ParseRes<T> error = ParseRes.failed();
|
||||
|
||||
for (var parser : parsers) {
|
||||
var res = parser.parse(src, i + n);
|
||||
if (res.isSuccess()) return res.addN(n);
|
||||
if (res.isError() && error.isFailed()) error = res.chainError();
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package me.topchetoeu.jscript.common.parsing;
|
||||
|
||||
public interface Parser<T> {
|
||||
public ParseRes<T> parse(Source src, int i);
|
||||
}
|
||||
452
src/main/java/me/topchetoeu/jscript/common/parsing/Parsing.java
Normal file
452
src/main/java/me/topchetoeu/jscript/common/parsing/Parsing.java
Normal file
@@ -0,0 +1,452 @@
|
||||
package me.topchetoeu.jscript.common.parsing;
|
||||
|
||||
import me.topchetoeu.jscript.common.SyntaxException;
|
||||
import me.topchetoeu.jscript.compilation.values.constants.NumberNode;
|
||||
|
||||
public class Parsing {
|
||||
public static boolean isDigit(Character c) {
|
||||
return c != null && c >= '0' && c <= '9';
|
||||
}
|
||||
public static boolean isAny(char c, String alphabet) {
|
||||
return alphabet.contains(Character.toString(c));
|
||||
}
|
||||
|
||||
public static int fromHex(char c) {
|
||||
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
|
||||
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int skipEmpty(Source src, int i) {
|
||||
return skipEmpty(src, i, true);
|
||||
}
|
||||
|
||||
public static int skipEmpty(Source src, int i, boolean noComments) {
|
||||
int n = 0;
|
||||
|
||||
if (i == 0 && src.is(0, "#!")) {
|
||||
while (!src.is(n, '\n')) n++;
|
||||
n++;
|
||||
}
|
||||
|
||||
var isSingle = false;
|
||||
var isMulti = false;
|
||||
|
||||
while (i + n < src.size()) {
|
||||
if (isSingle) {
|
||||
if (src.is(i + n, '\n')) {
|
||||
n++;
|
||||
isSingle = false;
|
||||
}
|
||||
else n++;
|
||||
}
|
||||
else if (isMulti) {
|
||||
if (src.is(i + n, "*/")) {
|
||||
n += 2;
|
||||
isMulti = false;
|
||||
}
|
||||
else n++;
|
||||
}
|
||||
else if (src.is(i + n, "//")) {
|
||||
n += 2;
|
||||
isSingle = true;
|
||||
}
|
||||
else if (src.is(i + n, "/*")) {
|
||||
n += 2;
|
||||
isMulti = true;
|
||||
}
|
||||
else if (src.is(i + n, Character::isWhitespace)) {
|
||||
n++;
|
||||
}
|
||||
else break;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
public static ParseRes<Character> parseChar(Source src, int i) {
|
||||
int n = 0;
|
||||
|
||||
if (src.is(i + n, '\\')) {
|
||||
n++;
|
||||
char c = src.at(i + n++);
|
||||
|
||||
if (c == 'b') return ParseRes.res('\b', n);
|
||||
else if (c == 't') return ParseRes.res('\t', n);
|
||||
else if (c == 'n') return ParseRes.res('\n', n);
|
||||
else if (c == 'f') return ParseRes.res('\f', n);
|
||||
else if (c == 'r') return ParseRes.res('\r', n);
|
||||
else if (c == '0') {
|
||||
if (src.is(i + n, Parsing::isDigit)) return ParseRes.error(src.loc(i), "Octal escape sequences are not allowed");
|
||||
else return ParseRes.res('\0', n);
|
||||
}
|
||||
else if (c >= '1' && c <= '9') return ParseRes.error(src.loc(i), "Octal escape sequences are not allowed");
|
||||
else if (c == 'x') {
|
||||
var newC = 0;
|
||||
|
||||
for (var j = 0; j < 2; j++) {
|
||||
if (i + n >= src.size()) return ParseRes.error(src.loc(i), "Invalid hexadecimal escape sequence");
|
||||
|
||||
int val = fromHex(src.at(i + n));
|
||||
if (val == -1) throw new SyntaxException(src.loc(i + n), "Invalid hexadecimal escape sequence");
|
||||
n++;
|
||||
|
||||
newC = (newC << 4) | val;
|
||||
}
|
||||
|
||||
return ParseRes.res((char)newC, n);
|
||||
}
|
||||
else if (c == 'u') {
|
||||
var newC = 0;
|
||||
|
||||
for (var j = 0; j < 4; j++) {
|
||||
if (i + n >= src.size()) return ParseRes.error(src.loc(i), "Invalid Unicode escape sequence");
|
||||
|
||||
int val = fromHex(src.at(i + n));
|
||||
if (val == -1) throw new SyntaxException(src.loc(i + n), "Invalid Unicode escape sequence");
|
||||
n++;
|
||||
|
||||
newC = (newC << 4) | val;
|
||||
}
|
||||
|
||||
return ParseRes.res((char)newC, n);
|
||||
}
|
||||
else if (c == '\n') return ParseRes.res(null, n);
|
||||
else n--;
|
||||
}
|
||||
|
||||
return ParseRes.res(src.at(i + n), n + 1);
|
||||
}
|
||||
|
||||
public static ParseRes<String> parseIdentifier(Source src, int i) {
|
||||
var n = skipEmpty(src, i);
|
||||
var res = new StringBuilder();
|
||||
var first = true;
|
||||
|
||||
while (true) {
|
||||
if (i + n > src.size()) break;
|
||||
char c = src.at(i + n, '\0');
|
||||
|
||||
if (first && Parsing.isDigit(c)) break;
|
||||
if (!Character.isLetterOrDigit(c) && c != '_' && c != '$') break;
|
||||
res.append(c);
|
||||
n++;
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (res.length() <= 0) return ParseRes.failed();
|
||||
else return ParseRes.res(res.toString(), n);
|
||||
}
|
||||
public static ParseRes<String> parseIdentifier(Source src, int i, String test) {
|
||||
var n = skipEmpty(src, i);
|
||||
var res = new StringBuilder();
|
||||
var first = true;
|
||||
|
||||
while (true) {
|
||||
if (i + n > src.size()) break;
|
||||
char c = src.at(i + n, '\0');
|
||||
|
||||
if (first && Parsing.isDigit(c)) break;
|
||||
if (!Character.isLetterOrDigit(c) && c != '_' && c != '$') break;
|
||||
res.append(c);
|
||||
n++;
|
||||
first = false;
|
||||
}
|
||||
|
||||
if (res.length() <= 0) return ParseRes.failed();
|
||||
else if (test == null || res.toString().equals(test)) return ParseRes.res(res.toString(), n);
|
||||
else return ParseRes.failed();
|
||||
}
|
||||
public static boolean isIdentifier(Source src, int i, String test) {
|
||||
return parseIdentifier(src, i, test).isSuccess();
|
||||
}
|
||||
|
||||
public static ParseRes<String> parseOperator(Source src, int i, String op) {
|
||||
var n = skipEmpty(src, i);
|
||||
|
||||
if (src.is(i + n, op)) return ParseRes.res(op, n + op.length());
|
||||
else return ParseRes.failed();
|
||||
}
|
||||
|
||||
private static ParseRes<Double> parseHex(Source src, int i) {
|
||||
int n = 0;
|
||||
double res = 0;
|
||||
|
||||
while (true) {
|
||||
int digit = Parsing.fromHex(src.at(i + n, '\0'));
|
||||
if (digit < 0) {
|
||||
if (n <= 0) return ParseRes.failed();
|
||||
else return ParseRes.res(res, n);
|
||||
}
|
||||
n++;
|
||||
|
||||
res *= 16;
|
||||
res += digit;
|
||||
}
|
||||
}
|
||||
private static ParseRes<Double> parseOct(Source src, int i) {
|
||||
int n = 0;
|
||||
double res = 0;
|
||||
|
||||
while (true) {
|
||||
int digit = src.at(i + n, '\0') - '0';
|
||||
if (digit < 0 || digit > 9) break;
|
||||
if (digit > 7) return ParseRes.error(src.loc(i + n), "Digits in octal literals must be from 0 to 7, encountered " + digit);
|
||||
|
||||
if (digit < 0) {
|
||||
if (n <= 0) return ParseRes.failed();
|
||||
else return ParseRes.res(res, n);
|
||||
}
|
||||
n++;
|
||||
|
||||
res *= 8;
|
||||
res += digit;
|
||||
}
|
||||
|
||||
return ParseRes.res(res, n);
|
||||
}
|
||||
private static ParseRes<Double> parseBin(Source src, int i) {
|
||||
int n = 0;
|
||||
double res = 0;
|
||||
|
||||
while (true) {
|
||||
int digit = src.at(i + n, '\0') - '0';
|
||||
if (digit < 0 || digit > 9) break;
|
||||
if (digit > 1) return ParseRes.error(src.loc(i + n), "Digits in binary literals must be from 0 to 1, encountered " + digit);
|
||||
|
||||
if (digit < 0) {
|
||||
if (n <= 0) return ParseRes.failed();
|
||||
else return ParseRes.res(res, n);
|
||||
}
|
||||
n++;
|
||||
|
||||
res *= 2;
|
||||
res += digit;
|
||||
}
|
||||
|
||||
return ParseRes.res(res, n);
|
||||
}
|
||||
|
||||
public static ParseRes<String> parseString(Source src, int i) {
|
||||
var n = skipEmpty(src, i);
|
||||
|
||||
char quote;
|
||||
|
||||
if (src.is(i + n, '\'')) quote = '\'';
|
||||
else if (src.is(i + n, '"')) quote = '"';
|
||||
else return ParseRes.failed();
|
||||
n++;
|
||||
|
||||
var res = new StringBuilder();
|
||||
|
||||
while (true) {
|
||||
if (i + n >= src.size()) return ParseRes.error(src.loc(i + n), "Unterminated string literal");
|
||||
if (src.is(i + n, quote)) {
|
||||
n++;
|
||||
return ParseRes.res(res.toString(), n);
|
||||
}
|
||||
|
||||
var charRes = parseChar(src, i + n);
|
||||
if (!charRes.isSuccess()) return charRes.chainError(src.loc(i + n), "Invalid character");
|
||||
n += charRes.n;
|
||||
|
||||
if (charRes.result != null) res.append(charRes.result);
|
||||
}
|
||||
}
|
||||
public static ParseRes<Double> parseNumber(Source src, int i, boolean withMinus) {
|
||||
var n = skipEmpty(src, i);
|
||||
|
||||
double whole = 0;
|
||||
double fract = 0;
|
||||
long exponent = 0;
|
||||
boolean parsedAny = false;
|
||||
boolean negative = false;
|
||||
|
||||
if (withMinus && src.is(i + n, "-")) {
|
||||
negative = true;
|
||||
n++;
|
||||
}
|
||||
|
||||
if (src.is(i + n, "0x") || src.is(i + n, "0X")) {
|
||||
n += 2;
|
||||
|
||||
var res = parseHex(src, i + n);
|
||||
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete hexadecimal literal");
|
||||
n += res.n;
|
||||
|
||||
if (negative) return ParseRes.res(-res.result, n);
|
||||
else return ParseRes.res(res.result, n);
|
||||
}
|
||||
else if (src.is(i + n, "0o") || src.is(i + n, "0O")) {
|
||||
n += 2;
|
||||
|
||||
var res = parseOct(src, i + n);
|
||||
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete octal literal");
|
||||
n += res.n;
|
||||
|
||||
if (negative) return ParseRes.res(-res.result, n);
|
||||
else return ParseRes.res(res.result, n);
|
||||
}
|
||||
else if (src.is(i + n, "0b") || src.is(i + n, "0B")) {
|
||||
n += 2;
|
||||
|
||||
var res = parseBin(src, i + n);
|
||||
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete binary literal");
|
||||
n += res.n;
|
||||
|
||||
if (negative) return ParseRes.res(-res.result, n);
|
||||
else return ParseRes.res(res.result, n);
|
||||
}
|
||||
else if (src.is(i + n, '0')) {
|
||||
n++;
|
||||
parsedAny = true;
|
||||
if (src.is(i + n, Parsing::isDigit)) return ParseRes.error(src.loc(i + n), "Decimals with leading zeroes are not allowed");
|
||||
}
|
||||
|
||||
while (src.is(i + n, Parsing::isDigit)) {
|
||||
parsedAny = true;
|
||||
whole *= 10;
|
||||
whole += src.at(i + n++) - '0';
|
||||
}
|
||||
|
||||
if (src.is(i + n, '.')) {
|
||||
parsedAny = true;
|
||||
n++;
|
||||
|
||||
while (src.is(i + n, Parsing::isDigit)) {
|
||||
fract += src.at(i + n++) - '0';
|
||||
fract /= 10;
|
||||
}
|
||||
}
|
||||
|
||||
if (parsedAny && src.is(i + n, 'e') || src.is(i + n, 'E')) {
|
||||
n++;
|
||||
boolean expNegative = false;
|
||||
boolean parsedE = false;
|
||||
|
||||
if (src.is(i + n, '-')) {
|
||||
expNegative = true;
|
||||
n++;
|
||||
}
|
||||
else if (src.is(i + n, '+')) n++;
|
||||
|
||||
while (src.is(i + n, Parsing::isDigit)) {
|
||||
parsedE = true;
|
||||
exponent *= 10;
|
||||
|
||||
if (expNegative) exponent -= src.at(i + n++) - '0';
|
||||
else exponent += src.at(i + n++) - '0';
|
||||
}
|
||||
|
||||
if (!parsedE) return ParseRes.error(src.loc(i + n), "Incomplete number exponent");
|
||||
}
|
||||
|
||||
if (!parsedAny) {
|
||||
if (negative) return ParseRes.error(src.loc(i + n), "Expected number immediatly after minus");
|
||||
return ParseRes.failed();
|
||||
}
|
||||
else if (negative) return ParseRes.res(-(whole + fract) * NumberNode.power(10, exponent), n);
|
||||
else return ParseRes.res((whole + fract) * NumberNode.power(10, exponent), n);
|
||||
}
|
||||
public static ParseRes<Double> parseFloat(Source src, int i, boolean withMinus) {
|
||||
var n = skipEmpty(src, i);
|
||||
|
||||
double whole = 0;
|
||||
double fract = 0;
|
||||
long exponent = 0;
|
||||
boolean parsedAny = false;
|
||||
boolean negative = false;
|
||||
|
||||
if (withMinus && src.is(i + n, "-")) {
|
||||
negative = true;
|
||||
n++;
|
||||
}
|
||||
|
||||
while (src.is(i + n, Parsing::isDigit)) {
|
||||
parsedAny = true;
|
||||
whole *= 10;
|
||||
whole += src.at(i + n++) - '0';
|
||||
}
|
||||
|
||||
if (src.is(i + n, '.')) {
|
||||
parsedAny = true;
|
||||
n++;
|
||||
|
||||
while (src.is(i + n, Parsing::isDigit)) {
|
||||
fract += src.at(i + n++) - '0';
|
||||
fract /= 10;
|
||||
}
|
||||
}
|
||||
|
||||
if (src.is(i + n, 'e') || src.is(i + n, 'E')) {
|
||||
n++;
|
||||
parsedAny = true;
|
||||
boolean expNegative = false;
|
||||
boolean parsedE = false;
|
||||
|
||||
if (src.is(i + n, '-')) {
|
||||
expNegative = true;
|
||||
n++;
|
||||
}
|
||||
else if (src.is(i + n, '+')) n++;
|
||||
|
||||
while (src.is(i + n, Parsing::isDigit)) {
|
||||
parsedE = true;
|
||||
exponent *= 10;
|
||||
|
||||
if (expNegative) exponent -= src.at(i + n++) - '0';
|
||||
else exponent += src.at(i + n++) - '0';
|
||||
}
|
||||
|
||||
if (!parsedE) return ParseRes.error(src.loc(i + n), "Incomplete number exponent");
|
||||
}
|
||||
|
||||
if (!parsedAny) {
|
||||
if (negative) return ParseRes.error(src.loc(i + n), "Expected number immediatly after minus");
|
||||
return ParseRes.failed();
|
||||
}
|
||||
else if (negative) return ParseRes.res(-(whole + fract) * NumberNode.power(10, exponent), n);
|
||||
else return ParseRes.res((whole + fract) * NumberNode.power(10, exponent), n);
|
||||
}
|
||||
public static ParseRes<Double> parseInt(Source src, int i, String alphabet, boolean withMinus) {
|
||||
var n = skipEmpty(src, i);
|
||||
|
||||
double result = 0;
|
||||
boolean parsedAny = false;
|
||||
boolean negative = false;
|
||||
|
||||
if (withMinus && src.is(i + n, "-")) {
|
||||
negative = true;
|
||||
n++;
|
||||
}
|
||||
|
||||
if (alphabet == null && src.is(i + n, "0x") || src.is(i + n, "0X")) {
|
||||
n += 2;
|
||||
|
||||
var res = parseHex(src, i);
|
||||
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete hexadecimal literal");
|
||||
n += res.n;
|
||||
|
||||
if (negative) return ParseRes.res(-res.result, n);
|
||||
else return ParseRes.res(res.result, n);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
var digit = alphabet.indexOf(Character.toLowerCase(src.at(i + n, '\0')));
|
||||
if (digit < 0) break;
|
||||
|
||||
parsedAny = true;
|
||||
result *= alphabet.length();
|
||||
result += digit;
|
||||
n++;
|
||||
}
|
||||
|
||||
if (!parsedAny) {
|
||||
if (negative) return ParseRes.error(src.loc(i + n), "Expected number immediatly after minus");
|
||||
return ParseRes.failed();
|
||||
}
|
||||
else if (negative) return ParseRes.res(-result, n);
|
||||
else return ParseRes.res(result, n);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package me.topchetoeu.jscript.common.parsing;
|
||||
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import me.topchetoeu.jscript.common.environment.Environment;
|
||||
|
||||
public class Source {
|
||||
public final Environment env;
|
||||
public final Filename filename;
|
||||
public final String src;
|
||||
|
||||
private int[] lineStarts;
|
||||
|
||||
public Location loc(int offset) {
|
||||
return new SourceLocation(filename, lineStarts, offset);
|
||||
}
|
||||
public boolean is(int i, char c) {
|
||||
return i >= 0 && i < src.length() && src.charAt(i) == c;
|
||||
}
|
||||
public boolean is(int i, String src) {
|
||||
if (i < 0 || i + src.length() > size()) return false;
|
||||
|
||||
for (int j = 0; j < src.length(); j++) {
|
||||
if (at(i + j) != src.charAt(j)) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
public boolean is(int i, Predicate<Character> predicate) {
|
||||
if (i < 0 || i >= src.length()) return false;
|
||||
return predicate.test(at(i));
|
||||
}
|
||||
public char at(int i) {
|
||||
return src.charAt(i);
|
||||
}
|
||||
public char at(int i, char defaultVal) {
|
||||
if (i < 0 || i >= src.length()) return defaultVal;
|
||||
else return src.charAt(i);
|
||||
}
|
||||
public int size() {
|
||||
return src.length();
|
||||
}
|
||||
public String slice(int start, int end) {
|
||||
return src.substring(start, end);
|
||||
}
|
||||
|
||||
public Source(Environment env, Filename filename, String src) {
|
||||
if (env == null) this.env = new Environment();
|
||||
else this.env = env;
|
||||
|
||||
this.filename = filename;
|
||||
this.src = src;
|
||||
|
||||
int n = 1;
|
||||
lineStarts = new int[16];
|
||||
lineStarts[0] = 0;
|
||||
|
||||
for (int i = src.indexOf("\n"); i > 0; i = src.indexOf("\n", i + 1)) {
|
||||
if (n >= lineStarts.length) {
|
||||
var newArr = new int[lineStarts.length * 2];
|
||||
System.arraycopy(lineStarts, 0, newArr, 0, n);
|
||||
lineStarts = newArr;
|
||||
}
|
||||
|
||||
lineStarts[n++] = i + 1;
|
||||
}
|
||||
|
||||
var newArr = new int[n];
|
||||
System.arraycopy(lineStarts, 0, newArr, 0, n);
|
||||
lineStarts = newArr;
|
||||
}
|
||||
public Source(String src) {
|
||||
this(null, null, src);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package me.topchetoeu.jscript.common.parsing;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
public class SourceLocation extends Location {
|
||||
private int[] lineStarts;
|
||||
private int line;
|
||||
private int start;
|
||||
private final Filename filename;
|
||||
private final int offset;
|
||||
|
||||
private void update() {
|
||||
if (lineStarts == null) return;
|
||||
|
||||
int a = 0;
|
||||
int b = lineStarts.length;
|
||||
|
||||
while (true) {
|
||||
if (a + 1 >= b) break;
|
||||
var mid = -((-a - b) >> 1);
|
||||
var el = lineStarts[mid];
|
||||
|
||||
if (el < offset) a = mid;
|
||||
else if (el > offset) b = mid;
|
||||
else {
|
||||
this.line = mid;
|
||||
this.start = 0;
|
||||
this.lineStarts = null;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.line = a;
|
||||
this.start = offset - lineStarts[a];
|
||||
this.lineStarts = null;
|
||||
return;
|
||||
}
|
||||
|
||||
@Override public Filename filename() { return filename; }
|
||||
@Override public int line() {
|
||||
update();
|
||||
return line;
|
||||
}
|
||||
@Override public int start() {
|
||||
update();
|
||||
return start;
|
||||
}
|
||||
|
||||
@Override public int hashCode() {
|
||||
return Objects.hash(offset);
|
||||
}
|
||||
@Override public int compareTo(Location other) {
|
||||
if (other instanceof SourceLocation srcLoc) return Integer.compare(offset, srcLoc.offset);
|
||||
else return super.compareTo(other);
|
||||
}
|
||||
@Override public boolean equals(Object obj) {
|
||||
if (obj instanceof SourceLocation other) return this.offset == other.offset;
|
||||
else return super.equals(obj);
|
||||
}
|
||||
|
||||
public SourceLocation(Filename filename, int[] lineStarts, int offset) {
|
||||
this.filename = filename;
|
||||
this.lineStarts = lineStarts;
|
||||
this.offset = offset;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package me.topchetoeu.jscript.compilation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import me.topchetoeu.jscript.common.FunctionBody;
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.common.environment.Environment;
|
||||
import me.topchetoeu.jscript.common.environment.Key;
|
||||
import me.topchetoeu.jscript.common.mapping.FunctionMap;
|
||||
import me.topchetoeu.jscript.common.mapping.FunctionMap.FunctionMapBuilder;
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
import me.topchetoeu.jscript.compilation.control.TryNode;
|
||||
import me.topchetoeu.jscript.compilation.scope.FunctionScope;
|
||||
import me.topchetoeu.jscript.compilation.scope.Variable;
|
||||
|
||||
public final class CompileResult {
|
||||
public static final Key<Void> DEBUG_LOG = new Key<>();
|
||||
|
||||
public final List<Instruction> instructions;
|
||||
public final List<CompileResult> children;
|
||||
public final Map<FunctionNode, CompileResult> childrenMap = new HashMap<>();
|
||||
public final Map<FunctionNode, Integer> childrenIndices = new HashMap<>();
|
||||
public final FunctionMapBuilder map;
|
||||
public final Environment env;
|
||||
public int length;
|
||||
public final FunctionScope scope;
|
||||
public final Map<TryNode, Variable> catchBindings = new HashMap<>();
|
||||
|
||||
public int temp() {
|
||||
instructions.add(null);
|
||||
return instructions.size() - 1;
|
||||
}
|
||||
|
||||
public CompileResult add(Instruction instr) {
|
||||
instructions.add(instr);
|
||||
return this;
|
||||
}
|
||||
public CompileResult set(int i, Instruction instr) {
|
||||
instructions.set(i, instr);
|
||||
return this;
|
||||
}
|
||||
|
||||
public int size() { return instructions.size(); }
|
||||
|
||||
public void setDebug(Location loc, BreakpointType type) {
|
||||
map.setDebug(loc, type);
|
||||
}
|
||||
public void setLocation(int i, Location loc) {
|
||||
map.setLocation(i, loc);
|
||||
}
|
||||
public void setLocationAndDebug(int i, Location loc, BreakpointType type) {
|
||||
map.setLocationAndDebug(i, loc, type);
|
||||
}
|
||||
public void setDebug(BreakpointType type) {
|
||||
setDebug(map.last(), type);
|
||||
}
|
||||
public void setLocation(Location type) {
|
||||
setLocation(instructions.size() - 1, type);
|
||||
}
|
||||
public void setLocationAndDebug(Location loc, BreakpointType type) {
|
||||
setLocationAndDebug(instructions.size() - 1, loc, type);
|
||||
}
|
||||
|
||||
public CompileResult addChild(FunctionNode node, CompileResult res) {
|
||||
this.children.add(res);
|
||||
this.childrenMap.put(node, res);
|
||||
this.childrenIndices.put(node, this.children.size() - 1);
|
||||
return res;
|
||||
}
|
||||
|
||||
public Instruction[] instructions() {
|
||||
return instructions.toArray(new Instruction[0]);
|
||||
}
|
||||
|
||||
public FunctionMap map(Function<Location, Location> mapper) {
|
||||
return map.map(mapper).build(scope.localNames(), scope.capturableNames(), scope.captureNames());
|
||||
}
|
||||
public FunctionMap map() {
|
||||
return map.build(scope.localNames(), scope.capturableNames(), scope.captureNames());
|
||||
}
|
||||
public FunctionBody body() {
|
||||
var builtChildren = new FunctionBody[children.size()];
|
||||
for (var i = 0; i < children.size(); i++) builtChildren[i] = children.get(i).body();
|
||||
|
||||
var instrRes = instructions();
|
||||
|
||||
if (env.has(DEBUG_LOG)) {
|
||||
System.out.println("================= BODY =================");
|
||||
System.out.println("LOCALS: " + scope.localsCount());
|
||||
System.out.println("CAPTURABLES: " + scope.capturablesCount());
|
||||
System.out.println("CAPTURES: " + scope.capturesCount());
|
||||
|
||||
for (var instr : instrRes) System.out.println(instr);
|
||||
}
|
||||
|
||||
return new FunctionBody(
|
||||
scope.localsCount(), scope.capturablesCount(), scope.capturesCount(),
|
||||
length, instrRes, builtChildren
|
||||
);
|
||||
}
|
||||
|
||||
public CompileResult subtarget() {
|
||||
return new CompileResult(env, new FunctionScope(scope), this);
|
||||
}
|
||||
|
||||
public CompileResult setEnvironment(Environment env) {
|
||||
return new CompileResult(env, scope, this);
|
||||
}
|
||||
/**
|
||||
* Returns a compile result with a child of the environment that relates to the given key.
|
||||
* In essence, this is used to create a compile result which is back at the root environment of the compilation
|
||||
*/
|
||||
public CompileResult rootEnvironment(Key<Environment> env) {
|
||||
return new CompileResult(this.env.get(env).child(), scope, this);
|
||||
}
|
||||
public CompileResult subEnvironment() {
|
||||
return new CompileResult(env.child(), scope, this);
|
||||
}
|
||||
|
||||
public CompileResult(Environment env, FunctionScope scope, int length) {
|
||||
this.scope = scope;
|
||||
this.instructions = new ArrayList<>();
|
||||
this.children = new LinkedList<>();
|
||||
this.map = FunctionMap.builder();
|
||||
this.env = env;
|
||||
this.length = length;
|
||||
}
|
||||
private CompileResult(Environment env, FunctionScope scope, CompileResult parent) {
|
||||
this.scope = scope;
|
||||
this.instructions = parent.instructions;
|
||||
this.children = parent.children;
|
||||
this.map = parent.map;
|
||||
this.env = env;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package me.topchetoeu.jscript.compilation;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||
import me.topchetoeu.jscript.common.parsing.Source;
|
||||
|
||||
|
||||
public class CompoundNode extends Node {
|
||||
public final Node[] statements;
|
||||
public Location end;
|
||||
|
||||
@Override public void resolve(CompileResult target) {
|
||||
for (var stm : statements) stm.resolve(target);
|
||||
}
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
for (var stm : statements) stm.compileFunctions(target);
|
||||
}
|
||||
|
||||
public void compile(CompileResult target, boolean pollute, BreakpointType type) {
|
||||
List<Node> statements = new ArrayList<Node>();
|
||||
|
||||
for (var stm : this.statements) {
|
||||
if (stm instanceof FunctionStatementNode func) {
|
||||
func.compile(target, false);
|
||||
}
|
||||
else statements.add(stm);
|
||||
}
|
||||
|
||||
var polluted = false;
|
||||
|
||||
for (var i = 0; i < statements.size(); i++) {
|
||||
var stm = statements.get(i);
|
||||
|
||||
if (i != statements.size() - 1) stm.compile(target, false, BreakpointType.STEP_OVER);
|
||||
else stm.compile(target, polluted = pollute, BreakpointType.STEP_OVER);
|
||||
}
|
||||
|
||||
if (!polluted && pollute) {
|
||||
target.add(Instruction.pushUndefined());
|
||||
}
|
||||
}
|
||||
|
||||
public CompoundNode setEnd(Location loc) {
|
||||
this.end = loc;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CompoundNode(Location loc, Node ...statements) {
|
||||
super(loc);
|
||||
this.statements = statements;
|
||||
}
|
||||
|
||||
public static ParseRes<CompoundNode> parseComma(Source src, int i, Node prev, int precedence) {
|
||||
if (precedence > 1) return ParseRes.failed();
|
||||
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
if (!src.is(i + n, ",")) return ParseRes.failed();
|
||||
n++;
|
||||
|
||||
var curr = JavaScript.parseExpression(src, i + n, 2);
|
||||
if (!curr.isSuccess()) return curr.chainError(src.loc(i + n), "Expected a value after the comma");
|
||||
n += curr.n;
|
||||
|
||||
if (prev instanceof CompoundNode comp) {
|
||||
var children = new ArrayList<Node>();
|
||||
children.addAll(Arrays.asList(comp.statements));
|
||||
children.add(curr.result);
|
||||
|
||||
return ParseRes.res(new CompoundNode(loc, children.toArray(new Node[0])), n);
|
||||
}
|
||||
else return ParseRes.res(new CompoundNode(loc, prev, curr.result), n);
|
||||
}
|
||||
public static ParseRes<CompoundNode> parse(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
||||
if (!src.is(i + n, "{")) return ParseRes.failed();
|
||||
n++;
|
||||
|
||||
var statements = new ArrayList<Node>();
|
||||
|
||||
while (true) {
|
||||
n += Parsing.skipEmpty(src, i + n);
|
||||
|
||||
if (src.is(i + n, "}")) {
|
||||
n++;
|
||||
break;
|
||||
}
|
||||
if (src.is(i + n, ";")) {
|
||||
n++;
|
||||
continue;
|
||||
}
|
||||
|
||||
var res = JavaScript.parseStatement(src, i + n);
|
||||
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a statement");
|
||||
n += res.n;
|
||||
|
||||
statements.add(res.result);
|
||||
}
|
||||
|
||||
return ParseRes.res(new CompoundNode(loc, statements.toArray(new Node[0])).setEnd(src.loc(i + n - 1)), n);
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user