Compare commits
242 Commits
v0.0.1-alp
...
0.9.10-bet
| Author | SHA1 | Date | |
|---|---|---|---|
|
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
|
|||
| 292d08d5a6 | |||
|
692fae4049
|
|||
|
3824d11c97
|
|||
|
7a226c49d3
|
|||
|
8f0c0226a8
|
|||
|
0343c97d5e
|
|||
|
fea17d944b
|
|||
|
8fb01e1091
|
|||
|
1ce5fc9d99
|
|||
|
0e04459fe7
|
|||
|
78b192babe
|
|||
|
38656bd654
|
|||
|
29d3f378a5
|
|||
|
d1b37074a6
|
|||
| a1e07a8046 | |||
| 54c6af52cf |
10
.gitattributes
vendored
10
.gitattributes
vendored
@@ -1,9 +1 @@
|
||||
#
|
||||
# https://help.github.com/articles/dealing-with-line-endings/
|
||||
#
|
||||
# Linux start script should use lf
|
||||
/gradlew text eol=lf
|
||||
|
||||
# These are Windows script files and should use crlf
|
||||
*.bat text eol=crlf
|
||||
|
||||
* -text
|
||||
20
.github/workflows/tagged-release.yml
vendored
20
.github/workflows/tagged-release.yml
vendored
@@ -11,20 +11,22 @@ jobs:
|
||||
runs-on: "ubuntu-latest"
|
||||
|
||||
steps:
|
||||
- name: Setup Java
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'adopt'
|
||||
java-version: '11'
|
||||
- name: Setup Gradle
|
||||
uses: gradle/gradle-build-action@v2
|
||||
- name: Clone repository
|
||||
uses: GuillaumeFalourd/clone-github-repo-action@main
|
||||
with:
|
||||
branch: 'master'
|
||||
owner: 'TopchetoEU'
|
||||
repository: 'java-jscript'
|
||||
- name: "Build"
|
||||
- name: Build
|
||||
run: |
|
||||
cd java-jscript;
|
||||
ls;
|
||||
mkdir -p dst/classes;
|
||||
rsync -av --exclude='*.java' src/ dst/classes;
|
||||
tsc --outDir dst/classes/me/topchetoeu/jscript/js --declarationDir dst/classes/me/topchetoeu/jscript/dts;
|
||||
find src -name "*.java" | xargs javac -d dst/classes;
|
||||
jar -c -f dst/jscript.jar -e me.topchetoeu.jscript.Main -C dst/classes .
|
||||
cd java-jscript; gradle build
|
||||
|
||||
- uses: "marvinpinto/action-automatic-releases@latest"
|
||||
with:
|
||||
@@ -32,4 +34,4 @@ jobs:
|
||||
prerelease: false
|
||||
files: |
|
||||
java-jscript/LICENSE
|
||||
java-jscript/dst/*.jar
|
||||
java-jscript/build/libs/*.jar
|
||||
32
.gitignore
vendored
32
.gitignore
vendored
@@ -1,8 +1,24 @@
|
||||
.vscode
|
||||
.gradle
|
||||
.ignore
|
||||
out
|
||||
build
|
||||
bin
|
||||
dst
|
||||
/*.js
|
||||
*
|
||||
|
||||
!/src
|
||||
!/src/**/*
|
||||
|
||||
!/doc
|
||||
!/doc/**/*
|
||||
|
||||
!/tests
|
||||
!/tests/**/*
|
||||
|
||||
!/.github
|
||||
!/.github/**/*
|
||||
|
||||
!/.gitignore
|
||||
!/.gitattributes
|
||||
!/LICENSE
|
||||
!/README.md
|
||||
!/settings.gradle
|
||||
!/build.gradle
|
||||
!/gradle.properties
|
||||
!/gradle
|
||||
!/gradle/wrapper
|
||||
!/gradle/wrapper/gradle-wrapper.properties
|
||||
37
README.md
37
README.md
@@ -2,33 +2,28 @@
|
||||
|
||||
**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());
|
||||
```
|
||||
|
||||
## NOTE:
|
||||
|
||||
To setup the typescript bundle in your sources, run `node build.js init-ts`. This will download the latest version of typescript, minify it, and add it to your src folder. If you are going to work with the `node build.js debug|release` command, this is not a necessary step.
|
||||
|
||||
32
build.gradle
Normal file
32
build.gradle
Normal file
@@ -0,0 +1,32 @@
|
||||
plugins {
|
||||
id "application"
|
||||
}
|
||||
|
||||
java {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(11)
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
jar {
|
||||
manifest.attributes["Main-class"] = project.main_class
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs = [ "src/java" ]
|
||||
main.resources.srcDirs = [ "src/assets" ]
|
||||
}
|
||||
|
||||
processResources {
|
||||
filesMatching "metadata.json", {
|
||||
expand(
|
||||
version: project.project_version,
|
||||
name: project.project_name
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
base.archivesName = project.project_name
|
||||
version = project.project_version
|
||||
group = project.project_group
|
||||
4
gradle.properties
Normal file
4
gradle.properties
Normal file
@@ -0,0 +1,4 @@
|
||||
project_group = me.topchetoeu
|
||||
project_name = jscript
|
||||
project_version = 0.9.10-beta
|
||||
main_class = me.topchetoeu.jscript.utils.JScriptRepl
|
||||
7
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
7
gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
191
lib/core.ts
191
lib/core.ts
@@ -1,191 +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 }
|
||||
|
||||
interface IArguments {
|
||||
[i: number]: any;
|
||||
length: number;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
//@ts-ignore
|
||||
declare const arguments: IArguments;
|
||||
declare const Math: MathObject;
|
||||
|
||||
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;
|
||||
|
||||
/** @internal */
|
||||
declare var internals: any;
|
||||
/** @internal */
|
||||
declare function run(file: string, pollute?: boolean): void;
|
||||
|
||||
/** @internal */
|
||||
type ReplaceThis<T, ThisT> = T extends ((...args: infer ArgsT) => infer RetT) ?
|
||||
((this: ThisT, ...args: ArgsT) => RetT) :
|
||||
T;
|
||||
|
||||
/** @internal */
|
||||
declare var setProps: <
|
||||
TargetT extends object,
|
||||
DescT extends { [x in Exclude<keyof TargetT, 'constructor'> ]?: ReplaceThis<TargetT[x], TargetT> }
|
||||
>(target: TargetT, desc: DescT) => void;
|
||||
/** @internal */
|
||||
declare var setConstr: <ConstrT, T extends { constructor: ConstrT }>(target: T, constr: ConstrT) => void;
|
||||
|
||||
declare function log(...vals: any[]): void;
|
||||
/** @internal */
|
||||
declare var lgt: typeof globalThis, gt: typeof globalThis;
|
||||
|
||||
declare function assert(condition: () => unknown, message?: string): boolean;
|
||||
|
||||
gt.assert = (cond, msg) => {
|
||||
try {
|
||||
if (!cond()) throw 'condition not satisfied';
|
||||
log('Passed ' + msg);
|
||||
return true;
|
||||
}
|
||||
catch (e) {
|
||||
log('Failed ' + msg + ' because of: ' + e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
lgt.setProps = (target, desc) => {
|
||||
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
|
||||
);
|
||||
}
|
||||
}
|
||||
lgt.setConstr = (target, constr) => {
|
||||
internals.defineField(
|
||||
target, 'constructor', constr,
|
||||
true, // writable
|
||||
false, // enumerable
|
||||
true // configurable
|
||||
);
|
||||
}
|
||||
|
||||
run('values/object.js');
|
||||
run('values/symbol.js');
|
||||
run('values/function.js');
|
||||
run('values/errors.js');
|
||||
run('values/string.js');
|
||||
run('values/number.js');
|
||||
run('values/boolean.js');
|
||||
run('values/array.js');
|
||||
|
||||
internals.special(Object, Function, Error, Array);
|
||||
|
||||
gt.setTimeout = (func, delay, ...args) => {
|
||||
if (typeof func !== 'function') throw new TypeError("func must be a function.");
|
||||
delay = (delay ?? 0) - 0;
|
||||
return internals.setTimeout(() => func(...args), delay)
|
||||
};
|
||||
gt.setInterval = (func, delay, ...args) => {
|
||||
if (typeof func !== 'function') throw new TypeError("func must be a function.");
|
||||
delay = (delay ?? 0) - 0;
|
||||
return internals.setInterval(() => func(...args), delay)
|
||||
};
|
||||
|
||||
gt.clearTimeout = (id) => {
|
||||
id = id | 0;
|
||||
internals.clearTimeout(id);
|
||||
};
|
||||
gt.clearInterval = (id) => {
|
||||
id = id | 0;
|
||||
internals.clearInterval(id);
|
||||
};
|
||||
|
||||
|
||||
run('iterators.js');
|
||||
run('promise.js');
|
||||
run('map.js', true);
|
||||
run('set.js', true);
|
||||
run('regex.js');
|
||||
run('require.js');
|
||||
|
||||
log('Loaded polyfills!');
|
||||
}
|
||||
catch (e: any) {
|
||||
var 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;
|
||||
}
|
||||
216
lib/iterators.ts
216
lib/iterators.ts
@@ -1,216 +0,0 @@
|
||||
interface SymbolConstructor {
|
||||
readonly iterator: unique symbol;
|
||||
readonly asyncIterator: unique symbol;
|
||||
}
|
||||
|
||||
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 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 Generator<T = unknown, TReturn = any, TNext = unknown> extends Iterator<T, TReturn, TNext> {
|
||||
// NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
|
||||
next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
|
||||
return(value: TReturn): IteratorResult<T, TReturn>;
|
||||
throw(e: any): IteratorResult<T, TReturn>;
|
||||
[Symbol.iterator](): Generator<T, TReturn, TNext>;
|
||||
}
|
||||
|
||||
interface GeneratorFunction {
|
||||
/**
|
||||
* Creates a new Generator object.
|
||||
* @param args A list of arguments the function accepts.
|
||||
*/
|
||||
new (...args: any[]): Generator;
|
||||
/**
|
||||
* Creates a new Generator object.
|
||||
* @param args A list of arguments the function accepts.
|
||||
*/
|
||||
(...args: any[]): Generator;
|
||||
/**
|
||||
* The length of the arguments.
|
||||
*/
|
||||
readonly length: number;
|
||||
/**
|
||||
* Returns the name of the function.
|
||||
*/
|
||||
readonly name: string;
|
||||
/**
|
||||
* A reference to the prototype.
|
||||
*/
|
||||
readonly prototype: Generator;
|
||||
}
|
||||
|
||||
interface GeneratorFunctionConstructor {
|
||||
/**
|
||||
* Creates a new Generator function.
|
||||
* @param args A list of arguments the function accepts.
|
||||
*/
|
||||
new (...args: string[]): GeneratorFunction;
|
||||
/**
|
||||
* Creates a new Generator function.
|
||||
* @param args A list of arguments the function accepts.
|
||||
*/
|
||||
(...args: string[]): GeneratorFunction;
|
||||
/**
|
||||
* The length of the arguments.
|
||||
*/
|
||||
readonly length: number;
|
||||
/**
|
||||
* Returns the name of the function.
|
||||
*/
|
||||
readonly name: string;
|
||||
/**
|
||||
* A reference to the prototype.
|
||||
*/
|
||||
}
|
||||
|
||||
interface AsyncIterator<T, TReturn = any, TNext = undefined> {
|
||||
// NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
|
||||
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 AsyncGenerator<T = unknown, TReturn = any, TNext = unknown> extends AsyncIterator<T, TReturn, TNext> {
|
||||
// NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
|
||||
next(...args: [] | [TNext]): Promise<IteratorResult<T, TReturn>>;
|
||||
return(value: TReturn | Thenable<TReturn>): Promise<IteratorResult<T, TReturn>>;
|
||||
throw(e: any): Promise<IteratorResult<T, TReturn>>;
|
||||
[Symbol.asyncIterator](): AsyncGenerator<T, TReturn, TNext>;
|
||||
}
|
||||
|
||||
interface AsyncGeneratorFunction {
|
||||
/**
|
||||
* Creates a new AsyncGenerator object.
|
||||
* @param args A list of arguments the function accepts.
|
||||
*/
|
||||
new (...args: any[]): AsyncGenerator;
|
||||
/**
|
||||
* Creates a new AsyncGenerator object.
|
||||
* @param args A list of arguments the function accepts.
|
||||
*/
|
||||
(...args: any[]): AsyncGenerator;
|
||||
/**
|
||||
* The length of the arguments.
|
||||
*/
|
||||
readonly length: number;
|
||||
/**
|
||||
* Returns the name of the function.
|
||||
*/
|
||||
readonly name: string;
|
||||
/**
|
||||
* A reference to the prototype.
|
||||
*/
|
||||
readonly prototype: AsyncGenerator;
|
||||
}
|
||||
|
||||
interface AsyncGeneratorFunctionConstructor {
|
||||
/**
|
||||
* Creates a new AsyncGenerator function.
|
||||
* @param args A list of arguments the function accepts.
|
||||
*/
|
||||
new (...args: string[]): AsyncGeneratorFunction;
|
||||
/**
|
||||
* Creates a new AsyncGenerator function.
|
||||
* @param args A list of arguments the function accepts.
|
||||
*/
|
||||
(...args: string[]): AsyncGeneratorFunction;
|
||||
/**
|
||||
* The length of the arguments.
|
||||
*/
|
||||
readonly length: number;
|
||||
/**
|
||||
* Returns the name of the function.
|
||||
*/
|
||||
readonly name: string;
|
||||
/**
|
||||
* A reference to the prototype.
|
||||
*/
|
||||
readonly prototype: AsyncGeneratorFunction;
|
||||
}
|
||||
|
||||
|
||||
interface Array<T> extends IterableIterator<T> {
|
||||
entries(): IterableIterator<[number, T]>;
|
||||
values(): IterableIterator<T>;
|
||||
keys(): IterableIterator<number>;
|
||||
}
|
||||
|
||||
setProps(Symbol, {
|
||||
iterator: Symbol("Symbol.iterator") as any,
|
||||
asyncIterator: Symbol("Symbol.asyncIterator") as any,
|
||||
});
|
||||
|
||||
setProps(Array.prototype, {
|
||||
[Symbol.iterator]: function() {
|
||||
return this.values();
|
||||
},
|
||||
|
||||
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 };
|
||||
},
|
||||
[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 };
|
||||
},
|
||||
[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 };
|
||||
},
|
||||
[Symbol.iterator]() { return this; }
|
||||
};
|
||||
},
|
||||
});
|
||||
44
lib/map.ts
44
lib/map.ts
@@ -1,44 +0,0 @@
|
||||
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();
|
||||
}
|
||||
|
||||
Map.prototype[Symbol.iterator] = function() {
|
||||
return this.entries();
|
||||
};
|
||||
|
||||
var entries = Map.prototype.entries;
|
||||
var keys = Map.prototype.keys;
|
||||
var values = Map.prototype.values;
|
||||
|
||||
Map.prototype.entries = function() {
|
||||
var it = entries.call(this);
|
||||
it[Symbol.iterator] = () => it;
|
||||
return it;
|
||||
};
|
||||
Map.prototype.keys = function() {
|
||||
var it = keys.call(this);
|
||||
it[Symbol.iterator] = () => it;
|
||||
return it;
|
||||
};
|
||||
Map.prototype.values = function() {
|
||||
var it = values.call(this);
|
||||
it[Symbol.iterator] = () => it;
|
||||
return it;
|
||||
};
|
||||
@@ -1,43 +0,0 @@
|
||||
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; }
|
||||
|
||||
interface Thenable<T> {
|
||||
then<NextT>(this: Promise<T>, onFulfilled: PromiseThenFunc<T, NextT>, onRejected?: PromiseRejectFunc): Promise<Awaited<NextT>>;
|
||||
then(this: Promise<T>, onFulfilled: undefined, onRejected?: PromiseRejectFunc): Promise<T>;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
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>;
|
||||
|
||||
any<T>(promises: (Promise<T>|T)[]): Promise<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]>>}]>;
|
||||
}
|
||||
|
||||
interface Promise<T> extends Thenable<T> {
|
||||
constructor: PromiseConstructor;
|
||||
catch(func: PromiseRejectFunc): Promise<T>;
|
||||
finally(func: () => void): Promise<T>;
|
||||
}
|
||||
|
||||
declare var Promise: PromiseConstructor;
|
||||
|
||||
(Promise.prototype as any)[Symbol.typeName] = 'Promise';
|
||||
211
lib/regex.ts
211
lib/regex.ts
@@ -1,211 +0,0 @@
|
||||
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 SymbolConstructor {
|
||||
readonly match: unique symbol;
|
||||
readonly matchAll: unique symbol;
|
||||
readonly split: unique symbol;
|
||||
readonly replace: unique symbol;
|
||||
readonly search: unique symbol;
|
||||
}
|
||||
|
||||
type ReplaceFunc = (match: string, ...args: any[]) => string;
|
||||
|
||||
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): string;
|
||||
}
|
||||
interface Searcher {
|
||||
[Symbol.search](target: string, reverse?: boolean, start?: number): number;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
(Symbol as any).replace = Symbol('Symbol.replace');
|
||||
(Symbol as any).match = Symbol('Symbol.match');
|
||||
(Symbol as any).matchAll = Symbol('Symbol.matchAll');
|
||||
(Symbol as any).split = Symbol('Symbol.split');
|
||||
(Symbol as any).search = Symbol('Symbol.search');
|
||||
|
||||
setProps(RegExp.prototype, {
|
||||
[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;
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -1,15 +0,0 @@
|
||||
type RequireFunc = (path: string) => any;
|
||||
|
||||
interface Module {
|
||||
exports: any;
|
||||
name: string;
|
||||
}
|
||||
|
||||
declare var require: RequireFunc;
|
||||
declare var exports: any;
|
||||
declare var module: Module;
|
||||
|
||||
gt.require = function(path: string) {
|
||||
if (typeof path !== 'string') path = path + '';
|
||||
return internals.require(path);
|
||||
};
|
||||
45
lib/set.ts
45
lib/set.ts
@@ -1,45 +0,0 @@
|
||||
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();
|
||||
}
|
||||
|
||||
Set.prototype[Symbol.iterator] = function() {
|
||||
return this.values();
|
||||
};
|
||||
|
||||
(() => {
|
||||
var entries = Set.prototype.entries;
|
||||
var keys = Set.prototype.keys;
|
||||
var values = Set.prototype.values;
|
||||
|
||||
Set.prototype.entries = function() {
|
||||
var it = entries.call(this);
|
||||
it[Symbol.iterator] = () => it;
|
||||
return it;
|
||||
};
|
||||
Set.prototype.keys = function() {
|
||||
var it = keys.call(this);
|
||||
it[Symbol.iterator] = () => it;
|
||||
return it;
|
||||
};
|
||||
Set.prototype.values = function() {
|
||||
var it = values.call(this);
|
||||
it[Symbol.iterator] = () => it;
|
||||
return it;
|
||||
};
|
||||
})();
|
||||
@@ -1,369 +0,0 @@
|
||||
// god this is awful
|
||||
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 Array<T> {
|
||||
[i: number]: T;
|
||||
|
||||
constructor: ArrayConstructor;
|
||||
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;
|
||||
}
|
||||
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>;
|
||||
}
|
||||
|
||||
declare var Array: ArrayConstructor;
|
||||
|
||||
gt.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;
|
||||
|
||||
Array.prototype = ([] as any).__proto__ as Array<any>;
|
||||
setConstr(Array.prototype, Array);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
lgt.wrapI = wrapI;
|
||||
lgt.clampI = clampI;
|
||||
|
||||
(Array.prototype as any)[Symbol.typeName] = "Array";
|
||||
|
||||
setProps(Array.prototype, {
|
||||
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.isArr(val); }
|
||||
});
|
||||
@@ -1,22 +0,0 @@
|
||||
interface Boolean {
|
||||
valueOf(): boolean;
|
||||
constructor: BooleanConstructor;
|
||||
}
|
||||
interface BooleanConstructor {
|
||||
(val: any): boolean;
|
||||
new (val: any): Boolean;
|
||||
prototype: Boolean;
|
||||
}
|
||||
|
||||
declare var Boolean: BooleanConstructor;
|
||||
|
||||
gt.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;
|
||||
|
||||
Boolean.prototype = (false as any).__proto__ as Boolean;
|
||||
setConstr(Boolean.prototype, Boolean);
|
||||
@@ -1,89 +0,0 @@
|
||||
interface Error {
|
||||
constructor: ErrorConstructor;
|
||||
name: string;
|
||||
message: string;
|
||||
stack: 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 {
|
||||
constructor: TypeErrorConstructor;
|
||||
name: 'TypeError';
|
||||
}
|
||||
|
||||
interface RangeErrorConstructor extends ErrorConstructor {
|
||||
(msg?: any): RangeError;
|
||||
new (msg?: any): RangeError;
|
||||
prototype: Error;
|
||||
}
|
||||
interface RangeError extends Error {
|
||||
constructor: RangeErrorConstructor;
|
||||
name: 'RangeError';
|
||||
}
|
||||
|
||||
interface SyntaxErrorConstructor extends ErrorConstructor {
|
||||
(msg?: any): RangeError;
|
||||
new (msg?: any): RangeError;
|
||||
prototype: Error;
|
||||
}
|
||||
interface SyntaxError extends Error {
|
||||
constructor: SyntaxErrorConstructor;
|
||||
name: 'SyntaxError';
|
||||
}
|
||||
|
||||
|
||||
declare var Error: ErrorConstructor;
|
||||
declare var RangeError: RangeErrorConstructor;
|
||||
declare var TypeError: TypeErrorConstructor;
|
||||
declare var SyntaxError: SyntaxErrorConstructor;
|
||||
|
||||
gt.Error = function Error(msg: string) {
|
||||
if (msg === undefined) msg = '';
|
||||
else msg += '';
|
||||
|
||||
return Object.setPrototypeOf({
|
||||
message: msg,
|
||||
stack: [] as string[],
|
||||
}, Error.prototype);
|
||||
} as ErrorConstructor;
|
||||
|
||||
Error.prototype = internals.err ?? {};
|
||||
Error.prototype.name = 'Error';
|
||||
setConstr(Error.prototype, Error);
|
||||
|
||||
Error.prototype.toString = function() {
|
||||
if (!(this instanceof Error)) return '';
|
||||
|
||||
if (this.message === '') return this.name;
|
||||
else return this.name + ': ' + this.message;
|
||||
};
|
||||
|
||||
function makeError<T extends ErrorConstructor>(name: string, proto: any): T {
|
||||
var err = function (msg: string) {
|
||||
var res = new Error(msg);
|
||||
(res as any).__proto__ = err.prototype;
|
||||
return res;
|
||||
} as T;
|
||||
|
||||
err.prototype = proto;
|
||||
err.prototype.name = name;
|
||||
setConstr(err.prototype, err as ErrorConstructor);
|
||||
(err.prototype as any).__proto__ = Error.prototype;
|
||||
(err as any).__proto__ = Error;
|
||||
internals.special(err);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
gt.RangeError = makeError('RangeError', internals.range ?? {});
|
||||
gt.TypeError = makeError('TypeError', internals.type ?? {});
|
||||
gt.SyntaxError = makeError('SyntaxError', internals.syntax ?? {});
|
||||
@@ -1,181 +0,0 @@
|
||||
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;
|
||||
constructor: FunctionConstructor;
|
||||
readonly length: number;
|
||||
name: string;
|
||||
}
|
||||
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 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;
|
||||
}
|
||||
|
||||
declare var Function: FunctionConstructor;
|
||||
|
||||
gt.Function = function() {
|
||||
throw 'Using the constructor Function() is forbidden.';
|
||||
} as unknown as FunctionConstructor;
|
||||
|
||||
Function.prototype = (Function as any).__proto__ as Function;
|
||||
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 (Array.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) {
|
||||
var func = this;
|
||||
|
||||
var res = function() {
|
||||
var resArgs = [];
|
||||
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
resArgs[i] = args[i];
|
||||
}
|
||||
for (var 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 = Function.generator(func as any).apply(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) {
|
||||
const gen = Function.generator<any[], ['await' | 'yield', any]>((_yield) => func(
|
||||
val => _yield(['await', val]) as any,
|
||||
val => _yield(['yield', val])
|
||||
)).apply(this, arguments as 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)),
|
||||
[Symbol.asyncIterator]() { return this; }
|
||||
}
|
||||
}
|
||||
},
|
||||
generator(func) {
|
||||
if (typeof func !== 'function') throw new TypeError('Expected func to be function.');
|
||||
const gen = internals.makeGenerator(func);
|
||||
return (...args: any[]) => {
|
||||
const it = gen(args);
|
||||
|
||||
return {
|
||||
next: it.next,
|
||||
return: it.return,
|
||||
throw: it.throw,
|
||||
[Symbol.iterator]() { return this; }
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -1,50 +0,0 @@
|
||||
interface Number {
|
||||
toString(): string;
|
||||
valueOf(): number;
|
||||
constructor: NumberConstructor;
|
||||
}
|
||||
interface NumberConstructor {
|
||||
(val: any): number;
|
||||
new (val: any): Number;
|
||||
prototype: Number;
|
||||
parseInt(val: unknown): number;
|
||||
parseFloat(val: unknown): number;
|
||||
}
|
||||
|
||||
declare var Number: NumberConstructor;
|
||||
declare var parseInt: typeof Number.parseInt;
|
||||
declare var parseFloat: typeof Number.parseFloat;
|
||||
declare var NaN: number;
|
||||
declare var Infinity: number;
|
||||
|
||||
gt.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;
|
||||
|
||||
Number.prototype = (0 as any).__proto__ as Number;
|
||||
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(Number.parseFloat(val)); },
|
||||
parseFloat(val) { return internals.parseFloat(val); },
|
||||
});
|
||||
|
||||
Object.defineProperty(gt, 'parseInt', { value: Number.parseInt, writable: false });
|
||||
Object.defineProperty(gt, 'parseFloat', { value: Number.parseFloat, writable: false });
|
||||
Object.defineProperty(gt, 'NaN', { value: 0 / 0, writable: false });
|
||||
Object.defineProperty(gt, 'Infinity', { value: 1 / 0, writable: false });
|
||||
@@ -1,234 +0,0 @@
|
||||
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;
|
||||
}
|
||||
|
||||
declare var Object: ObjectConstructor;
|
||||
|
||||
gt.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;
|
||||
|
||||
Object.prototype = ({} as any).__proto__ as Object;
|
||||
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: function(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) {
|
||||
onlyString = !!(onlyString ?? true);
|
||||
return internals.keys(obj, onlyString);
|
||||
},
|
||||
entries(obj, onlyString) {
|
||||
return Object.keys(obj, onlyString).map(v => [ v, (obj as any)[v] ]);
|
||||
},
|
||||
values(obj, onlyString) {
|
||||
return Object.keys(obj, onlyString).map(v => (obj as any)[v]);
|
||||
},
|
||||
|
||||
getOwnPropertyDescriptor(obj, key) {
|
||||
return internals.ownProp(obj, key);
|
||||
},
|
||||
getOwnPropertyDescriptors(obj) {
|
||||
return Object.fromEntries([
|
||||
...Object.getOwnPropertyNames(obj),
|
||||
...Object.getOwnPropertySymbols(obj)
|
||||
].map(v => [ v, Object.getOwnPropertyDescriptor(obj, v) ])) as any;
|
||||
},
|
||||
|
||||
getOwnPropertyNames(obj) {
|
||||
return internals.ownPropKeys(obj, false);
|
||||
},
|
||||
getOwnPropertySymbols(obj) {
|
||||
return internals.ownPropKeys(obj, true);
|
||||
},
|
||||
hasOwn(obj, key) {
|
||||
if (Object.getOwnPropertyNames(obj).includes(key)) return true;
|
||||
if (Object.getOwnPropertySymbols(obj).includes(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.preventExtensions(obj);
|
||||
return obj;
|
||||
},
|
||||
seal(obj) {
|
||||
throwNotObject(obj, 'seal');
|
||||
internals.seal(obj);
|
||||
return obj;
|
||||
},
|
||||
freeze(obj) {
|
||||
throwNotObject(obj, 'freeze');
|
||||
internals.freeze(obj);
|
||||
return obj;
|
||||
},
|
||||
|
||||
isExtensible(obj) {
|
||||
if (!check(obj)) return false;
|
||||
return internals.extensible(obj);
|
||||
},
|
||||
isSealed(obj) {
|
||||
if (!check(obj)) return true;
|
||||
if (Object.isExtensible(obj)) return false;
|
||||
return Object.getOwnPropertyNames(obj).every(v => !Object.getOwnPropertyDescriptor(obj, v).configurable);
|
||||
},
|
||||
isFrozen(obj) {
|
||||
if (!check(obj)) return true;
|
||||
if (Object.isExtensible(obj)) return false;
|
||||
return Object.getOwnPropertyNames(obj).every(v => {
|
||||
var prop = Object.getOwnPropertyDescriptor(obj, v);
|
||||
if ('writable' in prop && prop.writable) return false;
|
||||
return !prop.configurable;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
setProps(Object.prototype, {
|
||||
valueOf() {
|
||||
return this;
|
||||
},
|
||||
toString() {
|
||||
return '[object ' + (this[Symbol.typeName] ?? 'Unknown') + ']';
|
||||
},
|
||||
hasOwnProperty(key) {
|
||||
return Object.hasOwn(this, key);
|
||||
},
|
||||
});
|
||||
@@ -1,261 +0,0 @@
|
||||
interface Replacer {
|
||||
[Symbol.replace](target: string, val: string | ((match: string, ...args: any[]) => string)): string;
|
||||
}
|
||||
|
||||
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): string;
|
||||
replaceAll(pattern: string | Replacer, val: string): 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;
|
||||
|
||||
constructor: StringConstructor;
|
||||
}
|
||||
interface StringConstructor {
|
||||
(val: any): string;
|
||||
new (val: any): String;
|
||||
|
||||
fromCharCode(val: number): string;
|
||||
|
||||
prototype: String;
|
||||
}
|
||||
|
||||
declare var String: StringConstructor;
|
||||
|
||||
gt.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;
|
||||
|
||||
String.prototype = ('' as any).__proto__ as String;
|
||||
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;
|
||||
return internals.substring(this, start, end);
|
||||
},
|
||||
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;
|
||||
return this.substring(start, length + start);
|
||||
},
|
||||
|
||||
toLowerCase() {
|
||||
return internals.toLower(this + '');
|
||||
},
|
||||
toUpperCase() {
|
||||
return internals.toUpper(this + '');
|
||||
},
|
||||
|
||||
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) {
|
||||
var res = this.charAt(pos);
|
||||
if (res === '') return NaN;
|
||||
else return internals.toCharCode(res);
|
||||
},
|
||||
|
||||
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;
|
||||
return internals.startsWith(this, term + '', pos);
|
||||
},
|
||||
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;
|
||||
return internals.endsWith(this, term + '', pos);
|
||||
},
|
||||
|
||||
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[Symbol.search] !== 'function') term = RegExp.escape(term);
|
||||
|
||||
return term[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[Symbol.search] !== 'function') term = RegExp.escape(term);
|
||||
|
||||
return term[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[Symbol.replace] !== 'function') pattern = RegExp.escape(pattern);
|
||||
|
||||
return pattern[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[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[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[Symbol.match] !== 'function') pattern = RegExp.escape(pattern);
|
||||
|
||||
return pattern[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[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[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[Symbol.split] !== 'function') pattern = RegExp.escape(pattern, "g");
|
||||
|
||||
return pattern[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.fromCharCode(val | 0);
|
||||
},
|
||||
})
|
||||
|
||||
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,38 +0,0 @@
|
||||
interface Symbol {
|
||||
valueOf(): symbol;
|
||||
constructor: SymbolConstructor;
|
||||
}
|
||||
interface SymbolConstructor {
|
||||
(val?: any): symbol;
|
||||
prototype: Symbol;
|
||||
for(key: string): symbol;
|
||||
keyFor(sym: symbol): string;
|
||||
readonly typeName: unique symbol;
|
||||
}
|
||||
|
||||
declare var Symbol: SymbolConstructor;
|
||||
|
||||
gt.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, true);
|
||||
} as SymbolConstructor;
|
||||
|
||||
Symbol.prototype = internals.symbolProto;
|
||||
setConstr(Symbol.prototype, Symbol);
|
||||
(Symbol as any).typeName = Symbol("Symbol.name");
|
||||
|
||||
setProps(Symbol, {
|
||||
for(key) {
|
||||
if (typeof key !== 'string' && key !== undefined) throw new TypeError('key must be a string or undefined.');
|
||||
return internals.symbol(key, false);
|
||||
},
|
||||
keyFor(sym) {
|
||||
if (typeof sym !== 'symbol') throw new TypeError('sym must be a symbol.');
|
||||
return internals.symStr(sym);
|
||||
},
|
||||
typeName: Symbol('Symbol.name') as any,
|
||||
});
|
||||
|
||||
Object.defineProperty(Object.prototype, Symbol.typeName, { value: 'Object' });
|
||||
Object.defineProperty(gt, Symbol.typeName, { value: 'Window' });
|
||||
5
settings.gradle
Normal file
5
settings.gradle
Normal file
@@ -0,0 +1,5 @@
|
||||
plugins {
|
||||
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0'
|
||||
}
|
||||
|
||||
rootProject.name = properties.project_name
|
||||
BIN
src/assets/debugger/favicon.png
Normal file
BIN
src/assets/debugger/favicon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
30
src/assets/debugger/index.html
Normal file
30
src/assets/debugger/index.html
Normal file
@@ -0,0 +1,30 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>JScript Debugger</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
This is the debugger of JScript. It implement the <a href="https://chromedevtools.github.io/devtools-protocol/1-2/">V8 Debugging protocol</a>,
|
||||
so you can use the devtools in chrome. <br>
|
||||
The debugger is still in early development, so please report any issues to
|
||||
<a href="https://github.com/TopchetoEU/java-jscript/issues">the github repo</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Here are the available entrypoints:
|
||||
<ul>
|
||||
<li><a href="json/version">/json/version</a> - version and other stuff about the JScript engine</li>
|
||||
<li><a href="json/list">/json/list</a> - a list of all entrypoints</li>
|
||||
<li><a href="json/protocol">/json/protocol</a> - documentation of the implemented V8 protocol</li>
|
||||
<li>/(any target) - websocket entrypoints for debugging</li>
|
||||
</ul>
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Running ${NAME} v${VERSION} by ${AUTHOR}
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
1004
src/assets/debugger/protocol.json
Normal file
1004
src/assets/debugger/protocol.json
Normal file
File diff suppressed because it is too large
Load Diff
5
src/assets/metadata.json
Normal file
5
src/assets/metadata.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"version": "${version}",
|
||||
"name": "${name}",
|
||||
"author": "TopchetoEU"
|
||||
}
|
||||
58
src/java/me/topchetoeu/jscript/common/Buffer.java
Normal file
58
src/java/me/topchetoeu/jscript/common/Buffer.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package me.topchetoeu.jscript.common;
|
||||
|
||||
public class Buffer {
|
||||
private byte[] data;
|
||||
private int length;
|
||||
|
||||
public void write(int i, byte[] val) {
|
||||
if (i + val.length > data.length) {
|
||||
var newCap = i + val.length + 1;
|
||||
if (newCap < data.length * 2) newCap = data.length * 2;
|
||||
|
||||
var tmp = new byte[newCap];
|
||||
System.arraycopy(this.data, 0, tmp, 0, length);
|
||||
this.data = tmp;
|
||||
}
|
||||
|
||||
System.arraycopy(val, 0, data, i, val.length);
|
||||
if (i + val.length > length) length = i + val.length;
|
||||
}
|
||||
public int read(int i, byte[] buff) {
|
||||
int n = buff.length;
|
||||
if (i + n > length) n = length - i;
|
||||
System.arraycopy(data, i, buff, 0, n);
|
||||
return n;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
data = new byte[128];
|
||||
length = 0;
|
||||
}
|
||||
|
||||
public void append(byte b) {
|
||||
write(length, new byte[] { b });
|
||||
}
|
||||
|
||||
public byte[] data() {
|
||||
var res = new byte[length];
|
||||
System.arraycopy(this.data, 0, res, 0, length);
|
||||
return res;
|
||||
}
|
||||
public int length() {
|
||||
return length;
|
||||
}
|
||||
|
||||
public Buffer(byte[] data) {
|
||||
this.data = new byte[data.length];
|
||||
this.length = data.length;
|
||||
System.arraycopy(data, 0, this.data, 0, data.length);
|
||||
}
|
||||
public Buffer(int capacity) {
|
||||
this.data = new byte[capacity];
|
||||
this.length = 0;
|
||||
}
|
||||
public Buffer() {
|
||||
this.data = new byte[128];
|
||||
this.length = 0;
|
||||
}
|
||||
}
|
||||
57
src/java/me/topchetoeu/jscript/common/Filename.java
Normal file
57
src/java/me/topchetoeu/jscript/common/Filename.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package me.topchetoeu.jscript.common;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
||||
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 Path normalize(String path) {
|
||||
return Path.of(Path.of("/" + path.trim().replace("\\", "/")).normalize().toString().substring(1));
|
||||
}
|
||||
public static Filename fromFile(File file) {
|
||||
return new Filename("file", file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
14
src/java/me/topchetoeu/jscript/common/FunctionBody.java
Normal file
14
src/java/me/topchetoeu/jscript/common/FunctionBody.java
Normal file
@@ -0,0 +1,14 @@
|
||||
package me.topchetoeu.jscript.common;
|
||||
|
||||
public class FunctionBody {
|
||||
public final FunctionBody[] children;
|
||||
public final Instruction[] instructions;
|
||||
public final int localsN, argsN;
|
||||
|
||||
public FunctionBody(int localsN, int argsN, Instruction[] instructions, FunctionBody[] children) {
|
||||
this.children = children;
|
||||
this.argsN = argsN;
|
||||
this.localsN = localsN;
|
||||
this.instructions = instructions;
|
||||
}
|
||||
}
|
||||
369
src/java/me/topchetoeu/jscript/common/Instruction.java
Normal file
369
src/java/me/topchetoeu/jscript/common/Instruction.java
Normal file
@@ -0,0 +1,369 @@
|
||||
package me.topchetoeu.jscript.common;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
||||
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
||||
|
||||
public class Instruction {
|
||||
public static enum Type {
|
||||
NOP(0),
|
||||
RETURN(1),
|
||||
THROW(2),
|
||||
THROW_SYNTAX(3),
|
||||
DELETE(4),
|
||||
TRY_START(5),
|
||||
TRY_END(6),
|
||||
|
||||
CALL(7),
|
||||
CALL_NEW(8),
|
||||
JMP_IF(9),
|
||||
JMP_IFN(10),
|
||||
JMP(11),
|
||||
|
||||
PUSH_UNDEFINED(12),
|
||||
PUSH_NULL(13),
|
||||
PUSH_BOOL(14),
|
||||
PUSH_NUMBER(15),
|
||||
PUSH_STRING(16),
|
||||
|
||||
LOAD_VAR(17),
|
||||
LOAD_MEMBER(18),
|
||||
LOAD_GLOB(20),
|
||||
|
||||
LOAD_FUNC(21),
|
||||
LOAD_ARR(22),
|
||||
LOAD_OBJ(23),
|
||||
STORE_SELF_FUNC(24),
|
||||
LOAD_REGEX(25),
|
||||
|
||||
DUP(26),
|
||||
|
||||
STORE_VAR(27),
|
||||
STORE_MEMBER(28),
|
||||
DISCARD(29),
|
||||
|
||||
MAKE_VAR(30),
|
||||
DEF_PROP(31),
|
||||
KEYS(32),
|
||||
|
||||
TYPEOF(33),
|
||||
OPERATION(34);
|
||||
|
||||
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 {
|
||||
NONE,
|
||||
STEP_OVER,
|
||||
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];
|
||||
}
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T get(int i, T defaultVal) {
|
||||
if (i >= params.length || i < 0) return defaultVal;
|
||||
return (T)params[i];
|
||||
}
|
||||
public boolean match(Object ...args) {
|
||||
if (args.length != params.length) return false;
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
var a = params[i];
|
||||
var b = args[i];
|
||||
if (a == null || b == null) {
|
||||
if (!(a == null && b == null)) return false;
|
||||
}
|
||||
if (!a.equals(b)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public boolean is(int i, Object arg) {
|
||||
if (params.length <= i) return false;
|
||||
return params[i].equals(arg);
|
||||
}
|
||||
|
||||
public void write(DataOutputStream writer) throws IOException {
|
||||
var rawType = type.numeric;
|
||||
|
||||
switch (type) {
|
||||
case KEYS:
|
||||
case PUSH_BOOL:
|
||||
case STORE_MEMBER: rawType |= (boolean)get(0) ? 128 : 0; break;
|
||||
case STORE_VAR: rawType |= (boolean)get(1) ? 128 : 0; break;
|
||||
case TYPEOF: rawType |= params.length > 0 ? 128 : 0; break;
|
||||
default:
|
||||
}
|
||||
|
||||
writer.writeByte(rawType);
|
||||
|
||||
switch (type) {
|
||||
case CALL: writer.writeInt(get(0)); break;
|
||||
case CALL_NEW: writer.writeInt(get(0)); break;
|
||||
case DUP: writer.writeInt(get(0)); break;
|
||||
case JMP: writer.writeInt(get(0)); break;
|
||||
case JMP_IF: writer.writeInt(get(0)); break;
|
||||
case JMP_IFN: writer.writeInt(get(0)); break;
|
||||
case LOAD_ARR: writer.writeInt(get(0)); break;
|
||||
case LOAD_FUNC: {
|
||||
writer.writeInt(params.length - 1);
|
||||
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
writer.writeInt(get(i + 1));
|
||||
}
|
||||
|
||||
writer.writeInt(get(0));
|
||||
break;
|
||||
}
|
||||
case LOAD_REGEX: writer.writeUTF(get(0)); break;
|
||||
case LOAD_VAR: writer.writeInt(get(0)); break;
|
||||
case MAKE_VAR: writer.writeUTF(get(0)); break;
|
||||
case OPERATION: writer.writeByte(((Operation)get(0)).numeric); break;
|
||||
case PUSH_NUMBER: writer.writeDouble(get(0)); break;
|
||||
case PUSH_STRING: writer.writeUTF(get(0)); break;
|
||||
case STORE_SELF_FUNC: writer.writeInt(get(0)); break;
|
||||
case STORE_VAR: writer.writeInt(get(0)); break;
|
||||
case THROW_SYNTAX: writer.writeUTF(get(0));
|
||||
case TRY_START:
|
||||
writer.writeInt(get(0));
|
||||
writer.writeInt(get(1));
|
||||
writer.writeInt(get(2));
|
||||
break;
|
||||
case TYPEOF:
|
||||
if (params.length > 0) writer.writeUTF(get(0));
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
private Instruction(Type type, Object ...params) {
|
||||
this.type = type;
|
||||
this.params = params;
|
||||
}
|
||||
|
||||
public static Instruction read(DataInputStream stream) throws IOException {
|
||||
var rawType = stream.readUnsignedByte();
|
||||
var type = Type.fromNumeric(rawType & 127);
|
||||
var flag = (rawType & 128) != 0;
|
||||
|
||||
switch (type) {
|
||||
case CALL: return call(stream.readInt());
|
||||
case CALL_NEW: return callNew(stream.readInt());
|
||||
case DEF_PROP: return defProp();
|
||||
case DELETE: return delete();
|
||||
case DISCARD: return discard();
|
||||
case DUP: return dup(stream.readInt());
|
||||
case JMP: return jmp(stream.readInt());
|
||||
case JMP_IF: return jmpIf(stream.readInt());
|
||||
case JMP_IFN: return jmpIfNot(stream.readInt());
|
||||
case KEYS: return keys(flag);
|
||||
case LOAD_ARR: return loadArr(stream.readInt());
|
||||
case LOAD_FUNC: {
|
||||
var captures = new int[stream.readInt()];
|
||||
|
||||
for (var i = 0; i < captures.length; i++) {
|
||||
captures[i] = stream.readInt();
|
||||
}
|
||||
|
||||
return loadFunc(stream.readInt(), captures);
|
||||
}
|
||||
case LOAD_GLOB: return loadGlob();
|
||||
case LOAD_MEMBER: return loadMember();
|
||||
case LOAD_OBJ: return loadObj();
|
||||
case LOAD_REGEX: return loadRegex(stream.readUTF(), null);
|
||||
case LOAD_VAR: return loadVar(stream.readInt());
|
||||
case MAKE_VAR: return makeVar(stream.readUTF());
|
||||
case OPERATION: return operation(Operation.fromNumeric(stream.readUnsignedByte()));
|
||||
case PUSH_NULL: return pushNull();
|
||||
case PUSH_UNDEFINED: return pushUndefined();
|
||||
case PUSH_BOOL: return pushValue(flag);
|
||||
case PUSH_NUMBER: return pushValue(stream.readDouble());
|
||||
case PUSH_STRING: return pushValue(stream.readUTF());
|
||||
case RETURN: return ret();
|
||||
case STORE_MEMBER: return storeMember(flag);
|
||||
case STORE_SELF_FUNC: return storeSelfFunc(stream.readInt());
|
||||
case STORE_VAR: return storeVar(stream.readInt(), flag);
|
||||
case THROW: return throwInstr();
|
||||
case THROW_SYNTAX: return throwSyntax(stream.readUTF());
|
||||
case TRY_END: return tryEnd();
|
||||
case TRY_START: return tryStart(stream.readInt(), stream.readInt(), stream.readInt());
|
||||
case TYPEOF: return flag ? typeof(stream.readUTF()) : typeof();
|
||||
case NOP:
|
||||
if (flag) return null;
|
||||
else return nop();
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static Instruction tryStart(int catchStart, int finallyStart, int end) {
|
||||
return new Instruction(Type.TRY_START, catchStart, finallyStart, end);
|
||||
}
|
||||
public static Instruction tryEnd() {
|
||||
return new Instruction(Type.TRY_END);
|
||||
}
|
||||
public static Instruction throwInstr() {
|
||||
return new Instruction(Type.THROW);
|
||||
}
|
||||
public static Instruction throwSyntax(SyntaxException err) {
|
||||
return new Instruction(Type.THROW_SYNTAX, err.getMessage());
|
||||
}
|
||||
public static Instruction throwSyntax(String err) {
|
||||
return new Instruction(Type.THROW_SYNTAX, err);
|
||||
}
|
||||
public static Instruction delete() {
|
||||
return new Instruction(Type.DELETE);
|
||||
}
|
||||
public static Instruction ret() {
|
||||
return new Instruction(Type.RETURN);
|
||||
}
|
||||
public static Instruction debug() {
|
||||
return new Instruction(Type.NOP, "debug");
|
||||
}
|
||||
|
||||
public static Instruction nop(Object ...params) {
|
||||
return new Instruction(Type.NOP, params);
|
||||
}
|
||||
|
||||
public static Instruction call(int argn) {
|
||||
return new Instruction(Type.CALL, argn);
|
||||
}
|
||||
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 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 makeVar(String name) {
|
||||
return new Instruction(Type.MAKE_VAR, name);
|
||||
}
|
||||
public static Instruction loadVar(Object i) {
|
||||
return new Instruction(Type.LOAD_VAR, i);
|
||||
}
|
||||
public static Instruction loadGlob() {
|
||||
return new Instruction(Type.LOAD_GLOB);
|
||||
}
|
||||
public static Instruction loadMember() {
|
||||
return new Instruction(Type.LOAD_MEMBER);
|
||||
}
|
||||
|
||||
public static Instruction loadRegex(String pattern, String flags) {
|
||||
return new Instruction(Type.LOAD_REGEX, pattern, flags);
|
||||
}
|
||||
public static Instruction loadFunc(int id, int[] captures) {
|
||||
var args = new Object[1 + captures.length];
|
||||
args[0] = id;
|
||||
for (var i = 0; i < captures.length; i++) args[i + 1] = 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);
|
||||
}
|
||||
public static Instruction dup(int count) {
|
||||
return new Instruction(Type.DUP, count);
|
||||
}
|
||||
|
||||
public static Instruction storeSelfFunc(int i) {
|
||||
return new Instruction(Type.STORE_SELF_FUNC, i);
|
||||
}
|
||||
public static Instruction storeVar(Object i) {
|
||||
return new Instruction(Type.STORE_VAR, i, false);
|
||||
}
|
||||
public static Instruction storeVar(Object i, boolean keep) {
|
||||
return new Instruction(Type.STORE_VAR, i, keep);
|
||||
}
|
||||
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 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 forInFormat) {
|
||||
return new Instruction(Type.KEYS, forInFormat);
|
||||
}
|
||||
|
||||
public static Instruction defProp() {
|
||||
return new Instruction(Type.DEF_PROP);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
101
src/java/me/topchetoeu/jscript/common/Location.java
Normal file
101
src/java/me/topchetoeu/jscript/common/Location.java
Normal file
@@ -0,0 +1,101 @@
|
||||
package me.topchetoeu.jscript.common;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Location implements Comparable<Location> {
|
||||
public static final Location INTERNAL = new Location(-1, -1, new Filename("jscript", "native"));
|
||||
private int line;
|
||||
private int start;
|
||||
private Filename filename;
|
||||
|
||||
public int line() { return line; }
|
||||
public int start() { return start; }
|
||||
public Filename filename() { return filename; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
var res = new ArrayList<String>();
|
||||
|
||||
if (filename != null) res.add(filename.toString());
|
||||
if (line >= 0) res.add(line + "");
|
||||
if (start >= 0) res.add(start + "");
|
||||
|
||||
return String.join(":", res);
|
||||
}
|
||||
|
||||
public Location add(int n, boolean clone) {
|
||||
if (clone) return new Location(line, start + n, filename);
|
||||
this.start += n;
|
||||
return this;
|
||||
}
|
||||
public Location add(int n) {
|
||||
return add(n, false);
|
||||
}
|
||||
public Location nextLine() {
|
||||
line++;
|
||||
start = 0;
|
||||
return this;
|
||||
}
|
||||
public Location clone() {
|
||||
return new Location(line, start, filename);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
int result = 1;
|
||||
result = prime * result + line;
|
||||
result = prime * result + start;
|
||||
result = prime * result + ((filename == null) ? 0 : filename.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;
|
||||
Location other = (Location) obj;
|
||||
if (line != other.line) return false;
|
||||
if (start != other.start) return false;
|
||||
if (filename == null && other.filename != null) return false;
|
||||
else if (!filename.equals(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 Location(int line, int start, Filename filename) {
|
||||
this.line = line;
|
||||
this.start = start;
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
public static Location parse(String raw) {
|
||||
int i0 = -1, i1 = -1;
|
||||
for (var i = raw.length() - 1; i >= 0; i--) {
|
||||
if (raw.charAt(i) == ':') {
|
||||
if (i1 == -1) i1 = i;
|
||||
else if (i0 == -1) {
|
||||
i0 = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return new Location(
|
||||
Integer.parseInt(raw.substring(i0 + 1, i1)),
|
||||
Integer.parseInt(raw.substring(i1 + 1)),
|
||||
Filename.parse(raw.substring(0, i0))
|
||||
);
|
||||
}
|
||||
}
|
||||
29
src/java/me/topchetoeu/jscript/common/Metadata.java
Normal file
29
src/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/java/me/topchetoeu/jscript/common/Operation.java
Normal file
54
src/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);
|
||||
}
|
||||
}
|
||||
28
src/java/me/topchetoeu/jscript/common/Reading.java
Normal file
28
src/java/me/topchetoeu/jscript/common/Reading.java
Normal file
@@ -0,0 +1,28 @@
|
||||
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;
|
||||
|
||||
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 String streamToString(InputStream in) {
|
||||
try {
|
||||
return new String(in.readAllBytes());
|
||||
}
|
||||
catch (IOException e) { throw new UncheckedIOException(e); }
|
||||
}
|
||||
public static InputStream resourceToStream(String name) {
|
||||
return Reading.class.getResourceAsStream("/" + name);
|
||||
}
|
||||
public static String resourceToString(String name) {
|
||||
return streamToString(resourceToStream(name));
|
||||
}
|
||||
}
|
||||
23
src/java/me/topchetoeu/jscript/common/RefTracker.java
Normal file
23
src/java/me/topchetoeu/jscript/common/RefTracker.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package me.topchetoeu.jscript.common;
|
||||
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
public class RefTracker {
|
||||
public static void onDestroy(Object obj, Runnable runnable) {
|
||||
var queue = new ReferenceQueue<>();
|
||||
var ref = new WeakReference<>(obj, queue);
|
||||
obj = null;
|
||||
|
||||
var th = new Thread(() -> {
|
||||
try {
|
||||
queue.remove();
|
||||
ref.get();
|
||||
runnable.run();
|
||||
}
|
||||
catch (InterruptedException e) { return; }
|
||||
});
|
||||
th.setDaemon(true);
|
||||
th.start();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package me.topchetoeu.jscript.common;
|
||||
|
||||
public interface ResultRunnable<T> {
|
||||
T run();
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
package me.topchetoeu.jscript.events;
|
||||
package me.topchetoeu.jscript.common.events;
|
||||
|
||||
public class DataNotifier<T> implements Awaitable<T> {
|
||||
public class DataNotifier<T> {
|
||||
private Notifier notifier = new Notifier();
|
||||
private boolean isErr;
|
||||
private T val;
|
||||
@@ -11,15 +11,12 @@ public class DataNotifier<T> implements Awaitable<T> {
|
||||
isErr = true;
|
||||
notifier.next();
|
||||
}
|
||||
public void error(Throwable t) {
|
||||
error(new RuntimeException(t));
|
||||
}
|
||||
public void next(T val) {
|
||||
this.val = val;
|
||||
isErr = false;
|
||||
notifier.next();
|
||||
}
|
||||
public T await() throws InterruptedException {
|
||||
public T await() {
|
||||
notifier.await();
|
||||
|
||||
try {
|
||||
19
src/java/me/topchetoeu/jscript/common/events/Notifier.java
Normal file
19
src/java/me/topchetoeu/jscript/common/events/Notifier.java
Normal file
@@ -0,0 +1,19 @@
|
||||
package me.topchetoeu.jscript.common.events;
|
||||
|
||||
import me.topchetoeu.jscript.runtime.exceptions.InterruptException;
|
||||
|
||||
public class Notifier {
|
||||
private boolean ok = false;
|
||||
|
||||
public synchronized void next() {
|
||||
ok = true;
|
||||
notifyAll();
|
||||
}
|
||||
public synchronized void await() {
|
||||
try {
|
||||
while (!ok) wait();
|
||||
ok = false;
|
||||
}
|
||||
catch (InterruptedException e) { throw new InterruptException(e); }
|
||||
}
|
||||
}
|
||||
@@ -1,29 +1,96 @@
|
||||
package me.topchetoeu.jscript.json;
|
||||
package me.topchetoeu.jscript.common.json;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
||||
import me.topchetoeu.jscript.parsing.Operator;
|
||||
import me.topchetoeu.jscript.parsing.ParseRes;
|
||||
import me.topchetoeu.jscript.parsing.Parsing;
|
||||
import me.topchetoeu.jscript.parsing.Token;
|
||||
import me.topchetoeu.jscript.common.Filename;
|
||||
import me.topchetoeu.jscript.compilation.parsing.Operator;
|
||||
import me.topchetoeu.jscript.compilation.parsing.ParseRes;
|
||||
import me.topchetoeu.jscript.compilation.parsing.Parsing;
|
||||
import me.topchetoeu.jscript.compilation.parsing.Token;
|
||||
import me.topchetoeu.jscript.runtime.Context;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
||||
import me.topchetoeu.jscript.runtime.values.ArrayValue;
|
||||
import me.topchetoeu.jscript.runtime.values.ObjectValue;
|
||||
import me.topchetoeu.jscript.runtime.values.Values;
|
||||
|
||||
public class JSON {
|
||||
public static Object toJs(JSONElement val) {
|
||||
if (val.isBoolean()) return val.bool();
|
||||
if (val.isString()) return val.string();
|
||||
if (val.isNumber()) return val.number();
|
||||
if (val.isList()) return ArrayValue.of(null, val.list().stream().map(JSON::toJs).collect(Collectors.toList()));
|
||||
if (val.isMap()) {
|
||||
var res = new ObjectValue();
|
||||
for (var el : val.map().entrySet()) {
|
||||
res.defineProperty(null, el.getKey(), toJs(el.getValue()));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
if (val.isNull()) return Values.NULL;
|
||||
return null;
|
||||
}
|
||||
private static JSONElement fromJs(Context ctx, Object val, HashSet<Object> prev) {
|
||||
if (val instanceof Boolean) return JSONElement.bool((boolean)val);
|
||||
if (val instanceof Number) return JSONElement.number(((Number)val).doubleValue());
|
||||
if (val instanceof String) return JSONElement.string((String)val);
|
||||
if (val == Values.NULL) return JSONElement.NULL;
|
||||
if (val instanceof ArrayValue) {
|
||||
if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
|
||||
prev.add(val);
|
||||
|
||||
var res = new JSONList();
|
||||
|
||||
for (var el : ((ArrayValue)val).toArray()) {
|
||||
var jsonEl = fromJs(ctx, el, prev);
|
||||
if (jsonEl == null) jsonEl = JSONElement.NULL;
|
||||
res.add(jsonEl);
|
||||
}
|
||||
|
||||
prev.remove(val);
|
||||
return JSONElement.of(res);
|
||||
}
|
||||
if (val instanceof ObjectValue) {
|
||||
if (prev.contains(val)) throw new EngineException("Circular dependency in JSON.");
|
||||
prev.add(val);
|
||||
|
||||
var res = new JSONMap();
|
||||
|
||||
for (var el : Values.getMembers(ctx, val, false, false)) {
|
||||
var jsonEl = fromJs(ctx, Values.getMember(ctx, val, el), prev);
|
||||
if (jsonEl == null) continue;
|
||||
if (el instanceof String || el instanceof Number) res.put(el.toString(), jsonEl);
|
||||
}
|
||||
|
||||
prev.remove(val);
|
||||
return JSONElement.of(res);
|
||||
}
|
||||
if (val == null) return null;
|
||||
return null;
|
||||
}
|
||||
public static JSONElement fromJs(Context ctx, Object val) {
|
||||
return fromJs(ctx, val, new HashSet<>());
|
||||
}
|
||||
|
||||
public static ParseRes<String> parseIdentifier(List<Token> tokens, int i) {
|
||||
return Parsing.parseIdentifier(tokens, i);
|
||||
}
|
||||
public static ParseRes<String> parseString(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<String> parseString(Filename filename, List<Token> tokens, int i) {
|
||||
var res = Parsing.parseString(filename, tokens, i);
|
||||
if (res.isSuccess()) return ParseRes.res((String)res.result.value, res.n);
|
||||
else return res.transform();
|
||||
}
|
||||
public static ParseRes<Double> parseNumber(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<Double> parseNumber(Filename filename, List<Token> tokens, int i) {
|
||||
var minus = Parsing.isOperator(tokens, i, Operator.SUBTRACT);
|
||||
if (minus) i++;
|
||||
|
||||
var res = Parsing.parseNumber(filename, tokens, i);
|
||||
if (res.isSuccess()) return ParseRes.res((Double)res.result.value, res.n);
|
||||
if (res.isSuccess()) return ParseRes.res((minus ? -1 : 1) * (Double)res.result.value, res.n + (minus ? 1 : 0));
|
||||
else return res.transform();
|
||||
}
|
||||
public static ParseRes<Boolean> parseBool(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<Boolean> parseBool(Filename filename, List<Token> tokens, int i) {
|
||||
var id = parseIdentifier(tokens, i);
|
||||
|
||||
if (!id.isSuccess()) return ParseRes.failed();
|
||||
@@ -32,7 +99,7 @@ public class JSON {
|
||||
else return ParseRes.failed();
|
||||
}
|
||||
|
||||
public static ParseRes<?> parseValue(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<?> parseValue(Filename filename, List<Token> tokens, int i) {
|
||||
return ParseRes.any(
|
||||
parseString(filename, tokens, i),
|
||||
parseNumber(filename, tokens, i),
|
||||
@@ -42,7 +109,7 @@ public class JSON {
|
||||
);
|
||||
}
|
||||
|
||||
public static ParseRes<JSONMap> parseMap(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<JSONMap> parseMap(Filename filename, List<Token> tokens, int i) {
|
||||
int n = 0;
|
||||
if (!Parsing.isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed();
|
||||
|
||||
@@ -82,7 +149,7 @@ public class JSON {
|
||||
|
||||
return ParseRes.res(values, n);
|
||||
}
|
||||
public static ParseRes<JSONList> parseList(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<JSONList> parseList(Filename filename, List<Token> tokens, int i) {
|
||||
int n = 0;
|
||||
if (!Parsing.isOperator(tokens, i + n++, Operator.BRACKET_OPEN)) return ParseRes.failed();
|
||||
|
||||
@@ -109,7 +176,8 @@ public class JSON {
|
||||
|
||||
return ParseRes.res(values, n);
|
||||
}
|
||||
public static JSONElement parse(String filename, String raw) {
|
||||
public static JSONElement parse(Filename filename, String raw) {
|
||||
if (filename == null) filename = new Filename("jscript", "json");
|
||||
var res = parseValue(filename, Parsing.tokenize(filename, raw), 0);
|
||||
if (res.isFailed()) throw new SyntaxException(null, "Invalid JSON given.");
|
||||
else if (res.isError()) throw new SyntaxException(null, res.error);
|
||||
@@ -120,7 +188,28 @@ public class JSON {
|
||||
if (el.isNumber()) return Double.toString(el.number());
|
||||
if (el.isBoolean()) return el.bool() ? "true" : "false";
|
||||
if (el.isNull()) return "null";
|
||||
if (el.isString()) return "\"" + el.string().replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
|
||||
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++) {
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.topchetoeu.jscript.json;
|
||||
package me.topchetoeu.jscript.common.json;
|
||||
|
||||
public class JSONElement {
|
||||
public static enum Type {
|
||||
@@ -65,10 +65,22 @@ public class JSONElement {
|
||||
return (double)value;
|
||||
}
|
||||
public boolean bool() {
|
||||
if (!isNumber()) throw new IllegalStateException("Element is not a boolean.");
|
||||
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;
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.topchetoeu.jscript.json;
|
||||
package me.topchetoeu.jscript.common.json;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -10,6 +10,9 @@ public class JSONList extends ArrayList<JSONElement> {
|
||||
public JSONList(JSONElement ...els) {
|
||||
super(List.of(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; }
|
||||
@@ -17,5 +20,7 @@ public class JSONList extends ArrayList<JSONElement> {
|
||||
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; }
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.topchetoeu.jscript.json;
|
||||
package me.topchetoeu.jscript.common.json;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
@@ -51,7 +51,7 @@ public class JSONMap implements Map<String, JSONElement> {
|
||||
|
||||
public JSONMap map(String path) {
|
||||
var el = get(path);
|
||||
if (el == null) throw new IllegalStateException(String.format("'%s' doesn't exist.", path));
|
||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
||||
return el.map();
|
||||
}
|
||||
public JSONMap map(String path, JSONMap defaultVal) {
|
||||
@@ -63,7 +63,7 @@ public class JSONMap implements Map<String, JSONElement> {
|
||||
|
||||
public JSONList list(String path) {
|
||||
var el = get(path);
|
||||
if (el == null) throw new IllegalStateException(String.format("'%s' doesn't exist.", path));
|
||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
||||
return el.list();
|
||||
}
|
||||
public JSONList list(String path, JSONList defaultVal) {
|
||||
@@ -75,7 +75,7 @@ public class JSONMap implements Map<String, JSONElement> {
|
||||
|
||||
public String string(String path) {
|
||||
var el = get(path);
|
||||
if (el == null) throw new IllegalStateException(String.format("'%s' doesn't exist.", path));
|
||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
||||
return el.string();
|
||||
}
|
||||
public String string(String path, String defaultVal) {
|
||||
@@ -87,7 +87,7 @@ public class JSONMap implements Map<String, JSONElement> {
|
||||
|
||||
public double number(String path) {
|
||||
var el = get(path);
|
||||
if (el == null) throw new IllegalStateException(String.format("'%s' doesn't exist.", path));
|
||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
||||
return el.number();
|
||||
}
|
||||
public double number(String path, double defaultVal) {
|
||||
@@ -99,7 +99,7 @@ public class JSONMap implements Map<String, JSONElement> {
|
||||
|
||||
public boolean bool(String path) {
|
||||
var el = get(path);
|
||||
if (el == null) throw new IllegalStateException(String.format("'%s' doesn't exist.", path));
|
||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
||||
return el.bool();
|
||||
}
|
||||
public boolean bool(String path, boolean defaultVal) {
|
||||
@@ -0,0 +1,8 @@
|
||||
package me.topchetoeu.jscript.common.mapping;
|
||||
|
||||
public enum ConvertType {
|
||||
Exact,
|
||||
Lower,
|
||||
Upper,
|
||||
Both,
|
||||
}
|
||||
190
src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java
Normal file
190
src/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java
Normal file
@@ -0,0 +1,190 @@
|
||||
package me.topchetoeu.jscript.common.mapping;
|
||||
|
||||
import java.util.ArrayList;
|
||||
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.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import me.topchetoeu.jscript.common.Filename;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord;
|
||||
import me.topchetoeu.jscript.utils.mapping.SourceMap;
|
||||
|
||||
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 FunctionMap build(String[] localNames, String[] captureNames) {
|
||||
return new FunctionMap(sourceMap, breakpoints, localNames, captureNames);
|
||||
}
|
||||
public FunctionMap build(LocalScopeRecord scope) {
|
||||
return new FunctionMap(sourceMap, breakpoints, scope.locals(), scope.captures());
|
||||
}
|
||||
public FunctionMap build() {
|
||||
return new FunctionMap(sourceMap, breakpoints, 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, 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()) {
|
||||
res.add(candidate.getValue().ceiling(new Location(line, column, candidate.getKey())));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
public List<Location> breakpoints(Location start, Location end) {
|
||||
if (!Objects.equals(start.filename(), end.filename())) return List.of();
|
||||
NavigableSet<Location> set = bpLocs.get(start.filename());
|
||||
if (set == null) return List.of();
|
||||
|
||||
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 apply(SourceMap map) {
|
||||
var res = new FunctionMap(Map.of(), Map.of(), localNames, captureNames);
|
||||
|
||||
for (var el : pcToLoc.entrySet()) {
|
||||
res.pcToLoc.put(el.getKey(), map.toCompiled(el.getValue()));
|
||||
}
|
||||
|
||||
res.bps.putAll(bps);
|
||||
|
||||
for (var el : bpLocs.entrySet()) {
|
||||
for (var loc : el.getValue()) {
|
||||
loc = map.toCompiled(loc);
|
||||
if (loc == null) continue;
|
||||
|
||||
if (!res.bpLocs.containsKey(loc.filename())) res.bpLocs.put(loc.filename(), new TreeSet<>());
|
||||
res.bpLocs.get(loc.filename()).add(loc);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public FunctionMap clone() {
|
||||
var res = new FunctionMap(Map.of(), Map.of(), localNames, 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[] 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;
|
||||
}
|
||||
private FunctionMap() {
|
||||
localNames = new String[0];
|
||||
captureNames = new String[0];
|
||||
}
|
||||
|
||||
public static FunctionMapBuilder builder() {
|
||||
return new FunctionMapBuilder();
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
package me.topchetoeu.jscript.compilation;
|
||||
|
||||
import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.engine.Operation;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.common.Operation;
|
||||
|
||||
public abstract class AssignableStatement extends Statement {
|
||||
public abstract AssignStatement toAssign(Statement val, Operation operation);
|
||||
public abstract Statement toAssign(Statement val, Operation operation);
|
||||
|
||||
protected AssignableStatement(Location loc) {
|
||||
super(loc);
|
||||
@@ -0,0 +1,78 @@
|
||||
package me.topchetoeu.jscript.compilation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Vector;
|
||||
|
||||
import me.topchetoeu.jscript.common.FunctionBody;
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.common.mapping.FunctionMap;
|
||||
import me.topchetoeu.jscript.common.mapping.FunctionMap.FunctionMapBuilder;
|
||||
import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord;
|
||||
|
||||
public class CompileResult {
|
||||
public final Vector<Instruction> instructions = new Vector<>();
|
||||
public final List<CompileResult> children = new LinkedList<>();
|
||||
public final FunctionMapBuilder map = FunctionMap.builder();
|
||||
public final LocalScopeRecord scope;
|
||||
public int length = 0;
|
||||
|
||||
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 Instruction get(int i) {
|
||||
return instructions.get(i);
|
||||
}
|
||||
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(CompileResult child) {
|
||||
this.children.add(child);
|
||||
return child;
|
||||
}
|
||||
|
||||
public FunctionMap map() {
|
||||
return map.build(scope);
|
||||
}
|
||||
public FunctionBody body() {
|
||||
var builtChildren = new FunctionBody[children.size()];
|
||||
|
||||
for (var i = 0; i < children.size(); i++) builtChildren[i] = children.get(i).body();
|
||||
|
||||
return new FunctionBody(scope.localsCount(), length, instructions.toArray(Instruction[]::new), builtChildren);
|
||||
}
|
||||
|
||||
public CompileResult(LocalScopeRecord scope) {
|
||||
this.scope = scope;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package me.topchetoeu.jscript.compilation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
|
||||
|
||||
public class CompoundStatement extends Statement {
|
||||
public final Statement[] statements;
|
||||
public final boolean separateFuncs;
|
||||
public Location end;
|
||||
|
||||
@Override public boolean pure() {
|
||||
for (var stm : statements) {
|
||||
if (!stm.pure()) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void declare(CompileResult target) {
|
||||
for (var stm : statements) stm.declare(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute, BreakpointType type) {
|
||||
List<Statement> statements = new Vector<Statement>();
|
||||
if (separateFuncs) for (var stm : this.statements) {
|
||||
if (stm instanceof FunctionStatement && ((FunctionStatement)stm).statement) {
|
||||
stm.compile(target, false);
|
||||
}
|
||||
else statements.add(stm);
|
||||
}
|
||||
else statements = List.of(this.statements);
|
||||
|
||||
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 CompoundStatement setEnd(Location loc) {
|
||||
this.end = loc;
|
||||
return this;
|
||||
}
|
||||
|
||||
public CompoundStatement(Location loc, boolean separateFuncs, Statement ...statements) {
|
||||
super(loc);
|
||||
this.separateFuncs = separateFuncs;
|
||||
this.statements = statements;
|
||||
}
|
||||
}
|
||||
27
src/java/me/topchetoeu/jscript/compilation/Statement.java
Normal file
27
src/java/me/topchetoeu/jscript/compilation/Statement.java
Normal file
@@ -0,0 +1,27 @@
|
||||
package me.topchetoeu.jscript.compilation;
|
||||
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
|
||||
public abstract class Statement {
|
||||
private Location _loc;
|
||||
|
||||
public boolean pure() { return false; }
|
||||
public void declare(CompileResult target) { }
|
||||
|
||||
public void compile(CompileResult target, boolean pollute, BreakpointType type) {
|
||||
int start = target.size();
|
||||
compile(target, pollute);
|
||||
if (target.size() != start) target.setLocationAndDebug(start, loc(), type);
|
||||
}
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
compile(target, pollute, BreakpointType.NONE);
|
||||
}
|
||||
|
||||
public Location loc() { return _loc; }
|
||||
public void setLoc(Location loc) { _loc = loc; }
|
||||
|
||||
protected Statement(Location loc) {
|
||||
this._loc = loc;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package me.topchetoeu.jscript.compilation;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
||||
|
||||
public class ThrowSyntaxStatement extends Statement {
|
||||
public final String name;
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
target.add(Instruction.throwSyntax(name));
|
||||
}
|
||||
|
||||
public ThrowSyntaxStatement(SyntaxException e) {
|
||||
super(e.loc);
|
||||
this.name = e.msg;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package me.topchetoeu.jscript.compilation;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.compilation.values.FunctionStatement;
|
||||
|
||||
public class VariableDeclareStatement extends Statement {
|
||||
public static class Pair {
|
||||
public final String name;
|
||||
public final Statement value;
|
||||
public final Location location;
|
||||
|
||||
public Pair(String name, Statement value, Location location) {
|
||||
this.name = name;
|
||||
this.value = value;
|
||||
this.location = location;
|
||||
}
|
||||
}
|
||||
|
||||
public final List<Pair> values;
|
||||
|
||||
@Override
|
||||
public void declare(CompileResult target) {
|
||||
for (var key : values) {
|
||||
target.scope.define(key.name);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
for (var entry : values) {
|
||||
if (entry.name == null) continue;
|
||||
var key = target.scope.getKey(entry.name);
|
||||
|
||||
if (key instanceof String) target.add(Instruction.makeVar((String)key));
|
||||
|
||||
if (entry.value != null) {
|
||||
FunctionStatement.compileWithName(entry.value, target, true, entry.name, BreakpointType.STEP_OVER);
|
||||
target.add(Instruction.storeVar(key));
|
||||
}
|
||||
}
|
||||
|
||||
if (pollute) target.add(Instruction.pushUndefined());
|
||||
}
|
||||
|
||||
public VariableDeclareStatement(Location loc, List<Pair> values) {
|
||||
super(loc);
|
||||
this.values = values;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package me.topchetoeu.jscript.compilation.control;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class BreakStatement extends Statement {
|
||||
public final String label;
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
target.add(Instruction.nop("break", label));
|
||||
if (pollute) target.add(Instruction.pushUndefined());
|
||||
}
|
||||
|
||||
public BreakStatement(Location loc, String label) {
|
||||
super(loc);
|
||||
this.label = label;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package me.topchetoeu.jscript.compilation.control;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class ContinueStatement extends Statement {
|
||||
public final String label;
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
target.add(Instruction.nop(loc(), "cont", label));
|
||||
if (pollute) target.add(Instruction.pushUndefined());
|
||||
}
|
||||
|
||||
public ContinueStatement(Location loc, String label) {
|
||||
super(loc);
|
||||
this.label = label;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package me.topchetoeu.jscript.compilation.control;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class DebugStatement extends Statement {
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
target.add(Instruction.debug());
|
||||
if (pollute) target.add(Instruction.pushUndefined());
|
||||
}
|
||||
|
||||
public DebugStatement(Location loc) {
|
||||
super(loc);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package me.topchetoeu.jscript.compilation.control;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class DeleteStatement extends Statement {
|
||||
public final Statement key;
|
||||
public final Statement value;
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
value.compile(target, true);
|
||||
key.compile(target, true);
|
||||
|
||||
target.add(Instruction.delete());
|
||||
if (pollute) target.add(Instruction.pushValue(true));
|
||||
}
|
||||
|
||||
public DeleteStatement(Location loc, Statement key, Statement value) {
|
||||
super(loc);
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package me.topchetoeu.jscript.compilation.control;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class DoWhileStatement extends Statement {
|
||||
public final Statement condition, body;
|
||||
public final String label;
|
||||
|
||||
@Override
|
||||
public void declare(CompileResult target) {
|
||||
body.declare(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
int start = target.size();
|
||||
body.compile(target, false, BreakpointType.STEP_OVER);
|
||||
int mid = target.size();
|
||||
condition.compile(target, true, BreakpointType.STEP_OVER);
|
||||
int end = target.size();
|
||||
|
||||
WhileStatement.replaceBreaks(target, label, start, mid - 1, mid, end + 1);
|
||||
target.add(Instruction.jmpIf(start - end));
|
||||
}
|
||||
|
||||
public DoWhileStatement(Location loc, String label, Statement condition, Statement body) {
|
||||
super(loc);
|
||||
this.label = label;
|
||||
this.condition = condition;
|
||||
this.body = body;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package me.topchetoeu.jscript.compilation.control;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.common.Operation;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class ForInStatement extends Statement {
|
||||
public final String varName;
|
||||
public final boolean isDeclaration;
|
||||
public final Statement varValue, object, body;
|
||||
public final String label;
|
||||
public final Location varLocation;
|
||||
|
||||
@Override
|
||||
public void declare(CompileResult target) {
|
||||
body.declare(target);
|
||||
if (isDeclaration) target.scope.define(varName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
var key = target.scope.getKey(varName);
|
||||
|
||||
if (key instanceof String) target.add(Instruction.makeVar((String)key));
|
||||
|
||||
if (varValue != null) {
|
||||
varValue.compile(target, true);
|
||||
target.add(Instruction.storeVar(target.scope.getKey(varName)));
|
||||
}
|
||||
|
||||
object.compile(target, true, BreakpointType.STEP_OVER);
|
||||
target.add(Instruction.keys(true));
|
||||
|
||||
int start = target.size();
|
||||
target.add(Instruction.dup());
|
||||
target.add(Instruction.pushUndefined());
|
||||
target.add(Instruction.operation(Operation.EQUALS));
|
||||
int mid = target.temp();
|
||||
|
||||
target.add(Instruction.pushValue("value")).setLocation(varLocation);
|
||||
target.add(Instruction.loadMember()).setLocation(varLocation);
|
||||
target.add(Instruction.storeVar(key)).setLocationAndDebug(object.loc(), BreakpointType.STEP_OVER);
|
||||
|
||||
body.compile(target, false, BreakpointType.STEP_OVER);
|
||||
|
||||
int end = target.size();
|
||||
|
||||
WhileStatement.replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
||||
|
||||
target.add(Instruction.jmp(start - end));
|
||||
target.add(Instruction.discard());
|
||||
target.set(mid, Instruction.jmpIf(end - mid + 1));
|
||||
if (pollute) target.add(Instruction.pushUndefined());
|
||||
}
|
||||
|
||||
public ForInStatement(Location loc, Location varLocation, String label, boolean isDecl, String varName, Statement varValue, Statement object, Statement body) {
|
||||
super(loc);
|
||||
this.varLocation = varLocation;
|
||||
this.label = label;
|
||||
this.isDeclaration = isDecl;
|
||||
this.varName = varName;
|
||||
this.varValue = varValue;
|
||||
this.object = object;
|
||||
this.body = body;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package me.topchetoeu.jscript.compilation.control;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class ForStatement extends Statement {
|
||||
public final Statement declaration, assignment, condition, body;
|
||||
public final String label;
|
||||
|
||||
@Override
|
||||
public void declare(CompileResult target) {
|
||||
declaration.declare(target);
|
||||
body.declare(target);
|
||||
}
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
declaration.compile(target, false, BreakpointType.STEP_OVER);
|
||||
|
||||
int start = target.size();
|
||||
condition.compile(target, true, BreakpointType.STEP_OVER);
|
||||
int mid = target.temp();
|
||||
body.compile(target, false, BreakpointType.STEP_OVER);
|
||||
int beforeAssign = target.size();
|
||||
assignment.compile(target, false, BreakpointType.STEP_OVER);
|
||||
int end = target.size();
|
||||
|
||||
WhileStatement.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1);
|
||||
|
||||
target.add(Instruction.jmp(start - end));
|
||||
target.set(mid, Instruction.jmpIfNot(end - mid + 1));
|
||||
if (pollute) target.add(Instruction.pushUndefined());
|
||||
}
|
||||
|
||||
public ForStatement(Location loc, String label, Statement declaration, Statement condition, Statement assignment, Statement body) {
|
||||
super(loc);
|
||||
this.label = label;
|
||||
this.declaration = declaration;
|
||||
this.condition = condition;
|
||||
this.assignment = assignment;
|
||||
this.body = body;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package me.topchetoeu.jscript.compilation.control;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class IfStatement extends Statement {
|
||||
public final Statement condition, body, elseBody;
|
||||
|
||||
@Override
|
||||
public void declare(CompileResult target) {
|
||||
body.declare(target);
|
||||
if (elseBody != null) elseBody.declare(target);
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute, BreakpointType breakpoint) {
|
||||
condition.compile(target, true, breakpoint);
|
||||
|
||||
if (elseBody == null) {
|
||||
int i = target.temp();
|
||||
body.compile(target, pollute, breakpoint);
|
||||
int endI = target.size();
|
||||
target.set(i, Instruction.jmpIfNot(endI - i));
|
||||
}
|
||||
else {
|
||||
int start = target.temp();
|
||||
body.compile(target, pollute, breakpoint);
|
||||
int mid = target.temp();
|
||||
elseBody.compile(target, pollute, breakpoint);
|
||||
int end = target.size();
|
||||
|
||||
target.set(start, Instruction.jmpIfNot(mid - start + 1));
|
||||
target.set(mid, Instruction.jmp(end - mid));
|
||||
}
|
||||
}
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
compile(target, pollute, BreakpointType.STEP_IN);
|
||||
}
|
||||
|
||||
public IfStatement(Location loc, Statement condition, Statement body, Statement elseBody) {
|
||||
super(loc);
|
||||
this.condition = condition;
|
||||
this.body = body;
|
||||
this.elseBody = elseBody;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package me.topchetoeu.jscript.compilation.control;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class ReturnStatement extends Statement {
|
||||
public final Statement value;
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
if (value == null) target.add(Instruction.pushUndefined());
|
||||
else value.compile(target, true);
|
||||
target.add(Instruction.ret()).setLocation(loc());
|
||||
}
|
||||
|
||||
public ReturnStatement(Location loc, Statement value) {
|
||||
super(loc);
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package me.topchetoeu.jscript.compilation.control;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.common.Operation;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.common.Instruction.Type;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class SwitchStatement extends Statement {
|
||||
public static class SwitchCase {
|
||||
public final Statement value;
|
||||
public final int statementI;
|
||||
|
||||
public SwitchCase(Statement value, int statementI) {
|
||||
this.value = value;
|
||||
this.statementI = statementI;
|
||||
}
|
||||
}
|
||||
|
||||
public final Statement value;
|
||||
public final SwitchCase[] cases;
|
||||
public final Statement[] body;
|
||||
public final int defaultI;
|
||||
|
||||
@Override
|
||||
public void declare(CompileResult target) {
|
||||
for (var stm : body) stm.declare(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
var caseToStatement = new HashMap<Integer, Integer>();
|
||||
var statementToIndex = new HashMap<Integer, Integer>();
|
||||
|
||||
value.compile(target, true, BreakpointType.STEP_OVER);
|
||||
|
||||
for (var ccase : cases) {
|
||||
target.add(Instruction.dup());
|
||||
ccase.value.compile(target, true);
|
||||
target.add(Instruction.operation(Operation.EQUALS));
|
||||
caseToStatement.put(target.temp(), ccase.statementI);
|
||||
}
|
||||
|
||||
int start = target.temp();
|
||||
|
||||
for (var stm : body) {
|
||||
statementToIndex.put(statementToIndex.size(), target.size());
|
||||
stm.compile(target, false, BreakpointType.STEP_OVER);
|
||||
}
|
||||
|
||||
int end = target.size();
|
||||
target.add(Instruction.discard());
|
||||
if (pollute) target.add(Instruction.pushUndefined());
|
||||
|
||||
if (defaultI < 0 || defaultI >= body.length) target.set(start, Instruction.jmp(end - start));
|
||||
else target.set(start, Instruction.jmp(statementToIndex.get(defaultI) - start));
|
||||
|
||||
for (int i = start; i < end; i++) {
|
||||
var instr = target.get(i);
|
||||
if (instr.type == Type.NOP && instr.is(0, "break") && instr.get(1) == null) {
|
||||
target.set(i, Instruction.jmp(end - i));
|
||||
}
|
||||
}
|
||||
for (var el : caseToStatement.entrySet()) {
|
||||
var i = statementToIndex.get(el.getValue());
|
||||
if (i == null) i = end;
|
||||
target.set(el.getKey(), Instruction.jmpIf(i - el.getKey()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public SwitchStatement(Location loc, Statement value, int defaultI, SwitchCase[] cases, Statement[] body) {
|
||||
super(loc);
|
||||
this.value = value;
|
||||
this.defaultI = defaultI;
|
||||
this.cases = cases;
|
||||
this.body = body;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package me.topchetoeu.jscript.compilation.control;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class ThrowStatement extends Statement {
|
||||
public final Statement value;
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
value.compile(target, true);
|
||||
target.add(Instruction.throwInstr()).setLocation(loc());
|
||||
}
|
||||
|
||||
public ThrowStatement(Location loc, Statement value) {
|
||||
super(loc);
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package me.topchetoeu.jscript.compilation.control;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class TryStatement extends Statement {
|
||||
public final Statement tryBody;
|
||||
public final Statement catchBody;
|
||||
public final Statement finallyBody;
|
||||
public final String name;
|
||||
|
||||
@Override
|
||||
public void declare(CompileResult target) {
|
||||
tryBody.declare(target);
|
||||
if (catchBody != null) catchBody.declare(target);
|
||||
if (finallyBody != null) finallyBody.declare(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute, BreakpointType bpt) {
|
||||
int replace = target.temp();
|
||||
|
||||
int start = replace + 1, catchStart = -1, finallyStart = -1;
|
||||
|
||||
tryBody.compile(target, false);
|
||||
target.add(Instruction.tryEnd());
|
||||
|
||||
if (catchBody != null) {
|
||||
catchStart = target.size() - start;
|
||||
target.scope.define(name, true);
|
||||
catchBody.compile(target, false);
|
||||
target.scope.undefine();
|
||||
target.add(Instruction.tryEnd());
|
||||
}
|
||||
|
||||
if (finallyBody != null) {
|
||||
finallyStart = target.size() - start;
|
||||
finallyBody.compile(target, false);
|
||||
target.add(Instruction.tryEnd());
|
||||
}
|
||||
|
||||
target.set(replace, Instruction.tryStart(catchStart, finallyStart, target.size() - start));
|
||||
target.setLocationAndDebug(replace, loc(), BreakpointType.STEP_OVER);
|
||||
|
||||
if (pollute) target.add(Instruction.pushUndefined());
|
||||
}
|
||||
|
||||
public TryStatement(Location loc, Statement tryBody, Statement catchBody, Statement finallyBody, String name) {
|
||||
super(loc);
|
||||
this.tryBody = tryBody;
|
||||
this.catchBody = catchBody;
|
||||
this.finallyBody = finallyBody;
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package me.topchetoeu.jscript.compilation.control;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.common.Instruction.Type;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class WhileStatement extends Statement {
|
||||
public final Statement condition, body;
|
||||
public final String label;
|
||||
|
||||
@Override
|
||||
public void declare(CompileResult target) {
|
||||
body.declare(target);
|
||||
}
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
int start = target.size();
|
||||
condition.compile(target, true);
|
||||
int mid = target.temp();
|
||||
body.compile(target, false, BreakpointType.STEP_OVER);
|
||||
|
||||
int end = target.size();
|
||||
|
||||
replaceBreaks(target, label, mid + 1, end, start, end + 1);
|
||||
|
||||
target.add(Instruction.jmp(start - end));
|
||||
target.set(mid, Instruction.jmpIfNot(end - mid + 1));
|
||||
if (pollute) target.add(Instruction.pushUndefined());
|
||||
}
|
||||
|
||||
public WhileStatement(Location loc, String label, Statement condition, Statement body) {
|
||||
super(loc);
|
||||
this.label = label;
|
||||
this.condition = condition;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
public static void replaceBreaks(CompileResult target, String label, int start, int end, int continuePoint, int breakPoint) {
|
||||
for (int i = start; i < end; i++) {
|
||||
var instr = target.get(i);
|
||||
if (instr.type == Type.NOP && instr.is(0, "cont") && (instr.get(1) == null || instr.is(1, label))) {
|
||||
target.set(i, Instruction.jmp(continuePoint - i));
|
||||
}
|
||||
if (instr.type == Type.NOP && instr.is(0, "break") && (instr.get(1) == null || instr.is(1, label))) {
|
||||
target.set(i, Instruction.jmp(breakPoint - i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package me.topchetoeu.jscript.parsing;
|
||||
package me.topchetoeu.jscript.compilation.parsing;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import me.topchetoeu.jscript.engine.Operation;
|
||||
import me.topchetoeu.jscript.common.Operation;
|
||||
|
||||
public enum Operator {
|
||||
MULTIPLY("*", Operation.MULTIPLY, 13),
|
||||
@@ -55,7 +55,7 @@ public enum Operator {
|
||||
INCREASE("++"),
|
||||
DECREASE("--");
|
||||
|
||||
public final String value;
|
||||
public final String readable;
|
||||
public final Operation operation;
|
||||
public final int precedence;
|
||||
public final boolean reverse;
|
||||
@@ -63,7 +63,7 @@ public enum Operator {
|
||||
|
||||
static {
|
||||
for (var el : Operator.values()) {
|
||||
ops.put(el.value, el);
|
||||
ops.put(el.readable, el);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,38 +74,38 @@ public enum Operator {
|
||||
}
|
||||
|
||||
private Operator() {
|
||||
this.value = null;
|
||||
this.readable = null;
|
||||
this.operation = null;
|
||||
this.precedence = -1;
|
||||
this.reverse = false;
|
||||
}
|
||||
private Operator(String value) {
|
||||
this. value = value;
|
||||
this.readable = value;
|
||||
this.operation = null;
|
||||
this.precedence = -1;
|
||||
this.reverse = false;
|
||||
}
|
||||
private Operator(String value, int precedence) {
|
||||
this. value = value;
|
||||
this.readable = value;
|
||||
this.operation = null;
|
||||
this.precedence = precedence;
|
||||
this.reverse = false;
|
||||
}
|
||||
private Operator(String value, int precedence, boolean reverse) {
|
||||
this. value = value;
|
||||
this.readable = value;
|
||||
this.operation = null;
|
||||
this.precedence = precedence;
|
||||
this.reverse = reverse;
|
||||
}
|
||||
|
||||
private Operator(String value, Operation funcName, int precedence) {
|
||||
this. value = value;
|
||||
this.readable = value;
|
||||
this.operation = funcName;
|
||||
this.precedence = precedence;
|
||||
this.reverse = false;
|
||||
}
|
||||
private Operator(String value, Operation funcName, int precedence, boolean reverse) {
|
||||
this.value = value;
|
||||
this.readable = value;
|
||||
this.operation = funcName;
|
||||
this.precedence = precedence;
|
||||
this.reverse = reverse;
|
||||
@@ -1,10 +1,10 @@
|
||||
package me.topchetoeu.jscript.parsing;
|
||||
package me.topchetoeu.jscript.compilation.parsing;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.parsing.Parsing.Parser;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.compilation.parsing.Parsing.Parser;
|
||||
|
||||
public class ParseRes<T> {
|
||||
public static enum State {
|
||||
@@ -69,6 +69,9 @@ public class ParseRes<T> {
|
||||
|
||||
@SafeVarargs
|
||||
public static <T> ParseRes<? extends T> any(ParseRes<? extends T> ...parsers) {
|
||||
return any(List.of(parsers));
|
||||
}
|
||||
public static <T> ParseRes<? extends T> any(List<ParseRes<? extends T>> parsers) {
|
||||
ParseRes<? extends T> best = null;
|
||||
ParseRes<? extends T> error = ParseRes.failed();
|
||||
|
||||
@@ -1,44 +1,45 @@
|
||||
package me.topchetoeu.jscript.parsing;
|
||||
package me.topchetoeu.jscript.compilation.parsing;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
|
||||
import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.common.Filename;
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.common.Operation;
|
||||
import me.topchetoeu.jscript.compilation.*;
|
||||
import me.topchetoeu.jscript.compilation.Instruction.Type;
|
||||
import me.topchetoeu.jscript.compilation.VariableDeclareStatement.Pair;
|
||||
import me.topchetoeu.jscript.compilation.control.*;
|
||||
import me.topchetoeu.jscript.compilation.control.SwitchStatement.SwitchCase;
|
||||
import me.topchetoeu.jscript.compilation.parsing.ParseRes.State;
|
||||
import me.topchetoeu.jscript.compilation.scope.LocalScopeRecord;
|
||||
import me.topchetoeu.jscript.compilation.values.*;
|
||||
import me.topchetoeu.jscript.engine.Operation;
|
||||
import me.topchetoeu.jscript.engine.scope.GlobalScope;
|
||||
import me.topchetoeu.jscript.engine.scope.ValueVariable;
|
||||
import me.topchetoeu.jscript.engine.values.CodeFunction;
|
||||
import me.topchetoeu.jscript.engine.values.Values;
|
||||
import me.topchetoeu.jscript.exceptions.SyntaxException;
|
||||
import me.topchetoeu.jscript.parsing.ParseRes.State;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
||||
|
||||
// TODO: this has to be rewritten
|
||||
public class Parsing {
|
||||
public static interface Parser<T> {
|
||||
ParseRes<T> parse(String filename, List<Token> tokens, int i);
|
||||
ParseRes<T> parse(Filename filename, List<Token> tokens, int i);
|
||||
}
|
||||
|
||||
private static class ObjProp {
|
||||
public final Object name;
|
||||
public final String name;
|
||||
public final String access;
|
||||
public final FunctionStatement func;
|
||||
|
||||
public ObjProp(Object name, String access, FunctionStatement func) {
|
||||
public ObjProp(String name, String access, FunctionStatement func) {
|
||||
this.name = name;
|
||||
this.access = access;
|
||||
this.func = func;
|
||||
}
|
||||
}
|
||||
|
||||
public static final HashMap<Long, ArrayList<Instruction>> functions = new HashMap<>();
|
||||
|
||||
private static final HashSet<String> reserved = new HashSet<String>();
|
||||
static {
|
||||
reserved.add("true");
|
||||
@@ -66,7 +67,7 @@ public class Parsing {
|
||||
reserved.add("delete");
|
||||
reserved.add("break");
|
||||
reserved.add("continue");
|
||||
reserved.add("debug");
|
||||
reserved.add("debugger");
|
||||
reserved.add("implements");
|
||||
reserved.add("interface");
|
||||
reserved.add("package");
|
||||
@@ -77,8 +78,6 @@ public class Parsing {
|
||||
// Although ES5 allow these, we will comply to ES6 here
|
||||
reserved.add("const");
|
||||
reserved.add("let");
|
||||
reserved.add("async");
|
||||
reserved.add("super");
|
||||
// These are allowed too, however our parser considers them keywords
|
||||
reserved.add("undefined");
|
||||
reserved.add("arguments");
|
||||
@@ -88,7 +87,6 @@ public class Parsing {
|
||||
// We allow yield and await, because they're part of the custom async and generator functions
|
||||
}
|
||||
|
||||
|
||||
public static boolean isDigit(char c) {
|
||||
return c >= '0' && c <= '9';
|
||||
}
|
||||
@@ -119,7 +117,7 @@ public class Parsing {
|
||||
private static final int CURR_MULTI_COMMENT = 8;
|
||||
private static final int CURR_SINGLE_COMMENT = 9;
|
||||
|
||||
private static void addToken(StringBuilder currToken, int currStage, int line, int lastStart, String filename, List<RawToken> tokens) {
|
||||
private static void addToken(StringBuilder currToken, int currStage, int line, int lastStart, Filename filename, List<RawToken> tokens) {
|
||||
var res = currToken.toString();
|
||||
|
||||
switch (currStage) {
|
||||
@@ -140,7 +138,7 @@ public class Parsing {
|
||||
|
||||
// This method is so long because we're tokenizing the string using an iterative approach
|
||||
// instead of a recursive descent parser. This is mainly done for performance reasons.
|
||||
private static ArrayList<RawToken> splitTokens(String filename, String raw) {
|
||||
private static ArrayList<RawToken> splitTokens(Filename filename, String raw) {
|
||||
var tokens = new ArrayList<RawToken>();
|
||||
var currToken = new StringBuilder(64);
|
||||
|
||||
@@ -216,8 +214,10 @@ public class Parsing {
|
||||
currToken.append(c);
|
||||
continue;
|
||||
case CURR_SCIENTIFIC_NOT:
|
||||
if (c == '-') currStage = CURR_NEG_SCIENTIFIC_NOT;
|
||||
else if (!isDigit(c)) {
|
||||
if (c == '-') {
|
||||
if (currToken.toString().endsWith("e")) currStage = CURR_NEG_SCIENTIFIC_NOT;
|
||||
}
|
||||
if (currStage == CURR_SCIENTIFIC_NOT && !isDigit(c)) {
|
||||
i--; start--;
|
||||
break;
|
||||
}
|
||||
@@ -254,7 +254,7 @@ public class Parsing {
|
||||
}
|
||||
break;
|
||||
case CURR_LITERAL:
|
||||
if (isAlphanumeric(c) || c == '_') {
|
||||
if (isAlphanumeric(c) || c == '_' || c == '$') {
|
||||
currToken.append(c);
|
||||
continue;
|
||||
}
|
||||
@@ -394,13 +394,17 @@ public class Parsing {
|
||||
return tokens;
|
||||
}
|
||||
|
||||
private static int fromHex(char 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;
|
||||
}
|
||||
|
||||
private static boolean inBounds(List<Token> tokens, int i) {
|
||||
return i >= 0 && i < tokens.size();
|
||||
}
|
||||
|
||||
private static String parseString(Location loc, String literal) {
|
||||
var res = new StringBuilder();
|
||||
|
||||
@@ -516,52 +520,86 @@ public class Parsing {
|
||||
|
||||
for (int i = 2; i < literal.length(); i++) {
|
||||
res *= 16;
|
||||
res += fromHex(literal.charAt(i));
|
||||
int dig = fromHex(literal.charAt(i));
|
||||
res += dig;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
private static List<Token> parseTokens(String filename, Collection<RawToken> tokens) {
|
||||
public static Double parseNumber(boolean octals, String value) {
|
||||
if (value.startsWith("0x") || value.startsWith("0X")) {
|
||||
if (value.length() == 2) return null;
|
||||
return parseHex(value);
|
||||
}
|
||||
if (value.endsWith("e") || value.endsWith("E") || value.endsWith("-")) return null;
|
||||
|
||||
int i = 0;
|
||||
double res = 0, dotDivisor = 1;
|
||||
boolean e = false, dot = false;
|
||||
int exponent = 0;
|
||||
|
||||
for (; i < value.length(); i++) {
|
||||
char c = value.charAt(i);
|
||||
if (c == '.') { dot = true; break; }
|
||||
if (c == 'e') { e = true; break; }
|
||||
if (!isDigit(c)) break;
|
||||
|
||||
res = res * 10 + c - '0';
|
||||
}
|
||||
|
||||
if (dot) for (i++; i < value.length(); i++) {
|
||||
char c = value.charAt(i);
|
||||
if (c == 'e') { e = true; break; }
|
||||
if (!isDigit(c)) break;
|
||||
|
||||
res += (c - '0') / (dotDivisor *= 10);
|
||||
}
|
||||
|
||||
if (e) for (i++; i < value.length(); i++) {
|
||||
char c = value.charAt(i);
|
||||
if (!isDigit(c)) break;
|
||||
exponent = 10 * exponent + c - '0';
|
||||
}
|
||||
|
||||
if (exponent < 0) for (int j = 0; j < -exponent; j++) res /= 10;
|
||||
else for (int j = 0; j < exponent; j++) res *= 10;
|
||||
|
||||
return res;
|
||||
}
|
||||
private static double parseNumber(Location loc, String value) {
|
||||
var res = parseNumber(false, value);
|
||||
if (res == null)
|
||||
throw new SyntaxException(loc, "Invalid number format.");
|
||||
else return res;
|
||||
}
|
||||
|
||||
private static List<Token> parseTokens(Filename filename, Collection<RawToken> tokens) {
|
||||
var res = new ArrayList<Token>();
|
||||
|
||||
for (var el : tokens) {
|
||||
var loc = new Location(el.line, el.start, filename);
|
||||
switch (el.type) {
|
||||
case LITERAL: res.add(Token.identifier(el.line, el.start, el.value)); break;
|
||||
case NUMBER:
|
||||
if (el.value.startsWith("0x") || el.value.startsWith("0X")) {
|
||||
if (el.value.endsWith("x") || el.value.endsWith("X")) {
|
||||
throw new SyntaxException(loc, "Invalid number format.");
|
||||
}
|
||||
res.add(Token.number(el.line, el.start, parseHex(el.value))); break;
|
||||
}
|
||||
if (
|
||||
el.value.endsWith("e") || el.value.endsWith("E") || el.value.endsWith("-")
|
||||
) throw new SyntaxException(loc, "Invalid number format.");
|
||||
else res.add(Token.number(el.line, el.start, Double.parseDouble(el.value))); break;
|
||||
case NUMBER: res.add(Token.number(el.line, el.start, parseNumber(loc, el.value), el.value)); break;
|
||||
case STRING: res.add(Token.string(el.line, el.start, parseString(loc, el.value), el.value)); break;
|
||||
case REGEX: res.add(Token.regex(el.line, el.start, parseRegex(loc, el.value), el.value)); break;
|
||||
case OPERATOR:
|
||||
Operator op = Operator.parse(el.value);
|
||||
if (op == null) throw new SyntaxException(loc, String.format("Unrecognized operator '%s'.", el.value));
|
||||
res.add(Token.operator(el.line, el.start, op));
|
||||
break;
|
||||
case STRING:
|
||||
res.add(Token.string(el.line, el.start, parseString(loc, el.value)));
|
||||
break;
|
||||
case REGEX:
|
||||
res.add(Token.regex(el.line, el.start, parseRegex(loc, el.value)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public static List<Token> tokenize(String filename, String raw) {
|
||||
public static List<Token> tokenize(Filename filename, String raw) {
|
||||
return parseTokens(filename, splitTokens(filename, raw));
|
||||
}
|
||||
|
||||
public static Location getLoc(String filename, List<Token> tokens, int i) {
|
||||
public static Location getLoc(Filename filename, List<Token> tokens, int i) {
|
||||
if (tokens.size() == 0 || tokens.size() == 0) return new Location(1, 1, filename);
|
||||
if (i >= tokens.size()) i = tokens.size() - 1;
|
||||
return new Location(tokens.get(i).line, tokens.get(i).start, filename);
|
||||
@@ -572,49 +610,41 @@ public class Parsing {
|
||||
}
|
||||
|
||||
public static ParseRes<String> parseIdentifier(List<Token> tokens, int i) {
|
||||
try {
|
||||
if (inBounds(tokens, i)) {
|
||||
if (tokens.get(i).isIdentifier()) {
|
||||
return ParseRes.res(tokens.get(i).identifier(), 1);
|
||||
}
|
||||
else return ParseRes.failed();
|
||||
}
|
||||
catch (IndexOutOfBoundsException e) {
|
||||
return ParseRes.failed();
|
||||
}
|
||||
else return ParseRes.failed();
|
||||
}
|
||||
public static ParseRes<Operator> parseOperator(List<Token> tokens, int i) {
|
||||
try {
|
||||
if (inBounds(tokens, i)) {
|
||||
if (tokens.get(i).isOperator()) {
|
||||
return ParseRes.res(tokens.get(i).operator(), 1);
|
||||
}
|
||||
else return ParseRes.failed();
|
||||
}
|
||||
catch (IndexOutOfBoundsException e) {
|
||||
return ParseRes.failed();
|
||||
}
|
||||
else return ParseRes.failed();
|
||||
}
|
||||
|
||||
public static boolean isIdentifier(List<Token> tokens, int i, String lit) {
|
||||
try {
|
||||
if (inBounds(tokens, i)) {
|
||||
if (tokens.get(i).isIdentifier(lit)) {
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
catch (IndexOutOfBoundsException e) {
|
||||
return false;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
public static boolean isOperator(List<Token> tokens, int i, Operator op) {
|
||||
try {
|
||||
if (inBounds(tokens, i)) {
|
||||
if (tokens.get(i).isOperator(op)) {
|
||||
return true;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
catch (IndexOutOfBoundsException e) {
|
||||
return false;
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
public static boolean isStatementEnd(List<Token> tokens, int i) {
|
||||
if (isOperator(tokens, i, Operator.SEMICOLON)) return true;
|
||||
@@ -627,53 +657,42 @@ public class Parsing {
|
||||
return !reserved.contains(name);
|
||||
}
|
||||
|
||||
public static ParseRes<ConstantStatement> parseString(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<ConstantStatement> parseString(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
try {
|
||||
if (inBounds(tokens, i)) {
|
||||
if (tokens.get(i).isString()) {
|
||||
return ParseRes.res(new ConstantStatement(loc, tokens.get(i).string()), 1);
|
||||
}
|
||||
else return ParseRes.failed();
|
||||
}
|
||||
catch (IndexOutOfBoundsException e) {
|
||||
return ParseRes.failed();
|
||||
else return ParseRes.failed();
|
||||
}
|
||||
}
|
||||
public static ParseRes<ConstantStatement> parseNumber(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<ConstantStatement> parseNumber(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
try {
|
||||
if (inBounds(tokens, i)) {
|
||||
if (tokens.get(i).isNumber()) {
|
||||
return ParseRes.res(new ConstantStatement(loc, tokens.get(i).number()), 1);
|
||||
}
|
||||
else return ParseRes.failed();
|
||||
}
|
||||
catch (IndexOutOfBoundsException e) {
|
||||
return ParseRes.failed();
|
||||
else return ParseRes.failed();
|
||||
}
|
||||
|
||||
}
|
||||
public static ParseRes<NewStatement> parseRegex(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<RegexStatement> parseRegex(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
try {
|
||||
if (inBounds(tokens, i)) {
|
||||
if (tokens.get(i).isRegex()) {
|
||||
var val = tokens.get(i).regex();
|
||||
var index = val.lastIndexOf('/');
|
||||
var first = val.substring(1, index);
|
||||
var second = val.substring(index + 1);
|
||||
return ParseRes.res(new NewStatement(loc,
|
||||
new VariableStatement(null, "RegExp"),
|
||||
new ConstantStatement(loc, first),
|
||||
new ConstantStatement(loc, second)
|
||||
), 1);
|
||||
return ParseRes.res(new RegexStatement(loc, first, second), 1);
|
||||
}
|
||||
else return ParseRes.failed();
|
||||
}
|
||||
catch (IndexOutOfBoundsException e) {
|
||||
return ParseRes.failed();
|
||||
}
|
||||
}
|
||||
|
||||
public static ParseRes<ArrayStatement> parseArray(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<ArrayStatement> parseArray(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0;
|
||||
if (!isOperator(tokens, i + n++, Operator.BRACKET_OPEN)) return ParseRes.failed();
|
||||
@@ -712,7 +731,7 @@ public class Parsing {
|
||||
return ParseRes.res(new ArrayStatement(loc, values.toArray(Statement[]::new)), n);
|
||||
}
|
||||
|
||||
public static ParseRes<List<String>> parseParamList(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<List<String>> parseParamList(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0;
|
||||
|
||||
@@ -744,17 +763,18 @@ public class Parsing {
|
||||
return ParseRes.res(args, n);
|
||||
}
|
||||
|
||||
public static ParseRes<? extends Object> parsePropName(String filename, List<Token> tokens, int i) {
|
||||
var idRes = parseIdentifier(tokens, i);
|
||||
if (idRes.isSuccess()) return ParseRes.res(idRes.result, 1);
|
||||
var strRes = parseString(null, tokens, i);
|
||||
if (strRes.isSuccess()) return ParseRes.res(strRes.result.value, 1);
|
||||
var numRes = parseNumber(null, tokens, i);
|
||||
if (numRes.isSuccess()) return ParseRes.res(numRes.result.value, 1);
|
||||
public static ParseRes<String> parsePropName(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
|
||||
return ParseRes.failed();
|
||||
if (inBounds(tokens, i)) {
|
||||
var token = tokens.get(i);
|
||||
|
||||
if (token.isNumber() || token.isIdentifier() || token.isString()) return ParseRes.res(token.rawValue, 1);
|
||||
else return ParseRes.error(loc, "Expected identifier, string or number literal.");
|
||||
}
|
||||
public static ParseRes<ObjProp> parseObjectProp(String filename, List<Token> tokens, int i) {
|
||||
else return ParseRes.failed();
|
||||
}
|
||||
public static ParseRes<ObjProp> parseObjectProp(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0;
|
||||
|
||||
@@ -776,19 +796,21 @@ public class Parsing {
|
||||
if (!res.isSuccess()) return ParseRes.error(loc, "Expected a compound statement for property accessor.", res);
|
||||
n += res.n;
|
||||
|
||||
var end = getLoc(filename, tokens, i + n - 1);
|
||||
|
||||
return ParseRes.res(new ObjProp(
|
||||
name, access,
|
||||
new FunctionStatement(loc, access + " " + name.toString(), argsRes.result.toArray(String[]::new), res.result)
|
||||
new FunctionStatement(loc, end, access + " " + name.toString(), argsRes.result.toArray(String[]::new), false, res.result)
|
||||
), n);
|
||||
}
|
||||
public static ParseRes<ObjectStatement> parseObject(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<ObjectStatement> parseObject(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0;
|
||||
if (!isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed();
|
||||
|
||||
var values = new LinkedHashMap<Object, Statement>();
|
||||
var getters = new LinkedHashMap<Object, FunctionStatement>();
|
||||
var setters = new LinkedHashMap<Object, FunctionStatement>();
|
||||
var values = new LinkedHashMap<String, Statement>();
|
||||
var getters = new LinkedHashMap<String, FunctionStatement>();
|
||||
var setters = new LinkedHashMap<String, FunctionStatement>();
|
||||
|
||||
if (isOperator(tokens, i + n, Operator.BRACE_CLOSE)) {
|
||||
n++;
|
||||
@@ -838,7 +860,7 @@ public class Parsing {
|
||||
|
||||
return ParseRes.res(new ObjectStatement(loc, values, getters, setters), n);
|
||||
}
|
||||
public static ParseRes<NewStatement> parseNew(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<CallStatement> parseNew(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
var n = 0;
|
||||
if (!isIdentifier(tokens, i + n++, "new")) return ParseRes.failed();
|
||||
@@ -849,12 +871,12 @@ public class Parsing {
|
||||
var callRes = parseCall(filename, tokens, i + n, valRes.result, 0);
|
||||
n += callRes.n;
|
||||
if (callRes.isError()) return callRes.transform();
|
||||
else if (callRes.isFailed()) return ParseRes.res(new NewStatement(loc, valRes.result), n);
|
||||
else if (callRes.isFailed()) return ParseRes.res(new CallStatement(loc, true, valRes.result), n);
|
||||
var call = (CallStatement)callRes.result;
|
||||
|
||||
return ParseRes.res(new NewStatement(loc, call.func, call.args), n);
|
||||
return ParseRes.res(new CallStatement(loc, true, call.func, call.args), n);
|
||||
}
|
||||
public static ParseRes<TypeofStatement> parseTypeof(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<TypeofStatement> parseTypeof(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
var n = 0;
|
||||
if (!isIdentifier(tokens, i + n++, "typeof")) return ParseRes.failed();
|
||||
@@ -865,7 +887,7 @@ public class Parsing {
|
||||
|
||||
return ParseRes.res(new TypeofStatement(loc, valRes.result), n);
|
||||
}
|
||||
public static ParseRes<VoidStatement> parseVoid(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<DiscardStatement> parseVoid(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
var n = 0;
|
||||
if (!isIdentifier(tokens, i + n++, "void")) return ParseRes.failed();
|
||||
@@ -874,9 +896,9 @@ public class Parsing {
|
||||
if (!valRes.isSuccess()) return ParseRes.error(loc, "Expected a value after 'void' keyword.", valRes);
|
||||
n += valRes.n;
|
||||
|
||||
return ParseRes.res(new VoidStatement(loc, valRes.result), n);
|
||||
return ParseRes.res(new DiscardStatement(loc, valRes.result), n);
|
||||
}
|
||||
public static ParseRes<? extends Statement> parseDelete(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<? extends Statement> parseDelete(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0;
|
||||
if (!isIdentifier(tokens, i + n++, "delete")) return ParseRes.failed();
|
||||
@@ -897,7 +919,7 @@ public class Parsing {
|
||||
}
|
||||
}
|
||||
|
||||
public static ParseRes<FunctionStatement> parseFunction(String filename, List<Token> tokens, int i, boolean statement) {
|
||||
public static ParseRes<FunctionStatement> parseFunction(Filename filename, List<Token> tokens, int i, boolean statement) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0;
|
||||
|
||||
@@ -935,12 +957,13 @@ public class Parsing {
|
||||
|
||||
var res = parseCompound(filename, tokens, i + n);
|
||||
n += res.n;
|
||||
var end = getLoc(filename, tokens, i + n - 1);
|
||||
|
||||
if (res.isSuccess()) return ParseRes.res(new FunctionStatement(loc, name, args.toArray(String[]::new), res.result), n);
|
||||
if (res.isSuccess()) return ParseRes.res(new FunctionStatement(loc, end, name, args.toArray(String[]::new), statement, res.result), n);
|
||||
else return ParseRes.error(loc, "Expected a compound statement for function.", res);
|
||||
}
|
||||
|
||||
public static ParseRes<OperationStatement> parseUnary(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<OperationStatement> parseUnary(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0;
|
||||
|
||||
@@ -959,9 +982,9 @@ public class Parsing {
|
||||
var res = parseValue(filename, tokens, n + i, 14);
|
||||
|
||||
if (res.isSuccess()) return ParseRes.res(new OperationStatement(loc, operation, res.result), n + res.n);
|
||||
else return ParseRes.error(loc, String.format("Expected a value after the unary operator '%s'.", op.value), res);
|
||||
else return ParseRes.error(loc, String.format("Expected a value after the unary operator '%s'.", op.readable), res);
|
||||
}
|
||||
public static ParseRes<ChangeStatement> parsePrefixChange(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<ChangeStatement> parsePrefixChange(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0;
|
||||
|
||||
@@ -978,7 +1001,7 @@ public class Parsing {
|
||||
if (!(res.result instanceof AssignableStatement)) return ParseRes.error(loc, "Expected assignable value after prefix operator.");
|
||||
return ParseRes.res(new ChangeStatement(loc, (AssignableStatement)res.result, change, false), n + res.n);
|
||||
}
|
||||
public static ParseRes<? extends Statement> parseParens(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<? extends Statement> parseParens(Filename filename, List<Token> tokens, int i) {
|
||||
int n = 0;
|
||||
if (!isOperator(tokens, i + n++, Operator.PAREN_OPEN)) return ParseRes.failed();
|
||||
|
||||
@@ -990,9 +1013,8 @@ public class Parsing {
|
||||
|
||||
return ParseRes.res(res.result, n);
|
||||
}
|
||||
@SuppressWarnings("all")
|
||||
public static ParseRes<? extends Statement> parseSimple(String filename, List<Token> tokens, int i, boolean statement) {
|
||||
var res = new ArrayList<>();
|
||||
public static ParseRes<? extends Statement> parseSimple(Filename filename, List<Token> tokens, int i, boolean statement) {
|
||||
var res = new ArrayList<ParseRes<? extends Statement>>();
|
||||
|
||||
if (!statement) {
|
||||
res.add(parseObject(filename, tokens, i));
|
||||
@@ -1015,10 +1037,10 @@ public class Parsing {
|
||||
parseDelete(filename, tokens, i)
|
||||
));
|
||||
|
||||
return ParseRes.any(res.toArray(ParseRes[]::new));
|
||||
return ParseRes.any(res);
|
||||
}
|
||||
|
||||
public static ParseRes<VariableStatement> parseVariable(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<VariableStatement> parseVariable(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
var literal = parseIdentifier(tokens, i);
|
||||
|
||||
@@ -1026,7 +1048,6 @@ public class Parsing {
|
||||
|
||||
if (!checkVarName(literal.result)) {
|
||||
if (literal.result.equals("await")) return ParseRes.error(loc, "'await' expressions are not supported.");
|
||||
if (literal.result.equals("async")) return ParseRes.error(loc, "'async' is not supported.");
|
||||
if (literal.result.equals("const")) return ParseRes.error(loc, "'const' declarations are not supported.");
|
||||
if (literal.result.equals("let")) return ParseRes.error(loc, "'let' declarations are not supported.");
|
||||
return ParseRes.error(loc, String.format("Unexpected identifier '%s'.", literal.result));
|
||||
@@ -1034,7 +1055,7 @@ public class Parsing {
|
||||
|
||||
return ParseRes.res(new VariableStatement(loc, literal.result), 1);
|
||||
}
|
||||
public static ParseRes<? extends Statement> parseLiteral(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<? extends Statement> parseLiteral(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
var id = parseIdentifier(tokens, i);
|
||||
if (!id.isSuccess()) return id.transform();
|
||||
@@ -1046,10 +1067,10 @@ public class Parsing {
|
||||
return ParseRes.res(new ConstantStatement(loc, false), 1);
|
||||
}
|
||||
if (id.result.equals("undefined")) {
|
||||
return ParseRes.res(new ConstantStatement(loc, null), 1);
|
||||
return ParseRes.res(ConstantStatement.ofUndefined(loc), 1);
|
||||
}
|
||||
if (id.result.equals("null")) {
|
||||
return ParseRes.res(new ConstantStatement(loc, Values.NULL), 1);
|
||||
return ParseRes.res(ConstantStatement.ofNull(loc), 1);
|
||||
}
|
||||
if (id.result.equals("this")) {
|
||||
return ParseRes.res(new VariableIndexStatement(loc, 0), 1);
|
||||
@@ -1062,7 +1083,7 @@ public class Parsing {
|
||||
}
|
||||
return ParseRes.failed();
|
||||
}
|
||||
public static ParseRes<IndexStatement> parseMember(String filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
public static ParseRes<IndexStatement> parseMember(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
var n = 0;
|
||||
|
||||
@@ -1075,7 +1096,7 @@ public class Parsing {
|
||||
|
||||
return ParseRes.res(new IndexStatement(loc, prev, new ConstantStatement(loc, literal.result)), n);
|
||||
}
|
||||
public static ParseRes<IndexStatement> parseIndex(String filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
public static ParseRes<IndexStatement> parseIndex(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
var n = 0;
|
||||
|
||||
@@ -1091,7 +1112,7 @@ public class Parsing {
|
||||
|
||||
return ParseRes.res(new IndexStatement(loc, prev, valRes.result), n);
|
||||
}
|
||||
public static ParseRes<? extends Statement> parseAssign(String filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
public static ParseRes<? extends Statement> parseAssign(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0 ;
|
||||
|
||||
@@ -1103,10 +1124,12 @@ public class Parsing {
|
||||
var op = opRes.result;
|
||||
if (!op.isAssign()) return ParseRes.failed();
|
||||
|
||||
if (!(prev instanceof AssignableStatement)) return ParseRes.error(loc, "Invalid expression on left hand side of assign operator.");
|
||||
if (!(prev instanceof AssignableStatement)) {
|
||||
return ParseRes.error(loc, "Invalid expression on left hand side of assign operator.");
|
||||
}
|
||||
|
||||
var res = parseValue(filename, tokens, i + n, 2);
|
||||
if (!res.isSuccess()) return ParseRes.error(loc, String.format("Expected value after assignment operator '%s'.", op.value), res);
|
||||
if (!res.isSuccess()) return ParseRes.error(loc, String.format("Expected value after assignment operator '%s'.", op.readable), res);
|
||||
n += res.n;
|
||||
|
||||
Operation operation = null;
|
||||
@@ -1125,7 +1148,7 @@ public class Parsing {
|
||||
|
||||
return ParseRes.res(((AssignableStatement)prev).toAssign(res.result, operation), n);
|
||||
}
|
||||
public static ParseRes<CallStatement> parseCall(String filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
public static ParseRes<CallStatement> parseCall(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
var n = 0;
|
||||
|
||||
@@ -1143,8 +1166,7 @@ public class Parsing {
|
||||
prevArg = true;
|
||||
}
|
||||
else if (argRes.isError()) return argRes.transform();
|
||||
else if (isOperator(tokens, i + n, Operator.COMMA)) {
|
||||
if (!prevArg) args.add(null);
|
||||
else if (prevArg && isOperator(tokens, i + n, Operator.COMMA)) {
|
||||
prevArg = false;
|
||||
n++;
|
||||
}
|
||||
@@ -1152,12 +1174,12 @@ public class Parsing {
|
||||
n++;
|
||||
break;
|
||||
}
|
||||
else return ParseRes.failed();
|
||||
else return ParseRes.error(getLoc(filename, tokens, i + n), prevArg ? "Expected a comma or a closing paren." : "Expected an expression or a closing paren.");
|
||||
}
|
||||
|
||||
return ParseRes.res(new CallStatement(loc, prev, args.toArray(Statement[]::new)), n);
|
||||
return ParseRes.res(new CallStatement(loc, false, prev, args.toArray(Statement[]::new)), n);
|
||||
}
|
||||
public static ParseRes<ChangeStatement> parsePostfixChange(String filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
public static ParseRes<ChangeStatement> parsePostfixChange(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0;
|
||||
|
||||
@@ -1175,7 +1197,7 @@ public class Parsing {
|
||||
if (!(prev instanceof AssignableStatement)) return ParseRes.error(loc, "Expected assignable value before suffix operator.");
|
||||
return ParseRes.res(new ChangeStatement(loc, (AssignableStatement)prev, change, true), n);
|
||||
}
|
||||
public static ParseRes<OperationStatement> parseInstanceof(String filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
public static ParseRes<OperationStatement> parseInstanceof(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0;
|
||||
|
||||
@@ -1188,7 +1210,7 @@ public class Parsing {
|
||||
|
||||
return ParseRes.res(new OperationStatement(loc, Operation.INSTANCEOF, prev, valRes.result), n);
|
||||
}
|
||||
public static ParseRes<OperationStatement> parseIn(String filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
public static ParseRes<OperationStatement> parseIn(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0;
|
||||
|
||||
@@ -1201,7 +1223,7 @@ public class Parsing {
|
||||
|
||||
return ParseRes.res(new OperationStatement(loc, Operation.IN, prev, valRes.result), n);
|
||||
}
|
||||
public static ParseRes<CommaStatement> parseComma(String filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
public static ParseRes<CompoundStatement> parseComma(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
var n = 0;
|
||||
|
||||
@@ -1212,9 +1234,9 @@ public class Parsing {
|
||||
if (!res.isSuccess()) return ParseRes.error(loc, "Expected a value after the comma.", res);
|
||||
n += res.n;
|
||||
|
||||
return ParseRes.res(new CommaStatement(loc, prev, res.result), n);
|
||||
return ParseRes.res(new CompoundStatement(loc, false, prev, res.result), n);
|
||||
}
|
||||
public static ParseRes<TernaryStatement> parseTernary(String filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
public static ParseRes<IfStatement> parseTernary(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
var n = 0;
|
||||
|
||||
@@ -1231,9 +1253,9 @@ public class Parsing {
|
||||
if (!b.isSuccess()) return ParseRes.error(loc, "Expected a second value after the ternary operator.", b);
|
||||
n += b.n;
|
||||
|
||||
return ParseRes.res(new TernaryStatement(loc, prev, a.result, b.result), n);
|
||||
return ParseRes.res(new IfStatement(loc, prev, a.result, b.result), n);
|
||||
}
|
||||
public static ParseRes<? extends Statement> parseOperator(String filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
public static ParseRes<? extends Statement> parseOperator(Filename filename, List<Token> tokens, int i, Statement prev, int precedence) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
var n = 0;
|
||||
|
||||
@@ -1245,7 +1267,7 @@ public class Parsing {
|
||||
if (op.isAssign()) return parseAssign(filename, tokens, i + n - 1, prev, precedence);
|
||||
|
||||
var res = parseValue(filename, tokens, i + n, op.precedence + (op.reverse ? 0 : 1));
|
||||
if (!res.isSuccess()) return ParseRes.error(loc, String.format("Expected a value after the '%s' operator.", op.value), res);
|
||||
if (!res.isSuccess()) return ParseRes.error(loc, String.format("Expected a value after the '%s' operator.", op.readable), res);
|
||||
n += res.n;
|
||||
|
||||
if (op == Operator.LAZY_AND) {
|
||||
@@ -1258,7 +1280,7 @@ public class Parsing {
|
||||
return ParseRes.res(new OperationStatement(loc, op.operation, prev, res.result), n);
|
||||
}
|
||||
|
||||
public static ParseRes<? extends Statement> parseValue(String filename, List<Token> tokens, int i, int precedence, boolean statement) {
|
||||
public static ParseRes<? extends Statement> parseValue(Filename filename, List<Token> tokens, int i, int precedence, boolean statement) {
|
||||
Statement prev = null;
|
||||
int n = 0;
|
||||
|
||||
@@ -1299,16 +1321,15 @@ public class Parsing {
|
||||
if (prev == null) return ParseRes.failed();
|
||||
else return ParseRes.res(prev, n);
|
||||
}
|
||||
public static ParseRes<? extends Statement> parseValue(String filename, List<Token> tokens, int i, int precedence) {
|
||||
public static ParseRes<? extends Statement> parseValue(Filename filename, List<Token> tokens, int i, int precedence) {
|
||||
return parseValue(filename, tokens, i, precedence, false);
|
||||
}
|
||||
|
||||
public static ParseRes<? extends Statement> parseValueStatement(String filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
public static ParseRes<? extends Statement> parseValueStatement(Filename filename, List<Token> tokens, int i) {
|
||||
var valRes = parseValue(filename, tokens, i, 0, true);
|
||||
if (!valRes.isSuccess()) return valRes.transform();
|
||||
|
||||
valRes.result.setLoc(loc);
|
||||
// valRes.result.setLoc(loc);
|
||||
var res = ParseRes.res(valRes.result, valRes.n);
|
||||
|
||||
if (isStatementEnd(tokens, i + res.n)) {
|
||||
@@ -1320,7 +1341,7 @@ public class Parsing {
|
||||
}
|
||||
else return ParseRes.error(getLoc(filename, tokens, i), "Expected an end of statement.", res);
|
||||
}
|
||||
public static ParseRes<VariableDeclareStatement> parseVariableDeclare(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<VariableDeclareStatement> parseVariableDeclare(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0;
|
||||
if (!isIdentifier(tokens, i + n++, "var")) return ParseRes.failed();
|
||||
@@ -1333,6 +1354,7 @@ public class Parsing {
|
||||
}
|
||||
|
||||
while (true) {
|
||||
var nameLoc = getLoc(filename, tokens, i + n);
|
||||
var nameRes = parseIdentifier(tokens, i + n++);
|
||||
if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a variable name.");
|
||||
|
||||
@@ -1350,7 +1372,7 @@ public class Parsing {
|
||||
val = valRes.result;
|
||||
}
|
||||
|
||||
res.add(new Pair(nameRes.result, val));
|
||||
res.add(new Pair(nameRes.result, val, nameLoc));
|
||||
|
||||
if (isOperator(tokens, i + n, Operator.COMMA)) {
|
||||
n++;
|
||||
@@ -1364,7 +1386,7 @@ public class Parsing {
|
||||
}
|
||||
}
|
||||
|
||||
public static ParseRes<ReturnStatement> parseReturn(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<ReturnStatement> parseReturn(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0;
|
||||
if (!isIdentifier(tokens, i + n++, "return")) return ParseRes.failed();
|
||||
@@ -1376,8 +1398,7 @@ public class Parsing {
|
||||
|
||||
var valRes = parseValue(filename, tokens, i + n, 0);
|
||||
n += valRes.n;
|
||||
if (valRes.isError())
|
||||
return ParseRes.error(loc, "Expected a return value.", valRes);
|
||||
if (valRes.isError()) return ParseRes.error(loc, "Expected a return value.", valRes);
|
||||
|
||||
var res = ParseRes.res(new ReturnStatement(loc, valRes.result), n);
|
||||
|
||||
@@ -1388,7 +1409,7 @@ public class Parsing {
|
||||
else
|
||||
return ParseRes.error(getLoc(filename, tokens, i), "Expected an end of statement.", valRes);
|
||||
}
|
||||
public static ParseRes<ThrowStatement> parseThrow(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<ThrowStatement> parseThrow(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0;
|
||||
if (!isIdentifier(tokens, i + n++, "throw")) return ParseRes.failed();
|
||||
@@ -1406,7 +1427,7 @@ public class Parsing {
|
||||
else return ParseRes.error(getLoc(filename, tokens, i), "Expected an end of statement.", valRes);
|
||||
}
|
||||
|
||||
public static ParseRes<BreakStatement> parseBreak(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<BreakStatement> parseBreak(Filename filename, List<Token> tokens, int i) {
|
||||
if (!isIdentifier(tokens, i, "break")) return ParseRes.failed();
|
||||
|
||||
if (isStatementEnd(tokens, i + 1)) {
|
||||
@@ -1424,7 +1445,7 @@ public class Parsing {
|
||||
}
|
||||
else return ParseRes.error(getLoc(filename, tokens, i), "Expected an end of statement.");
|
||||
}
|
||||
public static ParseRes<ContinueStatement> parseContinue(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<ContinueStatement> parseContinue(Filename filename, List<Token> tokens, int i) {
|
||||
if (!isIdentifier(tokens, i, "continue")) return ParseRes.failed();
|
||||
|
||||
if (isStatementEnd(tokens, i + 1)) {
|
||||
@@ -1442,8 +1463,8 @@ public class Parsing {
|
||||
}
|
||||
else return ParseRes.error(getLoc(filename, tokens, i), "Expected an end of statement.");
|
||||
}
|
||||
public static ParseRes<DebugStatement> parseDebug(String filename, List<Token> tokens, int i) {
|
||||
if (!isIdentifier(tokens, i, "debug")) return ParseRes.failed();
|
||||
public static ParseRes<DebugStatement> parseDebug(Filename filename, List<Token> tokens, int i) {
|
||||
if (!isIdentifier(tokens, i, "debugger")) return ParseRes.failed();
|
||||
|
||||
if (isStatementEnd(tokens, i + 1)) {
|
||||
if (isOperator(tokens, i + 1, Operator.SEMICOLON)) return ParseRes.res(new DebugStatement(getLoc(filename, tokens, i)), 2);
|
||||
@@ -1452,7 +1473,7 @@ public class Parsing {
|
||||
else return ParseRes.error(getLoc(filename, tokens, i), "Expected an end of statement.");
|
||||
}
|
||||
|
||||
public static ParseRes<CompoundStatement> parseCompound(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<CompoundStatement> parseCompound(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0;
|
||||
if (!isOperator(tokens, i + n++, Operator.BRACE_OPEN)) return ParseRes.failed();
|
||||
@@ -1478,7 +1499,7 @@ public class Parsing {
|
||||
statements.add(res.result);
|
||||
}
|
||||
|
||||
return ParseRes.res(new CompoundStatement(loc, statements.toArray(Statement[]::new)), n);
|
||||
return ParseRes.res(new CompoundStatement(loc, true, statements.toArray(Statement[]::new)).setEnd(getLoc(filename, tokens, i + n - 1)), n);
|
||||
}
|
||||
public static ParseRes<String> parseLabel(List<Token> tokens, int i) {
|
||||
int n = 0;
|
||||
@@ -1488,7 +1509,7 @@ public class Parsing {
|
||||
|
||||
return ParseRes.res(nameRes.result, n);
|
||||
}
|
||||
public static ParseRes<IfStatement> parseIf(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<IfStatement> parseIf(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0;
|
||||
|
||||
@@ -1499,8 +1520,7 @@ public class Parsing {
|
||||
if (!condRes.isSuccess()) return ParseRes.error(loc, "Expected an if condition.", condRes);
|
||||
n += condRes.n;
|
||||
|
||||
if (!isOperator(tokens, i + n++, Operator.PAREN_CLOSE))
|
||||
return ParseRes.error(loc, "Expected a closing paren after if condition.");
|
||||
if (!isOperator(tokens, i + n++, Operator.PAREN_CLOSE)) return ParseRes.error(loc, "Expected a closing paren after if condition.");
|
||||
|
||||
var res = parseStatement(filename, tokens, i + n);
|
||||
if (!res.isSuccess()) return ParseRes.error(loc, "Expected an if body.", res);
|
||||
@@ -1515,7 +1535,7 @@ public class Parsing {
|
||||
|
||||
return ParseRes.res(new IfStatement(loc, condRes.result, res.result, elseRes.result), n);
|
||||
}
|
||||
public static ParseRes<WhileStatement> parseWhile(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<WhileStatement> parseWhile(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0;
|
||||
|
||||
@@ -1537,7 +1557,7 @@ public class Parsing {
|
||||
|
||||
return ParseRes.res(new WhileStatement(loc, labelRes.result, condRes.result, res.result), n);
|
||||
}
|
||||
public static ParseRes<Statement> parseSwitchCase(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<Statement> parseSwitchCase(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0;
|
||||
|
||||
@@ -1557,7 +1577,7 @@ public class Parsing {
|
||||
|
||||
return ParseRes.res(null, 2);
|
||||
}
|
||||
public static ParseRes<SwitchStatement> parseSwitch(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<SwitchStatement> parseSwitch(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0;
|
||||
|
||||
@@ -1617,7 +1637,7 @@ public class Parsing {
|
||||
statements.toArray(Statement[]::new)
|
||||
), n);
|
||||
}
|
||||
public static ParseRes<DoWhileStatement> parseDoWhile(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<DoWhileStatement> parseDoWhile(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0;
|
||||
|
||||
@@ -1646,7 +1666,7 @@ public class Parsing {
|
||||
}
|
||||
else return ParseRes.error(getLoc(filename, tokens, i), "Expected a semicolon.");
|
||||
}
|
||||
public static ParseRes<Statement> parseFor(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<Statement> parseFor(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0;
|
||||
|
||||
@@ -1660,15 +1680,14 @@ public class Parsing {
|
||||
|
||||
if (isOperator(tokens, i + n, Operator.SEMICOLON)) {
|
||||
n++;
|
||||
decl = new CompoundStatement(loc);
|
||||
decl = new CompoundStatement(loc, false);
|
||||
}
|
||||
else {
|
||||
var declRes = ParseRes.any(
|
||||
parseVariableDeclare(filename, tokens, i + n),
|
||||
parseValueStatement(filename, tokens, i + n)
|
||||
);
|
||||
if (!declRes.isSuccess())
|
||||
return ParseRes.error(loc, "Expected a declaration or an expression.", declRes);
|
||||
if (!declRes.isSuccess()) return ParseRes.error(loc, "Expected a declaration or an expression.", declRes);
|
||||
n += declRes.n;
|
||||
decl = declRes.result;
|
||||
}
|
||||
@@ -1687,7 +1706,7 @@ public class Parsing {
|
||||
|
||||
if (isOperator(tokens, i + n, Operator.PAREN_CLOSE)) {
|
||||
n++;
|
||||
inc = new CompoundStatement(loc);
|
||||
inc = new CompoundStatement(loc, false);
|
||||
}
|
||||
else {
|
||||
var incRes = parseValue(filename, tokens, i + n, 0);
|
||||
@@ -1704,7 +1723,7 @@ public class Parsing {
|
||||
|
||||
return ParseRes.res(new ForStatement(loc, labelRes.result, decl, cond, inc, res.result), n);
|
||||
}
|
||||
public static ParseRes<ForInStatement> parseForIn(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<ForInStatement> parseForIn(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0;
|
||||
|
||||
@@ -1722,6 +1741,7 @@ public class Parsing {
|
||||
|
||||
var nameRes = parseIdentifier(tokens, i + n);
|
||||
if (!nameRes.isSuccess()) return ParseRes.error(loc, "Expected a variable name for 'for' loop.");
|
||||
var nameLoc = getLoc(filename, tokens, i + n);
|
||||
n += nameRes.n;
|
||||
|
||||
Statement varVal = null;
|
||||
@@ -1755,9 +1775,9 @@ public class Parsing {
|
||||
if (!bodyRes.isSuccess()) return ParseRes.error(loc, "Expected a for body.", bodyRes);
|
||||
n += bodyRes.n;
|
||||
|
||||
return ParseRes.res(new ForInStatement(loc, labelRes.result, isDecl, nameRes.result, varVal, objRes.result, bodyRes.result), n);
|
||||
return ParseRes.res(new ForInStatement(loc, nameLoc, labelRes.result, isDecl, nameRes.result, varVal, objRes.result, bodyRes.result), n);
|
||||
}
|
||||
public static ParseRes<TryStatement> parseCatch(String filename, List<Token> tokens, int i) {
|
||||
public static ParseRes<TryStatement> parseCatch(Filename filename, List<Token> tokens, int i) {
|
||||
var loc = getLoc(filename, tokens, i);
|
||||
int n = 0;
|
||||
|
||||
@@ -1798,8 +1818,8 @@ public class Parsing {
|
||||
return ParseRes.res(new TryStatement(loc, res.result, catchBody, finallyBody, name), n);
|
||||
}
|
||||
|
||||
public static ParseRes<? extends Statement> parseStatement(String filename, List<Token> tokens, int i) {
|
||||
if (isOperator(tokens, i, Operator.SEMICOLON)) return ParseRes.res(new CompoundStatement(getLoc(filename, tokens, i)), 1);
|
||||
public static ParseRes<? extends Statement> parseStatement(Filename filename, List<Token> tokens, int i) {
|
||||
if (isOperator(tokens, i, Operator.SEMICOLON)) return ParseRes.res(new CompoundStatement(getLoc(filename, tokens, i), false), 1);
|
||||
if (isIdentifier(tokens, i, "with")) return ParseRes.error(getLoc(filename, tokens, i), "'with' statements are not allowed.");
|
||||
return ParseRes.any(
|
||||
parseVariableDeclare(filename, tokens, i),
|
||||
@@ -1821,7 +1841,7 @@ public class Parsing {
|
||||
);
|
||||
}
|
||||
|
||||
public static Statement[] parse(String filename, String raw) {
|
||||
public static Statement[] parse(Filename filename, String raw) {
|
||||
var tokens = tokenize(filename, raw);
|
||||
var list = new ArrayList<Statement>();
|
||||
int i = 0;
|
||||
@@ -1842,42 +1862,31 @@ public class Parsing {
|
||||
return list.toArray(Statement[]::new);
|
||||
}
|
||||
|
||||
public static CodeFunction compile(GlobalScope scope, Statement... statements) {
|
||||
var target = scope.globalChild();
|
||||
var subscope = target.child();
|
||||
var res = new ArrayList<Instruction>();
|
||||
var body = new CompoundStatement(null, statements);
|
||||
// var optimized = body.optimize();
|
||||
if (body instanceof CompoundStatement) body = (CompoundStatement)body;
|
||||
else body = new CompoundStatement(null, new Statement[] { body });
|
||||
public static CompileResult compile(Statement ...statements) {
|
||||
var target = new CompileResult(new LocalScopeRecord());
|
||||
var stm = new CompoundStatement(null, true, statements);
|
||||
|
||||
subscope.define("this");
|
||||
subscope.define("arguments");
|
||||
|
||||
body.declare(target);
|
||||
target.scope.define("this");
|
||||
target.scope.define("arguments");
|
||||
|
||||
try {
|
||||
body.compile(res, subscope);
|
||||
FunctionStatement.checkBreakAndCont(res, 0);
|
||||
stm.compile(target, true);
|
||||
FunctionStatement.checkBreakAndCont(target, 0);
|
||||
}
|
||||
catch (SyntaxException e) {
|
||||
res.clear();
|
||||
res.add(Instruction.throwSyntax(e));
|
||||
target = new CompileResult(new LocalScopeRecord());
|
||||
|
||||
target.scope.define("this");
|
||||
target.scope.define("arguments");
|
||||
|
||||
target.add(Instruction.throwSyntax(e)).setLocation(stm.loc());
|
||||
}
|
||||
|
||||
if (res.size() != 0 && res.get(res.size() - 1).type == Type.DISCARD) {
|
||||
res.set(res.size() - 1, Instruction.ret());
|
||||
}
|
||||
else res.add(Instruction.ret());
|
||||
target.add(Instruction.ret()).setLocation(stm.loc());
|
||||
|
||||
return new CodeFunction("", subscope.localsCount(), 0, scope, new ValueVariable[0], res.toArray(Instruction[]::new));
|
||||
}
|
||||
public static CodeFunction compile(GlobalScope scope, String filename, String raw) {
|
||||
try {
|
||||
return compile(scope, parse(filename, raw));
|
||||
}
|
||||
catch (SyntaxException e) {
|
||||
return new CodeFunction(null, 2, 0, scope, new ValueVariable[0], new Instruction[] { Instruction.throwSyntax(e) });
|
||||
return target;
|
||||
}
|
||||
public static CompileResult compile(Filename filename, String raw) {
|
||||
return compile(parse(filename, raw));
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.topchetoeu.jscript.parsing;
|
||||
package me.topchetoeu.jscript.compilation.parsing;
|
||||
|
||||
public class RawToken {
|
||||
public final String value;
|
||||
@@ -1,7 +1,7 @@
|
||||
package me.topchetoeu.jscript.parsing;
|
||||
package me.topchetoeu.jscript.compilation.parsing;
|
||||
|
||||
import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.parsing.ParseRes.State;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.compilation.parsing.ParseRes.State;
|
||||
|
||||
public class TestRes {
|
||||
public final State state;
|
||||
@@ -1,21 +1,24 @@
|
||||
package me.topchetoeu.jscript.parsing;
|
||||
package me.topchetoeu.jscript.compilation.parsing;
|
||||
|
||||
public class Token {
|
||||
public final Object value;
|
||||
public final String rawValue;
|
||||
public final boolean isString;
|
||||
public final boolean isRegex;
|
||||
public final int line;
|
||||
public final int start;
|
||||
|
||||
private Token(int line, int start, Object value, boolean isString, boolean isRegex) {
|
||||
private Token(int line, int start, Object value, String rawValue, boolean isString, boolean isRegex) {
|
||||
this.value = value;
|
||||
this.rawValue = rawValue;
|
||||
this.line = line;
|
||||
this.start = start;
|
||||
this.isString = isString;
|
||||
this.isRegex = isRegex;
|
||||
}
|
||||
private Token(int line, int start, Object value) {
|
||||
private Token(int line, int start, Object value, String rawValue) {
|
||||
this.value = value;
|
||||
this.rawValue = rawValue;
|
||||
this.line = line;
|
||||
this.start = start;
|
||||
this.isString = false;
|
||||
@@ -37,19 +40,19 @@ public class Token {
|
||||
public String identifier() { return (String)value; }
|
||||
public Operator operator() { return (Operator)value; }
|
||||
|
||||
public static Token regex(int line, int start, String val) {
|
||||
return new Token(line, start, val, false, true);
|
||||
public static Token regex(int line, int start, String val, String rawValue) {
|
||||
return new Token(line, start, val, rawValue, false, true);
|
||||
}
|
||||
public static Token string(int line, int start, String val) {
|
||||
return new Token(line, start, val, true, false);
|
||||
public static Token string(int line, int start, String val, String rawValue) {
|
||||
return new Token(line, start, val, rawValue, true, false);
|
||||
}
|
||||
public static Token number(int line, int start, double val) {
|
||||
return new Token(line, start, val);
|
||||
public static Token number(int line, int start, double val, String rawValue) {
|
||||
return new Token(line, start, val, rawValue);
|
||||
}
|
||||
public static Token identifier(int line, int start, String val) {
|
||||
return new Token(line, start, val);
|
||||
return new Token(line, start, val, val);
|
||||
}
|
||||
public static Token operator(int line, int start, Operator val) {
|
||||
return new Token(line, start, val);
|
||||
return new Token(line, start, val, val.readable);
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package me.topchetoeu.jscript.parsing;
|
||||
package me.topchetoeu.jscript.compilation.parsing;
|
||||
|
||||
enum TokenType {
|
||||
REGEX,
|
||||
@@ -1,25 +1,22 @@
|
||||
package me.topchetoeu.jscript.engine.scope;
|
||||
package me.topchetoeu.jscript.compilation.scope;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import me.topchetoeu.jscript.engine.CallContext;
|
||||
|
||||
public class LocalScopeRecord implements ScopeRecord {
|
||||
public final LocalScopeRecord parent;
|
||||
public final GlobalScope global;
|
||||
|
||||
private final ArrayList<String> captures = new ArrayList<>();
|
||||
private final ArrayList<String> locals = new ArrayList<>();
|
||||
|
||||
public String[] captures() {
|
||||
return captures.toArray(String[]::new);
|
||||
}
|
||||
public String[] locals() {
|
||||
return locals.toArray(String[]::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocalScopeRecord parent() { return parent; }
|
||||
|
||||
public LocalScopeRecord child() {
|
||||
return new LocalScopeRecord(this, global);
|
||||
return new LocalScopeRecord(this);
|
||||
}
|
||||
|
||||
public int localsCount() {
|
||||
@@ -59,12 +56,6 @@ public class LocalScopeRecord implements ScopeRecord {
|
||||
|
||||
return name;
|
||||
}
|
||||
public boolean has(CallContext ctx, String name) throws InterruptedException {
|
||||
return
|
||||
global.has(ctx, name) ||
|
||||
locals.contains(name) ||
|
||||
parent != null && parent.has(ctx, name);
|
||||
}
|
||||
public Object define(String name, boolean force) {
|
||||
if (!force && locals.contains(name)) return locals.indexOf(name);
|
||||
locals.add(name);
|
||||
@@ -77,12 +68,10 @@ public class LocalScopeRecord implements ScopeRecord {
|
||||
locals.remove(locals.size() - 1);
|
||||
}
|
||||
|
||||
public LocalScopeRecord(GlobalScope global) {
|
||||
public LocalScopeRecord() {
|
||||
this.parent = null;
|
||||
this.global = global;
|
||||
}
|
||||
public LocalScopeRecord(LocalScopeRecord parent, GlobalScope global) {
|
||||
public LocalScopeRecord(LocalScopeRecord parent) {
|
||||
this.parent = parent;
|
||||
this.global = global;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
package me.topchetoeu.jscript.engine.scope;
|
||||
package me.topchetoeu.jscript.compilation.scope;
|
||||
|
||||
public interface ScopeRecord {
|
||||
public Object getKey(String name);
|
||||
public Object define(String name);
|
||||
public ScopeRecord parent();
|
||||
public LocalScopeRecord child();
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class ArrayStatement extends Statement {
|
||||
public final Statement[] statements;
|
||||
|
||||
@Override public boolean pure() {
|
||||
for (var stm : statements) {
|
||||
if (!stm.pure()) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
target.add(Instruction.loadArr(statements.length));
|
||||
|
||||
for (var i = 0; i < statements.length; i++) {
|
||||
var el = statements[i];
|
||||
if (el != null) {
|
||||
target.add(Instruction.dup());
|
||||
target.add(Instruction.pushValue(i));
|
||||
el.compile(target, true);
|
||||
target.add(Instruction.storeMember());
|
||||
}
|
||||
}
|
||||
|
||||
if (!pollute) target.add(Instruction.discard());
|
||||
}
|
||||
|
||||
public ArrayStatement(Location loc, Statement[] statements) {
|
||||
super(loc);
|
||||
this.statements = statements;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class CallStatement extends Statement {
|
||||
public final Statement func;
|
||||
public final Statement[] args;
|
||||
public final boolean isNew;
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute, BreakpointType type) {
|
||||
if (isNew) func.compile(target, true);
|
||||
else if (func instanceof IndexStatement) {
|
||||
((IndexStatement)func).compile(target, true, true);
|
||||
}
|
||||
else {
|
||||
target.add(Instruction.pushUndefined());
|
||||
func.compile(target, true);
|
||||
}
|
||||
|
||||
for (var arg : args) arg.compile(target, true);
|
||||
|
||||
if (isNew) target.add(Instruction.callNew(args.length)).setLocationAndDebug(loc(), type);
|
||||
else target.add(Instruction.call(args.length)).setLocationAndDebug(loc(), type);
|
||||
|
||||
if (!pollute) target.add(Instruction.discard());
|
||||
}
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
compile(target, pollute, BreakpointType.STEP_IN);
|
||||
}
|
||||
|
||||
public CallStatement(Location loc, boolean isNew, Statement func, Statement ...args) {
|
||||
super(loc);
|
||||
this.isNew = isNew;
|
||||
this.func = func;
|
||||
this.args = args;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,11 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import me.topchetoeu.jscript.Location;
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.common.Operation;
|
||||
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
||||
import me.topchetoeu.jscript.compilation.Instruction;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
import me.topchetoeu.jscript.engine.Operation;
|
||||
import me.topchetoeu.jscript.engine.scope.ScopeRecord;
|
||||
|
||||
public class ChangeStatement extends Statement {
|
||||
public final AssignableStatement value;
|
||||
@@ -15,11 +13,13 @@ public class ChangeStatement extends Statement {
|
||||
public final boolean postfix;
|
||||
|
||||
@Override
|
||||
public boolean pollutesStack() { return true; }
|
||||
|
||||
@Override
|
||||
public void compile(List<Instruction> target, ScopeRecord scope) {
|
||||
value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, scope, postfix);
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
value.toAssign(new ConstantStatement(loc(), -addAmount), Operation.SUBTRACT).compile(target, true);
|
||||
if (!pollute) target.add(Instruction.discard());
|
||||
else if (postfix) {
|
||||
target.add(Instruction.pushValue(addAmount));
|
||||
target.add(Instruction.operation(Operation.SUBTRACT));
|
||||
}
|
||||
}
|
||||
|
||||
public ChangeStatement(Location loc, AssignableStatement value, double addAmount, boolean postfix) {
|
||||
@@ -0,0 +1,47 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class ConstantStatement extends Statement {
|
||||
public final Object value;
|
||||
public final boolean isNull;
|
||||
|
||||
@Override public boolean pure() { return true; }
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
if (pollute) {
|
||||
if (isNull) target.add(Instruction.pushNull());
|
||||
else if (value instanceof Double) target.add(Instruction.pushValue((Double)value));
|
||||
else if (value instanceof String) target.add(Instruction.pushValue((String)value));
|
||||
else if (value instanceof Boolean) target.add(Instruction.pushValue((Boolean)value));
|
||||
else target.add(Instruction.pushUndefined());
|
||||
}
|
||||
}
|
||||
|
||||
private ConstantStatement(Location loc, Object val, boolean isNull) {
|
||||
super(loc);
|
||||
this.value = val;
|
||||
this.isNull = isNull;
|
||||
}
|
||||
|
||||
public ConstantStatement(Location loc, boolean val) {
|
||||
this(loc, val, false);
|
||||
}
|
||||
public ConstantStatement(Location loc, String val) {
|
||||
this(loc, val, false);
|
||||
}
|
||||
public ConstantStatement(Location loc, double val) {
|
||||
this(loc, val, false);
|
||||
}
|
||||
|
||||
public static ConstantStatement ofUndefined(Location loc) {
|
||||
return new ConstantStatement(loc, null, false);
|
||||
}
|
||||
public static ConstantStatement ofNull(Location loc) {
|
||||
return new ConstantStatement(loc, null, true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class DiscardStatement extends Statement {
|
||||
public final Statement value;
|
||||
|
||||
@Override public boolean pure() { return value.pure(); }
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
value.compile(target, false);
|
||||
if (pollute) target.add(Instruction.pushUndefined());
|
||||
}
|
||||
|
||||
public DiscardStatement(Location loc, Statement val) {
|
||||
super(loc);
|
||||
this.value = val;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.common.Instruction.Type;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.CompoundStatement;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
||||
|
||||
public class FunctionStatement extends Statement {
|
||||
public final CompoundStatement body;
|
||||
public final String varName;
|
||||
public final String[] args;
|
||||
public final boolean statement;
|
||||
public final Location end;
|
||||
|
||||
@Override public boolean pure() { return varName == null && statement; }
|
||||
|
||||
@Override
|
||||
public void declare(CompileResult target) {
|
||||
if (varName != null && statement) target.scope.define(varName);
|
||||
}
|
||||
|
||||
public static void checkBreakAndCont(CompileResult target, int start) {
|
||||
for (int i = start; i < target.size(); i++) {
|
||||
if (target.get(i).type == Type.NOP) {
|
||||
if (target.get(i).is(0, "break") ) {
|
||||
throw new SyntaxException(target.map.toLocation(i), "Break was placed outside a loop.");
|
||||
}
|
||||
if (target.get(i).is(0, "cont")) {
|
||||
throw new SyntaxException(target.map.toLocation(i), "Continue was placed outside a loop.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CompileResult compileBody(CompileResult target, boolean pollute, BreakpointType bp) {
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
for (var j = 0; j < i; j++) {
|
||||
if (args[i].equals(args[j])) {
|
||||
throw new SyntaxException(loc(), "Duplicate parameter '" + args[i] + "'.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var subtarget = new CompileResult(target.scope.child());
|
||||
|
||||
subtarget.scope.define("this");
|
||||
var argsVar = subtarget.scope.define("arguments");
|
||||
|
||||
if (args.length > 0) {
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
subtarget.add(Instruction.loadVar(argsVar));
|
||||
subtarget.add(Instruction.pushValue(i));
|
||||
subtarget.add(Instruction.loadMember());
|
||||
subtarget.add(Instruction.storeVar(subtarget.scope.define(args[i])));
|
||||
}
|
||||
}
|
||||
|
||||
if (!statement && this.varName != null) {
|
||||
subtarget.add(Instruction.storeSelfFunc((int)subtarget.scope.define(this.varName))).setLocationAndDebug(loc(), bp);
|
||||
}
|
||||
|
||||
body.declare(subtarget);
|
||||
body.compile(subtarget, false);
|
||||
subtarget.length = args.length;
|
||||
subtarget.add(Instruction.ret()).setLocation(end);
|
||||
checkBreakAndCont(subtarget, 0);
|
||||
|
||||
if (pollute) target.add(Instruction.loadFunc(target.children.size(), subtarget.scope.getCaptures()));
|
||||
return target.addChild(subtarget);
|
||||
}
|
||||
|
||||
public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
||||
if (this.varName != null) name = this.varName;
|
||||
|
||||
var hasVar = this.varName != null && statement;
|
||||
var hasName = name != null;
|
||||
|
||||
compileBody(target, pollute || hasVar || hasName, bp);
|
||||
|
||||
if (hasName) {
|
||||
if (pollute || hasVar) target.add(Instruction.dup());
|
||||
target.add(Instruction.pushValue("name"));
|
||||
target.add(Instruction.pushValue(name));
|
||||
target.add(Instruction.storeMember());
|
||||
}
|
||||
|
||||
if (hasVar) {
|
||||
var key = target.scope.getKey(this.varName);
|
||||
|
||||
if (key instanceof String) target.add(Instruction.makeVar((String)key));
|
||||
target.add(Instruction.storeVar(target.scope.getKey(this.varName), false));
|
||||
}
|
||||
}
|
||||
public void compile(CompileResult target, boolean pollute, String name) {
|
||||
compile(target, pollute, name, BreakpointType.NONE);
|
||||
}
|
||||
@Override public void compile(CompileResult target, boolean pollute, BreakpointType bp) {
|
||||
compile(target, pollute, (String)null, bp);
|
||||
}
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
compile(target, pollute, (String)null, BreakpointType.NONE);
|
||||
}
|
||||
|
||||
public FunctionStatement(Location loc, Location end, String varName, String[] args, boolean statement, CompoundStatement body) {
|
||||
super(loc);
|
||||
|
||||
this.end = end;
|
||||
this.varName = varName;
|
||||
this.statement = statement;
|
||||
|
||||
this.args = args;
|
||||
this.body = body;
|
||||
}
|
||||
|
||||
public static void compileWithName(Statement stm, CompileResult target, boolean pollute, String name) {
|
||||
if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, pollute, name);
|
||||
else stm.compile(target, pollute);
|
||||
}
|
||||
public static void compileWithName(Statement stm, CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
||||
if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, pollute, name, bp);
|
||||
else stm.compile(target, pollute, bp);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class GlobalThisStatement extends Statement {
|
||||
@Override public boolean pure() { return true; }
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
if (pollute) target.add(Instruction.loadGlob());
|
||||
}
|
||||
|
||||
public GlobalThisStatement(Location loc) {
|
||||
super(loc);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.common.Operation;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class IndexAssignStatement extends Statement {
|
||||
public final Statement object;
|
||||
public final Statement index;
|
||||
public final Statement value;
|
||||
public final Operation operation;
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
if (operation != null) {
|
||||
object.compile(target, true);
|
||||
index.compile(target, true);
|
||||
target.add(Instruction.dup(2));
|
||||
|
||||
target.add(Instruction.loadMember());
|
||||
value.compile(target, true);
|
||||
target.add(Instruction.operation(operation));
|
||||
|
||||
target.add(Instruction.storeMember(pollute)).setLocationAndDebug(loc(), BreakpointType.STEP_IN);
|
||||
}
|
||||
else {
|
||||
object.compile(target, true);
|
||||
index.compile(target, true);
|
||||
value.compile(target, true);
|
||||
|
||||
target.add(Instruction.storeMember(pollute)).setLocationAndDebug(loc(), BreakpointType.STEP_IN);;
|
||||
}
|
||||
}
|
||||
|
||||
public IndexAssignStatement(Location loc, Statement object, Statement index, Statement value, Operation operation) {
|
||||
super(loc);
|
||||
this.object = object;
|
||||
this.index = index;
|
||||
this.value = value;
|
||||
this.operation = operation;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.common.Operation;
|
||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class IndexStatement extends AssignableStatement {
|
||||
public final Statement object;
|
||||
public final Statement index;
|
||||
|
||||
@Override
|
||||
public Statement toAssign(Statement val, Operation operation) {
|
||||
return new IndexAssignStatement(loc(), object, index, val, operation);
|
||||
}
|
||||
public void compile(CompileResult target, boolean dupObj, boolean pollute) {
|
||||
object.compile(target, true);
|
||||
if (dupObj) target.add(Instruction.dup());
|
||||
|
||||
index.compile(target, true);
|
||||
target.add(Instruction.loadMember()).setLocationAndDebug(loc(), BreakpointType.STEP_IN);
|
||||
if (!pollute) target.add(Instruction.discard());
|
||||
}
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
compile(target, false, pollute);
|
||||
}
|
||||
|
||||
public IndexStatement(Location loc, Statement object, Statement index) {
|
||||
super(loc);
|
||||
this.object = object;
|
||||
this.index = index;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class LazyAndStatement extends Statement {
|
||||
public final Statement first, second;
|
||||
|
||||
@Override public boolean pure() { return first.pure() && second.pure(); }
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
first.compile(target, true);
|
||||
if (pollute) target.add(Instruction.dup());
|
||||
int start = target.temp();
|
||||
if (pollute) target.add(Instruction.discard());
|
||||
second.compile(target, pollute);
|
||||
target.set(start, Instruction.jmpIfNot(target.size() - start));
|
||||
}
|
||||
|
||||
public LazyAndStatement(Location loc, Statement first, Statement second) {
|
||||
super(loc);
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class LazyOrStatement extends Statement {
|
||||
public final Statement first, second;
|
||||
|
||||
@Override public boolean pure() { return first.pure() && second.pure(); }
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
first.compile(target, true);
|
||||
if (pollute) target.add(Instruction.dup());
|
||||
int start = target.temp();
|
||||
if (pollute) target.add(Instruction.discard());
|
||||
second.compile(target, pollute);
|
||||
target.set(start, Instruction.jmpIf(target.size() - start));
|
||||
}
|
||||
|
||||
public LazyOrStatement(Location loc, Statement first, Statement second) {
|
||||
super(loc);
|
||||
this.first = first;
|
||||
this.second = second;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class ObjectStatement extends Statement {
|
||||
public final Map<String, Statement> map;
|
||||
public final Map<String, FunctionStatement> getters;
|
||||
public final Map<String, FunctionStatement> setters;
|
||||
|
||||
@Override public boolean pure() {
|
||||
for (var el : map.values()) {
|
||||
if (!el.pure()) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
target.add(Instruction.loadObj());
|
||||
|
||||
for (var el : map.entrySet()) {
|
||||
target.add(Instruction.dup());
|
||||
target.add(Instruction.pushValue(el.getKey()));
|
||||
var val = el.getValue();
|
||||
FunctionStatement.compileWithName(val, target, true, el.getKey().toString());
|
||||
target.add(Instruction.storeMember());
|
||||
}
|
||||
|
||||
var keys = new ArrayList<Object>();
|
||||
keys.addAll(getters.keySet());
|
||||
keys.addAll(setters.keySet());
|
||||
|
||||
for (var key : keys) {
|
||||
target.add(Instruction.pushValue((String)key));
|
||||
|
||||
if (getters.containsKey(key)) getters.get(key).compile(target, true);
|
||||
else target.add(Instruction.pushUndefined());
|
||||
|
||||
if (setters.containsKey(key)) setters.get(key).compile(target, true);
|
||||
else target.add(Instruction.pushUndefined());
|
||||
|
||||
target.add(Instruction.defProp());
|
||||
}
|
||||
|
||||
if (!pollute) target.add(Instruction.discard());
|
||||
}
|
||||
|
||||
public ObjectStatement(Location loc, Map<String, Statement> map, Map<String, FunctionStatement> getters, Map<String, FunctionStatement> setters) {
|
||||
super(loc);
|
||||
this.map = map;
|
||||
this.getters = getters;
|
||||
this.setters = setters;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.common.Operation;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class OperationStatement extends Statement {
|
||||
public final Statement[] args;
|
||||
public final Operation operation;
|
||||
|
||||
@Override public boolean pure() {
|
||||
for (var el : args) {
|
||||
if (!el.pure()) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
for (var arg : args) {
|
||||
arg.compile(target, true);
|
||||
}
|
||||
|
||||
if (pollute) target.add(Instruction.operation(operation));
|
||||
else target.add(Instruction.discard());
|
||||
}
|
||||
|
||||
public OperationStatement(Location loc, Operation operation, Statement ...args) {
|
||||
super(loc);
|
||||
this.operation = operation;
|
||||
this.args = args;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class RegexStatement extends Statement {
|
||||
public final String pattern, flags;
|
||||
|
||||
// Not really pure, since a function is called, but can be ignored.
|
||||
@Override public boolean pure() { return true; }
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
target.add(Instruction.loadRegex(pattern, flags));
|
||||
if (!pollute) target.add(Instruction.discard());
|
||||
}
|
||||
|
||||
public RegexStatement(Location loc, String pattern, String flags) {
|
||||
super(loc);
|
||||
this.pattern = pattern;
|
||||
this.flags = flags;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class TypeofStatement extends Statement {
|
||||
public final Statement value;
|
||||
|
||||
// Not really pure, since a variable from the global scope could be accessed,
|
||||
// which could lead to code execution, that would get omitted
|
||||
@Override public boolean pure() { return true; }
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
if (value instanceof VariableStatement) {
|
||||
var i = target.scope.getKey(((VariableStatement)value).name);
|
||||
if (i instanceof String) {
|
||||
target.add(Instruction.typeof((String)i));
|
||||
return;
|
||||
}
|
||||
}
|
||||
value.compile(target, pollute);
|
||||
target.add(Instruction.typeof());
|
||||
}
|
||||
|
||||
public TypeofStatement(Location loc, Statement value) {
|
||||
super(loc);
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.common.Operation;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class VariableAssignStatement extends Statement {
|
||||
public final String name;
|
||||
public final Statement value;
|
||||
public final Operation operation;
|
||||
|
||||
@Override public boolean pure() { return false; }
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
var i = target.scope.getKey(name);
|
||||
if (operation != null) {
|
||||
target.add(Instruction.loadVar(i));
|
||||
FunctionStatement.compileWithName(value, target, true, name);
|
||||
target.add(Instruction.operation(operation));
|
||||
target.add(Instruction.storeVar(i, pollute));
|
||||
}
|
||||
else {
|
||||
FunctionStatement.compileWithName(value, target, true, name);
|
||||
target.add(Instruction.storeVar(i, pollute));
|
||||
}
|
||||
}
|
||||
|
||||
public VariableAssignStatement(Location loc, String name, Statement val, Operation operation) {
|
||||
super(loc);
|
||||
this.name = name;
|
||||
this.value = val;
|
||||
this.operation = operation;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class VariableIndexStatement extends Statement {
|
||||
public final int index;
|
||||
|
||||
@Override public boolean pure() { return true; }
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
if (pollute) target.add(Instruction.loadVar(index));
|
||||
}
|
||||
|
||||
public VariableIndexStatement(Location loc, int i) {
|
||||
super(loc);
|
||||
this.index = i;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package me.topchetoeu.jscript.compilation.values;
|
||||
|
||||
import me.topchetoeu.jscript.common.Instruction;
|
||||
import me.topchetoeu.jscript.common.Location;
|
||||
import me.topchetoeu.jscript.common.Operation;
|
||||
import me.topchetoeu.jscript.compilation.AssignableStatement;
|
||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Statement;
|
||||
|
||||
public class VariableStatement extends AssignableStatement {
|
||||
public final String name;
|
||||
|
||||
@Override public boolean pure() { return false; }
|
||||
|
||||
@Override
|
||||
public Statement toAssign(Statement val, Operation operation) {
|
||||
return new VariableAssignStatement(loc(), name, val, operation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void compile(CompileResult target, boolean pollute) {
|
||||
var i = target.scope.getKey(name);
|
||||
target.add(Instruction.loadVar(i));
|
||||
if (!pollute) target.add(Instruction.discard());
|
||||
}
|
||||
|
||||
public VariableStatement(Location loc, String name) {
|
||||
super(loc);
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
456
src/java/me/topchetoeu/jscript/lib/ArrayLib.java
Normal file
456
src/java/me/topchetoeu/jscript/lib/ArrayLib.java
Normal file
@@ -0,0 +1,456 @@
|
||||
package me.topchetoeu.jscript.lib;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Stack;
|
||||
|
||||
import me.topchetoeu.jscript.runtime.values.ArrayValue;
|
||||
import me.topchetoeu.jscript.runtime.values.FunctionValue;
|
||||
import me.topchetoeu.jscript.runtime.values.NativeFunction;
|
||||
import me.topchetoeu.jscript.runtime.values.ObjectValue;
|
||||
import me.topchetoeu.jscript.runtime.values.Values;
|
||||
import me.topchetoeu.jscript.utils.interop.Arguments;
|
||||
import me.topchetoeu.jscript.utils.interop.Expose;
|
||||
import me.topchetoeu.jscript.utils.interop.ExposeConstructor;
|
||||
import me.topchetoeu.jscript.utils.interop.ExposeTarget;
|
||||
import me.topchetoeu.jscript.utils.interop.ExposeType;
|
||||
import me.topchetoeu.jscript.utils.interop.WrapperName;
|
||||
|
||||
@WrapperName("Array")
|
||||
public class ArrayLib {
|
||||
private static int normalizeI(int len, int i, boolean clamp) {
|
||||
if (i < 0) i += len;
|
||||
if (clamp) {
|
||||
if (i < 0) i = 0;
|
||||
if (i > len) i = len;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
@Expose(value = "length", type = ExposeType.GETTER)
|
||||
public static int __getLength(Arguments args) {
|
||||
return args.self(ArrayValue.class).size();
|
||||
}
|
||||
@Expose(value = "length", type = ExposeType.SETTER)
|
||||
public static void __setLength(Arguments args) {
|
||||
args.self(ArrayValue.class).setSize(args.getInt(0));
|
||||
}
|
||||
|
||||
@Expose public static ObjectValue __values(Arguments args) {
|
||||
return __iterator(args);
|
||||
}
|
||||
@Expose public static ObjectValue __keys(Arguments args) {
|
||||
return Values.toJSIterator(args.ctx, () -> new Iterator<Object>() {
|
||||
private int i = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return i < args.self(ArrayValue.class).size();
|
||||
}
|
||||
@Override
|
||||
public Object next() {
|
||||
if (!hasNext()) return null;
|
||||
return i++;
|
||||
}
|
||||
});
|
||||
}
|
||||
@Expose public static ObjectValue __entries(Arguments args) {
|
||||
return Values.toJSIterator(args.ctx, () -> new Iterator<Object>() {
|
||||
private int i = 0;
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return i < args.self(ArrayValue.class).size();
|
||||
}
|
||||
@Override
|
||||
public Object next() {
|
||||
if (!hasNext()) return null;
|
||||
return new ArrayValue(args.ctx, i, args.self(ArrayValue.class).get(i++));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Expose(value = "@@Symbol.iterator")
|
||||
public static ObjectValue __iterator(Arguments args) {
|
||||
return Values.toJSIterator(args.ctx, args.self(ArrayValue.class));
|
||||
}
|
||||
@Expose(value = "@@Symbol.asyncIterator")
|
||||
public static ObjectValue __asyncIterator(Arguments args) {
|
||||
return Values.toJSAsyncIterator(args.ctx, args.self(ArrayValue.class).iterator());
|
||||
}
|
||||
|
||||
@Expose public static ArrayValue __concat(Arguments args) {
|
||||
// TODO: Fully implement with non-array spreadable objects
|
||||
var arrs = args.slice(-1);
|
||||
var size = 0;
|
||||
|
||||
for (int i = 0; i < arrs.n(); i++) {
|
||||
if (arrs.get(i) instanceof ArrayValue) size += arrs.convert(i, ArrayValue.class).size();
|
||||
else i++;
|
||||
}
|
||||
|
||||
var res = new ArrayValue(size);
|
||||
|
||||
for (int i = 0, j = 0; i < arrs.n(); i++) {
|
||||
if (arrs.get(i) instanceof ArrayValue) {
|
||||
var arrEl = arrs.convert(i, ArrayValue.class);
|
||||
int n = arrEl.size();
|
||||
arrEl.copyTo(res, 0, j, n);
|
||||
j += n;
|
||||
}
|
||||
else {
|
||||
res.set(args.ctx, j++, arrs.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
@Expose public static ArrayValue __sort(Arguments args) {
|
||||
var arr = args.self(ArrayValue.class);
|
||||
var cmp = args.convert(0, FunctionValue.class);
|
||||
|
||||
var defaultCmp = new NativeFunction("", _args -> {
|
||||
return _args.getString(0).compareTo(_args.getString(1));
|
||||
});
|
||||
|
||||
arr.sort((a, b) -> {
|
||||
var res = Values.toNumber(args.ctx, (cmp == null ? defaultCmp : cmp).call(args.ctx, null, a, b));
|
||||
if (res < 0) return -1;
|
||||
if (res > 0) return 1;
|
||||
return 0;
|
||||
});
|
||||
return arr;
|
||||
}
|
||||
|
||||
@Expose public static ArrayValue __fill(Arguments args) {
|
||||
var arr = args.self(ArrayValue.class);
|
||||
var val = args.get(0);
|
||||
var start = normalizeI(arr.size(), args.getInt(1, 0), true);
|
||||
var end = normalizeI(arr.size(), args.getInt(2, arr.size()), true);
|
||||
|
||||
for (; start < end; start++) arr.set(args.ctx, start, val);
|
||||
|
||||
return arr;
|
||||
}
|
||||
@Expose public static boolean __every(Arguments args) {
|
||||
var arr = args.self(ArrayValue.class);
|
||||
|
||||
for (var i = 0; i < arr.size(); i++) {
|
||||
if (arr.has(i) && !Values.toBoolean(Values.call(
|
||||
args.ctx, args.get(0), args.get(1),
|
||||
arr.get(i), i, arr
|
||||
))) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
@Expose public static boolean __some(Arguments args) {
|
||||
var arr = args.self(ArrayValue.class);
|
||||
|
||||
for (var i = 0; i < arr.size(); i++) {
|
||||
if (arr.has(i) && Values.toBoolean(Values.call(
|
||||
args.ctx, args.get(0), args.get(1),
|
||||
arr.get(i), i, arr
|
||||
))) return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
@Expose public static ArrayValue __filter(Arguments args) {
|
||||
var arr = args.self(ArrayValue.class);
|
||||
var res = new ArrayValue(arr.size());
|
||||
|
||||
for (int i = 0, j = 0; i < arr.size(); i++) {
|
||||
if (arr.has(i) && Values.toBoolean(Values.call(
|
||||
args.ctx, args.get(0), args.get(1),
|
||||
arr.get(i), i, arr
|
||||
))) res.set(args.ctx, j++, arr.get(i));
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
@Expose public static ArrayValue __map(Arguments args) {
|
||||
var arr = args.self(ArrayValue.class);
|
||||
var res = new ArrayValue(arr.size());
|
||||
res.setSize(arr.size());
|
||||
|
||||
for (int i = 0; i < arr.size(); i++) {
|
||||
if (arr.has(i)) res.set(args.ctx, i, Values.call(args.ctx, args.get(0), args.get(1), arr.get(i), i, arr));
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@Expose public static void __forEach(Arguments args) {
|
||||
var arr = args.self(ArrayValue.class);
|
||||
var func = args.convert(0, FunctionValue.class);
|
||||
var thisArg = args.get(1);
|
||||
|
||||
for (int i = 0; i < arr.size(); i++) {
|
||||
if (arr.has(i)) func.call(args.ctx, thisArg, arr.get(i), i, arr);
|
||||
}
|
||||
}
|
||||
|
||||
@Expose public static Object __reduce(Arguments args) {
|
||||
var arr = args.self(ArrayValue.class);
|
||||
var func = args.convert(0, FunctionValue.class);
|
||||
var res = args.get(1);
|
||||
var i = 0;
|
||||
|
||||
if (args.n() < 2) {
|
||||
for (; i < arr.size(); i++) {
|
||||
if (arr.has(i)){
|
||||
res = arr.get(i++);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (; i < arr.size(); i++) {
|
||||
if (arr.has(i)) {
|
||||
res = func.call(args.ctx, null, res, arr.get(i), i, arr);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
@Expose public static Object __reduceRight(Arguments args) {
|
||||
var arr = args.self(ArrayValue.class);
|
||||
var func = args.convert(0, FunctionValue.class);
|
||||
var res = args.get(1);
|
||||
var i = arr.size();
|
||||
|
||||
if (args.n() < 2) {
|
||||
while (!arr.has(i--) && i >= 0) {
|
||||
res = arr.get(i);
|
||||
}
|
||||
}
|
||||
else i--;
|
||||
|
||||
for (; i >= 0; i--) {
|
||||
if (arr.has(i)) {
|
||||
res = func.call(args.ctx, null, res, arr.get(i), i, arr);
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
@Expose public static ArrayValue __flat(Arguments args) {
|
||||
var arr = args.self(ArrayValue.class);
|
||||
var depth = args.getInt(0, 1);
|
||||
var res = new ArrayValue(arr.size());
|
||||
var stack = new Stack<Object>();
|
||||
var depths = new Stack<Integer>();
|
||||
|
||||
stack.push(arr);
|
||||
depths.push(-1);
|
||||
|
||||
while (!stack.empty()) {
|
||||
var el = stack.pop();
|
||||
int d = depths.pop();
|
||||
|
||||
if ((d == -1 || d < depth) && el instanceof ArrayValue) {
|
||||
var arrEl = (ArrayValue)el;
|
||||
for (int i = arrEl.size() - 1; i >= 0; i--) {
|
||||
if (!arrEl.has(i)) continue;
|
||||
stack.push(arrEl.get(i));
|
||||
depths.push(d + 1);
|
||||
}
|
||||
}
|
||||
else res.set(args.ctx, res.size(), el);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
@Expose public static ArrayValue __flatMap(Arguments args) {
|
||||
return __flat(new Arguments(args.ctx, __map(args), 1));
|
||||
}
|
||||
|
||||
@Expose public static Object __find(Arguments args) {
|
||||
var arr = args.self(ArrayValue.class);
|
||||
|
||||
for (int i = 0; i < arr.size(); i++) {
|
||||
if (arr.has(i) && Values.toBoolean(Values.call(
|
||||
args.ctx, args.get(0), args.get(1),
|
||||
arr.get(i), i, args.self
|
||||
))) return arr.get(i);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
@Expose public static Object __findLast(Arguments args) {
|
||||
var arr = args.self(ArrayValue.class);
|
||||
|
||||
for (var i = arr.size() - 1; i >= 0; i--) {
|
||||
if (arr.has(i) && Values.toBoolean(Values.call(
|
||||
args.ctx, args.get(0), args.get(1),
|
||||
arr.get(i), i, args.self
|
||||
))) return arr.get(i);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Expose public static int __findIndex(Arguments args) {
|
||||
var arr = args.self(ArrayValue.class);
|
||||
|
||||
for (int i = 0; i < arr.size(); i++) {
|
||||
if (arr.has(i) && Values.toBoolean(Values.call(
|
||||
args.ctx, args.get(0), args.get(1),
|
||||
arr.get(i), i, args.self
|
||||
))) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
@Expose public static int __findLastIndex(Arguments args) {
|
||||
var arr = args.self(ArrayValue.class);
|
||||
|
||||
for (var i = arr.size() - 1; i >= 0; i--) {
|
||||
if (arr.has(i) && Values.toBoolean(Values.call(
|
||||
args.ctx, args.get(0), args.get(1),
|
||||
arr.get(i), i, args.self
|
||||
))) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Expose public static int __indexOf(Arguments args) {
|
||||
var arr = args.self(ArrayValue.class);
|
||||
var val = args.get(0);
|
||||
var start = normalizeI(arr.size(), args.getInt(1), true);
|
||||
|
||||
for (int i = start; i < arr.size(); i++) {
|
||||
if (Values.strictEquals(args.ctx, arr.get(i), val)) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
@Expose public static int __lastIndexOf(Arguments args) {
|
||||
var arr = args.self(ArrayValue.class);
|
||||
var val = args.get(0);
|
||||
var start = normalizeI(arr.size(), args.getInt(1), true);
|
||||
|
||||
for (int i = arr.size(); i >= start; i--) {
|
||||
if (Values.strictEquals(args.ctx, arr.get(i), val)) return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Expose public static boolean __includes(Arguments args) {
|
||||
return __indexOf(args) >= 0;
|
||||
}
|
||||
|
||||
@Expose public static Object __pop(Arguments args) {
|
||||
var arr = args.self(ArrayValue.class);
|
||||
if (arr.size() == 0) return null;
|
||||
|
||||
var val = arr.get(arr.size() - 1);
|
||||
arr.shrink(1);
|
||||
return val;
|
||||
}
|
||||
@Expose public static int __push(Arguments args) {
|
||||
var arr = args.self(ArrayValue.class);
|
||||
var values = args.args;
|
||||
|
||||
arr.copyFrom(args.ctx, values, 0, arr.size(), values.length);
|
||||
return arr.size();
|
||||
}
|
||||
|
||||
@Expose public static Object __shift(Arguments args) {
|
||||
var arr = args.self(ArrayValue.class);
|
||||
|
||||
if (arr.size() == 0) return null;
|
||||
var val = arr.get(0);
|
||||
|
||||
arr.move(1, 0, arr.size());
|
||||
arr.shrink(1);
|
||||
return val;
|
||||
}
|
||||
@Expose public static int __unshift(Arguments args) {
|
||||
var arr = args.self(ArrayValue.class);
|
||||
var values = args.slice(0).args;
|
||||
|
||||
arr.move(0, values.length, arr.size());
|
||||
arr.copyFrom(args.ctx, values, 0, 0, values.length);
|
||||
return arr.size();
|
||||
}
|
||||
|
||||
@Expose public static ArrayValue __slice(Arguments args) {
|
||||
var arr = args.self(ArrayValue.class);
|
||||
var start = normalizeI(arr.size(), args.getInt(0), true);
|
||||
var end = normalizeI(arr.size(), args.getInt(1, arr.size()), true);
|
||||
|
||||
var res = new ArrayValue(end - start);
|
||||
arr.copyTo(res, start, 0, end - start);
|
||||
return res;
|
||||
}
|
||||
|
||||
@Expose public static ArrayValue __splice(Arguments args) {
|
||||
var arr = args.self(ArrayValue.class);
|
||||
var start = normalizeI(arr.size(), args.getInt(0), true);
|
||||
var deleteCount = normalizeI(arr.size(), args.getInt(1, arr.size()), true);
|
||||
var items = args.slice(2).args;
|
||||
|
||||
if (start + deleteCount >= arr.size()) deleteCount = arr.size() - start;
|
||||
|
||||
var size = arr.size() - deleteCount + items.length;
|
||||
var res = new ArrayValue(deleteCount);
|
||||
arr.copyTo(res, start, 0, deleteCount);
|
||||
arr.move(start + deleteCount, start + items.length, arr.size() - start - deleteCount);
|
||||
arr.copyFrom(args.ctx, items, 0, start, items.length);
|
||||
arr.setSize(size);
|
||||
|
||||
return res;
|
||||
}
|
||||
@Expose public static String __toString(Arguments args) {
|
||||
return __join(new Arguments(args.ctx, args.self, ","));
|
||||
}
|
||||
|
||||
@Expose public static String __join(Arguments args) {
|
||||
var arr = args.self(ArrayValue.class);
|
||||
var sep = args.getString(0, ", ");
|
||||
var res = new StringBuilder();
|
||||
var comma = false;
|
||||
|
||||
for (int i = 0; i < arr.size(); i++) {
|
||||
if (!arr.has(i)) continue;
|
||||
|
||||
if (comma) res.append(sep);
|
||||
comma = true;
|
||||
|
||||
var el = arr.get(i);
|
||||
if (el == null || el == Values.NULL) continue;
|
||||
|
||||
res.append(Values.toString(args.ctx, el));
|
||||
}
|
||||
|
||||
return res.toString();
|
||||
}
|
||||
|
||||
@Expose(target = ExposeTarget.STATIC)
|
||||
public static boolean __isArray(Arguments args) {
|
||||
return args.get(0) instanceof ArrayValue;
|
||||
}
|
||||
@Expose(target = ExposeTarget.STATIC)
|
||||
public static ArrayValue __of(Arguments args) {
|
||||
return new ArrayValue(args.ctx, args.slice(0).args);
|
||||
}
|
||||
|
||||
@ExposeConstructor public static ArrayValue __constructor(Arguments args) {
|
||||
ArrayValue res;
|
||||
|
||||
if (args.n() == 1 && args.get(0) instanceof Number) {
|
||||
var len = args.getInt(0);
|
||||
res = new ArrayValue(len);
|
||||
res.setSize(len);
|
||||
}
|
||||
else {
|
||||
var val = args.args;
|
||||
res = new ArrayValue(val.length);
|
||||
res.copyFrom(args.ctx, val, 0, 0, val.length);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
85
src/java/me/topchetoeu/jscript/lib/AsyncFunctionLib.java
Normal file
85
src/java/me/topchetoeu/jscript/lib/AsyncFunctionLib.java
Normal file
@@ -0,0 +1,85 @@
|
||||
package me.topchetoeu.jscript.lib;
|
||||
|
||||
import me.topchetoeu.jscript.lib.PromiseLib.Handle;
|
||||
import me.topchetoeu.jscript.runtime.Context;
|
||||
import me.topchetoeu.jscript.runtime.Frame;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
||||
import me.topchetoeu.jscript.runtime.values.CodeFunction;
|
||||
import me.topchetoeu.jscript.runtime.values.FunctionValue;
|
||||
import me.topchetoeu.jscript.runtime.values.NativeFunction;
|
||||
import me.topchetoeu.jscript.runtime.values.Values;
|
||||
import me.topchetoeu.jscript.utils.interop.Arguments;
|
||||
import me.topchetoeu.jscript.utils.interop.WrapperName;
|
||||
|
||||
@WrapperName("AsyncFunction")
|
||||
public class AsyncFunctionLib extends FunctionValue {
|
||||
public final CodeFunction func;
|
||||
|
||||
private static class AsyncHelper {
|
||||
public PromiseLib promise = new PromiseLib();
|
||||
public Frame frame;
|
||||
|
||||
private boolean awaiting = false;
|
||||
|
||||
private void next(Context ctx, Object inducedValue, EngineException inducedError) {
|
||||
Object res = null;
|
||||
|
||||
frame.onPush();
|
||||
awaiting = false;
|
||||
while (!awaiting) {
|
||||
try {
|
||||
res = frame.next(inducedValue, Values.NO_RETURN, inducedError);
|
||||
inducedValue = Values.NO_RETURN;
|
||||
inducedError = null;
|
||||
|
||||
if (res != Values.NO_RETURN) {
|
||||
promise.fulfill(ctx, res);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (EngineException e) {
|
||||
promise.reject(ctx, e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
frame.onPop();
|
||||
|
||||
if (awaiting) {
|
||||
PromiseLib.handle(ctx, frame.pop(), new Handle() {
|
||||
@Override
|
||||
public void onFulfil(Object val) {
|
||||
next(ctx, val, null);
|
||||
}
|
||||
@Override
|
||||
public void onReject(EngineException err) {
|
||||
next(ctx, Values.NO_RETURN, err);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public Object await(Arguments args) {
|
||||
this.awaiting = true;
|
||||
return args.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object call(Context ctx, Object thisArg, Object ...args) {
|
||||
var handler = new AsyncHelper();
|
||||
|
||||
var newArgs = new Object[args.length + 1];
|
||||
newArgs[0] = new NativeFunction("await", handler::await);
|
||||
System.arraycopy(args, 0, newArgs, 1, args.length);
|
||||
|
||||
handler.frame = new Frame(ctx, thisArg, newArgs, (CodeFunction)func);
|
||||
handler.next(ctx, Values.NO_RETURN, null);
|
||||
return handler.promise;
|
||||
}
|
||||
|
||||
public AsyncFunctionLib(FunctionValue func) {
|
||||
super(func.name, func.length);
|
||||
if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function.");
|
||||
this.func = (CodeFunction)func;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package me.topchetoeu.jscript.lib;
|
||||
|
||||
import me.topchetoeu.jscript.runtime.Context;
|
||||
import me.topchetoeu.jscript.runtime.Frame;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
||||
import me.topchetoeu.jscript.runtime.values.CodeFunction;
|
||||
import me.topchetoeu.jscript.runtime.values.FunctionValue;
|
||||
import me.topchetoeu.jscript.runtime.values.NativeFunction;
|
||||
import me.topchetoeu.jscript.utils.interop.WrapperName;
|
||||
|
||||
@WrapperName("AsyncGeneratorFunction")
|
||||
public class AsyncGeneratorFunctionLib extends FunctionValue {
|
||||
public final CodeFunction func;
|
||||
|
||||
@Override
|
||||
public Object call(Context ctx, Object thisArg, Object ...args) {
|
||||
var handler = new AsyncGeneratorLib();
|
||||
|
||||
var newArgs = new Object[args.length + 2];
|
||||
newArgs[0] = new NativeFunction("await", handler::await);
|
||||
newArgs[1] = new NativeFunction("yield", handler::yield);
|
||||
System.arraycopy(args, 0, newArgs, 2, args.length);
|
||||
|
||||
handler.frame = new Frame(ctx, thisArg, newArgs, func);
|
||||
return handler;
|
||||
}
|
||||
|
||||
public AsyncGeneratorFunctionLib(CodeFunction func) {
|
||||
super(func.name, func.length);
|
||||
if (!(func instanceof CodeFunction)) throw EngineException.ofType("Return value of argument must be a js function.");
|
||||
this.func = func;
|
||||
}
|
||||
}
|
||||
107
src/java/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java
Normal file
107
src/java/me/topchetoeu/jscript/lib/AsyncGeneratorLib.java
Normal file
@@ -0,0 +1,107 @@
|
||||
package me.topchetoeu.jscript.lib;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import me.topchetoeu.jscript.lib.PromiseLib.Handle;
|
||||
import me.topchetoeu.jscript.runtime.Context;
|
||||
import me.topchetoeu.jscript.runtime.Frame;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
||||
import me.topchetoeu.jscript.runtime.values.ObjectValue;
|
||||
import me.topchetoeu.jscript.runtime.values.Values;
|
||||
import me.topchetoeu.jscript.utils.interop.Arguments;
|
||||
import me.topchetoeu.jscript.utils.interop.Expose;
|
||||
import me.topchetoeu.jscript.utils.interop.WrapperName;
|
||||
|
||||
@WrapperName("AsyncGenerator")
|
||||
public class AsyncGeneratorLib {
|
||||
private int state = 0;
|
||||
private boolean done = false;
|
||||
private PromiseLib currPromise;
|
||||
public Frame frame;
|
||||
|
||||
private void next(Context ctx, Object inducedValue, Object inducedReturn, EngineException inducedError) {
|
||||
if (done) {
|
||||
if (inducedError != null) throw inducedError;
|
||||
currPromise.fulfill(ctx, new ObjectValue(ctx, Map.of(
|
||||
"done", true,
|
||||
"value", inducedReturn == Values.NO_RETURN ? null : inducedReturn
|
||||
)));
|
||||
return;
|
||||
}
|
||||
|
||||
Object res = null;
|
||||
state = 0;
|
||||
|
||||
frame.onPush();
|
||||
while (state == 0) {
|
||||
try {
|
||||
res = frame.next(inducedValue, inducedReturn, inducedError);
|
||||
inducedValue = inducedReturn = Values.NO_RETURN;
|
||||
inducedError = null;
|
||||
|
||||
if (res != Values.NO_RETURN) {
|
||||
var obj = new ObjectValue();
|
||||
obj.defineProperty(ctx, "done", true);
|
||||
obj.defineProperty(ctx, "value", res);
|
||||
currPromise.fulfill(ctx, obj);
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (EngineException e) {
|
||||
currPromise.reject(ctx, e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
frame.onPop();
|
||||
|
||||
if (state == 1) {
|
||||
PromiseLib.handle(ctx, frame.pop(), new Handle() {
|
||||
@Override public void onFulfil(Object val) {
|
||||
next(ctx, val, Values.NO_RETURN, null);
|
||||
}
|
||||
@Override public void onReject(EngineException err) {
|
||||
next(ctx, Values.NO_RETURN, Values.NO_RETURN, err);
|
||||
}
|
||||
});
|
||||
}
|
||||
else if (state == 2) {
|
||||
var obj = new ObjectValue();
|
||||
obj.defineProperty(ctx, "done", false);
|
||||
obj.defineProperty(ctx, "value", frame.pop());
|
||||
currPromise.fulfill(ctx, obj);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (done) return "Generator [closed]";
|
||||
if (state != 0) return "Generator [suspended]";
|
||||
return "Generator [running]";
|
||||
}
|
||||
|
||||
public Object await(Arguments args) {
|
||||
this.state = 1;
|
||||
return args.get(0);
|
||||
}
|
||||
public Object yield(Arguments args) {
|
||||
this.state = 2;
|
||||
return args.get(0);
|
||||
}
|
||||
|
||||
@Expose public PromiseLib __next(Arguments args) {
|
||||
this.currPromise = new PromiseLib();
|
||||
if (args.has(0)) next(args.ctx, args.get(0), Values.NO_RETURN, null);
|
||||
else next(args.ctx, Values.NO_RETURN, Values.NO_RETURN, null);
|
||||
return this.currPromise;
|
||||
}
|
||||
@Expose public PromiseLib __return(Arguments args) {
|
||||
this.currPromise = new PromiseLib();
|
||||
next(args.ctx, Values.NO_RETURN, args.get(0), null);
|
||||
return this.currPromise;
|
||||
}
|
||||
@Expose public PromiseLib __throw(Arguments args) {
|
||||
this.currPromise = new PromiseLib();
|
||||
next(args.ctx, Values.NO_RETURN, Values.NO_RETURN, new EngineException(args.get(0)).setCtx(args.ctx));
|
||||
return this.currPromise;
|
||||
}
|
||||
}
|
||||
37
src/java/me/topchetoeu/jscript/lib/BooleanLib.java
Normal file
37
src/java/me/topchetoeu/jscript/lib/BooleanLib.java
Normal file
@@ -0,0 +1,37 @@
|
||||
package me.topchetoeu.jscript.lib;
|
||||
|
||||
import me.topchetoeu.jscript.runtime.values.ObjectValue;
|
||||
import me.topchetoeu.jscript.runtime.values.Values;
|
||||
import me.topchetoeu.jscript.utils.interop.Arguments;
|
||||
import me.topchetoeu.jscript.utils.interop.Expose;
|
||||
import me.topchetoeu.jscript.utils.interop.ExposeConstructor;
|
||||
import me.topchetoeu.jscript.utils.interop.WrapperName;
|
||||
|
||||
@WrapperName("Boolean")
|
||||
public class BooleanLib {
|
||||
public static final BooleanLib TRUE = new BooleanLib(true);
|
||||
public static final BooleanLib FALSE = new BooleanLib(false);
|
||||
|
||||
public final boolean value;
|
||||
|
||||
@Override public String toString() {
|
||||
return value + "";
|
||||
}
|
||||
|
||||
public BooleanLib(boolean val) {
|
||||
this.value = val;
|
||||
}
|
||||
|
||||
@ExposeConstructor public static Object __constructor(Arguments args) {
|
||||
var val = args.getBoolean(0);
|
||||
if (args.self instanceof ObjectValue) return val ? TRUE : FALSE;
|
||||
else return val;
|
||||
}
|
||||
@Expose public static String __toString(Arguments args) {
|
||||
return args.self(Boolean.class) ? "true" : "false";
|
||||
}
|
||||
@Expose public static boolean __valueOf(Arguments args) {
|
||||
if (Values.isWrapper(args.self, BooleanLib.class)) return Values.wrapper(args.self, BooleanLib.class).value;
|
||||
return args.self(Boolean.class);
|
||||
}
|
||||
}
|
||||
38
src/java/me/topchetoeu/jscript/lib/ConsoleLib.java
Normal file
38
src/java/me/topchetoeu/jscript/lib/ConsoleLib.java
Normal file
@@ -0,0 +1,38 @@
|
||||
package me.topchetoeu.jscript.lib;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import me.topchetoeu.jscript.runtime.values.Values;
|
||||
import me.topchetoeu.jscript.utils.filesystem.File;
|
||||
import me.topchetoeu.jscript.utils.interop.Arguments;
|
||||
import me.topchetoeu.jscript.utils.interop.Expose;
|
||||
import me.topchetoeu.jscript.utils.interop.WrapperName;
|
||||
|
||||
@WrapperName("Console")
|
||||
public class ConsoleLib {
|
||||
public static interface Writer {
|
||||
void writeLine(String val) throws IOException;
|
||||
}
|
||||
|
||||
private File file;
|
||||
|
||||
@Expose
|
||||
public void __log(Arguments args) {
|
||||
var res = new StringBuilder();
|
||||
var first = true;
|
||||
|
||||
for (var el : args.args) {
|
||||
if (!first) res.append(" ");
|
||||
first = false;
|
||||
res.append(Values.toReadable(args.ctx, el).getBytes());
|
||||
}
|
||||
|
||||
for (var line : res.toString().split("\n", -1)) {
|
||||
file.write(line.getBytes());
|
||||
}
|
||||
}
|
||||
|
||||
public ConsoleLib(File file) {
|
||||
this.file = file;
|
||||
}
|
||||
}
|
||||
266
src/java/me/topchetoeu/jscript/lib/DateLib.java
Normal file
266
src/java/me/topchetoeu/jscript/lib/DateLib.java
Normal file
@@ -0,0 +1,266 @@
|
||||
package me.topchetoeu.jscript.lib;
|
||||
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import me.topchetoeu.jscript.utils.interop.Arguments;
|
||||
import me.topchetoeu.jscript.utils.interop.Expose;
|
||||
import me.topchetoeu.jscript.utils.interop.ExposeConstructor;
|
||||
import me.topchetoeu.jscript.utils.interop.ExposeTarget;
|
||||
import me.topchetoeu.jscript.utils.interop.WrapperName;
|
||||
|
||||
@WrapperName("Date")
|
||||
public class DateLib {
|
||||
private Calendar normal;
|
||||
private Calendar utc;
|
||||
|
||||
private void updateUTC() {
|
||||
if (utc == null || normal == null) return;
|
||||
utc.setTimeInMillis(normal.getTimeInMillis());
|
||||
}
|
||||
private void updateNormal() {
|
||||
if (utc == null || normal == null) return;
|
||||
normal.setTimeInMillis(utc.getTimeInMillis());
|
||||
}
|
||||
private void invalidate() {
|
||||
normal = utc = null;
|
||||
}
|
||||
|
||||
@Expose public double __getYear() {
|
||||
if (normal == null) return Double.NaN;
|
||||
return normal.get(Calendar.YEAR) - 1900;
|
||||
}
|
||||
@Expose public double __setYeard(Arguments args) {
|
||||
var real = args.getDouble(0);
|
||||
if (real >= 0 && real <= 99) real = real + 1900;
|
||||
if (Double.isNaN(real)) invalidate();
|
||||
else normal.set(Calendar.YEAR, (int)real);
|
||||
updateUTC();
|
||||
return __getTime();
|
||||
}
|
||||
|
||||
@Expose public double __getFullYear() {
|
||||
if (normal == null) return Double.NaN;
|
||||
return normal.get(Calendar.YEAR);
|
||||
}
|
||||
@Expose public double __getMonth() {
|
||||
if (normal == null) return Double.NaN;
|
||||
return normal.get(Calendar.MONTH);
|
||||
}
|
||||
@Expose public double __getDate() {
|
||||
if (normal == null) return Double.NaN;
|
||||
return normal.get(Calendar.DAY_OF_MONTH);
|
||||
}
|
||||
@Expose public double __getDay() {
|
||||
if (normal == null) return Double.NaN;
|
||||
return normal.get(Calendar.DAY_OF_WEEK);
|
||||
}
|
||||
@Expose public double __getHours() {
|
||||
if (normal == null) return Double.NaN;
|
||||
return normal.get(Calendar.HOUR_OF_DAY);
|
||||
}
|
||||
@Expose public double __getMinutes() {
|
||||
if (normal == null) return Double.NaN;
|
||||
return normal.get(Calendar.MINUTE);
|
||||
}
|
||||
@Expose public double __getSeconds() {
|
||||
if (normal == null) return Double.NaN;
|
||||
return normal.get(Calendar.SECOND);
|
||||
}
|
||||
@Expose public double __getMilliseconds() {
|
||||
if (normal == null) return Double.NaN;
|
||||
return normal.get(Calendar.MILLISECOND);
|
||||
}
|
||||
|
||||
@Expose public double __getUTCFullYear() {
|
||||
if (utc == null) return Double.NaN;
|
||||
return utc.get(Calendar.YEAR);
|
||||
}
|
||||
@Expose public double __getUTCMonth() {
|
||||
if (utc == null) return Double.NaN;
|
||||
return utc.get(Calendar.MONTH);
|
||||
}
|
||||
@Expose public double __getUTCDate() {
|
||||
if (utc == null) return Double.NaN;
|
||||
return utc.get(Calendar.DAY_OF_MONTH);
|
||||
}
|
||||
@Expose public double __getUTCDay() {
|
||||
if (utc == null) return Double.NaN;
|
||||
return utc.get(Calendar.DAY_OF_WEEK);
|
||||
}
|
||||
@Expose public double __getUTCHours() {
|
||||
if (utc == null) return Double.NaN;
|
||||
return utc.get(Calendar.HOUR_OF_DAY);
|
||||
}
|
||||
@Expose public double __getUTCMinutes() {
|
||||
if (utc == null) return Double.NaN;
|
||||
return utc.get(Calendar.MINUTE);
|
||||
}
|
||||
@Expose public double __getUTCSeconds() {
|
||||
if (utc == null) return Double.NaN;
|
||||
return utc.get(Calendar.SECOND);
|
||||
}
|
||||
@Expose public double __getUTCMilliseconds() {
|
||||
if (utc == null) return Double.NaN;
|
||||
return utc.get(Calendar.MILLISECOND);
|
||||
}
|
||||
|
||||
@Expose public double __setFullYear(Arguments args) {
|
||||
var real = args.getDouble(0);
|
||||
if (Double.isNaN(real)) invalidate();
|
||||
else normal.set(Calendar.YEAR, (int)real);
|
||||
updateUTC();
|
||||
return __getTime();
|
||||
}
|
||||
@Expose public double __setMonthd(Arguments args) {
|
||||
var real = args.getDouble(0);
|
||||
if (Double.isNaN(real)) invalidate();
|
||||
else normal.set(Calendar.MONTH, (int)real);
|
||||
updateUTC();
|
||||
return __getTime();
|
||||
}
|
||||
@Expose public double __setDated(Arguments args) {
|
||||
var real = args.getDouble(0);
|
||||
if (Double.isNaN(real)) invalidate();
|
||||
else normal.set(Calendar.DAY_OF_MONTH, (int)real);
|
||||
updateUTC();
|
||||
return __getTime();
|
||||
}
|
||||
@Expose public double __setDayd(Arguments args) {
|
||||
var real = args.getDouble(0);
|
||||
if (Double.isNaN(real)) invalidate();
|
||||
else normal.set(Calendar.DAY_OF_WEEK, (int)real);
|
||||
updateUTC();
|
||||
return __getTime();
|
||||
}
|
||||
@Expose public double __setHoursd(Arguments args) {
|
||||
var real = args.getDouble(0);
|
||||
if (Double.isNaN(real)) invalidate();
|
||||
else normal.set(Calendar.HOUR_OF_DAY, (int)real);
|
||||
updateUTC();
|
||||
return __getTime();
|
||||
}
|
||||
@Expose public double __setMinutesd(Arguments args) {
|
||||
var real = args.getDouble(0);
|
||||
if (Double.isNaN(real)) invalidate();
|
||||
else normal.set(Calendar.MINUTE, (int)real);
|
||||
updateUTC();
|
||||
return __getTime();
|
||||
}
|
||||
@Expose public double __setSecondsd(Arguments args) {
|
||||
var real = args.getDouble(0);
|
||||
if (Double.isNaN(real)) invalidate();
|
||||
else normal.set(Calendar.SECOND, (int)real);
|
||||
updateUTC();
|
||||
return __getTime();
|
||||
}
|
||||
@Expose public double __setMillisecondsd(Arguments args) {
|
||||
var real = args.getDouble(0);
|
||||
if (Double.isNaN(real)) invalidate();
|
||||
else normal.set(Calendar.MILLISECOND, (int)real);
|
||||
updateUTC();
|
||||
return __getTime();
|
||||
}
|
||||
|
||||
@Expose public double __setUTCFullYeard(Arguments args) {
|
||||
var real = args.getDouble(0);
|
||||
if (Double.isNaN(real)) invalidate();
|
||||
else utc.set(Calendar.YEAR, (int)real);
|
||||
updateNormal();
|
||||
return __getTime();
|
||||
}
|
||||
@Expose public double __setUTCMonthd(Arguments args) {
|
||||
var real = args.getDouble(0);
|
||||
if (Double.isNaN(real)) invalidate();
|
||||
else utc.set(Calendar.MONTH, (int)real);
|
||||
updateNormal();
|
||||
return __getTime();
|
||||
}
|
||||
@Expose public double __setUTCDated(Arguments args) {
|
||||
var real = args.getDouble(0);
|
||||
if (Double.isNaN(real)) invalidate();
|
||||
else utc.set(Calendar.DAY_OF_MONTH, (int)real);
|
||||
updateNormal();
|
||||
return __getTime();
|
||||
}
|
||||
@Expose public double __setUTCDayd(Arguments args) {
|
||||
var real = args.getDouble(0);
|
||||
if (Double.isNaN(real)) invalidate();
|
||||
else utc.set(Calendar.DAY_OF_WEEK, (int)real);
|
||||
updateNormal();
|
||||
return __getTime();
|
||||
}
|
||||
@Expose public double __setUTCHoursd(Arguments args) {
|
||||
var real = args.getDouble(0);
|
||||
if (Double.isNaN(real)) invalidate();
|
||||
else utc.set(Calendar.HOUR_OF_DAY, (int)real);
|
||||
updateNormal();
|
||||
return __getTime();
|
||||
}
|
||||
@Expose public double __setUTCMinutesd(Arguments args) {
|
||||
var real = args.getDouble(0);
|
||||
if (Double.isNaN(real)) invalidate();
|
||||
else utc.set(Calendar.MINUTE, (int)real);
|
||||
updateNormal();
|
||||
return __getTime();
|
||||
}
|
||||
@Expose public double __setUTCSecondsd(Arguments args) {
|
||||
var real = args.getDouble(0);
|
||||
if (Double.isNaN(real)) invalidate();
|
||||
else utc.set(Calendar.SECOND, (int)real);
|
||||
updateNormal();
|
||||
return __getTime();
|
||||
}
|
||||
@Expose public double __setUTCMillisecondsd(Arguments args) {
|
||||
var real = args.getDouble(0);
|
||||
if (Double.isNaN(real)) invalidate();
|
||||
else utc.set(Calendar.MILLISECOND, (int)real);
|
||||
updateNormal();
|
||||
return __getTime();
|
||||
}
|
||||
|
||||
@Expose public double __getTime() {
|
||||
if (utc == null) return Double.NaN;
|
||||
return utc.getTimeInMillis();
|
||||
}
|
||||
@Expose public double __getTimezoneOffset() {
|
||||
if (normal == null) return Double.NaN;
|
||||
return normal.getTimeZone().getRawOffset() / 60000;
|
||||
}
|
||||
|
||||
@Expose public double __valueOf() {
|
||||
if (normal == null) return Double.NaN;
|
||||
else return normal.getTimeInMillis();
|
||||
}
|
||||
|
||||
@Expose public String __toString() {
|
||||
return normal.getTime().toString();
|
||||
}
|
||||
|
||||
@Override public String toString() {
|
||||
return __toString();
|
||||
}
|
||||
|
||||
public DateLib(long timestamp) {
|
||||
normal = Calendar.getInstance();
|
||||
utc = Calendar.getInstance();
|
||||
normal.setTimeInMillis(timestamp);
|
||||
utc.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
utc.setTimeInMillis(timestamp);
|
||||
}
|
||||
|
||||
public DateLib() {
|
||||
this(new Date().getTime());
|
||||
}
|
||||
|
||||
@ExposeConstructor public static DateLib init(Arguments args) {
|
||||
if (args.has(0)) return new DateLib(args.getLong(0));
|
||||
else return new DateLib();
|
||||
}
|
||||
|
||||
@Expose(target = ExposeTarget.STATIC)
|
||||
public static double __now() {
|
||||
return new DateLib().__getTime();
|
||||
}
|
||||
}
|
||||
89
src/java/me/topchetoeu/jscript/lib/EncodingLib.java
Normal file
89
src/java/me/topchetoeu/jscript/lib/EncodingLib.java
Normal file
@@ -0,0 +1,89 @@
|
||||
package me.topchetoeu.jscript.lib;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import me.topchetoeu.jscript.common.Buffer;
|
||||
import me.topchetoeu.jscript.compilation.parsing.Parsing;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.EngineException;
|
||||
import me.topchetoeu.jscript.runtime.values.ArrayValue;
|
||||
import me.topchetoeu.jscript.runtime.values.Values;
|
||||
import me.topchetoeu.jscript.utils.interop.Arguments;
|
||||
import me.topchetoeu.jscript.utils.interop.Expose;
|
||||
import me.topchetoeu.jscript.utils.interop.ExposeTarget;
|
||||
import me.topchetoeu.jscript.utils.interop.WrapperName;
|
||||
|
||||
@WrapperName("Encoding")
|
||||
public class EncodingLib {
|
||||
private static final String HEX = "0123456789ABCDEF";
|
||||
|
||||
public static String encodeUriAny(String str, String keepAlphabet) {
|
||||
if (str == null) str = "undefined";
|
||||
|
||||
var bytes = str.getBytes();
|
||||
var sb = new StringBuilder(bytes.length);
|
||||
|
||||
for (byte c : bytes) {
|
||||
if (Parsing.isAlphanumeric((char)c) || Parsing.isAny((char)c, keepAlphabet)) sb.append((char)c);
|
||||
else {
|
||||
sb.append('%');
|
||||
sb.append(HEX.charAt(c / 16));
|
||||
sb.append(HEX.charAt(c % 16));
|
||||
}
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
public static String decodeUriAny(String str, String keepAlphabet) {
|
||||
if (str == null) str = "undefined";
|
||||
|
||||
var res = new Buffer();
|
||||
var bytes = str.getBytes();
|
||||
|
||||
for (var i = 0; i < bytes.length; i++) {
|
||||
var c = bytes[i];
|
||||
if (c == '%') {
|
||||
if (i >= bytes.length - 2) throw EngineException.ofError("URIError", "URI malformed.");
|
||||
var b = Parsing.fromHex((char)bytes[i + 1]) * 16 | Parsing.fromHex((char)bytes[i + 2]);
|
||||
if (!Parsing.isAny((char)b, keepAlphabet)) {
|
||||
i += 2;
|
||||
res.append((byte)b);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
res.append(c);
|
||||
}
|
||||
|
||||
return new String(res.data());
|
||||
}
|
||||
|
||||
@Expose(target = ExposeTarget.STATIC)
|
||||
public static ArrayValue __encode(Arguments args) {
|
||||
var res = new ArrayValue();
|
||||
for (var el : args.getString(0).getBytes()) res.set(null, res.size(), (int)el);
|
||||
return res;
|
||||
}
|
||||
@Expose(target = ExposeTarget.STATIC)
|
||||
public static String __decode(Arguments args) {
|
||||
var raw = args.convert(0, ArrayList.class);
|
||||
var res = new byte[raw.size()];
|
||||
for (var i = 0; i < raw.size(); i++) res[i] = (byte)Values.toNumber(args.ctx, raw.get(i));
|
||||
return new String(res);
|
||||
}
|
||||
|
||||
@Expose(target = ExposeTarget.STATIC)
|
||||
public static String __encodeURIComponent(Arguments args) {
|
||||
return EncodingLib.encodeUriAny(args.getString(0), ".-_!~*'()");
|
||||
}
|
||||
@Expose(target = ExposeTarget.STATIC)
|
||||
public static String __decodeURIComponent(Arguments args) {
|
||||
return decodeUriAny(args.getString(0), "");
|
||||
}
|
||||
@Expose(target = ExposeTarget.STATIC)
|
||||
public static String __encodeURI(Arguments args) {
|
||||
return encodeUriAny(args.getString(0), ";,/?:@&=+$#.-_!~*'()");
|
||||
}
|
||||
@Expose(target = ExposeTarget.STATIC)
|
||||
public static String __decodeURI(Arguments args) {
|
||||
return decodeUriAny(args.getString(0), ",/?:@&=+$#.");
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user