Compare commits
85 Commits
TopchetoEU
...
28e72503a6
| Author | SHA1 | Date | |
|---|---|---|---|
| 28e72503a6 | |||
| d0a0796e14 | |||
| 0de54e5505 | |||
| 96c9d29a6a | |||
| 37dc844cc4 | |||
|
b97e4bf163
|
|||
|
9ce0504948
|
|||
|
4c53689d9c
|
|||
|
6c8c329992
|
|||
|
74f08b7483
|
|||
|
2bf920c681
|
|||
|
1ac68425af
|
|||
|
7423e3f283
|
|||
|
afe4542682
|
|||
|
c971fde0e2
|
|||
|
8a34db833c
|
|||
|
398d88c0a5
|
|||
|
f80266618c
|
|||
|
f1997e4584
|
|||
|
e93498fed5
|
|||
|
29bea68525
|
|||
|
3a6401094f
|
|||
|
a705bf296e
|
|||
|
c3432306f7
|
|||
|
d8c18ccf17
|
|||
|
277f59e54a
|
|||
|
a1bb5bdba6
|
|||
|
c699102e56
|
|||
|
b2f5068e12
|
|||
|
05bafc7317
|
|||
|
c7c31c3859
|
|||
|
c18015b9a0
|
|||
|
5346b8917d
|
|||
|
2ed7959088
|
|||
|
239d0ae8d7
|
|||
|
6fac295b99
|
|||
|
66440a9649
|
|||
|
274a925ff8
|
|||
|
130fe17441
|
|||
|
493c54ed39
|
|||
|
75786f39ad
|
|||
|
bce8b7293c
|
|||
|
00aeef5321
|
|||
|
ff4aa3dcfd
|
|||
|
45f52ff123
|
|||
|
bed4014bef
|
|||
|
17406c6b81
|
|||
|
3abdd8d3c9
|
|||
|
a329f615cf
|
|||
|
058d20b27f
|
|||
|
ea158c1e60
|
|||
|
4e18c76bb1
|
|||
|
8d7939d85a
|
|||
|
814e0d7b7e
|
|||
|
138baebacb
|
|||
|
db241919f2
|
|||
|
db45eb529d
|
|||
| caf44a50e5 | |||
|
65f9debecc
|
|||
|
3f5e1a5fd8
|
|||
|
b0d8a072aa
|
|||
|
2e8e123ec4
|
|||
|
54d55814af
|
|||
|
28679f44d5
|
|||
|
611be55bbb
|
|||
|
4992d0211b
|
|||
|
ba7505e148
|
|||
|
3c13799c2f
|
|||
|
5c2fd00bfb
|
|||
|
39eb6ffac5
|
|||
|
7f6df49fc5
|
|||
|
61c5df5003
|
|||
|
41bb27e4dd
|
|||
|
b4e7a42975
|
|||
|
92fb0dbbfd
|
|||
|
fe8f65faf5
|
|||
|
54fe16393a
|
|||
|
14e4aade35
|
|||
|
754648fbf6
|
|||
|
20f2c3c5e9
|
|||
|
c5067cbfdd
|
|||
|
5644966dd7
|
|||
|
50eb204da7
|
|||
|
45308e6d65
|
|||
|
0ebf189c95
|
15
.github/workflows/tagged-release.yml
vendored
15
.github/workflows/tagged-release.yml
vendored
@@ -11,6 +11,8 @@ jobs:
|
|||||||
runs-on: "ubuntu-latest"
|
runs-on: "ubuntu-latest"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
|
- name: Clone repository
|
||||||
|
uses: actions/checkout@v2
|
||||||
- name: Setup Java
|
- name: Setup Java
|
||||||
uses: actions/setup-java@v3
|
uses: actions/setup-java@v3
|
||||||
with:
|
with:
|
||||||
@@ -18,16 +20,13 @@ jobs:
|
|||||||
java-version: '11'
|
java-version: '11'
|
||||||
- name: Setup Gradle
|
- name: Setup Gradle
|
||||||
uses: gradle/gradle-build-action@v2
|
uses: gradle/gradle-build-action@v2
|
||||||
- name: Clone repository
|
|
||||||
uses: GuillaumeFalourd/clone-github-repo-action@main
|
|
||||||
with:
|
with:
|
||||||
branch: 'master'
|
cache-disabled: true
|
||||||
owner: 'TopchetoEU'
|
gradle-version: "8.10"
|
||||||
repository: 'java-jscript'
|
- name: NPM
|
||||||
|
run: npm i
|
||||||
- name: Build
|
- name: Build
|
||||||
run: |
|
run: gradle build
|
||||||
cd java-jscript; gradle build
|
|
||||||
|
|
||||||
- uses: "marvinpinto/action-automatic-releases@latest"
|
- uses: "marvinpinto/action-automatic-releases@latest"
|
||||||
with:
|
with:
|
||||||
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
repo_token: "${{ secrets.GITHUB_TOKEN }}"
|
||||||
|
|||||||
16
.gitignore
vendored
16
.gitignore
vendored
@@ -1,24 +1,18 @@
|
|||||||
*
|
/*
|
||||||
|
|
||||||
!/src
|
!/src
|
||||||
!/src/**/*
|
|
||||||
|
|
||||||
!/doc
|
!/doc
|
||||||
!/doc/**/*
|
|
||||||
|
|
||||||
!/tests
|
!/tests
|
||||||
!/tests/**/*
|
|
||||||
|
|
||||||
!/.github
|
!/.github
|
||||||
!/.github/**/*
|
|
||||||
|
|
||||||
!/.gitignore
|
!/.gitignore
|
||||||
!/.gitattributes
|
!/.gitattributes
|
||||||
!/LICENSE
|
!/LICENSE
|
||||||
!/README.md
|
!/README.md
|
||||||
|
|
||||||
!/settings.gradle
|
!/settings.gradle
|
||||||
!/build.gradle
|
!/build.gradle
|
||||||
!/gradle.properties
|
!/gradle.properties
|
||||||
!/gradle
|
|
||||||
!/gradle/wrapper
|
!/package.json
|
||||||
!/gradle/wrapper/gradle-wrapper.properties
|
!/rollup.config.js
|
||||||
|
|||||||
86
build.gradle
86
build.gradle
@@ -1,41 +1,68 @@
|
|||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'application'
|
id 'application';
|
||||||
id 'net.nemerosa.versioning' version '2.15.0'
|
id 'com.github.node-gradle.node' version '5.0.0';
|
||||||
id 'org.ajoberstar.grgit' version '5.0.0-rc.3' // required by gradle
|
id 'net.nemerosa.versioning' version '2.15.0';
|
||||||
|
id 'org.ajoberstar.grgit' version '5.0.0-rc.3'; // required by gradle
|
||||||
|
|
||||||
// TODO: figure out how to integrate proguard
|
// TODO: figure out how to integrate proguard
|
||||||
// id "com.github.xaverkapeller.proguard-annotations"
|
// id "com.github.xaverkapeller.proguard-annotations"
|
||||||
}
|
}
|
||||||
|
|
||||||
base.archivesName = project.project_name
|
base.archivesName = project.project_name;
|
||||||
version = project.project_version
|
version = project.project_version;
|
||||||
group = project.project_group
|
group = project.project_group;
|
||||||
description = 'ES5-compliant JavaScript interpreter'
|
description = 'ES5-compliant JavaScript interpreter';
|
||||||
|
|
||||||
|
node {
|
||||||
|
version = '20.0.0';
|
||||||
|
npmVersion = '8.0.0';
|
||||||
|
download = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
task compileEnv(type: NpmTask) {
|
||||||
|
inputs.files('rollup.config.js');
|
||||||
|
inputs.dir('src/lib/libs');
|
||||||
|
outputs.files("build/js/env.js");
|
||||||
|
|
||||||
|
// group = 'build'
|
||||||
|
args = ['run', 'build-env'];
|
||||||
|
}
|
||||||
|
task compileTypescript(type: NpmTask) {
|
||||||
|
inputs.files('rollup.config.js');
|
||||||
|
inputs.dir('src/lib/transpiler');
|
||||||
|
outputs.files("build/js/ts.js");
|
||||||
|
// nom nom tasty ram
|
||||||
|
environment.put("NODE_OPTIONS", "--max-old-space-size=4096");
|
||||||
|
|
||||||
|
// group = 'build'
|
||||||
|
args = ['run', 'build-ts'];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral();
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2'
|
annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2';
|
||||||
compileOnly 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2'
|
compileOnly 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2';
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2'
|
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2';
|
||||||
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
|
testRuntimeOnly 'org.junit.platform:junit-platform-launcher';
|
||||||
}
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
sourceCompatibility = JavaVersion.VERSION_17
|
sourceCompatibility = JavaVersion.VERSION_17;
|
||||||
targetCompatibility = JavaVersion.VERSION_17
|
targetCompatibility = JavaVersion.VERSION_17;
|
||||||
|
|
||||||
toolchain {
|
toolchain {
|
||||||
languageVersion = JavaLanguageVersion.of(17)
|
languageVersion = JavaLanguageVersion.of(17);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
configure([tasks.compileJava]) {
|
configure([tasks.compileJava]) {
|
||||||
options.release = 8
|
options.release = 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
jar {
|
jar {
|
||||||
@@ -46,20 +73,20 @@ jar {
|
|||||||
'Build-Branch': versioning.info.branch,
|
'Build-Branch': versioning.info.branch,
|
||||||
'Build-Revision': versioning.info.commit,
|
'Build-Revision': versioning.info.commit,
|
||||||
'Build-Jdk': "${System.properties['java.version']} (${System.properties['java.vendor']} ${System.properties['java.vm.version']})",
|
'Build-Jdk': "${System.properties['java.version']} (${System.properties['java.vendor']} ${System.properties['java.vm.version']})",
|
||||||
'Build-Author': 'TopchetoEU'
|
'Build-Author': 'TopchetoEU',
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
application {
|
application {
|
||||||
mainClass = project.main_class
|
mainClass = project.main_class;
|
||||||
applicationDefaultJvmArgs = ['-Xmx2G', '-Xms2G', '-server', '-Dfile.encoding=UTF-8']
|
applicationDefaultJvmArgs = ['-Xmx2G', '-Xms2G', '-server', '-Dfile.encoding=UTF-8'];
|
||||||
}
|
}
|
||||||
|
|
||||||
distZip {
|
distZip {
|
||||||
eachFile { file ->
|
eachFile { file ->
|
||||||
if (file.path.contains('bin')) {
|
if (file.path.contains('bin')) {
|
||||||
file.exclude()
|
file.exclude();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -67,24 +94,31 @@ distZip {
|
|||||||
distTar {
|
distTar {
|
||||||
eachFile { file ->
|
eachFile { file ->
|
||||||
if (file.path.contains('bin')) {
|
if (file.path.contains('bin')) {
|
||||||
file.exclude()
|
file.exclude();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
processResources {
|
processResources {
|
||||||
|
dependsOn compileEnv;
|
||||||
|
dependsOn compileTypescript;
|
||||||
|
|
||||||
|
from("build/js") {
|
||||||
|
into "lib";
|
||||||
|
}
|
||||||
|
|
||||||
filesMatching "metadata.json", {
|
filesMatching "metadata.json", {
|
||||||
expand(
|
expand(
|
||||||
version: project.project_version,
|
version: project.project_version,
|
||||||
name: project.project_name
|
name: project.project_name,
|
||||||
)
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test {
|
test {
|
||||||
useJUnitPlatform()
|
useJUnitPlatform();
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapper {
|
wrapper {
|
||||||
gradleVersion = '8.10'
|
gradleVersion = '8.10';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
project_group = me.topchetoeu
|
project_group = me.topchetoeu
|
||||||
project_name = jscript
|
project_name = jscript
|
||||||
project_version = 0.9.41-beta
|
project_version = 0.10-beta
|
||||||
main_class = me.topchetoeu.jscript.runtime.SimpleRepl
|
main_class = me.topchetoeu.jscript.repl.SimpleRepl
|
||||||
|
|||||||
7
gradle/wrapper/gradle-wrapper.properties
vendored
7
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,7 +0,0 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
|
||||||
distributionPath=wrapper/dists
|
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
|
|
||||||
networkTimeout=10000
|
|
||||||
validateDistributionUrl=true
|
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
|
||||||
zipStorePath=wrapper/dists
|
|
||||||
31
package.json
Normal file
31
package.json
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
"scripts": {
|
||||||
|
"build-env": "rollup -c --environment INPUT:src/lib/libs/_entry.ts,OUTPUT:build/js/index.js,POLYFILLS:src/lib/libs/polyfills",
|
||||||
|
"build-ts": "rollup -c --environment INPUT:src/lib/transpiler/_entry.ts,OUTPUT:build/js/ts.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@babel/core": "^7.26.0",
|
||||||
|
"@babel/runtime": "^7.26.0",
|
||||||
|
"@babel/standalone": "^7.26.4",
|
||||||
|
"@rollup/plugin-json": "^6.1.0",
|
||||||
|
"@types/babel__preset-env": "^7.9.7",
|
||||||
|
"@types/babel__standalone": "^7.1.9",
|
||||||
|
"@types/coffeescript": "^2.5.7",
|
||||||
|
"coffeescript": "^2.7.0",
|
||||||
|
"typescript": "^5.7.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/plugin-transform-class-properties": "^7.25.9",
|
||||||
|
"@babel/plugin-transform-runtime": "^7.25.9",
|
||||||
|
"@babel/plugin-transform-typescript": "^7.25.9",
|
||||||
|
"@babel/preset-env": "^7.26.0",
|
||||||
|
"@rollup/plugin-babel": "^6.0.4",
|
||||||
|
"@rollup/plugin-commonjs": "^28.0.1",
|
||||||
|
"@rollup/plugin-node-resolve": "^15.3.0",
|
||||||
|
"@rollup/plugin-terser": "^0.4.4",
|
||||||
|
"@rollup/plugin-typescript": "^12.1.1",
|
||||||
|
"@types/node": "^22.10.1",
|
||||||
|
"rollup": "^4.24.0",
|
||||||
|
"tslib": "^2.8.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
130
rollup.config.js
Normal file
130
rollup.config.js
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
const { defineConfig } = require("rollup");
|
||||||
|
const terser = require("@rollup/plugin-terser");
|
||||||
|
const typescript = require("@rollup/plugin-typescript");
|
||||||
|
const babel = require("@rollup/plugin-babel");
|
||||||
|
const commonjs = require("@rollup/plugin-commonjs");
|
||||||
|
const nodeResolve = require("@rollup/plugin-node-resolve");
|
||||||
|
const json = require("@rollup/plugin-json");
|
||||||
|
const { resolve } = require("path");
|
||||||
|
|
||||||
|
const shouldMinify = () => false;
|
||||||
|
const shouldEmitSourcemaps = () => true;
|
||||||
|
const shouldPolyfill = () => !!process.env.POLYFILLS;
|
||||||
|
|
||||||
|
const construct = (input, output) => defineConfig({
|
||||||
|
input,
|
||||||
|
plugins: [
|
||||||
|
shouldPolyfill() && {
|
||||||
|
name: "babel-fake-runtime",
|
||||||
|
resolveId(source) {
|
||||||
|
if (source.startsWith("!polyfills:/helpers")) return resolve(process.env.POLYFILLS) + source.slice(19) + ".js";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
commonjs(),
|
||||||
|
nodeResolve(),
|
||||||
|
json(),
|
||||||
|
babel({
|
||||||
|
extensions: [],
|
||||||
|
exclude: ["node_modules/**"],
|
||||||
|
|
||||||
|
babelHelpers: "runtime",
|
||||||
|
plugins: [
|
||||||
|
["@babel/plugin-transform-typescript", {
|
||||||
|
onlyRemoveTypeImports: true,
|
||||||
|
optimizeConstEnums: true,
|
||||||
|
allowDeclareFields: true,
|
||||||
|
}],
|
||||||
|
["@babel/plugin-transform-class-properties"],
|
||||||
|
["@babel/plugin-transform-runtime", {
|
||||||
|
moduleName: shouldPolyfill() ? "!polyfills:" : undefined,
|
||||||
|
version: "^7.24.0",
|
||||||
|
}],
|
||||||
|
]
|
||||||
|
}),
|
||||||
|
babel({
|
||||||
|
extensions: [],
|
||||||
|
exclude: shouldPolyfill() ? [process.env.POLYFILLS + "/**"] : [],
|
||||||
|
|
||||||
|
assumptions: {
|
||||||
|
ignoreToPrimitiveHint: true,
|
||||||
|
noClassCalls: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
env: {
|
||||||
|
development: { compact: false },
|
||||||
|
},
|
||||||
|
|
||||||
|
babelHelpers: "runtime",
|
||||||
|
plugins: [
|
||||||
|
"@babel/plugin-transform-arrow-functions",
|
||||||
|
"@babel/plugin-transform-block-scoping",
|
||||||
|
"@babel/plugin-transform-classes",
|
||||||
|
"@babel/plugin-transform-computed-properties",
|
||||||
|
"@babel/plugin-transform-destructuring",
|
||||||
|
"@babel/plugin-transform-for-of",
|
||||||
|
"@babel/plugin-transform-object-super",
|
||||||
|
"@babel/plugin-transform-parameters",
|
||||||
|
"@babel/plugin-transform-shorthand-properties",
|
||||||
|
"@babel/plugin-transform-spread",
|
||||||
|
"@babel/plugin-transform-object-rest-spread",
|
||||||
|
"@babel/plugin-transform-template-literals",
|
||||||
|
"@babel/plugin-transform-unicode-escapes",
|
||||||
|
"@babel/plugin-transform-unicode-regex",
|
||||||
|
"@babel/plugin-transform-exponentiation-operator",
|
||||||
|
"@babel/plugin-transform-async-to-generator",
|
||||||
|
"@babel/plugin-transform-async-generator-functions",
|
||||||
|
"@babel/plugin-transform-nullish-coalescing-operator",
|
||||||
|
"@babel/plugin-transform-optional-chaining",
|
||||||
|
"@babel/plugin-transform-logical-assignment-operators",
|
||||||
|
"@babel/plugin-transform-numeric-separator",
|
||||||
|
"@babel/plugin-transform-class-properties",
|
||||||
|
"@babel/plugin-transform-class-static-block",
|
||||||
|
"@babel/plugin-transform-regenerator",
|
||||||
|
|
||||||
|
["@babel/plugin-transform-runtime", {
|
||||||
|
moduleName: shouldPolyfill() ? "!polyfills:" : undefined,
|
||||||
|
version: "^7.24.0",
|
||||||
|
}],
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
typescript({
|
||||||
|
exclude: ["node_modules/**", "*.js"],
|
||||||
|
compilerOptions: {
|
||||||
|
allowImportingTsExtensions: true,
|
||||||
|
noEmit: true,
|
||||||
|
},
|
||||||
|
noForceEmit: true,
|
||||||
|
noEmitOnError: true,
|
||||||
|
}),
|
||||||
|
shouldMinify() && terser({
|
||||||
|
sourceMap: shouldEmitSourcemaps(),
|
||||||
|
keep_classnames: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
output: {
|
||||||
|
file: output,
|
||||||
|
format: "iife",
|
||||||
|
globals: {
|
||||||
|
fs: "null",
|
||||||
|
path: "null",
|
||||||
|
os: "null",
|
||||||
|
inspector: "null",
|
||||||
|
tty: "null",
|
||||||
|
util: "null",
|
||||||
|
assert: "null",
|
||||||
|
url: "null",
|
||||||
|
"@babel/preset-typescript/package.json": "null",
|
||||||
|
module: "null",
|
||||||
|
process: "null",
|
||||||
|
v8: "null",
|
||||||
|
},
|
||||||
|
// plugins: [babel.getBabelOutputPlugin({
|
||||||
|
// allowAllFormats: true,
|
||||||
|
// })],
|
||||||
|
|
||||||
|
sourcemap: shouldEmitSourcemaps(),
|
||||||
|
inlineDynamicImports: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = construct(process.env.INPUT, process.env.OUTPUT);
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
pluginManagement {
|
pluginManagement {
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral();
|
||||||
gradlePluginPortal()
|
gradlePluginPortal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0'
|
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rootProject.name = properties.project_name;
|
||||||
rootProject.name = properties.project_name
|
|
||||||
|
|||||||
81
src/lib/libs/_entry.ts
Normal file
81
src/lib/libs/_entry.ts
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
import { object, setGlobalPrototypes, target } from "./primordials.ts";
|
||||||
|
import { Error, RangeError, SyntaxError, TypeError } from "./errors.ts";
|
||||||
|
import { Boolean } from "./boolean.ts";
|
||||||
|
import { Function } from "./function.ts";
|
||||||
|
import { Number } from "./number.ts";
|
||||||
|
import { Object } from "./object.ts";
|
||||||
|
import { String } from "./string.ts";
|
||||||
|
import { Symbol } from "./symbol.ts";
|
||||||
|
import { Array } from "./array.ts";
|
||||||
|
import { Map, WeakMap } from "./map.ts";
|
||||||
|
import { RegExp } from "./regex.ts";
|
||||||
|
import { Date } from "./date.ts";
|
||||||
|
import { Math as _Math } from "./math.ts";
|
||||||
|
import { Set, WeakSet } from "./set.ts";
|
||||||
|
import { JSON } from "./json.ts";
|
||||||
|
import { console } from "./console.ts";
|
||||||
|
import { encodeURI, encodeURIComponent } from "./url.ts";
|
||||||
|
import { Promise } from "./promise.ts";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
function print(...args: any[]): void;
|
||||||
|
function measure(func: Function): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fixup<T extends Function>(clazz: T) {
|
||||||
|
object.setPrototype(clazz, Function.prototype);
|
||||||
|
object.setPrototype(clazz.prototype as any, Object.prototype);
|
||||||
|
return clazz;
|
||||||
|
}
|
||||||
|
|
||||||
|
object.setPrototype(target, Object.prototype);
|
||||||
|
|
||||||
|
object.defineField(target, "undefined", { e: false, c: false, w: false, v: void 0 });
|
||||||
|
|
||||||
|
target.Symbol = fixup(Symbol);
|
||||||
|
target.Number = fixup(Number);
|
||||||
|
target.String = fixup(String);
|
||||||
|
target.Boolean = fixup(Boolean);
|
||||||
|
|
||||||
|
target.Object = Object;
|
||||||
|
target.Function = fixup(Function);
|
||||||
|
target.Array = fixup(Array);
|
||||||
|
|
||||||
|
target.Error = fixup(Error);
|
||||||
|
target.RangeError = RangeError;
|
||||||
|
target.SyntaxError = SyntaxError;
|
||||||
|
target.TypeError = TypeError;
|
||||||
|
|
||||||
|
target.Map = fixup(Map);
|
||||||
|
target.WeakMap = fixup(WeakMap);
|
||||||
|
target.Set = fixup(Set);
|
||||||
|
target.WeakSet = fixup(WeakSet);
|
||||||
|
target.RegExp = fixup(RegExp);
|
||||||
|
target.Date = fixup(Date);
|
||||||
|
target.Promise = fixup(Promise);
|
||||||
|
target.Math = object.setPrototype(_Math, Object.prototype);
|
||||||
|
target.JSON = object.setPrototype(JSON, Object.prototype);
|
||||||
|
target.console = object.setPrototype(console, Object.prototype);
|
||||||
|
target.TYPED_ARRAY_SUPPORT = false;
|
||||||
|
|
||||||
|
target.parseInt = Number.parseInt;
|
||||||
|
target.parseFloat = Number.parseFloat;
|
||||||
|
target.NaN = Number.NaN;
|
||||||
|
target.Infinity = Number.POSITIVE_INFINITY;
|
||||||
|
target.encodeURI = encodeURI;
|
||||||
|
target.encodeURIComponent = encodeURIComponent;
|
||||||
|
|
||||||
|
setGlobalPrototypes({
|
||||||
|
string: String.prototype,
|
||||||
|
number: Number.prototype,
|
||||||
|
boolean: Boolean.prototype,
|
||||||
|
symbol: Symbol.prototype,
|
||||||
|
object: Object.prototype,
|
||||||
|
array: Array.prototype,
|
||||||
|
function: Function.prototype,
|
||||||
|
error: Error.prototype,
|
||||||
|
syntax: SyntaxError.prototype,
|
||||||
|
range: RangeError.prototype,
|
||||||
|
type: TypeError.prototype,
|
||||||
|
regex: RegExp,
|
||||||
|
});
|
||||||
330
src/lib/libs/array.ts
Normal file
330
src/lib/libs/array.ts
Normal file
@@ -0,0 +1,330 @@
|
|||||||
|
import { Error } from "./errors.ts";
|
||||||
|
import { func, object, string } from "./primordials.ts";
|
||||||
|
import { String } from "./string.ts";
|
||||||
|
import { limitI, symbols, wrapI } from "./utils.ts";
|
||||||
|
|
||||||
|
export const Array = (() => {
|
||||||
|
class Array {
|
||||||
|
public forEach(this: any[], cb: (val: any, i: number, self: this) => void, self?: any) {
|
||||||
|
for (let i = 0; i < this.length; i++) {
|
||||||
|
if (i in this) func.invoke(cb, self, [this[i], i, this]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public join(this: any[], delim = ",") {
|
||||||
|
delim = String(delim);
|
||||||
|
const parts = [];
|
||||||
|
if (delim) {
|
||||||
|
for (let i = 0; i < this.length; i++) {
|
||||||
|
if (i) parts[parts.length] = delim;
|
||||||
|
parts[parts.length] = (i in this) ? String(this[i]) : "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (let i = 0; i < this.length; i++) {
|
||||||
|
parts[i] = (i in this) ? String(this[i]) : "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.stringBuild(parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public push(this: any[]) {
|
||||||
|
const start = this.length;
|
||||||
|
for (let i = arguments.length - 1; i >= 0; i--) {
|
||||||
|
this[start + i] = arguments[i];
|
||||||
|
}
|
||||||
|
return arguments.length;
|
||||||
|
}
|
||||||
|
public pop(this: any[]) {
|
||||||
|
if (this.length === 0) return undefined;
|
||||||
|
else {
|
||||||
|
const res = this[this.length - 1];
|
||||||
|
this.length--;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public unshift(this: any[]) {
|
||||||
|
for (let i = this.length + arguments.length - 1; i >= arguments.length; i--) {
|
||||||
|
this[i] = this[i - arguments.length];
|
||||||
|
}
|
||||||
|
for (let i = 0; i < arguments.length; i++) {
|
||||||
|
this[i] = arguments[i];
|
||||||
|
}
|
||||||
|
return arguments.length;
|
||||||
|
}
|
||||||
|
public shift(this: any[]) {
|
||||||
|
if (this.length === 0) return undefined;
|
||||||
|
|
||||||
|
const tmp = this[0];
|
||||||
|
|
||||||
|
for (let i = 1; i < this.length; i++) {
|
||||||
|
this[i - 1] = this[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.length--;
|
||||||
|
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public concat(this: any[]) {
|
||||||
|
const res: any[] = [];
|
||||||
|
|
||||||
|
function add(arr: any) {
|
||||||
|
if (Array.isArray(arr) || symbols.isConcatSpreadable in arr) {
|
||||||
|
const start = res.length;
|
||||||
|
res.length += arr.length;
|
||||||
|
|
||||||
|
for (let i = 0; i < res.length; i++) {
|
||||||
|
if (i in arr) res[start + i] = arr[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else res[res.length] = arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
add(this);
|
||||||
|
|
||||||
|
for (let i = 0; i < arguments.length; i++) {
|
||||||
|
add(arguments[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
public slice(this: any[], start = 0, end = this.length) {
|
||||||
|
start = wrapI(start, this.length);
|
||||||
|
end = wrapI(end, this.length);
|
||||||
|
|
||||||
|
if (end <= start) return [];
|
||||||
|
|
||||||
|
const res: any[] = [];
|
||||||
|
res.length = end - start;
|
||||||
|
|
||||||
|
for (let i = 0; i < end - start; i++) {
|
||||||
|
res[i] = this[start + i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
public splice(this: any[], start = 0, count = this.length - start) {
|
||||||
|
const vals: any[] = []
|
||||||
|
for (let i = 0; i < arguments.length - 2; i++) vals[i] = arguments[i + 2];
|
||||||
|
|
||||||
|
start = limitI(wrapI(start, this.length), this.length);
|
||||||
|
count = limitI(wrapI(count, this.length), this.length - start);
|
||||||
|
|
||||||
|
const res: any[] = [];
|
||||||
|
const change = vals.length - count;
|
||||||
|
|
||||||
|
for (let i = start; i < start + count; i++) {
|
||||||
|
res[i - start] = this[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (change < 0) {
|
||||||
|
for (let i = start - change; i < this.length; i++) {
|
||||||
|
this[i + change] = this[i];
|
||||||
|
}
|
||||||
|
this.length = this.length + change;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (let i = this.length - 1; i >= start - change; i--) {
|
||||||
|
this[i + change] = this[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < vals.length; i++) {
|
||||||
|
this[i + start] = vals[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public map(this: any[], cb: Function, self?: any) {
|
||||||
|
const res = [];
|
||||||
|
res.length = this.length;
|
||||||
|
|
||||||
|
for (let i = 0; i < this.length; i++) {
|
||||||
|
if (i in this) res[i] = func.invoke(cb, self, [this[i], i, this]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
public filter(this: any[], cb: Function, self?: any) {
|
||||||
|
const res = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < this.length; i++) {
|
||||||
|
if (i in this && func.invoke(cb, self, [this[i], i, this])) res[res.length] = this[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
public reduce(this: any[], cb: Function, initial: any) {
|
||||||
|
let i = 0;
|
||||||
|
if (arguments.length <= 1) initial = this[i++];
|
||||||
|
|
||||||
|
for (; i < this.length; i++) {
|
||||||
|
initial = cb(initial, this[i], i, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return initial;
|
||||||
|
}
|
||||||
|
public some(this: any[], cb: Function, self?: any) {
|
||||||
|
for (let i = 0; i < this.length; i++) {
|
||||||
|
if (i in this && func.invoke(cb, self, [this[i], i, this])) return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
public every(this: any[], cb: Function, self?: any) {
|
||||||
|
for (let i = 0; i < this.length; i++) {
|
||||||
|
if (i in this && !func.invoke(cb, self, [this[i], i, this])) return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public find(this: any[], cb: Function, self?: any) {
|
||||||
|
for (let i = 0; i < this.length; i++) {
|
||||||
|
if (i in this && func.invoke(cb, self, [this[i], i, this])) return this[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
public indexOf(this: any[], val: any, start = 0) {
|
||||||
|
start |= 0;
|
||||||
|
if (start < 0) start = 0;
|
||||||
|
for (let i = start; i < this.length; i++) {
|
||||||
|
if (i in this && this[i] === val) return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
public lastIndexOf(this: any[], val: any, start = 0) {
|
||||||
|
start |= 0;
|
||||||
|
if (start < 0) start = 0;
|
||||||
|
|
||||||
|
for (let i = this.length - 1; i >= start; i--) {
|
||||||
|
if (i in this && this[i] === val) return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
public includes(this: any[], val: any) {
|
||||||
|
for (let i = 0; i < this.length; i++) {
|
||||||
|
if (i in this && this[i] === val) return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public sort(this: any[], cb?: Function) {
|
||||||
|
cb ||= (a: any, b: any) => {
|
||||||
|
if (String(a) < String(b)) return -1;
|
||||||
|
if (String(a) === String(b)) return 0;
|
||||||
|
return 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
return object.sort(this, cb);
|
||||||
|
}
|
||||||
|
public reverse(this: any[]) {
|
||||||
|
const mid = this.length >> 1;
|
||||||
|
const end = this.length - 1;
|
||||||
|
|
||||||
|
for (let i = 0; i < mid; i++) {
|
||||||
|
const tmp = this[i];
|
||||||
|
this[i] = this[end - i];
|
||||||
|
this[end - i] = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public [symbols.iterator](this: any[]) {
|
||||||
|
let i = 0;
|
||||||
|
let arr: any[] | undefined = func.invoke(Array.prototype.slice, this, []);
|
||||||
|
|
||||||
|
return {
|
||||||
|
next() {
|
||||||
|
if (arr == null) return { done: true, value: undefined };
|
||||||
|
|
||||||
|
if (i >= arr.length) {
|
||||||
|
arr = undefined;
|
||||||
|
return { done: true, value: undefined };
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const res = arr![i];
|
||||||
|
|
||||||
|
if (i in arr!) {
|
||||||
|
i++;
|
||||||
|
return { done: false, value: res };
|
||||||
|
}
|
||||||
|
else i++;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[symbols.iterator]() { return this; }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor (len: unknown) {
|
||||||
|
if (arguments.length === 1 && typeof len === "number") {
|
||||||
|
const res: any[] = [];
|
||||||
|
res.length = len;
|
||||||
|
return res as any;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const res: any[] = [];
|
||||||
|
res.length = arguments.length;
|
||||||
|
for (let i = 0; i < arguments.length; i++) {
|
||||||
|
res[i] = arguments[i];
|
||||||
|
}
|
||||||
|
return res as any;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static isArray(val: any): val is any[] {
|
||||||
|
return object.isArray(val);
|
||||||
|
}
|
||||||
|
public static from(val: any, cb?: Function, self?: any): any[] {
|
||||||
|
if (symbols.iterator in val) {
|
||||||
|
const res = [];
|
||||||
|
const it = val[symbols.iterator]();
|
||||||
|
|
||||||
|
if (cb) {
|
||||||
|
for (let val = it.next(); !val.done; val = it.next()) {
|
||||||
|
res[res.length] = func.invoke(cb, self, [val.value]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (let val = it.next(); !val.done; val = it.next()) {
|
||||||
|
res[res.length] = val.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
else if ("length" in val) {
|
||||||
|
const res = [];
|
||||||
|
|
||||||
|
if (cb) {
|
||||||
|
for (let i = 0; i < val.length; i++) {
|
||||||
|
if (i in val) res[i] = func.invoke(cb, self, [val[i]]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (let i = 0; i < val.length; i++) {
|
||||||
|
if (i in val) res[i] = val[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
else if (val == null) throw new Error("Illegal argument");
|
||||||
|
else return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func.setCallable(Array, true);
|
||||||
|
func.setConstructable(Array, true);
|
||||||
|
|
||||||
|
return Array as any as typeof Array & ((value?: unknown) => object);
|
||||||
|
})();
|
||||||
|
export type Array = InstanceType<typeof Array>;
|
||||||
29
src/lib/libs/boolean.ts
Normal file
29
src/lib/libs/boolean.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { func } from "./primordials.ts";
|
||||||
|
import { unwrapThis, valueKey } from "./utils.ts";
|
||||||
|
|
||||||
|
export const Boolean = (() => {
|
||||||
|
class Boolean {
|
||||||
|
[valueKey]!: boolean;
|
||||||
|
|
||||||
|
public toString() {
|
||||||
|
return "" + unwrapThis(this, "boolean", Boolean, "Boolean.prototype.toString");
|
||||||
|
}
|
||||||
|
public valueOf() {
|
||||||
|
return unwrapThis(this, "boolean", Boolean, "Boolean.prototype.valueOf");
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor(value?: unknown) {
|
||||||
|
if (func.invokeType(arguments, this) === "call") {
|
||||||
|
if (arguments.length === 0) return false as any;
|
||||||
|
else return !!value as any;
|
||||||
|
}
|
||||||
|
this[valueKey] = (Boolean as any)(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
func.setCallable(Boolean, true);
|
||||||
|
func.setConstructable(Boolean, true);
|
||||||
|
|
||||||
|
return Boolean as any as typeof Boolean & ((value?: unknown) => symbol);
|
||||||
|
})();
|
||||||
|
export type Boolean = InstanceType<typeof Boolean>;
|
||||||
11
src/lib/libs/console.ts
Normal file
11
src/lib/libs/console.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { func, json, object } from "./primordials";
|
||||||
|
|
||||||
|
export const console = {};
|
||||||
|
|
||||||
|
function method(name: string, func: Function) {
|
||||||
|
object.defineField(console, name, { c: true, e: false, w: true, v: func });
|
||||||
|
}
|
||||||
|
|
||||||
|
method("log", function log() {
|
||||||
|
func.invoke(print, null, arguments as any);
|
||||||
|
});
|
||||||
20
src/lib/libs/date.ts
Normal file
20
src/lib/libs/date.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
import { now, symbol } from "./primordials.ts";
|
||||||
|
|
||||||
|
const timeKey: unique symbol = symbol.makeSymbol("") as any;
|
||||||
|
|
||||||
|
export const Date = (() => {
|
||||||
|
class Date {
|
||||||
|
[timeKey]!: number;
|
||||||
|
|
||||||
|
public constructor() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public static now() {
|
||||||
|
return now();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Date as any as typeof Date & ((val?: unknown) => string);
|
||||||
|
})();
|
||||||
|
export type Date = InstanceType<typeof Date>;
|
||||||
39
src/lib/libs/errors.ts
Normal file
39
src/lib/libs/errors.ts
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
import { func, object } from "./primordials.ts";
|
||||||
|
import { String } from "./string.ts";
|
||||||
|
|
||||||
|
export class Error {
|
||||||
|
public declare name: string;
|
||||||
|
public declare message: string;
|
||||||
|
|
||||||
|
public toString() {
|
||||||
|
let res = this.name || "Error";
|
||||||
|
const msg = this.message;
|
||||||
|
|
||||||
|
if (msg) res += ": " + msg;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor (msg = "") {
|
||||||
|
if (func.invokeType(arguments, this) === "call") return new Error(msg);
|
||||||
|
this.message = String(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object.defineField(Error.prototype, "name", { c: true, e: false, w: true, v: "Error" });
|
||||||
|
object.defineField(Error.prototype, "message", { c: true, e: false, w: true, v: "" });
|
||||||
|
func.setCallable(Error, true);
|
||||||
|
func.setConstructable(Error, true);
|
||||||
|
|
||||||
|
export class SyntaxError extends Error { }
|
||||||
|
object.defineField(SyntaxError.prototype, "name", { c: true, e: false, w: true, v: "SyntaxError" });
|
||||||
|
func.setCallable(SyntaxError, true);
|
||||||
|
func.setConstructable(SyntaxError, true);
|
||||||
|
|
||||||
|
export class TypeError extends Error { }
|
||||||
|
object.defineField(TypeError.prototype, "name", { c: true, e: false, w: true, v: "TypeError" });
|
||||||
|
func.setCallable(TypeError, true);
|
||||||
|
func.setConstructable(TypeError, true);
|
||||||
|
|
||||||
|
export class RangeError extends Error { }
|
||||||
|
object.defineField(RangeError.prototype, "name", { c: true, e: false, w: true, v: "RangeError" });
|
||||||
|
func.setCallable(RangeError, true);
|
||||||
|
func.setConstructable(RangeError, true);
|
||||||
82
src/lib/libs/function.ts
Normal file
82
src/lib/libs/function.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import { compile, func, string } from "./primordials.ts";
|
||||||
|
import { String } from "./string.ts";
|
||||||
|
|
||||||
|
export const Function = (() => {
|
||||||
|
class Function {
|
||||||
|
declare public readonly name: string;
|
||||||
|
declare public readonly length: number;
|
||||||
|
|
||||||
|
public toString(this: Function) {
|
||||||
|
if (this.name !== "") return "function " + this.name + "(...) { ... }";
|
||||||
|
else return "function (...) { ... }";
|
||||||
|
}
|
||||||
|
public valueOf() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public apply(this: (...args: any) => any, self: any, args: any[]) {
|
||||||
|
return func.invoke(this, self, args);
|
||||||
|
}
|
||||||
|
public call(this: (...args: any) => any, self: any) {
|
||||||
|
const args: any[] = [];
|
||||||
|
for (let i = arguments.length - 1; i >= 1; i--) args[i - 1] = arguments[i];
|
||||||
|
return func.invoke(this, self, args);
|
||||||
|
}
|
||||||
|
public bind(this: (...args: any) => any, self: any) {
|
||||||
|
const cb = this;
|
||||||
|
if (arguments.length === 0) return function (this: any) { return func.invoke(cb, this, arguments as any) };
|
||||||
|
if (arguments.length <= 1) return function () { return func.invoke(cb, self, arguments as any); }
|
||||||
|
|
||||||
|
const base: any[] = [];
|
||||||
|
const offset = arguments.length - 1;
|
||||||
|
base.length = offset;
|
||||||
|
|
||||||
|
for (let i = 0; i < offset; i++) base[i] = arguments[i + 1];
|
||||||
|
|
||||||
|
return function () {
|
||||||
|
for (let i = 0; i < arguments.length; i++) {
|
||||||
|
base[offset + i] = arguments[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return func.invoke(cb, self, base);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor () {
|
||||||
|
const parts = ["(function anonymous("];
|
||||||
|
for (let i = 0; i < arguments.length - 1; i++) {
|
||||||
|
if (i > 0) parts[parts.length] = ",";
|
||||||
|
parts[parts.length] = arguments[i];
|
||||||
|
}
|
||||||
|
parts[parts.length] = "){\n";
|
||||||
|
parts[parts.length] = String(arguments[arguments.length - 1]);
|
||||||
|
parts[parts.length] = "\n})";
|
||||||
|
var res = compile(string.stringBuild(parts))();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static compile(src = "", { globals = [], wrap = false }: { globals?: string[], wrap?: boolean } = {}) {
|
||||||
|
const parts = [];
|
||||||
|
|
||||||
|
if (wrap) parts[parts.length] = "return (function() {\n";
|
||||||
|
if (globals.length > 0) {
|
||||||
|
parts[parts.length] = "let {";
|
||||||
|
for (let i = 0; i < globals.length; i++) {
|
||||||
|
if (i > 0) parts[parts.length] = ",";
|
||||||
|
parts[parts.length] = globals[i];
|
||||||
|
}
|
||||||
|
parts[parts.length] = "} = arguments[0];";
|
||||||
|
}
|
||||||
|
parts[parts.length] = src;
|
||||||
|
if (wrap) parts[parts.length] = "\n})(arguments[0])";
|
||||||
|
|
||||||
|
const res = compile(string.stringBuild(parts));
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func.setCallable(Function, true);
|
||||||
|
func.setConstructable(Function, true);
|
||||||
|
|
||||||
|
return Function as any as typeof Function & ((value?: unknown) => (...args: any[]) => any);
|
||||||
|
})();
|
||||||
15
src/lib/libs/json.ts
Normal file
15
src/lib/libs/json.ts
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import { json, object } from "./primordials";
|
||||||
|
|
||||||
|
export const JSON = {};
|
||||||
|
|
||||||
|
function method(name: string, func: Function) {
|
||||||
|
object.defineField(JSON, name, { c: true, e: false, w: true, v: func });
|
||||||
|
}
|
||||||
|
|
||||||
|
method("parse", function parse(val: string) {
|
||||||
|
return json.parse(val);
|
||||||
|
});
|
||||||
|
|
||||||
|
method("stringify", function stringify(val: string) {
|
||||||
|
return json.stringify(val);
|
||||||
|
});
|
||||||
128
src/lib/libs/map.ts
Normal file
128
src/lib/libs/map.ts
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
import { Array } from "./array.ts";
|
||||||
|
import { func, map, symbol } from "./primordials.ts";
|
||||||
|
import { symbols } from "./utils.ts";
|
||||||
|
|
||||||
|
const mapKey: unique symbol = symbol.makeSymbol("Map.impl") as any;
|
||||||
|
|
||||||
|
export class Map<K, V> {
|
||||||
|
private [mapKey]: InstanceType<typeof map>;
|
||||||
|
|
||||||
|
public get size() {
|
||||||
|
return this[mapKey].size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get(key: K): V {
|
||||||
|
return this[mapKey].get(key);
|
||||||
|
}
|
||||||
|
public has(key: K): boolean {
|
||||||
|
return this[mapKey].has(key);
|
||||||
|
}
|
||||||
|
public set(key: K, val: V) {
|
||||||
|
this[mapKey].set(key, val);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public delete(key: K): boolean {
|
||||||
|
if (!this[mapKey].has(key)) return false;
|
||||||
|
else {
|
||||||
|
this[mapKey].delete(key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public clear() {
|
||||||
|
this[mapKey].clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public keys(): K[] {
|
||||||
|
return this[mapKey].keys();
|
||||||
|
}
|
||||||
|
public values(): V[] {
|
||||||
|
const res = this[mapKey].keys();
|
||||||
|
for (let i = 0; i < res.length; i++) {
|
||||||
|
res[i] = this[mapKey].get(res[i]);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
public entries(): [K, V][] {
|
||||||
|
const res = this[mapKey].keys();
|
||||||
|
for (let i = 0; i < res.length; i++) {
|
||||||
|
res[i] = [res[i], this[mapKey].get(res[i])];
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public forEach(cb: Function, self?: any) {
|
||||||
|
const entries = this.entries();
|
||||||
|
for (let i = 0; i < entries.length; i++) {
|
||||||
|
func.invoke(cb, self, [entries[i][1], entries[i][0], this]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public [symbols.iterator](): Iterator<[K, V]> {
|
||||||
|
return func.invoke(Array.prototype[symbols.iterator], this.entries(), []) as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor(iterable?: Iterable<[K, V]>) {
|
||||||
|
const _map = this[mapKey] = new map();
|
||||||
|
|
||||||
|
if (iterable != null) {
|
||||||
|
if (Array.isArray(iterable)) {
|
||||||
|
for (let i = 0; i < iterable.length; i++) {
|
||||||
|
if (!(i in iterable)) continue;
|
||||||
|
_map.set(iterable[i][0], iterable[i][1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const it = (iterable as any)[symbols.iterator]();
|
||||||
|
for (let val = it.next(); !val.done; val = it.next()) {
|
||||||
|
_map.set(val.value[0], val.value[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class WeakMap<K, V> {
|
||||||
|
private [mapKey]: InstanceType<typeof map>;
|
||||||
|
|
||||||
|
public get(key: K): V {
|
||||||
|
return this[mapKey].get(key);
|
||||||
|
}
|
||||||
|
public has(key: K): boolean {
|
||||||
|
return this[mapKey].has(key);
|
||||||
|
}
|
||||||
|
public set(key: K, val: V) {
|
||||||
|
this[mapKey].set(key, val);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public delete(key: K): boolean {
|
||||||
|
if (!this[mapKey].has(key)) return false;
|
||||||
|
else {
|
||||||
|
this[mapKey].delete(key);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public clear() {
|
||||||
|
this[mapKey].clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor(iterable?: Iterable<[K, V]>) {
|
||||||
|
const _map = this[mapKey] = new map(true);
|
||||||
|
|
||||||
|
if (iterable != null) {
|
||||||
|
if (Array.isArray(iterable)) {
|
||||||
|
for (let i = 0; i < iterable.length; i++) {
|
||||||
|
if (!(i in iterable)) continue;
|
||||||
|
_map.set(iterable[i][0], iterable[i][1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const it = (iterable as any)[symbols.iterator]();
|
||||||
|
for (let val = it.next(); !val.done; val = it.next()) {
|
||||||
|
_map.set(val.value[0], val.value[1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func.setCallable(Map, false);
|
||||||
|
func.setCallable(WeakMap, false);
|
||||||
60
src/lib/libs/math.ts
Normal file
60
src/lib/libs/math.ts
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
import { number, object } from "./primordials";
|
||||||
|
|
||||||
|
export const Math = {};
|
||||||
|
|
||||||
|
function method(name: string, func: Function) {
|
||||||
|
object.defineField(Math, name, { c: true, e: false, w: true, v: func });
|
||||||
|
}
|
||||||
|
|
||||||
|
method("max", function max() {
|
||||||
|
let res = -number.Infinity;
|
||||||
|
|
||||||
|
for (let i = 0; i < arguments.length; i++) {
|
||||||
|
if (res < arguments[i]) res = arguments[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
|
||||||
|
method("min", function min() {
|
||||||
|
let res = +number.Infinity;
|
||||||
|
|
||||||
|
for (let i = 0; i < arguments.length; i++) {
|
||||||
|
if (res > arguments[i]) res = arguments[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
});
|
||||||
|
|
||||||
|
method("abs", function abs(val: number) {
|
||||||
|
val = +val;
|
||||||
|
if (val < 0) return -val;
|
||||||
|
else return val;
|
||||||
|
});
|
||||||
|
|
||||||
|
method("floor", function floor(val: number) {
|
||||||
|
val = val - 0;
|
||||||
|
if (number.isNaN(val)) return number.NaN;
|
||||||
|
|
||||||
|
let rem = val % 1;
|
||||||
|
if (rem < 0) rem += 1;
|
||||||
|
|
||||||
|
return val - rem;
|
||||||
|
});
|
||||||
|
method("ceil", function floor(val: number) {
|
||||||
|
val = val - 0;
|
||||||
|
if (number.isNaN(val)) return number.NaN;
|
||||||
|
|
||||||
|
let rem = val % 1;
|
||||||
|
if (rem === 0) return val;
|
||||||
|
if (rem < 0) rem += 1;
|
||||||
|
|
||||||
|
return val + (1 - rem);
|
||||||
|
});
|
||||||
|
|
||||||
|
method("pow", function pow(a: number, b: number) {
|
||||||
|
return number.pow(a, b);
|
||||||
|
});
|
||||||
|
method("log", function log(val: number) {
|
||||||
|
return number.log(val);
|
||||||
|
});
|
||||||
77
src/lib/libs/number.ts
Normal file
77
src/lib/libs/number.ts
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
import { func, number, object } from "./primordials.ts";
|
||||||
|
import { unwrapThis, valueKey } from "./utils.ts";
|
||||||
|
|
||||||
|
export const Number = (() => {
|
||||||
|
class Number {
|
||||||
|
[valueKey]!: number;
|
||||||
|
|
||||||
|
public toString() {
|
||||||
|
return "" + unwrapThis(this, "number", Number, "Number.prototype.toString");
|
||||||
|
}
|
||||||
|
public valueOf() {
|
||||||
|
return unwrapThis(this, "number", Number, "Number.prototype.toString");
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor (value?: unknown) {
|
||||||
|
if (func.invokeType(arguments, this) === "call") {
|
||||||
|
if (arguments.length === 0) return 0 as any;
|
||||||
|
else return +(value as any) as any;
|
||||||
|
}
|
||||||
|
this[valueKey] = (Number as any)(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static isFinite(value: number) {
|
||||||
|
value = unwrapThis(value, "number", Number, "Number.isFinite", "value");
|
||||||
|
if (value === undefined || value !== value) return false;
|
||||||
|
if (value === number.Infinity || value === -number.Infinity) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
public static isInteger(value: number) {
|
||||||
|
value = unwrapThis(value, "number", Number, "Number.isInteger", "value");
|
||||||
|
if (value === undefined) return false;
|
||||||
|
return number.parseInt(value) === value;
|
||||||
|
}
|
||||||
|
public static isNaN(value: number) {
|
||||||
|
return number.isNaN(value);
|
||||||
|
}
|
||||||
|
public static isSafeInteger(value: number) {
|
||||||
|
value = unwrapThis(value, "number", Number, "Number.isSafeInteger", "value");
|
||||||
|
if (value === undefined || number.parseInt(value) !== value) return false;
|
||||||
|
return value >= -9007199254740991 && value <= 9007199254740991;
|
||||||
|
}
|
||||||
|
public static parseFloat(value: unknown) {
|
||||||
|
if (typeof value === "number") return value;
|
||||||
|
else return number.parseFloat(value + "");
|
||||||
|
}
|
||||||
|
public static parseInt(value: unknown, radix = 10) {
|
||||||
|
radix = +radix;
|
||||||
|
if (number.isNaN(radix)) radix = 10;
|
||||||
|
|
||||||
|
if (typeof value === "number") return number.parseInt(value, radix);
|
||||||
|
else return number.parseInt(value + "", radix);
|
||||||
|
}
|
||||||
|
|
||||||
|
declare public static readonly EPSILON: number;
|
||||||
|
declare public static readonly MIN_SAFE_INTEGER: number;
|
||||||
|
declare public static readonly MAX_SAFE_INTEGER: number;
|
||||||
|
declare public static readonly POSITIVE_INFINITY: number;
|
||||||
|
declare public static readonly NEGATIVE_INFINITY: number;
|
||||||
|
declare public static readonly NaN: number;
|
||||||
|
declare public static readonly MAX_VALUE: number;
|
||||||
|
declare public static readonly MIN_VALUE: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
object.defineField(Number, "EPSILON", { c: false, e: false, w: false, v: 2.220446049250313e-16 });
|
||||||
|
object.defineField(Number, "MIN_SAFE_INTEGER", { c: false, e: false, w: false, v: -9007199254740991 });
|
||||||
|
object.defineField(Number, "MAX_SAFE_INTEGER", { c: false, e: false, w: false, v: 9007199254740991 });
|
||||||
|
object.defineField(Number, "POSITIVE_INFINITY", { c: false, e: false, w: false, v: +number.Infinity });
|
||||||
|
object.defineField(Number, "NEGATIVE_INFINITY", { c: false, e: false, w: false, v: -number.Infinity });
|
||||||
|
object.defineField(Number, "NaN", { c: false, e: false, w: false, v: number.NaN });
|
||||||
|
object.defineField(Number, "MAX_VALUE", { c: false, e: false, w: false, v: 1.7976931348623157e+308 });
|
||||||
|
object.defineField(Number, "MIN_VALUE", { c: false, e: false, w: false, v: 5e-324 });
|
||||||
|
func.setCallable(Number, true);
|
||||||
|
func.setConstructable(Number, true);
|
||||||
|
|
||||||
|
return Number as any as typeof Number & ((value?: unknown) => number);
|
||||||
|
})();
|
||||||
|
export type Number = InstanceType<typeof Number>;
|
||||||
200
src/lib/libs/object.ts
Normal file
200
src/lib/libs/object.ts
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
import { Boolean } from "./boolean.ts";
|
||||||
|
import { TypeError } from "./errors.ts";
|
||||||
|
import { Number } from "./number.ts";
|
||||||
|
import { func, object } from "./primordials.ts";
|
||||||
|
import { String } from "./string.ts";
|
||||||
|
import { symbols, valueKey } from "./utils.ts";
|
||||||
|
import { Symbol } from "./symbol.ts";
|
||||||
|
|
||||||
|
export const Object = (() => {
|
||||||
|
class Object {
|
||||||
|
public toString(this: unknown) {
|
||||||
|
if (this === undefined) return "[object Undefined]";
|
||||||
|
else if (this === null) return "[object Null]";
|
||||||
|
else if (typeof this === "object") {
|
||||||
|
if (symbols.toStringTag in this) return "[object " + (this as any)[symbols.toStringTag] + "]";
|
||||||
|
else if (object.isArray(this)) return "[object Array]";
|
||||||
|
else return "[object Object]";
|
||||||
|
}
|
||||||
|
else if (typeof this === "number" || this instanceof Number) return "[object Number]";
|
||||||
|
else if (typeof this === "symbol" || this instanceof Symbol) return "[object Symbol]";
|
||||||
|
else if (typeof this === "string" || this instanceof String) return "[object String]";
|
||||||
|
else if (typeof this === "boolean" || this instanceof Boolean) return "[object Boolean]";
|
||||||
|
else if (typeof this === "function") return "[object Function]";
|
||||||
|
}
|
||||||
|
public valueOf() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public hasOwnProperty(key: string) {
|
||||||
|
return object.getOwnMember(this, key) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor (value?: unknown) {
|
||||||
|
if (typeof value === 'object' && value !== null) return value as any;
|
||||||
|
if (typeof value === 'string') return new String(value) as any;
|
||||||
|
if (typeof value === 'number') return new Number(value) as any;
|
||||||
|
if (typeof value === 'boolean') return new Boolean(value) as any;
|
||||||
|
if (typeof value === 'symbol') {
|
||||||
|
const res: Symbol = {} as any;
|
||||||
|
object.setPrototype(res, Symbol.prototype);
|
||||||
|
res[valueKey] = value;
|
||||||
|
return res as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {} as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static getOwnPropertyDescriptor(obj: object, key: any) {
|
||||||
|
return object.getOwnMember(obj, key);
|
||||||
|
}
|
||||||
|
public static getOwnPropertyNames(obj: object): string[] {
|
||||||
|
return object.getOwnMembers(obj, false);
|
||||||
|
}
|
||||||
|
public static getOwnPropertySymbols(obj: object): symbol[] {
|
||||||
|
return object.getOwnSymbolMembers(obj, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static defineProperty(obj: object, key: string | symbol, desc: PropertyDescriptor) {
|
||||||
|
if (obj === null || typeof obj !== "function" && typeof obj !== "object") {
|
||||||
|
throw new TypeError("Object.defineProperty called on non-object");
|
||||||
|
}
|
||||||
|
if (desc === null || typeof desc !== "function" && typeof desc !== "object") {
|
||||||
|
throw new TypeError("Property description must be an object: " + desc);
|
||||||
|
}
|
||||||
|
const res: any = {};
|
||||||
|
|
||||||
|
if ("get" in desc || "set" in desc) {
|
||||||
|
if ("get" in desc) {
|
||||||
|
const get = desc.get;
|
||||||
|
if (get !== undefined && typeof get !== "function") throw new TypeError("Getter must be a function: " + get);
|
||||||
|
res.g = get;
|
||||||
|
}
|
||||||
|
if ("set" in desc) {
|
||||||
|
const set = desc.set;
|
||||||
|
if (set !== undefined && typeof set !== "function") throw new TypeError("Setter must be a function: " + set);
|
||||||
|
res.s = set;
|
||||||
|
}
|
||||||
|
if ("enumerable" in desc) res.e = !!desc.enumerable;
|
||||||
|
if ("configurable" in desc) res.e = !!desc.configurable;
|
||||||
|
|
||||||
|
if (!object.defineProperty(obj, key, res)) throw new TypeError("Cannot redefine property: " + String(key));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if ("enumerable" in desc) res.e = !!desc.enumerable;
|
||||||
|
if ("configurable" in desc) res.e = !!desc.configurable;
|
||||||
|
if ("writable" in desc) res.w = !!desc.writable;
|
||||||
|
if ("value" in desc) res.v = desc.value;
|
||||||
|
|
||||||
|
if (!object.defineField(obj, key, res)) throw new TypeError("Cannot redefine property: " + String(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
public static defineProperties(obj: object, desc: PropertyDescriptorMap) {
|
||||||
|
const keys = object.getOwnMembers(desc, true) as ((keyof typeof obj) & string)[];
|
||||||
|
const symbols = object.getOwnSymbolMembers(desc, true) as ((keyof typeof obj) & symbol)[];
|
||||||
|
|
||||||
|
for (let i = 0; i < keys.length; i++) {
|
||||||
|
Object.defineProperty(obj, keys[i], desc[keys[i]]);
|
||||||
|
}
|
||||||
|
for (let i = 0; i < symbols.length; i++) {
|
||||||
|
Object.defineProperty(obj, symbols[i], desc[symbols[i]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
public static create(proto: object, desc?: PropertyDescriptorMap) {
|
||||||
|
let res = object.setPrototype({}, proto);
|
||||||
|
if (desc != null) this.defineProperties(res, desc);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
public static assign(target: any) {
|
||||||
|
for (let i = 1; i < arguments.length; i++) {
|
||||||
|
const obj = arguments[i];
|
||||||
|
const keys = object.getOwnMembers(obj, false);
|
||||||
|
const symbols = object.getOwnSymbolMembers(obj, false);
|
||||||
|
|
||||||
|
for (let j = 0; j < keys.length; j++) {
|
||||||
|
target[keys[j]] = obj[keys[j]];
|
||||||
|
}
|
||||||
|
for (let j = 0; j < symbols.length; j++) {
|
||||||
|
target[symbols[j]] = obj[symbols[j]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static setPrototypeOf(obj: object, proto: object | null) {
|
||||||
|
object.setPrototype(obj, proto!);
|
||||||
|
}
|
||||||
|
public static getPrototypeOf(obj: object) {
|
||||||
|
return object.getPrototype(obj) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static keys(obj: any) {
|
||||||
|
const res: any[] = [];
|
||||||
|
const keys = object.getOwnMembers(obj, true);
|
||||||
|
const symbols = object.getOwnSymbolMembers(obj, true);
|
||||||
|
|
||||||
|
for (let i = 0; i < keys.length; i++) {
|
||||||
|
res[res.length] = keys[i];
|
||||||
|
}
|
||||||
|
for (let i = 0; i < symbols.length; i++) {
|
||||||
|
res[res.length] = symbols[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
public static values(obj: any) {
|
||||||
|
const res: any[] = [];
|
||||||
|
const keys = object.getOwnMembers(obj, true);
|
||||||
|
const symbols = object.getOwnSymbolMembers(obj, true);
|
||||||
|
|
||||||
|
for (let i = 0; i < keys.length; i++) {
|
||||||
|
res[res.length] = obj[keys[i]];
|
||||||
|
}
|
||||||
|
for (let i = 0; i < symbols.length; i++) {
|
||||||
|
res[res.length] = obj[symbols[i]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
public static entries(obj: any) {
|
||||||
|
const res: [any, any][] = [];
|
||||||
|
const keys = object.getOwnMembers(obj, true);
|
||||||
|
const symbols = object.getOwnSymbolMembers(obj, true);
|
||||||
|
|
||||||
|
for (let i = 0; i < keys.length; i++) {
|
||||||
|
res[res.length] = [keys[i], obj[keys[i]]];
|
||||||
|
}
|
||||||
|
for (let i = 0; i < symbols.length; i++) {
|
||||||
|
res[res.length] = [symbols[i], obj[symbols[i]]];
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static preventExtensions(obj: object) {
|
||||||
|
object.preventExt(obj);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
public static seal(obj: object) {
|
||||||
|
object.seal(obj);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
public static freeze(obj: object) {
|
||||||
|
object.freeze(obj);
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object.setPrototype(Object.prototype, undefined);
|
||||||
|
|
||||||
|
func.setCallable(Object, true);
|
||||||
|
func.setConstructable(Object, true);
|
||||||
|
|
||||||
|
return Object as any as typeof Object & ((value?: unknown) => object);
|
||||||
|
})();
|
||||||
|
export type Object = InstanceType<typeof Object>;
|
||||||
5
src/lib/libs/polyfills/callSuper.js
Normal file
5
src/lib/libs/polyfills/callSuper.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { func, object } from "../primordials.ts";
|
||||||
|
|
||||||
|
export default function _callSuper(self, constr, args) {
|
||||||
|
return func.construct(object.getPrototype(constr), func.target(1), args || []);
|
||||||
|
}
|
||||||
5
src/lib/libs/polyfills/classCallCheck.js
Normal file
5
src/lib/libs/polyfills/classCallCheck.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { func } from "../primordials.ts";
|
||||||
|
|
||||||
|
export default function _classCallCheck() {
|
||||||
|
if (func.invokeTypeInfer() !== "new") throw new TypeError("Cannot call a class as a function");
|
||||||
|
}
|
||||||
35
src/lib/libs/polyfills/createClass.js
Normal file
35
src/lib/libs/polyfills/createClass.js
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import { object } from "../primordials.ts";
|
||||||
|
|
||||||
|
function _defineProperties(target, arr) {
|
||||||
|
if (!arr) return;
|
||||||
|
for (var i = 0; i < arr.length; i++) {
|
||||||
|
var desc = arr[i];
|
||||||
|
var res;
|
||||||
|
var w, e, c;
|
||||||
|
|
||||||
|
c = desc.configurable;
|
||||||
|
if (c == null) c = true;
|
||||||
|
e = desc.enumerable;
|
||||||
|
if (e == null) e = false;
|
||||||
|
|
||||||
|
if ("value" in desc) {
|
||||||
|
w = desc.writable;
|
||||||
|
if (w == null) w = true;
|
||||||
|
|
||||||
|
if (desc.writable == null)
|
||||||
|
res = object.defineField(target, desc.key, { w: !!w, e: !!e, c: !!c, v: desc.value });
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
res = object.defineProperty(target, desc.key, { e: !!e, c: !!c, g: desc.get, s: desc.set });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!res) throw "Couldn't set property";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function _createClass(clazz, instance, nonInstance) {
|
||||||
|
_defineProperties(clazz.prototype, instance);
|
||||||
|
_defineProperties(clazz, nonInstance);
|
||||||
|
|
||||||
|
return clazz;
|
||||||
|
}
|
||||||
7
src/lib/libs/polyfills/defineProperty.js
Normal file
7
src/lib/libs/polyfills/defineProperty.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { object } from "../primordials.ts";
|
||||||
|
|
||||||
|
export default function _defineProperty(obj, key, val) {
|
||||||
|
if (obj == null) return;
|
||||||
|
object.defineField(obj, key, { c: true, e: true, w: true, v: val });
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
5
src/lib/libs/polyfills/getPrototypeOf.js
Normal file
5
src/lib/libs/polyfills/getPrototypeOf.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { object } from "../primordials.ts";
|
||||||
|
|
||||||
|
export default function _getPrototypeOf(obj) {
|
||||||
|
return object.getPrototype(obj) || null;
|
||||||
|
}
|
||||||
11
src/lib/libs/polyfills/inherits.js
Normal file
11
src/lib/libs/polyfills/inherits.js
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { object } from "../primordials.ts";
|
||||||
|
|
||||||
|
export default function _inherits(t, e) {
|
||||||
|
if (e == null) {
|
||||||
|
object.setPrototype(t.prototype, undefined);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
object.setPrototype(t.prototype, e.prototype);
|
||||||
|
object.setPrototype(t, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
2
src/lib/libs/polyfills/possibleConstructorReturn.js
Normal file
2
src/lib/libs/polyfills/possibleConstructorReturn.js
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
export default function _possibleConstructorReturn() {
|
||||||
|
}
|
||||||
3
src/lib/libs/polyfills/readOnlyError.js
Normal file
3
src/lib/libs/polyfills/readOnlyError.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default function _readOnlyError(name) {
|
||||||
|
throw name;
|
||||||
|
}
|
||||||
5
src/lib/libs/polyfills/setPrototypeOf.js
Normal file
5
src/lib/libs/polyfills/setPrototypeOf.js
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { object } from "../primordials";
|
||||||
|
|
||||||
|
export default function _setPrototypeOf(obj, proto) {
|
||||||
|
object.setPrototype(obj, proto);
|
||||||
|
}
|
||||||
3
src/lib/libs/polyfills/typeof.js
Normal file
3
src/lib/libs/polyfills/typeof.js
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
export default function _typeof(val) {
|
||||||
|
return typeof val;
|
||||||
|
}
|
||||||
111
src/lib/libs/primordials.ts
Normal file
111
src/lib/libs/primordials.ts
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
export interface SymbolPrimordials {
|
||||||
|
makeSymbol(name: string): symbol;
|
||||||
|
getSymbol(name: string): symbol;
|
||||||
|
getSymbolKey(symbol: symbol): string | undefined;
|
||||||
|
getSymbolDescription(symbol: symbol): string;
|
||||||
|
}
|
||||||
|
export interface NumberPrimordials {
|
||||||
|
NaN: number;
|
||||||
|
Infinity: number;
|
||||||
|
PI: number;
|
||||||
|
E: number;
|
||||||
|
|
||||||
|
parseInt(raw: string | number, radix?: number): number;
|
||||||
|
parseFloat(raw: string | number): number;
|
||||||
|
isNaN(num: number): boolean;
|
||||||
|
|
||||||
|
pow(a: number, b: number): number;
|
||||||
|
log(val: number): number;
|
||||||
|
}
|
||||||
|
export interface StringPrimordials {
|
||||||
|
stringBuild(parts: string[]): string;
|
||||||
|
fromCharCode(char: number): string;
|
||||||
|
fromCodePoint(char: number): string;
|
||||||
|
toCharCode(char: string): number;
|
||||||
|
toCodePoint(char: string, i: number): number;
|
||||||
|
indexOf(str: string, search: string, start: number, reverse?: boolean): number;
|
||||||
|
substring(str: string, start: number, end: number): string;
|
||||||
|
lower(str: string): string;
|
||||||
|
upper(str: string): string;
|
||||||
|
}
|
||||||
|
export interface ObjectPrimordials {
|
||||||
|
defineProperty(obj: object, key: string | number | symbol, conf: { g?: Function, s?: Function, e?: boolean, c?: boolean }): boolean;
|
||||||
|
defineField(obj: object, key: string | number | symbol, conf: { v?: any, e?: boolean, c?: boolean, w?: boolean }): boolean;
|
||||||
|
getOwnMember(obj: object, key: any): PropertyDescriptor | undefined;
|
||||||
|
getOwnMembers(obj: object, onlyEnumerable: boolean): string[];
|
||||||
|
getOwnSymbolMembers(obj: object, onlyEnumerable: boolean): symbol[];
|
||||||
|
getPrototype(obj: object): object | undefined;
|
||||||
|
setPrototype(obj: object, proto?: object): object;
|
||||||
|
preventExt(obj: object): void;
|
||||||
|
seal(obj: object): void;
|
||||||
|
freeze(obj: object): void;
|
||||||
|
|
||||||
|
isArray(obj: any): obj is any[];
|
||||||
|
subarray(arr: any[], start: number, end: number): any[];
|
||||||
|
|
||||||
|
memcpy(src: any[], dst: any[], srcI: number, dstI: number, n: number): void;
|
||||||
|
sort(arr: any[], cb: Function): any[];
|
||||||
|
}
|
||||||
|
export interface FunctionPrimordials {
|
||||||
|
invokeType(args: IArguments, self: any): "new" | "call";
|
||||||
|
invokeTypeInfer(): "new" | "call";
|
||||||
|
target(): Function | null | undefined;
|
||||||
|
setConstructable(func: Function, flag: boolean): void;
|
||||||
|
setCallable(func: Function, flag: boolean): void;
|
||||||
|
invoke(func: Function, self: any, args: any[]): any;
|
||||||
|
construct(func: Function, self: any, args: any[]): any;
|
||||||
|
}
|
||||||
|
export interface JSONPrimordials {
|
||||||
|
parse(data: string): any;
|
||||||
|
stringify(data: any): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Primordials {
|
||||||
|
symbol: SymbolPrimordials;
|
||||||
|
number: NumberPrimordials;
|
||||||
|
string: StringPrimordials;
|
||||||
|
object: ObjectPrimordials;
|
||||||
|
function: FunctionPrimordials;
|
||||||
|
json: JSONPrimordials;
|
||||||
|
map: new (weak?: boolean) => {
|
||||||
|
get(key: any): any;
|
||||||
|
has(key: any): boolean;
|
||||||
|
set(key: any, val: any): void;
|
||||||
|
delete(key: any): void;
|
||||||
|
keys(): any[];
|
||||||
|
clear(): void;
|
||||||
|
size(): number;
|
||||||
|
};
|
||||||
|
|
||||||
|
regex: new (source: string, multiline?: boolean, noCase?: boolean, dotall?: boolean, unicode?: boolean, unicodeClass?: boolean) => {
|
||||||
|
exec(target: string, offset: number, indices: boolean): { matches: RegExpMatchArray, end: number } | null;
|
||||||
|
groupCount(): number;
|
||||||
|
};
|
||||||
|
compile(src: string): Function;
|
||||||
|
setGlobalPrototypes(prototype: Record<string, any>): void;
|
||||||
|
now(): number;
|
||||||
|
next(func: () => void): void;
|
||||||
|
schedule(func: () => void, delay: number): () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
globalThis.undefined = void 0;
|
||||||
|
export const target = (globalThis as any).target;
|
||||||
|
export const primordials: Primordials = (globalThis as any).primordials;
|
||||||
|
|
||||||
|
export const {
|
||||||
|
symbol,
|
||||||
|
number,
|
||||||
|
string,
|
||||||
|
object,
|
||||||
|
function: func,
|
||||||
|
json,
|
||||||
|
map,
|
||||||
|
regex,
|
||||||
|
setGlobalPrototypes,
|
||||||
|
compile,
|
||||||
|
now,
|
||||||
|
next,
|
||||||
|
schedule,
|
||||||
|
} = primordials;
|
||||||
|
|
||||||
|
export type regex = InstanceType<typeof regex>;
|
||||||
154
src/lib/libs/promise.ts
Normal file
154
src/lib/libs/promise.ts
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
import { func, next, object, symbol } from "./primordials.ts";
|
||||||
|
|
||||||
|
enum PromiseState {
|
||||||
|
Pending = "pend",
|
||||||
|
Fulfilled = "ful",
|
||||||
|
Rejected = "rej",
|
||||||
|
}
|
||||||
|
|
||||||
|
const pState: unique symbol = symbol.makeSymbol("Promise.state") as any;
|
||||||
|
const pValue: unique symbol = symbol.makeSymbol("Promise.value") as any;
|
||||||
|
const pFulHandles: unique symbol = symbol.makeSymbol("Promise.fulfillHandles") as any;
|
||||||
|
const pRejHandles: unique symbol = symbol.makeSymbol("Promise.rejectHandles") as any;
|
||||||
|
|
||||||
|
function makePromise<T>(): Promise<T> {
|
||||||
|
return object.setPrototype({
|
||||||
|
[pState]: PromiseState.Pending,
|
||||||
|
[pFulHandles]: [],
|
||||||
|
[pRejHandles]: [],
|
||||||
|
}, Promise.prototype) as Promise<T>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function fulfill(self: Promise<any>, val: any) {
|
||||||
|
if (self[pState] !== PromiseState.Pending) return;
|
||||||
|
if (self === val) throw new Error("A promise may not be fulfilled with itself");
|
||||||
|
|
||||||
|
if (val != null && typeof val.then === "function") {
|
||||||
|
val.then(
|
||||||
|
(val: any) => fulfill(self, val),
|
||||||
|
(err: any) => reject(self, err),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self[pValue] = val;
|
||||||
|
self[pState] = PromiseState.Fulfilled;
|
||||||
|
|
||||||
|
const handles = self[pFulHandles]!;
|
||||||
|
|
||||||
|
for (let i = 0; i < handles.length; i++) {
|
||||||
|
handles[i](val);
|
||||||
|
}
|
||||||
|
|
||||||
|
self[pFulHandles] = undefined;
|
||||||
|
self[pRejHandles] = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function reject(self: Promise<any>, val: any) {
|
||||||
|
if (self[pState] !== PromiseState.Pending) return;
|
||||||
|
if (self === val) throw new Error("A promise may not be rejected with itself");
|
||||||
|
|
||||||
|
if (val != null && typeof val.then === "function") {
|
||||||
|
val.then(
|
||||||
|
(val: any) => reject(self, val),
|
||||||
|
(err: any) => reject(self, err),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
self[pValue] = val;
|
||||||
|
self[pState] = PromiseState.Rejected;
|
||||||
|
|
||||||
|
const handles = self[pRejHandles]!;
|
||||||
|
|
||||||
|
for (let i = 0; i < handles.length; i++) {
|
||||||
|
handles[i](val);
|
||||||
|
}
|
||||||
|
|
||||||
|
self[pFulHandles] = undefined;
|
||||||
|
self[pRejHandles] = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function handle<T>(self: Promise<T>, ful?: (val: T) => void, rej?: (err: any) => void) {
|
||||||
|
if (self[pState] === PromiseState.Pending) {
|
||||||
|
if (ful != null) {
|
||||||
|
self[pFulHandles]![self[pFulHandles]!.length] = ful;
|
||||||
|
}
|
||||||
|
if (rej != null) {
|
||||||
|
self[pRejHandles]![self[pRejHandles]!.length] = rej;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (self[pState] === PromiseState.Fulfilled) {
|
||||||
|
if (ful != null) ful(self[pValue] as T);
|
||||||
|
}
|
||||||
|
else if (self[pState] === PromiseState.Rejected) {
|
||||||
|
if (rej != null) rej(self[pValue]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class Promise<T> {
|
||||||
|
public [pState]: PromiseState;
|
||||||
|
public [pValue]?: T | unknown;
|
||||||
|
public [pFulHandles]?: ((val: T) => void)[] = [];
|
||||||
|
public [pRejHandles]?: ((val: T) => void)[] = [];
|
||||||
|
|
||||||
|
public then<Res>(ful?: (val: T) => Res, rej?: (err: any) => Res) {
|
||||||
|
if (typeof ful !== "function") ful = undefined;
|
||||||
|
if (typeof rej !== "function") rej = undefined;
|
||||||
|
|
||||||
|
const promise = makePromise<Res>();
|
||||||
|
|
||||||
|
handle(this,
|
||||||
|
val => next(() => {
|
||||||
|
if (ful == null) fulfill(promise, val);
|
||||||
|
else {
|
||||||
|
try { fulfill(promise, ful(val)); }
|
||||||
|
catch (e) { reject(promise, e); }
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
err => next(() => {
|
||||||
|
if (rej == null) reject(promise, err);
|
||||||
|
else {
|
||||||
|
try { fulfill(promise, rej(err)); }
|
||||||
|
catch (e) { reject(promise, e); }
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return promise;
|
||||||
|
}
|
||||||
|
public catch<Res>(rej?: (err: any) => Res) {
|
||||||
|
return this.then(undefined, rej);
|
||||||
|
}
|
||||||
|
public finally(fn?: () => void) {
|
||||||
|
if (typeof fn !== "function") return this["then"]();
|
||||||
|
|
||||||
|
return this.then(
|
||||||
|
v => {
|
||||||
|
fn();
|
||||||
|
return v;
|
||||||
|
},
|
||||||
|
v => {
|
||||||
|
fn();
|
||||||
|
throw v;
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor(fn: (fulfil: (val: T) => void, reject: (err: unknown) => void) => void) {
|
||||||
|
this[pState] = PromiseState.Pending;
|
||||||
|
|
||||||
|
fn(val => fulfill(this, val), err => reject(this, err));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static resolve(val: any) {
|
||||||
|
const res = makePromise();
|
||||||
|
fulfill(res, val);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
public static reject(val: any) {
|
||||||
|
const res = makePromise();
|
||||||
|
reject(res, val);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func.setCallable(Promise, false);
|
||||||
138
src/lib/libs/regex.ts
Normal file
138
src/lib/libs/regex.ts
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
import { func, regex, symbol } from "./primordials.ts";
|
||||||
|
import { String } from "./string.ts";
|
||||||
|
import { type ReplaceRange } from "./utils.ts";
|
||||||
|
import { applyReplaces } from "./utils.ts";
|
||||||
|
import { applySplits } from "./utils.ts";
|
||||||
|
import { symbols } from "./utils.ts";
|
||||||
|
|
||||||
|
const regexKey: unique symbol = symbol.makeSymbol("RegExp.impl") as any;
|
||||||
|
|
||||||
|
export class RegExp {
|
||||||
|
private [regexKey]!: InstanceType<typeof regex>;
|
||||||
|
|
||||||
|
public readonly source!: string;
|
||||||
|
public readonly flags!: string;
|
||||||
|
public lastIndex = 0;
|
||||||
|
|
||||||
|
public readonly indices!: boolean;
|
||||||
|
public readonly global!: boolean;
|
||||||
|
public readonly ignoreCase!: boolean;
|
||||||
|
public readonly multiline!: boolean;
|
||||||
|
public readonly dotall!: boolean;
|
||||||
|
public readonly unicode!: boolean;
|
||||||
|
public readonly unicodeSets!: boolean;
|
||||||
|
public readonly sticky!: boolean;
|
||||||
|
|
||||||
|
public constructor(source: any, flags = "") {
|
||||||
|
if (func.invokeType(arguments, this) === "call") return new RegExp(source, flags);
|
||||||
|
|
||||||
|
source = this.source = String(typeof source === "object" && "source" in source ? source.source : source);
|
||||||
|
flags = String(flags);
|
||||||
|
|
||||||
|
let indices = false;
|
||||||
|
let global = false;
|
||||||
|
let ignoreCase = false;
|
||||||
|
let multiline = false;
|
||||||
|
let dotall = false;
|
||||||
|
let unicode = false;
|
||||||
|
let unicodeSets = false;
|
||||||
|
let sticky = false;
|
||||||
|
|
||||||
|
for (let i = 0; i < flags.length; i++) {
|
||||||
|
switch (flags[i]) {
|
||||||
|
case "d": indices = true; break;
|
||||||
|
case "g": global = true; break;
|
||||||
|
case "i": ignoreCase = true; break;
|
||||||
|
case "m": multiline = true; break;
|
||||||
|
case "s": dotall = true; break;
|
||||||
|
case "u": unicode = true; break;
|
||||||
|
case "v": unicodeSets = true; break;
|
||||||
|
case "y": sticky = true; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
flags = "";
|
||||||
|
if (indices) flags += "d";
|
||||||
|
if (global) flags += "g";
|
||||||
|
if (ignoreCase) flags += "i";
|
||||||
|
if (multiline) flags += "m";
|
||||||
|
if (dotall) flags += "s";
|
||||||
|
if (unicode) flags += "u";
|
||||||
|
if (unicodeSets) flags += "v";
|
||||||
|
if (sticky) flags += "y";
|
||||||
|
this.flags = flags;
|
||||||
|
this.indices = indices;
|
||||||
|
this.global = global;
|
||||||
|
this.ignoreCase = ignoreCase;
|
||||||
|
this.multiline = multiline;
|
||||||
|
this.dotall = dotall;
|
||||||
|
this.unicode = unicode;
|
||||||
|
this.unicodeSets = unicodeSets;
|
||||||
|
this.sticky = sticky;
|
||||||
|
|
||||||
|
this[regexKey] = new regex(source, multiline, ignoreCase, dotall, unicode, unicodeSets);
|
||||||
|
}
|
||||||
|
|
||||||
|
public exec(target: string) {
|
||||||
|
const useLast = this.global || this.sticky;
|
||||||
|
const start = useLast ? this.lastIndex : 0;
|
||||||
|
|
||||||
|
const match = this[regexKey].exec(target, start, this.indices);
|
||||||
|
if (match != null && !(this.sticky && match.matches.index !== start)) {
|
||||||
|
if (useLast) this.lastIndex = match.end;
|
||||||
|
return match.matches;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useLast) this.lastIndex = 0;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
public test(target: string) {
|
||||||
|
return this.exec(target) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public [symbols.split](target: string, limit?: number) {
|
||||||
|
return applySplits(target, limit, offset => {
|
||||||
|
const val = this[regexKey].exec(target, offset, false);
|
||||||
|
if (val == null) return undefined;
|
||||||
|
|
||||||
|
return { start: val.matches.index!, end: val.end };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public [symbols.replace](target: string, replacer: any) {
|
||||||
|
const matches: ReplaceRange[] = [];
|
||||||
|
const regex = this[regexKey];
|
||||||
|
|
||||||
|
if (this.global) {
|
||||||
|
let offset = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const match = regex.exec(target, offset, false);
|
||||||
|
if (match == null) break;
|
||||||
|
|
||||||
|
const start = match.matches.index;
|
||||||
|
const end = match.end;
|
||||||
|
const arr: string[] = [];
|
||||||
|
for (let i = 0; i < match.matches.length; i++) {
|
||||||
|
arr[i] = match.matches[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
matches[matches.length] = { start: match.matches.index!, end: match.end, matches: arr };
|
||||||
|
|
||||||
|
if (start === end) offset = start + 1;
|
||||||
|
else offset = end;
|
||||||
|
}
|
||||||
|
|
||||||
|
return applyReplaces(target, matches, replacer, regex.groupCount() + 1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const match = this.exec(target);
|
||||||
|
if (match != null) matches[0] = {
|
||||||
|
start: match.index!,
|
||||||
|
end: match.index! + match[0].length,
|
||||||
|
matches: match,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return applyReplaces(target, matches, replacer, regex.groupCount() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
121
src/lib/libs/set.ts
Normal file
121
src/lib/libs/set.ts
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
import { Array } from "./array.ts";
|
||||||
|
import { func, map, symbol } from "./primordials.ts";
|
||||||
|
import { symbols } from "./utils.ts";
|
||||||
|
|
||||||
|
const mapKey: unique symbol = symbol.makeSymbol("Set.impl") as any;
|
||||||
|
|
||||||
|
export class Set<T> {
|
||||||
|
private [mapKey]: InstanceType<typeof map>;
|
||||||
|
|
||||||
|
public get size() {
|
||||||
|
return this[mapKey].size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public has(key: T): boolean {
|
||||||
|
return this[mapKey].has(key);
|
||||||
|
}
|
||||||
|
public add(val: T) {
|
||||||
|
this[mapKey].set(val, true);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public delete(val: T): boolean {
|
||||||
|
if (!this[mapKey].has(val)) return false;
|
||||||
|
else {
|
||||||
|
this[mapKey].delete(val);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public clear() {
|
||||||
|
this[mapKey].clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public keys(): T[] {
|
||||||
|
return this[mapKey].keys();
|
||||||
|
}
|
||||||
|
public values(): T[] {
|
||||||
|
return this[mapKey].keys();
|
||||||
|
}
|
||||||
|
public entries(): [T, T][] {
|
||||||
|
const res = this[mapKey].keys();
|
||||||
|
|
||||||
|
for (let i = 0; i < res.length; i++) {
|
||||||
|
res[i] = [res[i], res[i]];
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public forEach(cb: Function, self?: any) {
|
||||||
|
const vals = this.values();
|
||||||
|
|
||||||
|
for (let i = 0; i < vals.length; i++) {
|
||||||
|
func.invoke(cb, self, [vals[i], vals[i], this]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public [symbols.iterator](): Iterator<T> {
|
||||||
|
return func.invoke(Array.prototype[symbols.iterator], this.values(), []) as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor(iterable?: Iterable<T>) {
|
||||||
|
const _map = this[mapKey] = new map();
|
||||||
|
|
||||||
|
if (iterable != null) {
|
||||||
|
if (Array.isArray(iterable)) {
|
||||||
|
for (let i = 0; i < iterable.length; i++) {
|
||||||
|
if (!(i in iterable)) continue;
|
||||||
|
_map.set(iterable[i], true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const it = (iterable as any)[symbols.iterator]();
|
||||||
|
for (let val = it.next(); !val.done; val = it.next()) {
|
||||||
|
_map.set(val.value, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class WeakSet<T> {
|
||||||
|
private [mapKey]: InstanceType<typeof map>;
|
||||||
|
|
||||||
|
public has(key: T): boolean {
|
||||||
|
return this[mapKey].has(key);
|
||||||
|
}
|
||||||
|
public add(val: T) {
|
||||||
|
this[mapKey].set(val, true);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public delete(val: T): boolean {
|
||||||
|
if (!this[mapKey].has(val)) return false;
|
||||||
|
else {
|
||||||
|
this[mapKey].delete(val);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public clear() {
|
||||||
|
this[mapKey].clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor(iterable?: Iterable<T>) {
|
||||||
|
const _map = this[mapKey] = new map(true);
|
||||||
|
|
||||||
|
if (iterable != null) {
|
||||||
|
if (Array.isArray(iterable)) {
|
||||||
|
for (let i = 0; i < iterable.length; i++) {
|
||||||
|
if (!(i in iterable)) continue;
|
||||||
|
_map.set(iterable[i], true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const it = (iterable as any)[symbols.iterator]();
|
||||||
|
for (let val = it.next(); !val.done; val = it.next()) {
|
||||||
|
_map.set(val.value, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func.setCallable(Set, false);
|
||||||
|
func.setCallable(WeakSet, false);
|
||||||
276
src/lib/libs/string.ts
Normal file
276
src/lib/libs/string.ts
Normal file
@@ -0,0 +1,276 @@
|
|||||||
|
import { TypeError } from "./errors.ts";
|
||||||
|
import { func, number, regex, string } from "./primordials.ts";
|
||||||
|
import { RegExp } from "./regex.ts";
|
||||||
|
import { applyReplaces, applySplits, limitI, type ReplaceRange, symbols, unwrapThis, valueKey, wrapI } from "./utils.ts";
|
||||||
|
|
||||||
|
const trimStartRegex = new regex("^\\s+", false, false, false, false, false);
|
||||||
|
const trimEndRegex = new regex("\\s+$", false, false, false, false, false);
|
||||||
|
|
||||||
|
export const String = (() => {
|
||||||
|
class String {
|
||||||
|
[valueKey]!: string;
|
||||||
|
|
||||||
|
public at(index: number) {
|
||||||
|
throw "Not implemented :/";
|
||||||
|
return unwrapThis(this, "string", String, "String.prototype.at")[index];
|
||||||
|
}
|
||||||
|
public toString() {
|
||||||
|
return unwrapThis(this, "string", String, "String.prototype.toString");
|
||||||
|
}
|
||||||
|
public valueOf() {
|
||||||
|
return unwrapThis(this, "string", String, "String.prototype.valueOf");
|
||||||
|
}
|
||||||
|
|
||||||
|
public includes(search: string, offset = 0) {
|
||||||
|
const self = unwrapThis(this, "string", String, "String.prototype.indexOf");
|
||||||
|
return string.indexOf(self, (String as any)(search), +offset, false) >= 0;
|
||||||
|
}
|
||||||
|
public startsWith(search: string) {
|
||||||
|
const self = unwrapThis(this, "string", String, "String.prototype.indexOf");
|
||||||
|
if (self.length < search.length) return false;
|
||||||
|
return string.substring(self, 0, search.length) === search;
|
||||||
|
}
|
||||||
|
public endsWith(search: string) {
|
||||||
|
const self = unwrapThis(this, "string", String, "String.prototype.indexOf");
|
||||||
|
if (self.length < search.length) return false;
|
||||||
|
return string.substring(self, self.length - search.length, self.length) === search;
|
||||||
|
}
|
||||||
|
|
||||||
|
public indexOf(search: string, offset = 0) {
|
||||||
|
const self = unwrapThis(this, "string", String, "String.prototype.indexOf");
|
||||||
|
offset = +offset;
|
||||||
|
return string.indexOf(self, search, offset, false);
|
||||||
|
}
|
||||||
|
public lastIndexOf(search: string, offset = 0) {
|
||||||
|
const self = unwrapThis(this, "string", String, "String.prototype.lastIndexOf");
|
||||||
|
offset = +offset;
|
||||||
|
return string.indexOf(self, search, offset, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public trim() {
|
||||||
|
const self = unwrapThis(this, "string", String, "String.prototype.trim");
|
||||||
|
const start = trimStartRegex.exec(self, 0, false);
|
||||||
|
const end = trimEndRegex.exec(self, 0, false);
|
||||||
|
|
||||||
|
const startI = start == null ? 0 : start.end;
|
||||||
|
const endI = end == null ? self.length : end.matches.index!;
|
||||||
|
|
||||||
|
return string.substring(self, startI, endI);
|
||||||
|
}
|
||||||
|
public trimStart() {
|
||||||
|
const self = unwrapThis(this, "string", String, "String.prototype.trim");
|
||||||
|
const start = trimStartRegex.exec(self, 0, false);
|
||||||
|
const startI = start == null ? 0 : start.end;
|
||||||
|
|
||||||
|
return string.substring(self, startI, self.length);
|
||||||
|
}
|
||||||
|
public trimEnd() {
|
||||||
|
const self = unwrapThis(this, "string", String, "String.prototype.trim");
|
||||||
|
const end = trimEndRegex.exec(self, 0, false);
|
||||||
|
const endI = end == null ? self.length : end.matches.index!;
|
||||||
|
|
||||||
|
return string.substring(self, 0, endI);
|
||||||
|
}
|
||||||
|
|
||||||
|
public trimLeft() {
|
||||||
|
return func.invoke(String.prototype.trimStart, this, []);
|
||||||
|
}
|
||||||
|
public trimRight() {
|
||||||
|
return func.invoke(String.prototype.trimEnd, this, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public charAt(i: number) {
|
||||||
|
const self = unwrapThis(this, "string", String, "String.prototype.charAt");
|
||||||
|
return self[i];
|
||||||
|
}
|
||||||
|
public charCodeAt(i: number) {
|
||||||
|
const self = unwrapThis(this, "string", String, "String.prototype.charCodeAt");
|
||||||
|
return self[i] ? string.toCharCode(self[i]) : number.NaN;
|
||||||
|
}
|
||||||
|
public codePointAt(i: number) {
|
||||||
|
const self = unwrapThis(this, "string", String, "String.prototype.charCodeAt");
|
||||||
|
return i >= 0 && i < self.length ? string.toCodePoint(self, i) : number.NaN;
|
||||||
|
}
|
||||||
|
|
||||||
|
public split(val?: any, limit?: number) {
|
||||||
|
const self = unwrapThis(this, "string", String, "String.prototype.split");
|
||||||
|
if (val === undefined) return [self];
|
||||||
|
if (val !== null && typeof val === "object" && symbols.split in val) {
|
||||||
|
return val[symbols.split](self, limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
val = (String as any)(val);
|
||||||
|
|
||||||
|
return applySplits(self, limit, offset => {
|
||||||
|
const start = string.indexOf(self, val, offset, false);
|
||||||
|
if (start < 0) return undefined;
|
||||||
|
else return { start, end: start + val.length };
|
||||||
|
});
|
||||||
|
}
|
||||||
|
public replace(val: any, replacer: any) {
|
||||||
|
const self = unwrapThis(this, "string", String, "String.prototype.replace");
|
||||||
|
if (val !== null && typeof val === "object" && symbols.replace in val) {
|
||||||
|
return val[symbols.replace](self, replacer);
|
||||||
|
}
|
||||||
|
else val = (String as any)(val);
|
||||||
|
|
||||||
|
const i = string.indexOf(self, val, 0);
|
||||||
|
return applyReplaces(self, [{ start: i, end: i + val.length, matches: [val] }], replacer, false);
|
||||||
|
}
|
||||||
|
public replaceAll(val: any, replacer: any) {
|
||||||
|
const self = unwrapThis(this, "string", String, "String.prototype.replaceAll");
|
||||||
|
if (val !== null && typeof val === "object" && symbols.replace in val) {
|
||||||
|
if (val instanceof RegExp && !val.global) throw new TypeError("replaceAll must be called with a global RegExp");
|
||||||
|
return val[symbols.replace](self, replacer);
|
||||||
|
}
|
||||||
|
else val = (String as any)(val);
|
||||||
|
|
||||||
|
let offset = 0;
|
||||||
|
const matches: ReplaceRange[] = [];
|
||||||
|
const add = val.length === 0 ? 1 : val.length;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const i = string.indexOf(self, val, offset);
|
||||||
|
if (i < 0) break;
|
||||||
|
|
||||||
|
matches[matches.length] = { start: i, end: i + val.length, matches: [val] };
|
||||||
|
if (val.length === 0)
|
||||||
|
offset = i + add;
|
||||||
|
}
|
||||||
|
|
||||||
|
return applyReplaces(self, matches, replacer, false);
|
||||||
|
}
|
||||||
|
public repeat(n: number) {
|
||||||
|
const self = unwrapThis(this, "string", String, "String.prototype.replaceAll");
|
||||||
|
const res: string[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < n; i++) {
|
||||||
|
res[i] = self;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.stringBuild(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
public slice(start = 0, end?: number) {
|
||||||
|
const self = unwrapThis(this, "string", String, "String.prototype.slice");
|
||||||
|
if (end === undefined) end = self.length;
|
||||||
|
start = limitI(wrapI(start, self.length), self.length);
|
||||||
|
end = limitI(wrapI(end, self.length), self.length);
|
||||||
|
|
||||||
|
if (end <= start) return "";
|
||||||
|
return string.substring(self, start, end);
|
||||||
|
}
|
||||||
|
public substring(start = 0, end?: number) {
|
||||||
|
const self = unwrapThis(this, "string", String, "String.prototype.substring");
|
||||||
|
if (end === undefined) end = self.length;
|
||||||
|
start = limitI(start, self.length);
|
||||||
|
end = limitI(end, self.length);
|
||||||
|
|
||||||
|
if (end <= start) return "";
|
||||||
|
return string.substring(self, start, end);
|
||||||
|
}
|
||||||
|
public substr(start = 0, count?: number) {
|
||||||
|
const self = unwrapThis(this, "string", String, "String.prototype.substr");
|
||||||
|
count = self.length - start;
|
||||||
|
start = limitI(start, self.length);
|
||||||
|
count = limitI(count, self.length - start);
|
||||||
|
|
||||||
|
if (count <= 0) return "";
|
||||||
|
return string.substring(self, start, count + start);
|
||||||
|
}
|
||||||
|
public concat() {
|
||||||
|
const self = unwrapThis(this, "string", String, "String.prototype.concat");
|
||||||
|
const parts = [self];
|
||||||
|
for (let i = 0; i < arguments.length; i++) {
|
||||||
|
parts[i + 1] = (String as any)(arguments[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.stringBuild(parts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public toLowerCase() {
|
||||||
|
const self = unwrapThis(this, "string", String, "String.prototype.toLowerCase");
|
||||||
|
return string.lower(self);
|
||||||
|
}
|
||||||
|
public toUpperCase() {
|
||||||
|
const self = unwrapThis(this, "string", String, "String.prototype.toLowerCase");
|
||||||
|
return string.upper(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
public match(regex: RegExp) {
|
||||||
|
const self = unwrapThis(this, "string", String, "String.prototype.match");
|
||||||
|
if (!(regex instanceof RegExp)) throw new TypeError("Regexp expected for String.prototype.match");
|
||||||
|
|
||||||
|
if (regex.global) {
|
||||||
|
let matches: string[] | null = null;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const match = regex.exec(self);
|
||||||
|
if (match == null) break;
|
||||||
|
|
||||||
|
matches ||= [];
|
||||||
|
matches[matches.length] = match[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
return matches;
|
||||||
|
}
|
||||||
|
else return regex.exec(self);
|
||||||
|
}
|
||||||
|
|
||||||
|
public [symbols.iterator]() {
|
||||||
|
var i = 0;
|
||||||
|
var arr: string | undefined = unwrapThis(this, "string", String, "String.prototype[Symbol.iterator]");
|
||||||
|
|
||||||
|
return {
|
||||||
|
next () {
|
||||||
|
if (arr == null) return { done: true, value: undefined };
|
||||||
|
if (i > arr.length) {
|
||||||
|
arr = undefined;
|
||||||
|
return { done: true, value: undefined };
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var val = arr[i++];
|
||||||
|
if (i >= arr.length) arr = undefined;
|
||||||
|
return { done: false, value: val };
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[symbols.iterator]() { return this; }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor (value?: unknown) {
|
||||||
|
if (func.invokeType(arguments, this) === "call") {
|
||||||
|
if (arguments.length === 0) return "" as any;
|
||||||
|
else if (typeof value === "symbol") return value.toString() as any;
|
||||||
|
else return (value as any) + "" as any;
|
||||||
|
}
|
||||||
|
this[valueKey] = (String as any)(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static fromCharCode() {
|
||||||
|
const res: string[] = [];
|
||||||
|
res[arguments.length] = "";
|
||||||
|
|
||||||
|
for (let i = 0; i < arguments.length; i++) {
|
||||||
|
res[i] = string.fromCharCode(+arguments[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.stringBuild(res);
|
||||||
|
}
|
||||||
|
public static fromCodePoint() {
|
||||||
|
const res: string[] = [];
|
||||||
|
res[arguments.length] = "";
|
||||||
|
|
||||||
|
for (var i = 0; i < arguments.length; i++) {
|
||||||
|
res[i] = string.fromCodePoint(+arguments[i]);
|
||||||
|
}
|
||||||
|
return string.stringBuild(res);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func.setCallable(String, true);
|
||||||
|
func.setConstructable(String, true);
|
||||||
|
|
||||||
|
return String as any as typeof String & ((value?: unknown) => string);
|
||||||
|
})();
|
||||||
|
export type String = InstanceType<typeof String>;
|
||||||
53
src/lib/libs/symbol.ts
Normal file
53
src/lib/libs/symbol.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
import { func, object, symbol } from "./primordials.ts";
|
||||||
|
import { symbols, unwrapThis, valueKey } from "./utils.ts";
|
||||||
|
|
||||||
|
export const Symbol = (() => {
|
||||||
|
class Symbol {
|
||||||
|
[valueKey]!: symbol;
|
||||||
|
|
||||||
|
get description() {
|
||||||
|
return symbol.getSymbolDescription(unwrapThis(this, "symbol", Symbol, "Symbol.prototype.description"));
|
||||||
|
}
|
||||||
|
|
||||||
|
public toString() {
|
||||||
|
return "Symbol(" + unwrapThis(this, "symbol", Symbol, "Symbol.prototype.toString").description + ")";
|
||||||
|
}
|
||||||
|
public valueOf() {
|
||||||
|
return unwrapThis(this, "symbol", Symbol, "Symbol.prototype.valueOf");
|
||||||
|
}
|
||||||
|
|
||||||
|
public constructor(name = "") {
|
||||||
|
return symbol.makeSymbol(name + "") as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static for(name: string) {
|
||||||
|
return symbol.getSymbol(name + "");
|
||||||
|
}
|
||||||
|
|
||||||
|
declare public static readonly asyncIterator: unique symbol;
|
||||||
|
declare public static readonly iterator: unique symbol;
|
||||||
|
declare public static readonly match: unique symbol;
|
||||||
|
declare public static readonly matchAll: unique symbol;
|
||||||
|
declare public static readonly replace: unique symbol;
|
||||||
|
declare public static readonly search: unique symbol;
|
||||||
|
declare public static readonly split: unique symbol;
|
||||||
|
declare public static readonly toStringTag: unique symbol;
|
||||||
|
};
|
||||||
|
|
||||||
|
func.setCallable(Symbol, true);
|
||||||
|
func.setConstructable(Symbol, false);
|
||||||
|
|
||||||
|
object.defineField(Symbol, "asyncIterator", { c: false, e: false, w: false, v: symbols.asyncIterator });
|
||||||
|
object.defineField(Symbol, "iterator", { c: false, e: false, w: false, v: symbols.iterator });
|
||||||
|
object.defineField(Symbol, "match", { c: false, e: false, w: false, v: symbols.match });
|
||||||
|
object.defineField(Symbol, "matchAll", { c: false, e: false, w: false, v: symbols.matchAll });
|
||||||
|
object.defineField(Symbol, "replace", { c: false, e: false, w: false, v: symbols.replace });
|
||||||
|
object.defineField(Symbol, "search", { c: false, e: false, w: false, v: symbols.search });
|
||||||
|
object.defineField(Symbol, "split", { c: false, e: false, w: false, v: symbols.split });
|
||||||
|
object.defineField(Symbol, "toStringTag", { c: false, e: false, w: false, v: symbols.toStringTag });
|
||||||
|
object.defineField(Symbol, "isConcatSpreadable", { c: false, e: false, w: false, v: symbols.isConcatSpreadable });
|
||||||
|
|
||||||
|
return Symbol as any as typeof Symbol & ((name?: string) => ReturnType<SymbolConstructor>);
|
||||||
|
})();
|
||||||
|
export type Symbol = InstanceType<typeof Symbol>;
|
||||||
|
|
||||||
26
src/lib/libs/url.ts
Normal file
26
src/lib/libs/url.ts
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
import { regex, string } from "./primordials";
|
||||||
|
|
||||||
|
function escaper(matcher: regex) {
|
||||||
|
return (text: string) => {
|
||||||
|
const parts: string[] = [];
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const match = matcher.exec(text, i, false);
|
||||||
|
if (match == null) break;
|
||||||
|
|
||||||
|
const char = match.matches[0];
|
||||||
|
const code = string.toCharCode(char);
|
||||||
|
parts[parts.length] = string.substring(text, i, match.matches.index!);
|
||||||
|
parts[parts.length] = "%" + code;
|
||||||
|
i = match.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
parts[parts.length] = string.substring(text, i, text.length);
|
||||||
|
|
||||||
|
return string.stringBuild(parts);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export const encodeURI = escaper(new regex("[^A-Za-z0-9\\-+.!~*'()]"));
|
||||||
|
export const encodeURIComponent = escaper(new regex("[^A-Za-z0-9\\-+.!~*'();/?:@&=+$,#]"));
|
||||||
218
src/lib/libs/utils.ts
Normal file
218
src/lib/libs/utils.ts
Normal file
@@ -0,0 +1,218 @@
|
|||||||
|
import { func, string, symbol } from "./primordials.ts";
|
||||||
|
|
||||||
|
export const valueKey: unique symbol = symbol.makeSymbol("Primitive.value") as any;
|
||||||
|
export namespace symbols {
|
||||||
|
export const asyncIterator: unique symbol = symbol.makeSymbol("Symbol.asyncIterator") as any;
|
||||||
|
export const iterator: unique symbol = symbol.makeSymbol("Symbol.iterator") as any;
|
||||||
|
export const match: unique symbol = symbol.makeSymbol("Symbol.match") as any;
|
||||||
|
export const matchAll: unique symbol = symbol.makeSymbol("Symbol.matchAll") as any;
|
||||||
|
export const replace: unique symbol = symbol.makeSymbol("Symbol.replace") as any;
|
||||||
|
export const search: unique symbol = symbol.makeSymbol("Symbol.search") as any;
|
||||||
|
export const split: unique symbol = symbol.makeSymbol("Symbol.split") as any;
|
||||||
|
export const toStringTag: unique symbol = symbol.makeSymbol("Symbol.toStringTag") as any;
|
||||||
|
export const isConcatSpreadable: unique symbol = symbol.makeSymbol("Symbol.isConcatSpreadable") as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface TypeMap {
|
||||||
|
undefined: undefined;
|
||||||
|
boolean: boolean;
|
||||||
|
string: string;
|
||||||
|
number: number;
|
||||||
|
symbol: symbol;
|
||||||
|
object: null | object;
|
||||||
|
function: Function;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unwrapThis<T extends keyof TypeMap>(self: any, type: T, constr: Function, name: string, arg = "this", defaultVal?: TypeMap[T]): TypeMap[T] {
|
||||||
|
if (typeof self === type) return self;
|
||||||
|
if (self instanceof constr && valueKey in self) self = (self as any)[valueKey];
|
||||||
|
if (typeof self === type) return self;
|
||||||
|
if (defaultVal !== undefined) return defaultVal;
|
||||||
|
throw new TypeError(name + " requires that '" + arg + "' be a " + constr.name);
|
||||||
|
}
|
||||||
|
export function wrapI(i: number, length: number) {
|
||||||
|
if (i < 0) return (i + length) | 0;
|
||||||
|
else return i | 0;
|
||||||
|
}
|
||||||
|
export function limitI(i: number, max: number) {
|
||||||
|
i |= 0;
|
||||||
|
if (i < 0) return 0;
|
||||||
|
else if (i > max) return max;
|
||||||
|
else return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type ReplaceRange = { start: number; end: number; matches: string[]; groups?: Record<string, string>; };
|
||||||
|
type ReplaceLiteral = (string | ((_: { groups: string[]; prev: () => string; next: () => string; }) => string))[];
|
||||||
|
|
||||||
|
function parseReplacer(replacer: string, groupN: number) {
|
||||||
|
const parts: ReplaceLiteral = [];
|
||||||
|
let lastI = 0;
|
||||||
|
let lastSlice = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const i = string.indexOf(replacer, "$", lastI);
|
||||||
|
if (i < 0 || i + 1 >= replacer.length) break;
|
||||||
|
lastI = i + 1;
|
||||||
|
|
||||||
|
switch (replacer[i + 1]) {
|
||||||
|
case "$":
|
||||||
|
parts[parts.length] = string.substring(replacer, lastSlice, i);
|
||||||
|
parts[parts.length] = "$";
|
||||||
|
lastSlice = i + 2;
|
||||||
|
continue;
|
||||||
|
case "&":
|
||||||
|
parts[parts.length] = string.substring(replacer, lastSlice, i);
|
||||||
|
parts[parts.length] = ({ groups }) => groups[0];
|
||||||
|
lastSlice = i + 2;
|
||||||
|
continue;
|
||||||
|
case "`":
|
||||||
|
parts[parts.length] = string.substring(replacer, lastSlice, i);
|
||||||
|
parts[parts.length] = ({ prev }) => prev();
|
||||||
|
lastSlice = i + 2;
|
||||||
|
continue;
|
||||||
|
case "'":
|
||||||
|
parts[parts.length] = string.substring(replacer, lastSlice, i);
|
||||||
|
parts[parts.length] = ({ next }) => next();
|
||||||
|
lastSlice = i + 2;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let groupI = 0;
|
||||||
|
let hasGroup = false;
|
||||||
|
let consumedN = 1;
|
||||||
|
|
||||||
|
while (i + consumedN < replacer.length) {
|
||||||
|
const code = string.toCharCode(replacer[i + consumedN]);
|
||||||
|
if (code >= 48 && code <= 57) {
|
||||||
|
const newGroupI = groupI * 10 + code - 48;
|
||||||
|
if (newGroupI < 1 || newGroupI >= groupN) break;
|
||||||
|
|
||||||
|
groupI = newGroupI;
|
||||||
|
hasGroup = true;
|
||||||
|
}
|
||||||
|
consumedN++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hasGroup) {
|
||||||
|
parts[parts.length] = string.substring(replacer, lastSlice, i);
|
||||||
|
parts[parts.length] = ({ groups }) => groups[groupI];
|
||||||
|
lastSlice = i + consumedN;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lastSlice === 0) return [replacer];
|
||||||
|
else parts[parts.length] = string.substring(replacer, lastSlice, replacer.length);
|
||||||
|
|
||||||
|
return parts;
|
||||||
|
}
|
||||||
|
function executeReplacer(text: string, match: ReplaceRange, literal: ReplaceLiteral, prevEnd?: number, nextStart?: number) {
|
||||||
|
const res = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < literal.length; i++) {
|
||||||
|
const curr = literal[i];
|
||||||
|
if (typeof curr === "function") res[i] = curr({
|
||||||
|
groups: match.matches,
|
||||||
|
next: () => string.substring(text, prevEnd ?? 0, match.start),
|
||||||
|
prev: () => string.substring(text, match.end, nextStart ?? 0),
|
||||||
|
});
|
||||||
|
else res[i] = curr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.stringBuild(res);
|
||||||
|
}
|
||||||
|
export function applyReplaces(text: string, ranges: ReplaceRange[], replace: any, groupN?: number | false) {
|
||||||
|
if (ranges.length === 0) return text;
|
||||||
|
|
||||||
|
const res: string[] = [];
|
||||||
|
let offset = 0;
|
||||||
|
|
||||||
|
if (groupN !== false && typeof replace === "string") {
|
||||||
|
if (groupN == null) {
|
||||||
|
for (let i = 0; i < ranges.length; i++) {
|
||||||
|
const prevEnd = i - 1 >= 0 ? ranges[i - 1].end : undefined;
|
||||||
|
const nextStart = i + 1 < ranges.length ? ranges[i + 1].start : undefined;
|
||||||
|
const range = ranges[i];
|
||||||
|
res[res.length] = string.substring(text, offset, range.start);
|
||||||
|
res[res.length] = executeReplacer(text, range, parseReplacer(replace, range.matches.length), prevEnd, nextStart);
|
||||||
|
offset = range.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
res[res.length] = string.substring(text, offset, text.length);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
const literal = parseReplacer(replace, groupN);
|
||||||
|
|
||||||
|
for (let i = 0; i < ranges.length; i++) {
|
||||||
|
const prevEnd = i - 1 >= 0 ? ranges[i - 1].end : undefined;
|
||||||
|
const nextStart = i + 1 < ranges.length ? ranges[i + 1].start : undefined;
|
||||||
|
const range = ranges[i];
|
||||||
|
res[res.length] = string.substring(text, offset, range.start);
|
||||||
|
res[res.length] = executeReplacer(text, range, literal, prevEnd, nextStart);
|
||||||
|
offset = range.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
res[res.length] = string.substring(text, offset, text.length);
|
||||||
|
}
|
||||||
|
return string.stringBuild(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof replace === "string") {
|
||||||
|
for (let i = 0; i < ranges.length; i++) {
|
||||||
|
const range = ranges[i];
|
||||||
|
res[res.length] = string.substring(text, offset, range.start);
|
||||||
|
res[res.length] = replace;
|
||||||
|
offset = range.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
res[res.length] = string.substring(text, offset, text.length);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (let i = 0; i < ranges.length; i++) {
|
||||||
|
const range = ranges[i];
|
||||||
|
const args: any[] = range.matches;
|
||||||
|
args[args.length] = range.start;
|
||||||
|
args[args.length] = text;
|
||||||
|
args[args.length] = range.groups;
|
||||||
|
|
||||||
|
res[res.length] = string.substring(text, offset, range.start);
|
||||||
|
res[res.length] = func.invoke(replace, undefined, args);
|
||||||
|
offset = range.end;
|
||||||
|
}
|
||||||
|
|
||||||
|
res[res.length] = string.substring(text, offset, text.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.stringBuild(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function applySplits(text: string, limit: number | undefined, next: (offset: number) => { start: number; end: number; } | undefined) {
|
||||||
|
let lastEnd = 0;
|
||||||
|
let lastEmpty = true;
|
||||||
|
let offset = 0;
|
||||||
|
|
||||||
|
const res: string[] = [];
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
if (limit != null && limit >= 0 && res.length >= limit) break;
|
||||||
|
|
||||||
|
const curr = next(offset);
|
||||||
|
|
||||||
|
if (curr == null) {
|
||||||
|
if (!lastEmpty || !res.length) res[res.length] = string.substring(text, lastEnd, text.length);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { start, end } = curr;
|
||||||
|
const empty = start === end;
|
||||||
|
|
||||||
|
if (offset > 0 || !empty) res[res.length] = string.substring(text, lastEnd, start);
|
||||||
|
|
||||||
|
lastEnd = end;
|
||||||
|
offset = empty ? end + 1 : end;
|
||||||
|
lastEmpty = empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
7
src/lib/transpiler/_entry.ts
Normal file
7
src/lib/transpiler/_entry.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// import coffeescript from "./coffeescript.ts";
|
||||||
|
// import babel from "./babel.ts";
|
||||||
|
|
||||||
|
// register(v => coffeescript(babel(v)));
|
||||||
|
import typescript from "./typescript.ts";
|
||||||
|
|
||||||
|
register(typescript);
|
||||||
25
src/lib/transpiler/babel.ts
Normal file
25
src/lib/transpiler/babel.ts
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
import { SourceMap } from "./map.ts";
|
||||||
|
import { transform } from "@babel/standalone";
|
||||||
|
// import presetEnv from "@babel/preset-env";
|
||||||
|
|
||||||
|
export default function babel(next: Compiler): Compiler {
|
||||||
|
print("Loaded babel!");
|
||||||
|
|
||||||
|
return (filename, code, prevMap) => {
|
||||||
|
const res = transform(code, {
|
||||||
|
filename,
|
||||||
|
sourceMaps: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const map = SourceMap.parse({
|
||||||
|
file: "babel-internal://" + filename,
|
||||||
|
mappings: res.map!.mappings,
|
||||||
|
sources: [filename],
|
||||||
|
});
|
||||||
|
|
||||||
|
const compiled = next("babel-internal://" + filename, res.code!, SourceMap.chain(map, prevMap));
|
||||||
|
registerSource(filename, code);
|
||||||
|
return compiled;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
28
src/lib/transpiler/coffeescript.ts
Normal file
28
src/lib/transpiler/coffeescript.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { compile } from "coffeescript";
|
||||||
|
import { SourceMap } from "./map.ts";
|
||||||
|
|
||||||
|
export default function coffee(next: Compiler): Compiler {
|
||||||
|
print("Loaded coffeescript!");
|
||||||
|
|
||||||
|
return (filename, code, prevMap) => {
|
||||||
|
const {
|
||||||
|
js: result,
|
||||||
|
v3SourceMap: rawMap,
|
||||||
|
} = compile(code, {
|
||||||
|
filename,
|
||||||
|
sourceMap: true,
|
||||||
|
bare: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const map = SourceMap.parse({
|
||||||
|
file: "coffee-internal://" + filename,
|
||||||
|
mappings: JSON.parse(rawMap).mappings,
|
||||||
|
sources: [filename],
|
||||||
|
});
|
||||||
|
|
||||||
|
const compiled = next("coffee-internal://" + filename, result, SourceMap.chain(map, prevMap));
|
||||||
|
registerSource(filename, code);
|
||||||
|
return compiled;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
182
src/lib/transpiler/map.ts
Normal file
182
src/lib/transpiler/map.ts
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
const map: number[] = [];
|
||||||
|
let j = 0;
|
||||||
|
|
||||||
|
for (let i = 65; i <= 90; i++) map[i] = j++;
|
||||||
|
for (let i = 97; i <= 122; i++) map[i] = j++;
|
||||||
|
map[43] = j++;
|
||||||
|
map[47] = j++;
|
||||||
|
|
||||||
|
export type Location = readonly [file: string, line: number, start: number];
|
||||||
|
|
||||||
|
export function decodeVLQ(val: string): number[][][] {
|
||||||
|
const lines: number[][][] = [];
|
||||||
|
|
||||||
|
for (const line of val.split(";", -1)) {
|
||||||
|
const elements: number[][] = [];
|
||||||
|
|
||||||
|
for (const el of line.split(",", -1)) {
|
||||||
|
if (el.length === 0) elements.push([]);
|
||||||
|
else {
|
||||||
|
const list: number[] = [];
|
||||||
|
|
||||||
|
for (let i = 0; i < el.length;) {
|
||||||
|
let sign = 1;
|
||||||
|
let curr = map[el.charCodeAt(i++)];
|
||||||
|
let cont = (curr & 0x20) === 0x20;
|
||||||
|
if ((curr & 1) === 1) sign = -1;
|
||||||
|
let res = (curr & 0b11110) >> 1;
|
||||||
|
let n = 4;
|
||||||
|
|
||||||
|
for (; i < el.length && cont;) {
|
||||||
|
curr = map[el.charCodeAt(i++)];
|
||||||
|
cont = (curr & 0x20) == 0x20;
|
||||||
|
res |= (curr & 0b11111) << n;
|
||||||
|
n += 5;
|
||||||
|
if (!cont) break;
|
||||||
|
}
|
||||||
|
|
||||||
|
list.push(res * sign);
|
||||||
|
}
|
||||||
|
|
||||||
|
elements.push(list);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push(elements);
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace Location {
|
||||||
|
export function compare(a: Location, b: Location) {
|
||||||
|
const { 0: filenameA, 1: lineA, 2: startA } = a;
|
||||||
|
const { 0: filenameB, 1: lineB, 2: startB } = b;
|
||||||
|
|
||||||
|
if (filenameA < filenameB) return -1;
|
||||||
|
if (filenameA > filenameB) return 1;
|
||||||
|
|
||||||
|
const lineI = lineA - lineB;
|
||||||
|
if (lineI !== 0) return lineI;
|
||||||
|
|
||||||
|
return startA - startB;
|
||||||
|
}
|
||||||
|
export function comparePoints(a: Location, b: Location) {
|
||||||
|
const { 1: lineA, 2: startA } = a;
|
||||||
|
const { 1: lineB, 2: startB } = b;
|
||||||
|
|
||||||
|
const lineI = lineA - lineB;
|
||||||
|
if (lineI !== 0) return lineI;
|
||||||
|
|
||||||
|
return startA - startB;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SourceMap {
|
||||||
|
(loc: Location): Location | undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class VLQSourceMap {
|
||||||
|
public constructor(
|
||||||
|
public readonly array: Map<string, [start: number, dst: Location][][]>,
|
||||||
|
) { }
|
||||||
|
|
||||||
|
public converter() {
|
||||||
|
return (src: Location) => {
|
||||||
|
const file = this.array.get(src[0]);
|
||||||
|
if (file == null) return src;
|
||||||
|
|
||||||
|
const line = file[src[1]];
|
||||||
|
if (line == null || line.length === 0) return undefined;
|
||||||
|
|
||||||
|
let a = 0;
|
||||||
|
let b = line.length;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
const done = b - a <= 1;
|
||||||
|
|
||||||
|
const mid = (a + b) >> 1;
|
||||||
|
const el = line[mid];
|
||||||
|
|
||||||
|
const cmp = el[0] - src[1];
|
||||||
|
|
||||||
|
if (cmp < 0) {
|
||||||
|
if (done) {
|
||||||
|
if (b >= line.length) return undefined;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
a = mid;
|
||||||
|
}
|
||||||
|
else if (cmp > 0) {
|
||||||
|
if (done) {
|
||||||
|
if (a <= 0) return undefined;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
b = mid;
|
||||||
|
}
|
||||||
|
else return el[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return line[b][1];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static parseVLQ(compiled: string, filenames: string[], raw: string): VLQSourceMap {
|
||||||
|
const mapping = decodeVLQ(raw);
|
||||||
|
const file: [start: number, dst: Location][][] = [];
|
||||||
|
const res = new Map<string, [start: number, dst: Location][][]>([[compiled, file]]);
|
||||||
|
|
||||||
|
let originalRow = 0;
|
||||||
|
let originalCol = 0;
|
||||||
|
let originalFile = 0;
|
||||||
|
const lastCols = new Set<number>();
|
||||||
|
|
||||||
|
for (let compiledRow = 0; compiledRow < mapping.length; compiledRow++) {
|
||||||
|
const line = file[compiledRow] ??= [];
|
||||||
|
let compiledCol = 0;
|
||||||
|
|
||||||
|
for (const rawSeg of mapping[compiledRow]) {
|
||||||
|
compiledCol += rawSeg.length > 0 ? rawSeg[0] : 0;
|
||||||
|
originalFile += rawSeg.length > 1 ? rawSeg[1] : 0;
|
||||||
|
originalRow += rawSeg.length > 2 ? rawSeg[2] : 0;
|
||||||
|
originalCol += rawSeg.length > 3 ? rawSeg[3] : 0;
|
||||||
|
|
||||||
|
if (!lastCols.has(compiledCol)) {
|
||||||
|
line[line.length] = [compiledCol, [filenames[originalFile], originalRow, originalCol]];
|
||||||
|
}
|
||||||
|
lastCols.add(compiledCol);
|
||||||
|
}
|
||||||
|
|
||||||
|
line.sort((a, b) => a[0] - b[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new VLQSourceMap(res);
|
||||||
|
}
|
||||||
|
public static parse(raw: string | { file: string, mappings: string, sources: string[] }) {
|
||||||
|
if (typeof raw === "string") raw = JSON.parse(raw) as { file: string, mappings: string, sources: string[] };
|
||||||
|
|
||||||
|
const compiled = raw.file;
|
||||||
|
const mapping = raw.mappings;
|
||||||
|
let filenames = raw.sources;
|
||||||
|
if (filenames.length === 0 || filenames.length === 1 && filenames[0] === "") filenames = [compiled];
|
||||||
|
|
||||||
|
return this.parseVLQ(compiled, filenames, mapping);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export namespace SourceMap {
|
||||||
|
export function parse(raw: string | { file: string, mappings: string, sources: string[] }) {
|
||||||
|
return VLQSourceMap.parse(raw).converter();
|
||||||
|
}
|
||||||
|
export function chain(...maps: SourceMap[]): SourceMap {
|
||||||
|
return loc => {
|
||||||
|
for (const el of maps) {
|
||||||
|
const tmp = el(loc);
|
||||||
|
if (tmp == null) return undefined;
|
||||||
|
else loc = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
return loc;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/lib/transpiler/types.ts
Normal file
10
src/lib/transpiler/types.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
import { type SourceMap } from "./map.ts";
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
type CompilerFactory = (next: Compiler) => Compiler;
|
||||||
|
type Compiler = (filename: string, src: string, mapper: SourceMap) => Function;
|
||||||
|
|
||||||
|
function print(...args: any[]): void;
|
||||||
|
function register(factory: CompilerFactory): void;
|
||||||
|
function registerSource(filename: string, src: string): void;
|
||||||
|
}
|
||||||
127
src/lib/transpiler/typescript.ts
Normal file
127
src/lib/transpiler/typescript.ts
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
import { createDocumentRegistry, createLanguageService, ModuleKind, ScriptSnapshot, ScriptTarget, type Diagnostic, type CompilerOptions, type IScriptSnapshot, flattenDiagnosticMessageText, CompilerHost, LanguageService } from "typescript";
|
||||||
|
import { SourceMap } from "./map.ts";
|
||||||
|
|
||||||
|
declare function getResource(name: string): string | undefined;
|
||||||
|
declare function print(...args: any[]): void;
|
||||||
|
declare function register(factory: CompilerFactory): void;
|
||||||
|
declare function registerSource(filename: string, src: string): void;
|
||||||
|
|
||||||
|
type CompilerFactory = (next: Compiler) => Compiler;
|
||||||
|
type Compiler = (filename: string, src: string, mapper: SourceMap) => Function;
|
||||||
|
|
||||||
|
const resources: Record<string, string | undefined> = {};
|
||||||
|
|
||||||
|
|
||||||
|
function resource(name: string) {
|
||||||
|
if (name in resources) return resources[name];
|
||||||
|
else return resources[name] = getResource(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function typescript(next: Compiler): Compiler {
|
||||||
|
const files: Record<string, IScriptSnapshot> = {};
|
||||||
|
const versions: Record<string, number> = {};
|
||||||
|
let declI = 0;
|
||||||
|
|
||||||
|
const settings: CompilerOptions = {
|
||||||
|
target: ScriptTarget.ES5,
|
||||||
|
module: ModuleKind.Preserve,
|
||||||
|
|
||||||
|
allowImportingTsExtensions: true,
|
||||||
|
verbatimModuleSyntax: true,
|
||||||
|
|
||||||
|
strict: false,
|
||||||
|
skipLibCheck: true,
|
||||||
|
forceConsistentCasingInFileNames: true,
|
||||||
|
declaration: true,
|
||||||
|
sourceMap: true,
|
||||||
|
downlevelIteration: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
let service: LanguageService;
|
||||||
|
|
||||||
|
measure(() => {
|
||||||
|
service = createLanguageService({
|
||||||
|
getCurrentDirectory: () => "/",
|
||||||
|
getDefaultLibFileName: () => "/lib.d.ts",
|
||||||
|
getScriptFileNames: () => {
|
||||||
|
const res = ["/src.ts", "/lib.d.ts"];
|
||||||
|
for (let i = 0; i < declI; i++) res.push("/src." + i + ".d.ts");
|
||||||
|
return res;
|
||||||
|
},
|
||||||
|
getCompilationSettings: () => settings,
|
||||||
|
log: print,
|
||||||
|
fileExists: filename => filename in files || resource(filename) != null,
|
||||||
|
|
||||||
|
getScriptSnapshot: (filename) => {
|
||||||
|
if (filename in files) return files[filename];
|
||||||
|
else {
|
||||||
|
const src = resource(filename);
|
||||||
|
if (src == null) return undefined;
|
||||||
|
return files[filename] = ScriptSnapshot.fromString(src);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getScriptVersion: (filename) => String(versions[filename] || 0),
|
||||||
|
|
||||||
|
readFile: () => { throw "no"; },
|
||||||
|
writeFile: () => { throw "no"; },
|
||||||
|
}, createDocumentRegistry());
|
||||||
|
});
|
||||||
|
measure(() => {
|
||||||
|
service.getEmitOutput("/lib.d.ts");
|
||||||
|
});
|
||||||
|
print("Loaded typescript!");
|
||||||
|
|
||||||
|
return (filename, code, prevMap) => {
|
||||||
|
files["/src.ts"] = ScriptSnapshot.fromString(code);
|
||||||
|
versions["/src.ts"] ??= 0;
|
||||||
|
versions["/src.ts"]++;
|
||||||
|
|
||||||
|
const emit = service.getEmitOutput("/src.ts");
|
||||||
|
|
||||||
|
const diagnostics = new Array<Diagnostic>()
|
||||||
|
.concat(service.getSyntacticDiagnostics("/src.ts"))
|
||||||
|
.concat(service.getSemanticDiagnostics("/src.ts"))
|
||||||
|
.map(diagnostic => {
|
||||||
|
const message = flattenDiagnosticMessageText(diagnostic.messageText, "\n");
|
||||||
|
|
||||||
|
if (diagnostic.file != null) {
|
||||||
|
let file = diagnostic.file.fileName.substring(1);
|
||||||
|
if (file === "src.ts") file = filename;
|
||||||
|
|
||||||
|
if (diagnostic.start == null) return file;
|
||||||
|
|
||||||
|
const pos = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
|
||||||
|
return file + ":" + (pos.line + 1) + ":" + (pos.character + 1) + ": " + message;
|
||||||
|
}
|
||||||
|
else return message;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (diagnostics.length > 0) {
|
||||||
|
throw new SyntaxError(diagnostics.join("\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
const outputs: Record<string, string> = {};
|
||||||
|
|
||||||
|
for (const el of emit.outputFiles) {
|
||||||
|
outputs[el.name] = el.text;
|
||||||
|
}
|
||||||
|
|
||||||
|
const rawMap = JSON.parse(outputs["/src.js.map"]);
|
||||||
|
const map = SourceMap.parse({
|
||||||
|
file: "ts-internal://" + filename,
|
||||||
|
mappings: rawMap.mappings,
|
||||||
|
sources: [filename],
|
||||||
|
});
|
||||||
|
const result = outputs["/src.js"];
|
||||||
|
const declaration = outputs["/src.d.ts"];
|
||||||
|
|
||||||
|
const compiled = next("ts-internal://" + filename, result, SourceMap.chain(map, prevMap));
|
||||||
|
registerSource(filename, code);
|
||||||
|
|
||||||
|
return function (this: any) {
|
||||||
|
const res = compiled.apply(this, arguments);
|
||||||
|
if (declaration !== '') files["/src." + declI++ + ".d.ts"] = ScriptSnapshot.fromString(declaration);
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -17,10 +17,7 @@ public class Instruction {
|
|||||||
TRY_END(0x06),
|
TRY_END(0x06),
|
||||||
|
|
||||||
CALL(0x10),
|
CALL(0x10),
|
||||||
@Deprecated
|
|
||||||
CALL_MEMBER(0x11),
|
|
||||||
CALL_NEW(0x12),
|
CALL_NEW(0x12),
|
||||||
CALL_SUPER(0x13),
|
|
||||||
JMP_IF(0x18),
|
JMP_IF(0x18),
|
||||||
JMP_IFN(0x19),
|
JMP_IFN(0x19),
|
||||||
JMP(0x1A),
|
JMP(0x1A),
|
||||||
@@ -40,11 +37,12 @@ public class Instruction {
|
|||||||
|
|
||||||
LOAD_GLOB(0x38),
|
LOAD_GLOB(0x38),
|
||||||
LOAD_INTRINSICS(0x39),
|
LOAD_INTRINSICS(0x39),
|
||||||
LOAD_ARGS(0x3A),
|
LOAD_ARG(0x3A),
|
||||||
LOAD_REST_ARGS(0x3B),
|
LOAD_ARGS_N(0x3B),
|
||||||
LOAD_CALLEE(0x3C),
|
LOAD_ARGS(0x3C),
|
||||||
LOAD_THIS(0x3D),
|
LOAD_CALLED(0x3D),
|
||||||
LOAD_ERROR(0x3E),
|
LOAD_THIS(0x3E),
|
||||||
|
LOAD_ERROR(0x3F),
|
||||||
|
|
||||||
LOAD_VAR(0x40),
|
LOAD_VAR(0x40),
|
||||||
LOAD_MEMBER(0x41),
|
LOAD_MEMBER(0x41),
|
||||||
@@ -61,16 +59,10 @@ public class Instruction {
|
|||||||
KEYS(0x52),
|
KEYS(0x52),
|
||||||
TYPEOF(0x53),
|
TYPEOF(0x53),
|
||||||
OPERATION(0x54),
|
OPERATION(0x54),
|
||||||
EXTEND(0x55),
|
|
||||||
|
|
||||||
GLOB_GET(0x60),
|
GLOB_GET(0x60),
|
||||||
GLOB_SET(0x61),
|
GLOB_SET(0x61),
|
||||||
GLOB_DEF(0x62),
|
GLOB_DEF(0x62);
|
||||||
|
|
||||||
// CAP_INIT(0x70),
|
|
||||||
VAR_INIT(0x71),
|
|
||||||
CAP_FREE(0x72),
|
|
||||||
VAR_FREE(0x73);
|
|
||||||
|
|
||||||
private static final HashMap<Integer, Type> types = new HashMap<>();
|
private static final HashMap<Integer, Type> types = new HashMap<>();
|
||||||
public final int numeric;
|
public final int numeric;
|
||||||
@@ -88,8 +80,19 @@ public class Instruction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static enum BreakpointType {
|
public static enum BreakpointType {
|
||||||
|
/**
|
||||||
|
* A debugger should never stop at such instruction, unless a breakpoint has been set on it
|
||||||
|
*/
|
||||||
NONE,
|
NONE,
|
||||||
|
/**
|
||||||
|
* Debuggers should pause at instructions marked with this breakpoint type
|
||||||
|
* after any step command
|
||||||
|
*/
|
||||||
STEP_OVER,
|
STEP_OVER,
|
||||||
|
/**
|
||||||
|
* Debuggers should pause at instructions marked with this breakpoint type
|
||||||
|
* only after a step-in command
|
||||||
|
*/
|
||||||
STEP_IN;
|
STEP_IN;
|
||||||
|
|
||||||
public boolean shouldStepIn() {
|
public boolean shouldStepIn() {
|
||||||
@@ -108,205 +111,85 @@ public class Instruction {
|
|||||||
if (i >= params.length || i < 0) return null;
|
if (i >= params.length || i < 0) return null;
|
||||||
return (T)params[i];
|
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:
|
|
||||||
// case GLOB_SET:
|
|
||||||
// rawType |= (boolean)get(0) ? 128 : 0; break;
|
|
||||||
// case TYPEOF: rawType |= params.length > 0 ? 128 : 0; break;
|
|
||||||
// default:
|
|
||||||
// }
|
|
||||||
|
|
||||||
// writer.writeByte(rawType);
|
|
||||||
|
|
||||||
// switch (type) {
|
|
||||||
// case CALL:
|
|
||||||
// case CALL_NEW:
|
|
||||||
// case CALL_MEMBER:
|
|
||||||
// writer.writeInt(get(0));
|
|
||||||
// writer.writeUTF(get(1));
|
|
||||||
// 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));
|
|
||||||
// writer.writeUTF(get(0));
|
|
||||||
// break;
|
|
||||||
// }
|
|
||||||
// case LOAD_REGEX: writer.writeUTF(get(0)); break;
|
|
||||||
// case LOAD_VAR: writer.writeInt(get(0)); break;
|
|
||||||
// case GLOB_DEF: writer.writeUTF(get(0)); break;
|
|
||||||
// case GLOB_GET: writer.writeUTF(get(0)); break;
|
|
||||||
// case GLOB_SET:
|
|
||||||
// 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_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) {
|
private Instruction(Type type, Object ...params) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.params = params;
|
this.params = params;
|
||||||
}
|
}
|
||||||
|
|
||||||
// public static Instruction read(DataInputStream stream) throws IOException {
|
/**
|
||||||
// var rawType = stream.readUnsignedByte();
|
* Signals the start of a protected context
|
||||||
// var type = Type.fromNumeric(rawType & 127);
|
* @param catchStart The point to witch to jump if an error has been caught
|
||||||
// var flag = (rawType & 128) != 0;
|
* @param finallyStart The point to witch to jump after either the try or catch bodies have exited
|
||||||
|
* @param end The point to which to jump after exiting the whole protected context
|
||||||
// switch (type) {
|
*/
|
||||||
// case CALL: return call(stream.readInt(), stream.readUTF());
|
|
||||||
// case CALL_NEW: return callNew(stream.readInt(), stream.readUTF());
|
|
||||||
// case CALL_MEMBER: return callNew(stream.readInt(), stream.readUTF());
|
|
||||||
// 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(), stream.readUTF(), 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 GLOB_DEF: return globDef(stream.readUTF());
|
|
||||||
// case GLOB_GET: return globGet(stream.readUTF());
|
|
||||||
// case GLOB_SET: return globSet(stream.readUTF(), flag);
|
|
||||||
// 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_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(), 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) {
|
public static Instruction tryStart(int catchStart, int finallyStart, int end) {
|
||||||
return new Instruction(Type.TRY_START, catchStart, finallyStart, end);
|
return new Instruction(Type.TRY_START, catchStart, finallyStart, end);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Signifies that the current protected section (try, catch or finally) has ended
|
||||||
|
*/
|
||||||
public static Instruction tryEnd() {
|
public static Instruction tryEnd() {
|
||||||
return new Instruction(Type.TRY_END);
|
return new Instruction(Type.TRY_END);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Throws the top stack value
|
||||||
|
*/
|
||||||
public static Instruction throwInstr() {
|
public static Instruction throwInstr() {
|
||||||
return new Instruction(Type.THROW);
|
return new Instruction(Type.THROW);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Converts the given exception to a runtime syntax error and throws it
|
||||||
|
*/
|
||||||
public static Instruction throwSyntax(SyntaxException err) {
|
public static Instruction throwSyntax(SyntaxException err) {
|
||||||
return new Instruction(Type.THROW_SYNTAX, err.getMessage());
|
return new Instruction(Type.THROW_SYNTAX, err.getMessage());
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Converts the given exception to a runtime syntax error and throws it
|
||||||
|
*/
|
||||||
public static Instruction throwSyntax(String err) {
|
public static Instruction throwSyntax(String err) {
|
||||||
return new Instruction(Type.THROW_SYNTAX, err);
|
return new Instruction(Type.THROW_SYNTAX, err);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Converts the given exception to a runtime syntax error and throws it
|
||||||
|
*/
|
||||||
public static Instruction throwSyntax(Location loc, String err) {
|
public static Instruction throwSyntax(Location loc, String err) {
|
||||||
return new Instruction(Type.THROW_SYNTAX, new SyntaxException(loc, err).getMessage());
|
return new Instruction(Type.THROW_SYNTAX, new SyntaxException(loc, err).getMessage());
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Performs a JS object property deletion.
|
||||||
|
* Operands:
|
||||||
|
* 1. Object to manipulate
|
||||||
|
* 2. Key to delete
|
||||||
|
*/
|
||||||
public static Instruction delete() {
|
public static Instruction delete() {
|
||||||
return new Instruction(Type.DELETE);
|
return new Instruction(Type.DELETE);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Returns the top stack value
|
||||||
|
*/
|
||||||
public static Instruction ret() {
|
public static Instruction ret() {
|
||||||
return new Instruction(Type.RETURN);
|
return new Instruction(Type.RETURN);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* A special NOP instruction telling any debugger to pause
|
||||||
|
*/
|
||||||
public static Instruction debug() {
|
public static Instruction debug() {
|
||||||
return new Instruction(Type.NOP, "debug");
|
return new Instruction(Type.NOP, "debug");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does nothing. May be used for metadata or implementation-specific instructions that don't alter the behavior
|
||||||
|
*/
|
||||||
public static Instruction nop(Object ...params) {
|
public static Instruction nop(Object ...params) {
|
||||||
return new Instruction(Type.NOP, params);
|
return new Instruction(Type.NOP, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Instruction call(int argn, boolean hasSelf, String name) {
|
|
||||||
return new Instruction(Type.CALL, argn, hasSelf, name);
|
|
||||||
}
|
|
||||||
public static Instruction call(int argn, boolean hasSelf) {
|
public static Instruction call(int argn, boolean hasSelf) {
|
||||||
return call(argn, hasSelf, "");
|
return new Instruction(Type.CALL, argn, hasSelf);
|
||||||
}
|
|
||||||
@Deprecated
|
|
||||||
public static Instruction callMember(int argn, String name) {
|
|
||||||
return new Instruction(Type.CALL_MEMBER, argn, name);
|
|
||||||
}
|
|
||||||
@Deprecated
|
|
||||||
public static Instruction callMember(int argn) {
|
|
||||||
return new Instruction(Type.CALL_MEMBER, argn, "");
|
|
||||||
}
|
|
||||||
public static Instruction callNew(int argn, String name) {
|
|
||||||
return new Instruction(Type.CALL_NEW, argn, name);
|
|
||||||
}
|
}
|
||||||
public static Instruction callNew(int argn) {
|
public static Instruction callNew(int argn) {
|
||||||
return new Instruction(Type.CALL_NEW, argn, "");
|
return new Instruction(Type.CALL_NEW, argn);
|
||||||
}
|
|
||||||
public static Instruction callSuper(int argn) {
|
|
||||||
return new Instruction(Type.CALL_SUPER, argn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Instruction jmp(int offset) {
|
public static Instruction jmp(int offset) {
|
||||||
@@ -362,14 +245,30 @@ public class Instruction {
|
|||||||
public static Instruction loadThis() {
|
public static Instruction loadThis() {
|
||||||
return new Instruction(Type.LOAD_THIS);
|
return new Instruction(Type.LOAD_THIS);
|
||||||
}
|
}
|
||||||
public static Instruction loadArgs(boolean real) {
|
/**
|
||||||
return new Instruction(Type.LOAD_ARGS, real);
|
* Loads the given argument
|
||||||
|
* @param i The index of the argument to load. If -1, will get the index from the stack instead
|
||||||
|
*/
|
||||||
|
public static Instruction loadArg(int i) {
|
||||||
|
return new Instruction(Type.LOAD_ARG, i);
|
||||||
}
|
}
|
||||||
public static Instruction loadRestArgs(int offset) {
|
/**
|
||||||
return new Instruction(Type.LOAD_REST_ARGS, offset);
|
* Pushes the amount of arguments to the stack
|
||||||
|
*/
|
||||||
|
public static Instruction loadArgsN() {
|
||||||
|
return new Instruction(Type.LOAD_ARGS_N);
|
||||||
}
|
}
|
||||||
public static Instruction loadCallee() {
|
/**
|
||||||
return new Instruction(Type.LOAD_CALLEE);
|
* Pushes the arguments object to the stack
|
||||||
|
*/
|
||||||
|
public static Instruction loadArgs() {
|
||||||
|
return new Instruction(Type.LOAD_ARGS);
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Loads a reference to the function being called
|
||||||
|
*/
|
||||||
|
public static Instruction loadCalled() {
|
||||||
|
return new Instruction(Type.LOAD_CALLED);
|
||||||
}
|
}
|
||||||
public static Instruction loadGlob() {
|
public static Instruction loadGlob() {
|
||||||
return new Instruction(Type.LOAD_GLOB);
|
return new Instruction(Type.LOAD_GLOB);
|
||||||
@@ -394,17 +293,11 @@ public class Instruction {
|
|||||||
return new Instruction(Type.LOAD_REGEX, pattern, flags);
|
return new Instruction(Type.LOAD_REGEX, pattern, flags);
|
||||||
}
|
}
|
||||||
// TODO: make this capturing a concern of the compiler
|
// TODO: make this capturing a concern of the compiler
|
||||||
public static Instruction loadFunc(int id, boolean callable, boolean constructible, boolean captureThis, boolean noThis, String name, int[] captures) {
|
public static Instruction loadFunc(int id, String name, int[] captures) {
|
||||||
if (name == null) name = "";
|
var args = new Object[2 + captures.length];
|
||||||
|
|
||||||
var args = new Object[6 + captures.length];
|
|
||||||
args[0] = id;
|
args[0] = id;
|
||||||
args[1] = name;
|
args[1] = name;
|
||||||
args[2] = callable;
|
for (var i = 0; i < captures.length; i++) args[i + 2] = captures[i];
|
||||||
args[3] = constructible;
|
|
||||||
args[4] = captureThis;
|
|
||||||
args[5] = noThis;
|
|
||||||
for (var i = 0; i < captures.length; i++) args[i + 6] = captures[i];
|
|
||||||
return new Instruction(Type.LOAD_FUNC, args);
|
return new Instruction(Type.LOAD_FUNC, args);
|
||||||
}
|
}
|
||||||
public static Instruction loadObj() {
|
public static Instruction loadObj() {
|
||||||
@@ -420,9 +313,6 @@ public class Instruction {
|
|||||||
return new Instruction(Type.DUP, count, offset);
|
return new Instruction(Type.DUP, count, offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
// public static Instruction storeVar(int i) {
|
|
||||||
// return new Instruction(Type.STORE_VAR, i, false);
|
|
||||||
// }
|
|
||||||
public static Instruction storeVar(int i, boolean keep, boolean initialize) {
|
public static Instruction storeVar(int i, boolean keep, boolean initialize) {
|
||||||
return new Instruction(Type.STORE_VAR, i, keep, initialize);
|
return new Instruction(Type.STORE_VAR, i, keep, initialize);
|
||||||
}
|
}
|
||||||
@@ -463,36 +353,17 @@ public class Instruction {
|
|||||||
return new Instruction(Type.KEYS, own, onlyEnumerable);
|
return new Instruction(Type.KEYS, own, onlyEnumerable);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Instruction defProp(boolean setter, boolean enumerable) {
|
public static Instruction defProp(boolean setter) {
|
||||||
return new Instruction(Type.DEF_PROP, setter, enumerable);
|
return new Instruction(Type.DEF_PROP, setter);
|
||||||
}
|
}
|
||||||
public static Instruction defField(boolean enumerable) {
|
public static Instruction defField() {
|
||||||
return new Instruction(Type.DEF_FIELD, enumerable);
|
return new Instruction(Type.DEF_FIELD);
|
||||||
}
|
|
||||||
public static Instruction extend() {
|
|
||||||
return new Instruction(Type.EXTEND);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Instruction operation(Operation op) {
|
public static Instruction operation(Operation op) {
|
||||||
return new Instruction(Type.OPERATION, op);
|
return new Instruction(Type.OPERATION, op);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Instruction capFree(int i) {
|
|
||||||
return new Instruction(Type.CAP_FREE, i);
|
|
||||||
}
|
|
||||||
public static Instruction varFree(int i) {
|
|
||||||
return new Instruction(Type.VAR_FREE, i);
|
|
||||||
}
|
|
||||||
public static Instruction varInit(int i, boolean force) {
|
|
||||||
return new Instruction(Type.VAR_INIT, i, force);
|
|
||||||
}
|
|
||||||
// public static Instruction stackAlloc(int start, int n) {
|
|
||||||
// return new Instruction(Type.STACK_ALLOC, start, start + n);
|
|
||||||
// }
|
|
||||||
// public static Instruction stackRealloc(int start, int n) {
|
|
||||||
// return new Instruction(Type.STACK_REALLOC, start, start + n);
|
|
||||||
// }
|
|
||||||
|
|
||||||
@Override public String toString() {
|
@Override public String toString() {
|
||||||
var res = type.toString();
|
var res = type.toString();
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ public class Reading {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] streamToBytes(InputStream in) {
|
public static byte[] streamToBytes(InputStream in) {
|
||||||
|
if (in == null) return null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
List<byte[]> bufs = null;
|
List<byte[]> bufs = null;
|
||||||
byte[] result = null;
|
byte[] result = null;
|
||||||
@@ -73,7 +75,9 @@ public class Reading {
|
|||||||
catch (IOException e) { throw new UncheckedIOException(e); }
|
catch (IOException e) { throw new UncheckedIOException(e); }
|
||||||
}
|
}
|
||||||
public static String streamToString(InputStream in) {
|
public static String streamToString(InputStream in) {
|
||||||
return new String(streamToBytes(in));
|
var bytes = streamToBytes(in);
|
||||||
|
if (bytes == null) return null;
|
||||||
|
else return new String(bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static InputStream resourceToStream(String name) {
|
public static InputStream resourceToStream(String name) {
|
||||||
@@ -82,4 +86,7 @@ public class Reading {
|
|||||||
public static String resourceToString(String name) {
|
public static String resourceToString(String name) {
|
||||||
return streamToString(resourceToStream(name));
|
return streamToString(resourceToStream(name));
|
||||||
}
|
}
|
||||||
|
public static byte[] resourceToBytes(String name) {
|
||||||
|
return streamToBytes(resourceToStream(name));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package me.topchetoeu.jscript.common.environment;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Random;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
@@ -12,71 +11,13 @@ public class Environment {
|
|||||||
private final Map<Key<Object>, Object> map = new HashMap<>();
|
private final Map<Key<Object>, Object> map = new HashMap<>();
|
||||||
private final Set<Key<Object>> hidden = new HashSet<>();
|
private final Set<Key<Object>> hidden = new HashSet<>();
|
||||||
|
|
||||||
private final Map<MultiKey<Object>, Set<Object>> multi = new HashMap<>();
|
|
||||||
private final Map<MultiKey<Object>, Set<Object>> multiHidden = new HashMap<>();
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
private <T> Set<T> getAll(MultiKey<T> key, boolean forceClone) {
|
|
||||||
Set<T> parent = null, child = null;
|
|
||||||
boolean cloned = false;
|
|
||||||
|
|
||||||
if (this.parent != null && !hidden.contains(key)) {
|
|
||||||
parent = this.parent.getAll(key, false);
|
|
||||||
if (parent.size() == 0) parent = null;
|
|
||||||
else if (multiHidden.containsKey(key)) {
|
|
||||||
parent = new HashSet<>(parent);
|
|
||||||
parent.removeAll(multiHidden.get(key));
|
|
||||||
cloned = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (multi.containsKey(key)) {
|
|
||||||
child = (Set<T>)multi.get(key);
|
|
||||||
if (child.size() == 0) child = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!forceClone) {
|
|
||||||
if (parent == null && child == null) return new HashSet<>();
|
|
||||||
if (parent == null && child != null) return child;
|
|
||||||
if (parent != null && child == null) return parent;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cloned) parent = new HashSet<>();
|
|
||||||
parent.addAll(child);
|
|
||||||
return parent;
|
|
||||||
}
|
|
||||||
private <T> T getMulti(MultiKey<T> key) {
|
|
||||||
return key.of(getAll(key, false));
|
|
||||||
}
|
|
||||||
private boolean hasMulti(MultiKey<?> key) {
|
|
||||||
return getAll(key, false).size() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("all")
|
|
||||||
private <T> Environment addMulti(MultiKey<T> key, T value) {
|
|
||||||
if (!multi.containsKey(key)) {
|
|
||||||
if (hidden.contains(key)) {
|
|
||||||
multiHidden.put((MultiKey)key, (Set)parent.getAll(key, true));
|
|
||||||
hidden.remove(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
multi.put((MultiKey)key, new HashSet<>());
|
|
||||||
}
|
|
||||||
|
|
||||||
multi.get(key).add(value);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T> T get(Key<T> key) {
|
public <T> T get(Key<T> key) {
|
||||||
if (key instanceof MultiKey) return getMulti((MultiKey<T>)key);
|
|
||||||
|
|
||||||
if (map.containsKey(key)) return (T)map.get(key);
|
if (map.containsKey(key)) return (T)map.get(key);
|
||||||
else if (!hidden.contains(key) && parent != null) return parent.get(key);
|
else if (!hidden.contains(key) && parent != null) return parent.get(key);
|
||||||
else return null;
|
else return null;
|
||||||
}
|
}
|
||||||
public boolean has(Key<?> key) {
|
public boolean has(Key<?> key) {
|
||||||
if (key instanceof MultiKey) return hasMulti((MultiKey<?>)key);
|
|
||||||
|
|
||||||
if (map.containsKey(key)) return true;
|
if (map.containsKey(key)) return true;
|
||||||
else if (!hidden.contains(key) && parent != null) return parent.has(key);
|
else if (!hidden.contains(key) && parent != null) return parent.has(key);
|
||||||
else return false;
|
else return false;
|
||||||
@@ -97,8 +38,6 @@ public class Environment {
|
|||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public <T> Environment add(Key<T> key, T val) {
|
public <T> Environment add(Key<T> key, T val) {
|
||||||
if (key instanceof MultiKey) return add(key, val);
|
|
||||||
|
|
||||||
map.put((Key<Object>)key, val);
|
map.put((Key<Object>)key, val);
|
||||||
hidden.remove(key);
|
hidden.remove(key);
|
||||||
return this;
|
return this;
|
||||||
@@ -108,14 +47,6 @@ public class Environment {
|
|||||||
}
|
}
|
||||||
@SuppressWarnings("all")
|
@SuppressWarnings("all")
|
||||||
public Environment addAll(Map<Key<?>, ?> map, boolean iterableAsMulti) {
|
public Environment addAll(Map<Key<?>, ?> map, boolean iterableAsMulti) {
|
||||||
for (var pair : map.entrySet()) {
|
|
||||||
if (iterableAsMulti && pair.getKey() instanceof MultiKey && pair.getValue() instanceof Iterable) {
|
|
||||||
for (var val : (Iterable<?>)pair.getValue()) {
|
|
||||||
addMulti((MultiKey<Object>)pair.getKey(), val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else add((Key<Object>)pair.getKey(), pair.getValue());
|
|
||||||
}
|
|
||||||
map.putAll((Map)map);
|
map.putAll((Map)map);
|
||||||
hidden.removeAll(map.keySet());
|
hidden.removeAll(map.keySet());
|
||||||
return this;
|
return this;
|
||||||
@@ -127,26 +58,9 @@ public class Environment {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public Environment remove(Key<?> key) {
|
public Environment remove(Key<?> key) {
|
||||||
map.remove(key);
|
map.remove(key);
|
||||||
multi.remove(key);
|
|
||||||
multiHidden.remove(key);
|
|
||||||
hidden.add((Key<Object>)key);
|
hidden.add((Key<Object>)key);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
@SuppressWarnings("all")
|
|
||||||
public <T> Environment remove(MultiKey<T> key, T val) {
|
|
||||||
if (multi.containsKey(key)) {
|
|
||||||
multi.get(key).remove(val);
|
|
||||||
multiHidden.get(key).add(val);
|
|
||||||
|
|
||||||
if (multi.get(key).size() == 0) {
|
|
||||||
multi.remove(key);
|
|
||||||
multiHidden.remove(key);
|
|
||||||
hidden.add((Key)key);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public <T> T init(Key<T> key, T val) {
|
public <T> T init(Key<T> key, T val) {
|
||||||
if (!has(key)) this.add(key, val);
|
if (!has(key)) this.add(key, val);
|
||||||
@@ -180,8 +94,4 @@ public class Environment {
|
|||||||
public static Environment empty() {
|
public static Environment empty() {
|
||||||
return new Environment();
|
return new Environment();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static int nextId() {
|
|
||||||
return new Random().nextInt();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,3 @@
|
|||||||
package me.topchetoeu.jscript.common.environment;
|
package me.topchetoeu.jscript.common.environment;
|
||||||
|
|
||||||
public interface Key<T> {
|
public final class Key<T> { }
|
||||||
public static <T> Key<T> of() {
|
|
||||||
return new Key<>() { };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.common.environment;
|
|
||||||
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
public interface MultiKey<T> extends Key<T> {
|
|
||||||
public T of(Set<T> values);
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,7 @@ import java.util.stream.Collectors;
|
|||||||
|
|
||||||
import me.topchetoeu.jscript.common.SyntaxException;
|
import me.topchetoeu.jscript.common.SyntaxException;
|
||||||
import me.topchetoeu.jscript.common.parsing.Filename;
|
import me.topchetoeu.jscript.common.parsing.Filename;
|
||||||
|
import me.topchetoeu.jscript.common.parsing.Location;
|
||||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||||
import me.topchetoeu.jscript.common.parsing.Source;
|
import me.topchetoeu.jscript.common.parsing.Source;
|
||||||
@@ -41,7 +42,7 @@ public class JSON {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ParseRes<JSONMap> parseMap(Source src, int i) {
|
public static ParseRes<JSONElement> parseMap(Source src, int i) {
|
||||||
var n = Parsing.skipEmpty(src, i);
|
var n = Parsing.skipEmpty(src, i);
|
||||||
|
|
||||||
if (!src.is(i + n, "{")) return ParseRes.failed();
|
if (!src.is(i + n, "{")) return ParseRes.failed();
|
||||||
@@ -49,7 +50,7 @@ public class JSON {
|
|||||||
|
|
||||||
var values = new JSONMap();
|
var values = new JSONMap();
|
||||||
|
|
||||||
if (src.is(i + n, "}")) return ParseRes.res(new JSONMap(new HashMap<>()), n + 1);
|
if (src.is(i + n, "}")) return ParseRes.res(JSONElement.map(new JSONMap(new HashMap<>())), n + 1);
|
||||||
while (true) {
|
while (true) {
|
||||||
var name = parseString(src, i + n);
|
var name = parseString(src, i + n);
|
||||||
if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected an index");
|
if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected an index");
|
||||||
@@ -72,16 +73,16 @@ public class JSON {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParseRes.res(values, n);
|
return ParseRes.res(JSONElement.map(values), n);
|
||||||
}
|
}
|
||||||
public static ParseRes<JSONList> parseList(Source src, int i) {
|
public static ParseRes<JSONElement> parseList(Source src, int i) {
|
||||||
var n = Parsing.skipEmpty(src, i);
|
var n = Parsing.skipEmpty(src, i);
|
||||||
|
|
||||||
if (!src.is(i + n++, "[]")) return ParseRes.failed();
|
if (!src.is(i + n++, "[")) return ParseRes.failed();
|
||||||
|
|
||||||
var values = new JSONList();
|
var values = new JSONList();
|
||||||
|
|
||||||
if (src.is(i + n, "]")) return ParseRes.res(new JSONList(), n + 1);
|
if (src.is(i + n, "]")) return ParseRes.res(JSONElement.list(new JSONList()), n + 1);
|
||||||
while (true) {
|
while (true) {
|
||||||
var res = parseValue(src, i + n);
|
var res = parseValue(src, i + n);
|
||||||
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a list element");
|
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a list element");
|
||||||
@@ -96,14 +97,14 @@ public class JSON {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParseRes.res(values, n);
|
return ParseRes.res(JSONElement.list(values), n);
|
||||||
}
|
}
|
||||||
public static JSONElement parse(Filename filename, String raw) {
|
public static JSONElement parse(Filename filename, String raw) {
|
||||||
if (filename == null) filename = new Filename("jscript", "json");
|
if (filename == null) filename = new Filename("jscript", "json");
|
||||||
|
|
||||||
var res = parseValue(new Source(null, filename, raw), 0);
|
var res = parseValue(new Source(null, filename, raw), 0);
|
||||||
if (res.isFailed()) throw new SyntaxException(null, "Invalid JSON given.");
|
if (res.isFailed()) throw new SyntaxException(Location.of(filename, 0, 0), "Invalid JSON given");
|
||||||
else if (res.isError()) throw new SyntaxException(null, res.error);
|
else if (res.isError()) throw new SyntaxException(res.errorLocation, res.error);
|
||||||
else return JSONElement.of(res.result);
|
else return JSONElement.of(res.result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,13 +29,14 @@ public class JSONElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static JSONElement of(Object val) {
|
public static JSONElement of(Object val) {
|
||||||
if (val instanceof JSONMap) return map((JSONMap)val);
|
if (val instanceof JSONElement el) return el;
|
||||||
else if (val instanceof JSONList) return list((JSONList)val);
|
else if (val instanceof JSONMap map) return map(map);
|
||||||
else if (val instanceof String) return string((String)val);
|
else if (val instanceof JSONList list) return list(list);
|
||||||
else if (val instanceof Boolean) return bool((Boolean)val);
|
else if (val instanceof String str) return string(str);
|
||||||
else if (val instanceof Number) return number(((Number)val).doubleValue());
|
else if (val instanceof Boolean bool) return bool(bool);
|
||||||
|
else if (val instanceof Number num) return number(num.doubleValue());
|
||||||
else if (val == null) return NULL;
|
else if (val == null) return NULL;
|
||||||
else throw new IllegalArgumentException("val must be: String, Boolean, Number, JSONList or JSONMap.");
|
else throw new IllegalArgumentException("val must be: String, Boolean, Number, JSONList or JSONMap");
|
||||||
}
|
}
|
||||||
|
|
||||||
public final Type type;
|
public final Type type;
|
||||||
@@ -49,23 +50,23 @@ public class JSONElement {
|
|||||||
public boolean isNull() { return type == Type.NULL; }
|
public boolean isNull() { return type == Type.NULL; }
|
||||||
|
|
||||||
public JSONMap map() {
|
public JSONMap map() {
|
||||||
if (!isMap()) throw new IllegalStateException("Element is not a map.");
|
if (!isMap()) throw new IllegalStateException("Element is not a map");
|
||||||
return (JSONMap)value;
|
return (JSONMap)value;
|
||||||
}
|
}
|
||||||
public JSONList list() {
|
public JSONList list() {
|
||||||
if (!isList()) throw new IllegalStateException("Element is not a map.");
|
if (!isList()) throw new IllegalStateException("Element is not a map");
|
||||||
return (JSONList)value;
|
return (JSONList)value;
|
||||||
}
|
}
|
||||||
public String string() {
|
public String string() {
|
||||||
if (!isString()) throw new IllegalStateException("Element is not a string.");
|
if (!isString()) throw new IllegalStateException("Element is not a string");
|
||||||
return (String)value;
|
return (String)value;
|
||||||
}
|
}
|
||||||
public double number() {
|
public double number() {
|
||||||
if (!isNumber()) throw new IllegalStateException("Element is not a number.");
|
if (!isNumber()) throw new IllegalStateException("Element is not a number");
|
||||||
return (double)value;
|
return (double)value;
|
||||||
}
|
}
|
||||||
public boolean bool() {
|
public boolean bool() {
|
||||||
if (!isBoolean()) throw new IllegalStateException("Element is not a boolean.");
|
if (!isBoolean()) throw new IllegalStateException("Element is not a boolean");
|
||||||
return (boolean)value;
|
return (boolean)value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,16 +9,14 @@ public class JSONMap implements Map<String, JSONElement> {
|
|||||||
private Map<String, JSONElement> elements = new HashMap<>();
|
private Map<String, JSONElement> elements = new HashMap<>();
|
||||||
|
|
||||||
public JSONElement get(String path) {
|
public JSONElement get(String path) {
|
||||||
var curr = this;
|
JSONElement curr = JSONElement.map(this);
|
||||||
var segs = path.split("\\.");
|
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
while (true) {
|
for (var seg : path.split("\\.")) {
|
||||||
var tmp = curr.elements.get(segs[i++]);
|
if (!curr.isMap()) return null;
|
||||||
if (i == segs.length) return tmp;
|
curr = curr.map().elements.get(seg);
|
||||||
if (!tmp.isMap()) return null;
|
|
||||||
curr = tmp.map();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return curr;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMap(String path) {
|
public boolean isMap(String path) {
|
||||||
@@ -51,7 +49,7 @@ public class JSONMap implements Map<String, JSONElement> {
|
|||||||
|
|
||||||
public JSONMap map(String path) {
|
public JSONMap map(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path));
|
||||||
return el.map();
|
return el.map();
|
||||||
}
|
}
|
||||||
public JSONMap map(String path, JSONMap defaultVal) {
|
public JSONMap map(String path, JSONMap defaultVal) {
|
||||||
@@ -63,7 +61,7 @@ public class JSONMap implements Map<String, JSONElement> {
|
|||||||
|
|
||||||
public JSONList list(String path) {
|
public JSONList list(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path));
|
||||||
return el.list();
|
return el.list();
|
||||||
}
|
}
|
||||||
public JSONList list(String path, JSONList defaultVal) {
|
public JSONList list(String path, JSONList defaultVal) {
|
||||||
@@ -75,7 +73,7 @@ public class JSONMap implements Map<String, JSONElement> {
|
|||||||
|
|
||||||
public String string(String path) {
|
public String string(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path));
|
||||||
return el.string();
|
return el.string();
|
||||||
}
|
}
|
||||||
public String string(String path, String defaultVal) {
|
public String string(String path, String defaultVal) {
|
||||||
@@ -87,7 +85,7 @@ public class JSONMap implements Map<String, JSONElement> {
|
|||||||
|
|
||||||
public double number(String path) {
|
public double number(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path));
|
||||||
return el.number();
|
return el.number();
|
||||||
}
|
}
|
||||||
public double number(String path, double defaultVal) {
|
public double number(String path, double defaultVal) {
|
||||||
@@ -99,7 +97,7 @@ public class JSONMap implements Map<String, JSONElement> {
|
|||||||
|
|
||||||
public boolean bool(String path) {
|
public boolean bool(String path) {
|
||||||
var el = get(path);
|
var el = get(path);
|
||||||
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path));
|
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path));
|
||||||
return el.bool();
|
return el.bool();
|
||||||
}
|
}
|
||||||
public boolean bool(String path, boolean defaultVal) {
|
public boolean bool(String path, boolean defaultVal) {
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.common.mapping;
|
|
||||||
|
|
||||||
public enum ConvertType {
|
|
||||||
Exact,
|
|
||||||
Lower,
|
|
||||||
Upper,
|
|
||||||
Both,
|
|
||||||
}
|
|
||||||
@@ -9,13 +9,13 @@ import java.util.NavigableSet;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.common.parsing.Filename;
|
import me.topchetoeu.jscript.common.parsing.Filename;
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
import me.topchetoeu.jscript.common.parsing.Location;
|
||||||
import me.topchetoeu.jscript.compilation.scope.Scope;
|
|
||||||
|
|
||||||
public class FunctionMap {
|
public class FunctionMap {
|
||||||
public static class FunctionMapBuilder {
|
public static class FunctionMapBuilder {
|
||||||
@@ -51,14 +51,35 @@ public class FunctionMap {
|
|||||||
return sourceMap.lastEntry().getValue();
|
return sourceMap.lastEntry().getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionMap build(String[] localNames, String[] captureNames) {
|
public FunctionMapBuilder map(Function<Location, Location> mapper) {
|
||||||
return new FunctionMap(sourceMap, breakpoints, localNames, captureNames);
|
var newSourceMaps = new HashMap<Integer, Location>();
|
||||||
|
var newBreakpoints = new HashMap<Location, BreakpointType>();
|
||||||
|
|
||||||
|
for (var key : sourceMap.keySet()) {
|
||||||
|
var mapped = mapper.apply(sourceMap.get(key));
|
||||||
|
if (mapped == null) continue;
|
||||||
|
newSourceMaps.put(key, mapped);
|
||||||
}
|
}
|
||||||
public FunctionMap build(Scope scope) {
|
for (var key : breakpoints.keySet()) {
|
||||||
return new FunctionMap(sourceMap, breakpoints, new String[0], new String[0]);
|
var mapped = mapper.apply(key);
|
||||||
|
if (mapped == null) continue;
|
||||||
|
newBreakpoints.put(mapped, breakpoints.get(key));
|
||||||
}
|
}
|
||||||
public FunctionMap build() {
|
|
||||||
return new FunctionMap(sourceMap, breakpoints, new String[0], new String[0]);
|
sourceMap.clear();
|
||||||
|
sourceMap.putAll(newSourceMaps);
|
||||||
|
|
||||||
|
breakpoints.clear();
|
||||||
|
breakpoints.putAll(newBreakpoints);
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FunctionMap build(String[] localNames, String[] capturableNames, String[] captureNames) {
|
||||||
|
return new FunctionMap(sourceMap, breakpoints, localNames, capturableNames, captureNames);
|
||||||
|
}
|
||||||
|
public FunctionMap build(Function<Location, Location> mapper) {
|
||||||
|
return new FunctionMap(sourceMap, breakpoints, new String[0], new String[0], new String[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private FunctionMapBuilder() { }
|
private FunctionMapBuilder() { }
|
||||||
@@ -71,7 +92,7 @@ public class FunctionMap {
|
|||||||
|
|
||||||
private final TreeMap<Integer, Location> pcToLoc = new TreeMap<>();
|
private final TreeMap<Integer, Location> pcToLoc = new TreeMap<>();
|
||||||
|
|
||||||
public final String[] localNames, captureNames;
|
public final String[] localNames, capturableNames, captureNames;
|
||||||
|
|
||||||
public Location toLocation(int pc, boolean approxiamte) {
|
public Location toLocation(int pc, boolean approxiamte) {
|
||||||
if (pcToLoc.size() == 0 || pc < 0 || pc > pcToLoc.lastKey()) return null;
|
if (pcToLoc.size() == 0 || pc < 0 || pc > pcToLoc.lastKey()) return null;
|
||||||
@@ -131,30 +152,8 @@ public class FunctionMap {
|
|||||||
return pcToLoc.lastEntry().getValue();
|
return pcToLoc.lastEntry().getValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
// public static FunctionMap apply(FunctionMap funcMap, SourceMap map) {
|
|
||||||
// var res = new FunctionMap(Map.of(), Map.of(), funcMap.localNames, funcMap.captureNames);
|
|
||||||
|
|
||||||
// for (var el : funcMap.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() {
|
public FunctionMap clone() {
|
||||||
var res = new FunctionMap(new HashMap<>(), new HashMap<>(), localNames, captureNames);
|
var res = new FunctionMap(new HashMap<>(), new HashMap<>(), localNames, capturableNames, captureNames);
|
||||||
res.pcToLoc.putAll(this.pcToLoc);
|
res.pcToLoc.putAll(this.pcToLoc);
|
||||||
res.bps.putAll(bps);
|
res.bps.putAll(bps);
|
||||||
res.bpLocs.putAll(bpLocs);
|
res.bpLocs.putAll(bpLocs);
|
||||||
@@ -162,7 +161,7 @@ public class FunctionMap {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionMap(Map<Integer, Location> map, Map<Location, BreakpointType> breakpoints, String[] localNames, String[] captureNames) {
|
public FunctionMap(Map<Integer, Location> map, Map<Location, BreakpointType> breakpoints, String[] localNames, String[] capturableNames, String[] captureNames) {
|
||||||
var locToPc = new HashMap<Location, Integer>();
|
var locToPc = new HashMap<Location, Integer>();
|
||||||
|
|
||||||
for (var el : map.entrySet()) {
|
for (var el : map.entrySet()) {
|
||||||
@@ -180,10 +179,12 @@ public class FunctionMap {
|
|||||||
|
|
||||||
this.localNames = localNames;
|
this.localNames = localNames;
|
||||||
this.captureNames = captureNames;
|
this.captureNames = captureNames;
|
||||||
|
this.capturableNames = capturableNames;
|
||||||
}
|
}
|
||||||
private FunctionMap() {
|
private FunctionMap() {
|
||||||
localNames = new String[0];
|
localNames = new String[0];
|
||||||
captureNames = new String[0];
|
captureNames = new String[0];
|
||||||
|
capturableNames = new String[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static FunctionMapBuilder builder() {
|
public static FunctionMapBuilder builder() {
|
||||||
|
|||||||
@@ -47,12 +47,6 @@ public class Filename {
|
|||||||
if (i >= 0) return new Filename(val.substring(0, i).trim(), val.substring(i + 3).trim());
|
if (i >= 0) return new Filename(val.substring(0, i).trim(), val.substring(i + 3).trim());
|
||||||
else return new Filename("file", val.trim());
|
else return new Filename("file", val.trim());
|
||||||
}
|
}
|
||||||
// public static Path normalize(String path) {
|
|
||||||
// // File file = new File("/" + path.trim().replace("\\", "/"));
|
|
||||||
// // String normalizedPath = new File("/" + path.trim().replace("\\", "/")).getAbsolutePath().replaceFirst("^/", "").replace("\\", "/");
|
|
||||||
// // return normalizedPath;
|
|
||||||
// return Path.of(Path.of("/" + path.trim().replace("\\", "/")).normalize().toString().substring(1));
|
|
||||||
// }
|
|
||||||
public static Filename fromFile(File file) {
|
public static Filename fromFile(File file) {
|
||||||
return new Filename("file", file.getAbsolutePath());
|
return new Filename("file", file.getAbsolutePath());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,12 +30,15 @@ public abstract class Location implements Comparable<Location> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
public final Location nextLine() {
|
public final Location nextLine() {
|
||||||
|
return changeLine(1);
|
||||||
|
}
|
||||||
|
public final Location changeLine(int offset) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
return new Location() {
|
return new Location() {
|
||||||
@Override public Filename filename() { return self.filename(); }
|
@Override public Filename filename() { return self.filename(); }
|
||||||
@Override public int start() { return 0; }
|
@Override public int start() { return 0; }
|
||||||
@Override public int line() { return self.line() + 1; }
|
@Override public int line() { return self.line() + offset; }
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public class ParseRes<T> {
|
|||||||
return new ParseRes<>(state, null, null, result, this.n + n);
|
return new ParseRes<>(state, null, null, result, this.n + n);
|
||||||
}
|
}
|
||||||
public <T2> ParseRes<T2> chainError() {
|
public <T2> ParseRes<T2> chainError() {
|
||||||
if (isSuccess()) throw new RuntimeException("Can't transform a ParseRes that hasn't failed.");
|
if (isSuccess()) throw new RuntimeException("Can't transform a ParseRes that hasn't failed");
|
||||||
return new ParseRes<>(state, errorLocation, error, null, 0);
|
return new ParseRes<>(state, errorLocation, error, null, 0);
|
||||||
}
|
}
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
|||||||
@@ -86,10 +86,10 @@ public class Parsing {
|
|||||||
var newC = 0;
|
var newC = 0;
|
||||||
|
|
||||||
for (var j = 0; j < 2; j++) {
|
for (var j = 0; j < 2; j++) {
|
||||||
if (i + n >= src.size()) return ParseRes.error(src.loc(i), "Invalid hexadecimal escape sequence.");
|
if (i + n >= src.size()) return ParseRes.error(src.loc(i), "Invalid hexadecimal escape sequence");
|
||||||
|
|
||||||
int val = fromHex(src.at(i + n));
|
int val = fromHex(src.at(i + n));
|
||||||
if (val == -1) throw new SyntaxException(src.loc(i + n), "Invalid hexadecimal escape sequence.");
|
if (val == -1) throw new SyntaxException(src.loc(i + n), "Invalid hexadecimal escape sequence");
|
||||||
n++;
|
n++;
|
||||||
|
|
||||||
newC = (newC << 4) | val;
|
newC = (newC << 4) | val;
|
||||||
@@ -113,6 +113,7 @@ public class Parsing {
|
|||||||
return ParseRes.res((char)newC, n);
|
return ParseRes.res((char)newC, n);
|
||||||
}
|
}
|
||||||
else if (c == '\n') return ParseRes.res(null, n);
|
else if (c == '\n') return ParseRes.res(null, n);
|
||||||
|
else n--;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParseRes.res(src.at(i + n), n + 1);
|
return ParseRes.res(src.at(i + n), n + 1);
|
||||||
@@ -205,6 +206,27 @@ public class Parsing {
|
|||||||
|
|
||||||
return ParseRes.res(res, n);
|
return ParseRes.res(res, n);
|
||||||
}
|
}
|
||||||
|
private static ParseRes<Double> parseBin(Source src, int i) {
|
||||||
|
int n = 0;
|
||||||
|
double res = 0;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
int digit = src.at(i + n, '\0') - '0';
|
||||||
|
if (digit < 0 || digit > 9) break;
|
||||||
|
if (digit > 1) return ParseRes.error(src.loc(i + n), "Digits in binary literals must be from 0 to 1, encountered " + digit);
|
||||||
|
|
||||||
|
if (digit < 0) {
|
||||||
|
if (n <= 0) return ParseRes.failed();
|
||||||
|
else return ParseRes.res(res, n);
|
||||||
|
}
|
||||||
|
n++;
|
||||||
|
|
||||||
|
res *= 2;
|
||||||
|
res += digit;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParseRes.res(res, n);
|
||||||
|
}
|
||||||
|
|
||||||
public static ParseRes<String> parseString(Source src, int i) {
|
public static ParseRes<String> parseString(Source src, int i) {
|
||||||
var n = skipEmpty(src, i);
|
var n = skipEmpty(src, i);
|
||||||
@@ -266,6 +288,16 @@ public class Parsing {
|
|||||||
if (negative) return ParseRes.res(-res.result, n);
|
if (negative) return ParseRes.res(-res.result, n);
|
||||||
else return ParseRes.res(res.result, n);
|
else return ParseRes.res(res.result, n);
|
||||||
}
|
}
|
||||||
|
else if (src.is(i + n, "0b") || src.is(i + n, "0B")) {
|
||||||
|
n += 2;
|
||||||
|
|
||||||
|
var res = parseBin(src, i + n);
|
||||||
|
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete binary literal");
|
||||||
|
n += res.n;
|
||||||
|
|
||||||
|
if (negative) return ParseRes.res(-res.result, n);
|
||||||
|
else return ParseRes.res(res.result, n);
|
||||||
|
}
|
||||||
else if (src.is(i + n, '0')) {
|
else if (src.is(i + n, '0')) {
|
||||||
n++;
|
n++;
|
||||||
parsedAny = true;
|
parsedAny = true;
|
||||||
@@ -288,9 +320,8 @@ public class Parsing {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (src.is(i + n, 'e') || src.is(i + n, 'E')) {
|
if (parsedAny && src.is(i + n, 'e') || src.is(i + n, 'E')) {
|
||||||
n++;
|
n++;
|
||||||
parsedAny = true;
|
|
||||||
boolean expNegative = false;
|
boolean expNegative = false;
|
||||||
boolean parsedE = false;
|
boolean parsedE = false;
|
||||||
|
|
||||||
@@ -402,12 +433,13 @@ public class Parsing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
var digit = alphabet.indexOf(Character.toLowerCase(src.at(i + n)));
|
var digit = alphabet.indexOf(Character.toLowerCase(src.at(i + n, '\0')));
|
||||||
if (digit < 0) break;
|
if (digit < 0) break;
|
||||||
|
|
||||||
parsedAny = true;
|
parsedAny = true;
|
||||||
result += digit;
|
|
||||||
result *= alphabet.length();
|
result *= alphabet.length();
|
||||||
|
result += digit;
|
||||||
|
n++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!parsedAny) {
|
if (!parsedAny) {
|
||||||
@@ -415,6 +447,6 @@ public class Parsing {
|
|||||||
return ParseRes.failed();
|
return ParseRes.failed();
|
||||||
}
|
}
|
||||||
else if (negative) return ParseRes.res(-result, n);
|
else if (negative) return ParseRes.res(-result, n);
|
||||||
else return ParseRes.res(-result, n);
|
else return ParseRes.res(result, n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,244 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.SyntaxException;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
|
||||||
import me.topchetoeu.jscript.common.environment.Environment;
|
|
||||||
import me.topchetoeu.jscript.common.environment.Key;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Source;
|
|
||||||
import me.topchetoeu.jscript.compilation.members.FieldMemberNode;
|
|
||||||
import me.topchetoeu.jscript.compilation.members.Member;
|
|
||||||
import me.topchetoeu.jscript.compilation.members.MethodMemberNode;
|
|
||||||
import me.topchetoeu.jscript.compilation.members.PropertyMemberNode;
|
|
||||||
import me.topchetoeu.jscript.compilation.scope.FunctionScope;
|
|
||||||
|
|
||||||
public abstract class ClassNode extends FunctionNode {
|
|
||||||
public static final class ClassBody {
|
|
||||||
public final List<Member> staticMembers;
|
|
||||||
public final List<FieldMemberNode> protoFields;
|
|
||||||
public final List<Member> protoMembers;
|
|
||||||
public final Parameters constructorParameters;
|
|
||||||
public final CompoundNode constructorBody;
|
|
||||||
public final Node superExpr;
|
|
||||||
public final boolean hasConstr;
|
|
||||||
|
|
||||||
public ClassBody(
|
|
||||||
List<Member> staticMembers, List<FieldMemberNode> protoFields, List<Member> protoMembers,
|
|
||||||
Parameters constructorParameters, CompoundNode constructorBody,
|
|
||||||
Node superExpr, boolean hasConstr
|
|
||||||
) {
|
|
||||||
this.staticMembers = staticMembers;
|
|
||||||
this.protoFields = protoFields;
|
|
||||||
this.protoMembers = protoMembers;
|
|
||||||
this.constructorParameters = constructorParameters;
|
|
||||||
this.constructorBody = constructorBody;
|
|
||||||
this.superExpr = superExpr;
|
|
||||||
this.hasConstr = hasConstr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final Key<Environment> CLASS_ROOT = Key.of();
|
|
||||||
public static final Key<Consumer<CompileResult>> SUPER = Key.of();
|
|
||||||
public static final Key<Consumer<CompileResult>> SUPER_PROTO = Key.of();
|
|
||||||
public static final Key<Consumer<CompileResult>> SUPER_CONSTR = Key.of();
|
|
||||||
public static final Key<Consumer<CompileResult>> ON_SUPER_CALL = Key.of();
|
|
||||||
|
|
||||||
public final ClassBody body;
|
|
||||||
public final String name;
|
|
||||||
|
|
||||||
@Override public String name() { return name; }
|
|
||||||
|
|
||||||
public void compileStatic(CompileResult target) {
|
|
||||||
for (var member : body.staticMembers) {
|
|
||||||
member.compile(target, true, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public void compilePrototype(CompileResult target) {
|
|
||||||
if (body.protoMembers.size() > 0) {
|
|
||||||
target.add(Instruction.dup());
|
|
||||||
target.add(Instruction.loadMember("prototype"));
|
|
||||||
|
|
||||||
for (var i = 0; i < body.protoMembers.size() - 1; i++) {
|
|
||||||
body.protoMembers.get(i).compile(target, true, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
body.protoMembers.get(body.protoMembers.size() - 1).compile(target, false, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void compileFieldInits(CompileResult target) {
|
|
||||||
for (var member : body.protoFields) {
|
|
||||||
target.add(Instruction.loadThis());
|
|
||||||
member.compile(target, false, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override protected void compilePreBody(CompileResult target) {
|
|
||||||
if (target.env.hasNotNull(SUPER_PROTO)) {
|
|
||||||
if (!body.hasConstr) {
|
|
||||||
throw new SyntaxException(loc(), "Default constructors in derived classes not supported");
|
|
||||||
// compileFieldInits(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else compileFieldInits(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
|
||||||
if (body.superExpr == null) {
|
|
||||||
var id = target.addChild(compileBody(target, name, null));
|
|
||||||
target.add(_i -> Instruction.loadFunc(id, false, true, false, false, name, captures(id, target)));
|
|
||||||
compileStatic(target);
|
|
||||||
compilePrototype(target);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var subtarget = target.subtarget().rootEnvironment(JavaScript.COMPILE_ROOT);
|
|
||||||
subtarget.scope.singleEntry = true;
|
|
||||||
subtarget.beginScope();
|
|
||||||
var protoVar = target.scope.defineTemp();
|
|
||||||
var constrVar = target.scope.defineTemp();
|
|
||||||
|
|
||||||
subtarget.env.add(SUPER_PROTO, t -> {
|
|
||||||
var i = t.scope.get(protoVar, false);
|
|
||||||
t.add(_i -> i.index().toGet());
|
|
||||||
});
|
|
||||||
subtarget.env.add(SUPER_CONSTR, t -> {
|
|
||||||
var i = t.scope.get(constrVar, false);
|
|
||||||
t.add(_i -> i.index().toGet());
|
|
||||||
});
|
|
||||||
|
|
||||||
var staticTarget = subtarget.subEnvironment();
|
|
||||||
staticTarget.env.add(SUPER, subtarget.env.get(SUPER_CONSTR));
|
|
||||||
staticTarget.env.add(CLASS_ROOT, staticTarget.env);
|
|
||||||
|
|
||||||
var protoTarget = subtarget.subEnvironment();
|
|
||||||
protoTarget.env.add(SUPER, subtarget.env.get(SUPER_PROTO));
|
|
||||||
protoTarget.env.add(CLASS_ROOT, protoTarget.env);
|
|
||||||
|
|
||||||
var constrEnv = subtarget.env.child();
|
|
||||||
constrEnv.add(SUPER, subtarget.env.get(SUPER_PROTO));
|
|
||||||
constrEnv.add(ON_SUPER_CALL, this::compileFieldInits);
|
|
||||||
constrEnv.add(CLASS_ROOT, constrEnv);
|
|
||||||
|
|
||||||
var id = target.addChild(compileBody(constrEnv, new FunctionScope(subtarget.scope), false, name, null));
|
|
||||||
target.add(_i -> Instruction.loadFunc(id, false, true, false, true, name, captures(id, target)));
|
|
||||||
|
|
||||||
body.superExpr.compile(target, true);
|
|
||||||
|
|
||||||
target.add(Instruction.extend());
|
|
||||||
target.add(Instruction.dup(1, 0));
|
|
||||||
target.add(Instruction.loadMember("prototype"));
|
|
||||||
target.add(_i -> protoVar.index().toInit());
|
|
||||||
target.add(_i -> constrVar.index().toInit());
|
|
||||||
|
|
||||||
compileStatic(staticTarget);
|
|
||||||
compilePrototype(protoTarget);
|
|
||||||
subtarget.endScope();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClassNode(Location loc, Location end, String name, ClassBody body) {
|
|
||||||
super(loc, end, body.constructorParameters, body.constructorBody);
|
|
||||||
|
|
||||||
this.name = name;
|
|
||||||
this.body = body;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ParseRes<Member> parseMember(Source src, int i) {
|
|
||||||
return ParseRes.first(src, i,
|
|
||||||
PropertyMemberNode::parse,
|
|
||||||
FieldMemberNode::parseClass,
|
|
||||||
MethodMemberNode::parse
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ParseRes<ClassBody> parseBody(Source src, int i) {
|
|
||||||
var n = Parsing.skipEmpty(src, i);
|
|
||||||
var loc = src.loc(i + n);
|
|
||||||
|
|
||||||
ParseRes<Node> superExpr = ParseRes.failed();
|
|
||||||
|
|
||||||
if (Parsing.isIdentifier(src, i + n, "extends")) {
|
|
||||||
n += 7;
|
|
||||||
|
|
||||||
superExpr = JavaScript.parseExpression(src, i + n, 14);
|
|
||||||
if (!superExpr.isSuccess()) return superExpr.chainError(src.loc(i + n), "Expected an expression after 'extends'");
|
|
||||||
n += superExpr.n;
|
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!src.is(i + n, "{")) return ParseRes.error(src.loc(i + n), "Expected a class body");
|
|
||||||
n++;
|
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
|
||||||
|
|
||||||
var fields = new LinkedList<FieldMemberNode>();
|
|
||||||
var members = new LinkedList<Member>();
|
|
||||||
var statics = new LinkedList<Member>();
|
|
||||||
|
|
||||||
var params = new Parameters(new ArrayList<>());
|
|
||||||
var body = new CompoundNode(loc, false);
|
|
||||||
var hasConstr = false;
|
|
||||||
|
|
||||||
if (src.is(i + n, "}")) {
|
|
||||||
n++;
|
|
||||||
return ParseRes.res(new ClassBody(statics, fields, members, params, body, superExpr.result, false), n);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
ParseRes<Member> prop = parseMember(src, i + n);
|
|
||||||
|
|
||||||
if (prop.isSuccess()) {
|
|
||||||
n += prop.n;
|
|
||||||
|
|
||||||
if (prop.result instanceof FieldMemberNode field) fields.add(field);
|
|
||||||
else if (prop.result instanceof MethodMemberNode method && method.name().equals("constructor")) {
|
|
||||||
if (hasConstr) return ParseRes.error(loc, "A class may only have one constructor");
|
|
||||||
|
|
||||||
params = method.params;
|
|
||||||
body = method.body;
|
|
||||||
hasConstr = true;
|
|
||||||
}
|
|
||||||
else members.add(prop.result);
|
|
||||||
}
|
|
||||||
else if (Parsing.isIdentifier(src, i + n, "static")) {
|
|
||||||
n += 6;
|
|
||||||
|
|
||||||
var staticProp = parseMember(src, i + n);
|
|
||||||
if (!staticProp.isSuccess()) {
|
|
||||||
if (prop.isError()) return prop.chainError();
|
|
||||||
else return staticProp.chainError(src.loc(i + n), "Expected a member after 'static' keyword");
|
|
||||||
}
|
|
||||||
n += staticProp.n;
|
|
||||||
|
|
||||||
statics.add(staticProp.result);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var end = JavaScript.parseStatementEnd(src, i + n);
|
|
||||||
if (end.isSuccess()) n += end.n;
|
|
||||||
else return ParseRes.error(src.loc(i + n), "Expected a member, end of statement or a closing colon");
|
|
||||||
}
|
|
||||||
|
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
|
||||||
|
|
||||||
if (src.is(i + n, "}")) {
|
|
||||||
n++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// else return ParseRes.error(src.loc(i + n), "Expected a comma or a closing brace.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return ParseRes.res(new ClassBody(statics, fields, members, params, body, superExpr.result, hasConstr), n);
|
|
||||||
}
|
|
||||||
|
|
||||||
// public FunctionStatementNode(Location loc, Location end, Parameters params, CompoundNode body, String name) {
|
|
||||||
// super(loc, end, params, body);
|
|
||||||
// this.name = name;
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Source;
|
|
||||||
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
|
||||||
|
|
||||||
public class ClassStatementNode extends ClassNode {
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
|
||||||
super.compile(target, pollute, name, bp);
|
|
||||||
var i = target.scope.define(DeclarationType.LET, name(), loc());
|
|
||||||
target.add(_i -> i.index().toInit());
|
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
|
||||||
}
|
|
||||||
|
|
||||||
public ClassStatementNode(Location loc, Location end, String name, ClassBody body) {
|
|
||||||
super(loc, end, name, body);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ParseRes<ClassStatementNode> parse(Source src, int i) {
|
|
||||||
var n = Parsing.skipEmpty(src, i);
|
|
||||||
var loc = src.loc(i + n);
|
|
||||||
|
|
||||||
if (!Parsing.isIdentifier(src, i + n, "class")) return ParseRes.failed();
|
|
||||||
n += 5;
|
|
||||||
|
|
||||||
var name = Parsing.parseIdentifier(src, i + n);
|
|
||||||
if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected a class name");
|
|
||||||
n += name.n;
|
|
||||||
|
|
||||||
var body = parseBody(src, i + n);
|
|
||||||
if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a class body");
|
|
||||||
n += body.n;
|
|
||||||
|
|
||||||
return ParseRes.res(new ClassStatementNode(loc, src.loc(i + n), name.result, body.result), n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,11 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.function.Function;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.function.IntFunction;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.FunctionBody;
|
import me.topchetoeu.jscript.common.FunctionBody;
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
import me.topchetoeu.jscript.common.Instruction;
|
||||||
@@ -14,28 +15,22 @@ import me.topchetoeu.jscript.common.environment.Key;
|
|||||||
import me.topchetoeu.jscript.common.mapping.FunctionMap;
|
import me.topchetoeu.jscript.common.mapping.FunctionMap;
|
||||||
import me.topchetoeu.jscript.common.mapping.FunctionMap.FunctionMapBuilder;
|
import me.topchetoeu.jscript.common.mapping.FunctionMap.FunctionMapBuilder;
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
import me.topchetoeu.jscript.common.parsing.Location;
|
||||||
import me.topchetoeu.jscript.compilation.scope.Scope;
|
import me.topchetoeu.jscript.compilation.control.TryNode;
|
||||||
|
import me.topchetoeu.jscript.compilation.scope.FunctionScope;
|
||||||
|
import me.topchetoeu.jscript.compilation.scope.Variable;
|
||||||
|
|
||||||
public final class CompileResult {
|
public final class CompileResult {
|
||||||
public static final class ChildData {
|
public static final Key<Void> DEBUG_LOG = new Key<>();
|
||||||
public final int id;
|
|
||||||
public final CompileResult result;
|
|
||||||
|
|
||||||
public ChildData(int id, CompileResult result) {
|
public final List<Instruction> instructions;
|
||||||
this.result = result;
|
|
||||||
this.id = id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final List<IntFunction<Instruction>> instructions;
|
|
||||||
public final List<CompileResult> children;
|
public final List<CompileResult> children;
|
||||||
|
public final Map<FunctionNode, CompileResult> childrenMap = new HashMap<>();
|
||||||
|
public final Map<FunctionNode, Integer> childrenIndices = new HashMap<>();
|
||||||
public final FunctionMapBuilder map;
|
public final FunctionMapBuilder map;
|
||||||
public final Environment env;
|
public final Environment env;
|
||||||
public int length;
|
public int length;
|
||||||
public Runnable buildTask = () -> {
|
public final FunctionScope scope;
|
||||||
throw new IllegalStateException("Compile result is not ready to be built");
|
public final Map<TryNode, Variable> catchBindings = new HashMap<>();
|
||||||
};
|
|
||||||
public final Scope scope;
|
|
||||||
|
|
||||||
public int temp() {
|
public int temp() {
|
||||||
instructions.add(null);
|
instructions.add(null);
|
||||||
@@ -43,21 +38,14 @@ public final class CompileResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public CompileResult add(Instruction instr) {
|
public CompileResult add(Instruction instr) {
|
||||||
instructions.add(i -> instr);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
public CompileResult add(IntFunction<Instruction> instr) {
|
|
||||||
instructions.add(instr);
|
instructions.add(instr);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
public CompileResult set(int i, Instruction instr) {
|
public CompileResult set(int i, Instruction instr) {
|
||||||
instructions.set(i, _i -> instr);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
public CompileResult set(int i, IntFunction<Instruction>instr) {
|
|
||||||
instructions.set(i, instr);
|
instructions.set(i, instr);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int size() { return instructions.size(); }
|
public int size() { return instructions.size(); }
|
||||||
|
|
||||||
public void setDebug(Location loc, BreakpointType type) {
|
public void setDebug(Location loc, BreakpointType type) {
|
||||||
@@ -79,61 +67,36 @@ public final class CompileResult {
|
|||||||
setLocationAndDebug(instructions.size() - 1, loc, type);
|
setLocationAndDebug(instructions.size() - 1, loc, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void beginScope() {
|
public CompileResult addChild(FunctionNode node, CompileResult res) {
|
||||||
// for (var cap : scope.capturables()) {
|
|
||||||
// add(_i -> Instruction.capInit(cap.index().index));
|
|
||||||
// }
|
|
||||||
}
|
|
||||||
public void reallocScope() {
|
|
||||||
for (var cap : scope.capturables()) {
|
|
||||||
add(_i -> cap.index().toGet());
|
|
||||||
add(_i -> Instruction.capFree(cap.index().index));
|
|
||||||
add(_i -> cap.index().toInit());
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.end();
|
|
||||||
}
|
|
||||||
public void endScope() {
|
|
||||||
for (var cap : scope.capturables()) {
|
|
||||||
add(_i -> Instruction.capFree(cap.index().index));
|
|
||||||
}
|
|
||||||
for (var var : scope.locals()) {
|
|
||||||
add(_i -> Instruction.varFree(var.index().index));
|
|
||||||
}
|
|
||||||
|
|
||||||
scope.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int addChild(CompileResult res) {
|
|
||||||
this.children.add(res);
|
this.children.add(res);
|
||||||
return this.children.size() - 1;
|
this.childrenMap.put(node, res);
|
||||||
}
|
this.childrenIndices.put(node, this.children.size() - 1);
|
||||||
|
|
||||||
public Instruction[] instructions() {
|
|
||||||
var res = new Instruction[instructions.size()];
|
|
||||||
var i = 0;
|
|
||||||
for (var suppl : instructions) {
|
|
||||||
res[i] = suppl.apply(i);
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Instruction[] instructions() {
|
||||||
|
return instructions.toArray(new Instruction[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FunctionMap map(Function<Location, Location> mapper) {
|
||||||
|
return map.map(mapper).build(scope.localNames(), scope.capturableNames(), scope.captureNames());
|
||||||
|
}
|
||||||
public FunctionMap map() {
|
public FunctionMap map() {
|
||||||
return map.build(scope);
|
return map.build(scope.localNames(), scope.capturableNames(), scope.captureNames());
|
||||||
}
|
}
|
||||||
public FunctionBody body() {
|
public FunctionBody body() {
|
||||||
var builtChildren = new FunctionBody[children.size()];
|
var builtChildren = new FunctionBody[children.size()];
|
||||||
|
|
||||||
for (var i = 0; i < children.size(); i++) builtChildren[i] = children.get(i).body();
|
for (var i = 0; i < children.size(); i++) builtChildren[i] = children.get(i).body();
|
||||||
|
|
||||||
var instrRes = new Instruction[instructions.size()];
|
var instrRes = instructions();
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
for (var suppl : instructions) {
|
if (env.has(DEBUG_LOG)) {
|
||||||
instrRes[i] = suppl.apply(i);
|
System.out.println("================= BODY =================");
|
||||||
// System.out.println(instrRes[i]);
|
System.out.println("LOCALS: " + scope.localsCount());
|
||||||
i++;
|
System.out.println("CAPTURABLES: " + scope.capturablesCount());
|
||||||
|
System.out.println("CAPTURES: " + scope.capturesCount());
|
||||||
|
|
||||||
|
for (var instr : instrRes) System.out.println(instr);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new FunctionBody(
|
return new FunctionBody(
|
||||||
@@ -143,7 +106,7 @@ public final class CompileResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public CompileResult subtarget() {
|
public CompileResult subtarget() {
|
||||||
return new CompileResult(env, new Scope(scope), this);
|
return new CompileResult(env, new FunctionScope(scope), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompileResult setEnvironment(Environment env) {
|
public CompileResult setEnvironment(Environment env) {
|
||||||
@@ -160,16 +123,15 @@ public final class CompileResult {
|
|||||||
return new CompileResult(env.child(), scope, this);
|
return new CompileResult(env.child(), scope, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompileResult(Environment env, Scope scope, int length, Consumer<CompileResult> task) {
|
public CompileResult(Environment env, FunctionScope scope, int length) {
|
||||||
this.scope = scope;
|
this.scope = scope;
|
||||||
this.instructions = new ArrayList<>();
|
this.instructions = new ArrayList<>();
|
||||||
this.children = new LinkedList<>();
|
this.children = new LinkedList<>();
|
||||||
this.map = FunctionMap.builder();
|
this.map = FunctionMap.builder();
|
||||||
this.env = env;
|
this.env = env;
|
||||||
this.length = length;
|
this.length = length;
|
||||||
this.buildTask = () -> task.accept(this);
|
|
||||||
}
|
}
|
||||||
private CompileResult(Environment env, Scope scope, CompileResult parent) {
|
private CompileResult(Environment env, FunctionScope scope, CompileResult parent) {
|
||||||
this.scope = scope;
|
this.scope = scope;
|
||||||
this.instructions = parent.instructions;
|
this.instructions = parent.instructions;
|
||||||
this.children = parent.children;
|
this.children = parent.children;
|
||||||
|
|||||||
@@ -14,22 +14,21 @@ import me.topchetoeu.jscript.common.parsing.Source;
|
|||||||
|
|
||||||
public class CompoundNode extends Node {
|
public class CompoundNode extends Node {
|
||||||
public final Node[] statements;
|
public final Node[] statements;
|
||||||
public boolean hasScope;
|
|
||||||
public Location end;
|
public Location end;
|
||||||
|
|
||||||
@Override public void resolve(CompileResult target) {
|
@Override public void resolve(CompileResult target) {
|
||||||
for (var stm : statements) stm.resolve(target);
|
for (var stm : statements) stm.resolve(target);
|
||||||
}
|
}
|
||||||
|
@Override public void compileFunctions(CompileResult target) {
|
||||||
|
for (var stm : statements) stm.compileFunctions(target);
|
||||||
|
}
|
||||||
|
|
||||||
public void compile(CompileResult target, boolean pollute, boolean singleEntry, BreakpointType type) {
|
public void compile(CompileResult target, boolean pollute, BreakpointType type) {
|
||||||
List<Node> statements = new ArrayList<Node>();
|
List<Node> statements = new ArrayList<Node>();
|
||||||
|
|
||||||
var subtarget = hasScope ? target.subtarget() : target;
|
|
||||||
if (hasScope) subtarget.beginScope();
|
|
||||||
|
|
||||||
for (var stm : this.statements) {
|
for (var stm : this.statements) {
|
||||||
if (stm instanceof FunctionStatementNode func) {
|
if (stm instanceof FunctionStatementNode func) {
|
||||||
func.compile(subtarget, false);
|
func.compile(target, false);
|
||||||
}
|
}
|
||||||
else statements.add(stm);
|
else statements.add(stm);
|
||||||
}
|
}
|
||||||
@@ -39,41 +38,25 @@ public class CompoundNode extends Node {
|
|||||||
for (var i = 0; i < statements.size(); i++) {
|
for (var i = 0; i < statements.size(); i++) {
|
||||||
var stm = statements.get(i);
|
var stm = statements.get(i);
|
||||||
|
|
||||||
if (i != statements.size() - 1) stm.compile(subtarget, false, BreakpointType.STEP_OVER);
|
if (i != statements.size() - 1) stm.compile(target, false, BreakpointType.STEP_OVER);
|
||||||
else stm.compile(subtarget, polluted = pollute, BreakpointType.STEP_OVER);
|
else stm.compile(target, polluted = pollute, BreakpointType.STEP_OVER);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasScope) subtarget.endScope();
|
|
||||||
|
|
||||||
if (!polluted && pollute) {
|
if (!polluted && pollute) {
|
||||||
target.add(Instruction.pushUndefined());
|
target.add(Instruction.pushUndefined());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute, BreakpointType type) {
|
|
||||||
compile(target, pollute, true, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
public CompoundNode setEnd(Location loc) {
|
public CompoundNode setEnd(Location loc) {
|
||||||
this.end = loc;
|
this.end = loc;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompoundNode(Location loc, boolean hasScope, Node ...statements) {
|
public CompoundNode(Location loc, Node ...statements) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.hasScope = hasScope;
|
|
||||||
this.statements = statements;
|
this.statements = statements;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void compileMultiEntry(Node node, CompileResult target, boolean pollute, BreakpointType type) {
|
|
||||||
if (node instanceof CompoundNode comp) {
|
|
||||||
comp.compile(target, pollute, false, type);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
node.compile(target, pollute, type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ParseRes<CompoundNode> parseComma(Source src, int i, Node prev, int precedence) {
|
public static ParseRes<CompoundNode> parseComma(Source src, int i, Node prev, int precedence) {
|
||||||
if (precedence > 1) return ParseRes.failed();
|
if (precedence > 1) return ParseRes.failed();
|
||||||
|
|
||||||
@@ -92,9 +75,9 @@ public class CompoundNode extends Node {
|
|||||||
children.addAll(Arrays.asList(comp.statements));
|
children.addAll(Arrays.asList(comp.statements));
|
||||||
children.add(curr.result);
|
children.add(curr.result);
|
||||||
|
|
||||||
return ParseRes.res(new CompoundNode(loc, comp.hasScope, children.toArray(new Node[0])), n);
|
return ParseRes.res(new CompoundNode(loc, children.toArray(new Node[0])), n);
|
||||||
}
|
}
|
||||||
else return ParseRes.res(new CompoundNode(loc, false, prev, curr.result), n);
|
else return ParseRes.res(new CompoundNode(loc, prev, curr.result), n);
|
||||||
}
|
}
|
||||||
public static ParseRes<CompoundNode> parse(Source src, int i) {
|
public static ParseRes<CompoundNode> parse(Source src, int i) {
|
||||||
var n = Parsing.skipEmpty(src, i);
|
var n = Parsing.skipEmpty(src, i);
|
||||||
@@ -124,6 +107,6 @@ public class CompoundNode extends Node {
|
|||||||
statements.add(res.result);
|
statements.add(res.result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ParseRes.res(new CompoundNode(loc, true, statements.toArray(new Node[0])).setEnd(src.loc(i + n - 1)), n);
|
return ParseRes.res(new CompoundNode(loc, statements.toArray(new Node[0])).setEnd(src.loc(i + n - 1)), n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
|
||||||
import me.topchetoeu.jscript.common.environment.Environment;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Source;
|
|
||||||
import me.topchetoeu.jscript.compilation.control.ReturnNode;
|
|
||||||
import me.topchetoeu.jscript.compilation.patterns.Pattern;
|
|
||||||
|
|
||||||
public class FunctionArrowNode extends FunctionNode {
|
|
||||||
@Override public String name() { return null; }
|
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
|
||||||
var id = target.addChild(compileBody(target, name, null));
|
|
||||||
target.add(_i -> Instruction.loadFunc(id, true, false, true, false, null, captures(id, target)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override protected Environment rootEnv(Environment env) {
|
|
||||||
return env.getWith(ClassNode.CLASS_ROOT, () -> super.rootEnv(env));
|
|
||||||
}
|
|
||||||
|
|
||||||
public FunctionArrowNode(Location loc, Location end, Parameters params, Node body) {
|
|
||||||
super(loc, end, params, expToBody(body));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final CompoundNode expToBody(Node node) {
|
|
||||||
if (node instanceof CompoundNode res) return res;
|
|
||||||
else return new CompoundNode(node.loc(), false, new ReturnNode(node.loc(), node));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ParseRes<FunctionArrowNode> parse(Source src, int i) {
|
|
||||||
var n = Parsing.skipEmpty(src, i);
|
|
||||||
var loc = src.loc(i + n);
|
|
||||||
|
|
||||||
Parameters params;
|
|
||||||
|
|
||||||
if (src.is(i + n, "(")) {
|
|
||||||
var paramsRes = Parameters.parseParameters(src, i + n);
|
|
||||||
if (!paramsRes.isSuccess()) return paramsRes.chainError();
|
|
||||||
n += paramsRes.n;
|
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
|
||||||
|
|
||||||
params = paramsRes.result;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var singleParam = Pattern.parse(src, i + n, true);
|
|
||||||
if (!singleParam.isSuccess()) return ParseRes.failed();
|
|
||||||
n += singleParam.n;
|
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
|
||||||
|
|
||||||
params = new Parameters(Arrays.asList(singleParam.result));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!src.is(i + n, "=>")) return ParseRes.failed();
|
|
||||||
n += 2;
|
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
|
||||||
|
|
||||||
if (src.is(i + n, "{")) {
|
|
||||||
var body = CompoundNode.parse(src, i + n);
|
|
||||||
if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a compount statement after '=>'");
|
|
||||||
n += body.n;
|
|
||||||
|
|
||||||
return ParseRes.res(new FunctionArrowNode(loc, src.loc(i + n - 1), params, body.result), n);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var body = JavaScript.parseExpression(src, i + n, 2);
|
|
||||||
if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a compount statement after '=>'");
|
|
||||||
n += body.n;
|
|
||||||
|
|
||||||
return ParseRes.res(new FunctionArrowNode(loc, src.loc(i + n - 1), params, body.result), n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
import me.topchetoeu.jscript.common.Instruction;
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.common.environment.Environment;
|
import me.topchetoeu.jscript.common.environment.Environment;
|
||||||
@@ -7,68 +9,60 @@ import me.topchetoeu.jscript.common.parsing.Location;
|
|||||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||||
import me.topchetoeu.jscript.common.parsing.Source;
|
import me.topchetoeu.jscript.common.parsing.Source;
|
||||||
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
|
||||||
import me.topchetoeu.jscript.compilation.scope.FunctionScope;
|
import me.topchetoeu.jscript.compilation.scope.FunctionScope;
|
||||||
import me.topchetoeu.jscript.compilation.scope.Variable;
|
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||||
|
|
||||||
public abstract class FunctionNode extends Node {
|
public abstract class FunctionNode extends Node {
|
||||||
public final CompoundNode body;
|
public final CompoundNode body;
|
||||||
public final Parameters params;
|
public final List<VariableNode> params;
|
||||||
public final Location end;
|
public final Location end;
|
||||||
|
|
||||||
public abstract String name();
|
public abstract String name();
|
||||||
|
public final String name(String fallback) {
|
||||||
protected final int[] captures(int id, CompileResult target) {
|
return this.name() != null ? this.name() : fallback;
|
||||||
return ((FunctionScope)target.children.get(id).scope).getCaptureIndices();
|
}
|
||||||
|
protected final int[] captures(CompileResult target) {
|
||||||
|
return target.childrenMap.get(this).scope.getCaptureIndices();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void compilePreBody(CompileResult target) { }
|
protected final Environment rootEnv(Environment env) {
|
||||||
|
|
||||||
protected Environment rootEnv(Environment env) {
|
|
||||||
return env.get(JavaScript.COMPILE_ROOT);
|
return env.get(JavaScript.COMPILE_ROOT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public final CompileResult compileBody(Environment env, FunctionScope scope, boolean lastReturn, String _name, String selfName) {
|
@Override public void resolve(CompileResult target) { }
|
||||||
var name = this.name() != null ? this.name() : _name;
|
|
||||||
|
|
||||||
return new CompileResult(env, scope, params.params.size(), target -> {
|
public final CompileResult compileBody(Environment env, FunctionScope scope, boolean lastReturn, String selfName) {
|
||||||
compilePreBody(target);
|
var target = new CompileResult(env, scope, params.size());
|
||||||
|
|
||||||
if (params.params.size() > 0) {
|
|
||||||
target.add(Instruction.loadArgs(true));
|
|
||||||
if (params.params.size() > 1) target.add(Instruction.dup(params.params.size() - 1, 0));
|
|
||||||
var i = 0;
|
var i = 0;
|
||||||
|
|
||||||
for (var param : params.params) {
|
|
||||||
target.add(Instruction.loadMember(i++));
|
|
||||||
param.destruct(target, DeclarationType.VAR, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.rest != null) {
|
|
||||||
target.add(Instruction.loadRestArgs(params.params.size()));
|
|
||||||
params.rest.destruct(target, DeclarationType.VAR, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selfName != null && !scope.has(name, false)) {
|
|
||||||
var i = scope.defineSpecial(new Variable(selfName, true), end);
|
|
||||||
|
|
||||||
target.add(Instruction.loadCallee());
|
|
||||||
target.add(_i -> i.index().toInit());
|
|
||||||
}
|
|
||||||
|
|
||||||
body.resolve(target);
|
body.resolve(target);
|
||||||
|
|
||||||
|
for (var param : params) scope.define(param.name);
|
||||||
|
|
||||||
|
var hasSelf = false;
|
||||||
|
|
||||||
|
if (selfName != null && !scope.has(selfName, false)) {
|
||||||
|
hasSelf = true;
|
||||||
|
scope.define(selfName);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.compileFunctions(target);
|
||||||
|
|
||||||
|
for (var param : params) {
|
||||||
|
target.add(Instruction.loadArg(i++)).setLocation(param.loc());
|
||||||
|
target.add(scope.define(param.name).index().toSet(false)).setLocation(param.loc());
|
||||||
|
}
|
||||||
|
if (hasSelf) {
|
||||||
|
target.add(Instruction.loadCalled());
|
||||||
|
target.add(scope.define(selfName).index().toSet(false));
|
||||||
|
}
|
||||||
|
|
||||||
body.compile(target, lastReturn, BreakpointType.NONE);
|
body.compile(target, lastReturn, BreakpointType.NONE);
|
||||||
|
|
||||||
scope.end();
|
return target;
|
||||||
|
|
||||||
for (var child : target.children) child.buildTask.run();
|
|
||||||
|
|
||||||
scope.finish();
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
public final CompileResult compileBody(CompileResult parent, String name, String selfName) {
|
public final CompileResult compileBody(CompileResult parent, String selfName) {
|
||||||
return compileBody(rootEnv(parent.env).child(), new FunctionScope(parent.scope), false, name, selfName);
|
return compileBody(rootEnv(parent.env).child(), new FunctionScope(parent.scope), false, selfName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void compile(CompileResult target, boolean pollute, String name, BreakpointType bp);
|
public abstract void compile(CompileResult target, boolean pollute, String name, BreakpointType bp);
|
||||||
@@ -82,13 +76,12 @@ public abstract class FunctionNode extends Node {
|
|||||||
compile(target, pollute, (String)null, BreakpointType.NONE);
|
compile(target, pollute, (String)null, BreakpointType.NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionNode(Location loc, Location end, Parameters params, CompoundNode body) {
|
public FunctionNode(Location loc, Location end, List<VariableNode> params, CompoundNode body) {
|
||||||
super(loc);
|
super(loc);
|
||||||
|
|
||||||
this.end = end;
|
this.end = end;
|
||||||
this.params = params;
|
this.params = params;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
this.body.hasScope = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void compileWithName(Node stm, CompileResult target, boolean pollute, String name) {
|
public static void compileWithName(Node stm, CompileResult target, boolean pollute, String name) {
|
||||||
@@ -112,7 +105,7 @@ public abstract class FunctionNode extends Node {
|
|||||||
n += name.n;
|
n += name.n;
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
|
||||||
var params = Parameters.parseParameters(src, i + n);
|
var params = JavaScript.parseParameters(src, i + n);
|
||||||
if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected a parameter list");
|
if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected a parameter list");
|
||||||
n += params.n;
|
n += params.n;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
import me.topchetoeu.jscript.common.Instruction;
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
import me.topchetoeu.jscript.common.parsing.Location;
|
||||||
@@ -12,17 +14,19 @@ public class FunctionStatementNode extends FunctionNode {
|
|||||||
@Override public String name() { return name; }
|
@Override public String name() { return name; }
|
||||||
|
|
||||||
@Override public void resolve(CompileResult target) {
|
@Override public void resolve(CompileResult target) {
|
||||||
target.scope.define(new Variable(name, false), end);
|
target.scope.define(new Variable(name, false));
|
||||||
|
}
|
||||||
|
@Override public void compileFunctions(CompileResult target) {
|
||||||
|
target.addChild(this, compileBody(target, name()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
||||||
var id = target.addChild(compileBody(target, name, null));
|
target.add(Instruction.loadFunc(target.childrenIndices.get(this), name(name), captures(target))).setLocation(loc());
|
||||||
target.add(_i -> Instruction.loadFunc(id, true, true, false, false, name, captures(id, target)));
|
target.add(VariableNode.toSet(target, end, this.name, false, true)).setLocation(loc());
|
||||||
target.add(VariableNode.toInit(target, end, this.name));
|
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionStatementNode(Location loc, Location end, Parameters params, CompoundNode body, String name) {
|
public FunctionStatementNode(Location loc, Location end, List<VariableNode> params, CompoundNode body, String name) {
|
||||||
super(loc, end, params, body);
|
super(loc, end, params, body);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,20 +1,27 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
import me.topchetoeu.jscript.common.Instruction;
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
import me.topchetoeu.jscript.common.parsing.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||||
|
|
||||||
public class FunctionValueNode extends FunctionNode {
|
public class FunctionValueNode extends FunctionNode {
|
||||||
public final String name;
|
public final String name;
|
||||||
|
|
||||||
@Override public String name() { return name; }
|
@Override public String name() { return name; }
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
@Override public void compileFunctions(CompileResult target) {
|
||||||
var id = target.addChild(compileBody(target, name, null));
|
target.addChild(this, compileBody(target, name()));
|
||||||
target.add(_i -> Instruction.loadFunc(id, true, true, false, false, name, captures(id, target)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionValueNode(Location loc, Location end, Parameters params, CompoundNode body, String name) {
|
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
||||||
|
target.add(Instruction.loadFunc(target.childrenIndices.get(this), name(name), captures(target))).setLocation(loc());
|
||||||
|
if (!pollute) target.add(Instruction.discard());
|
||||||
|
}
|
||||||
|
|
||||||
|
public FunctionValueNode(Location loc, Location end, List<VariableNode> params, CompoundNode body, String name) {
|
||||||
super(loc, end, params, body);
|
super(loc, end, params, body);
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package me.topchetoeu.jscript.compilation;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.SyntaxException;
|
import me.topchetoeu.jscript.common.SyntaxException;
|
||||||
@@ -19,7 +20,6 @@ import me.topchetoeu.jscript.compilation.control.DeleteNode;
|
|||||||
import me.topchetoeu.jscript.compilation.control.DoWhileNode;
|
import me.topchetoeu.jscript.compilation.control.DoWhileNode;
|
||||||
import me.topchetoeu.jscript.compilation.control.ForInNode;
|
import me.topchetoeu.jscript.compilation.control.ForInNode;
|
||||||
import me.topchetoeu.jscript.compilation.control.ForNode;
|
import me.topchetoeu.jscript.compilation.control.ForNode;
|
||||||
import me.topchetoeu.jscript.compilation.control.ForOfNode;
|
|
||||||
import me.topchetoeu.jscript.compilation.control.IfNode;
|
import me.topchetoeu.jscript.compilation.control.IfNode;
|
||||||
import me.topchetoeu.jscript.compilation.control.ReturnNode;
|
import me.topchetoeu.jscript.compilation.control.ReturnNode;
|
||||||
import me.topchetoeu.jscript.compilation.control.SwitchNode;
|
import me.topchetoeu.jscript.compilation.control.SwitchNode;
|
||||||
@@ -29,10 +29,9 @@ import me.topchetoeu.jscript.compilation.control.WhileNode;
|
|||||||
import me.topchetoeu.jscript.compilation.scope.FunctionScope;
|
import me.topchetoeu.jscript.compilation.scope.FunctionScope;
|
||||||
import me.topchetoeu.jscript.compilation.values.ArgumentsNode;
|
import me.topchetoeu.jscript.compilation.values.ArgumentsNode;
|
||||||
import me.topchetoeu.jscript.compilation.values.ArrayNode;
|
import me.topchetoeu.jscript.compilation.values.ArrayNode;
|
||||||
import me.topchetoeu.jscript.compilation.values.ClassValueNode;
|
import me.topchetoeu.jscript.compilation.values.GlobalThisNode;
|
||||||
import me.topchetoeu.jscript.compilation.values.ObjectNode;
|
import me.topchetoeu.jscript.compilation.values.ObjectNode;
|
||||||
import me.topchetoeu.jscript.compilation.values.RegexNode;
|
import me.topchetoeu.jscript.compilation.values.RegexNode;
|
||||||
import me.topchetoeu.jscript.compilation.values.SuperNode;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.ThisNode;
|
import me.topchetoeu.jscript.compilation.values.ThisNode;
|
||||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||||
import me.topchetoeu.jscript.compilation.values.constants.BoolNode;
|
import me.topchetoeu.jscript.compilation.values.constants.BoolNode;
|
||||||
@@ -49,19 +48,11 @@ import me.topchetoeu.jscript.compilation.values.operations.TypeofNode;
|
|||||||
|
|
||||||
public final class JavaScript {
|
public final class JavaScript {
|
||||||
public static enum DeclarationType {
|
public static enum DeclarationType {
|
||||||
VAR(false, false),
|
@Deprecated
|
||||||
CONST(true, true),
|
VAR;
|
||||||
LET(true, false);
|
|
||||||
|
|
||||||
public final boolean strict, readonly;
|
|
||||||
|
|
||||||
private DeclarationType(boolean strict, boolean readonly) {
|
|
||||||
this.strict = strict;
|
|
||||||
this.readonly = readonly;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static final Key<Environment> COMPILE_ROOT = Key.of();
|
public static final Key<Environment> COMPILE_ROOT = new Key<>();
|
||||||
|
|
||||||
static final Set<String> reserved = new HashSet<>(Arrays.asList(
|
static final Set<String> reserved = new HashSet<>(Arrays.asList(
|
||||||
"true", "false", "void", "null", "this", "if", "else", "try", "catch",
|
"true", "false", "void", "null", "this", "if", "else", "try", "catch",
|
||||||
@@ -93,7 +84,6 @@ public final class JavaScript {
|
|||||||
return ParseRes.first(src, i,
|
return ParseRes.first(src, i,
|
||||||
(s, j) -> statement ? ParseRes.failed() : ObjectNode.parse(s, j),
|
(s, j) -> statement ? ParseRes.failed() : ObjectNode.parse(s, j),
|
||||||
(s, j) -> statement ? ParseRes.failed() : FunctionNode.parseFunction(s, j, false),
|
(s, j) -> statement ? ParseRes.failed() : FunctionNode.parseFunction(s, j, false),
|
||||||
(s, j) -> statement ? ParseRes.failed() : ClassValueNode.parse(s, j),
|
|
||||||
JavaScript::parseLiteral,
|
JavaScript::parseLiteral,
|
||||||
StringNode::parse,
|
StringNode::parse,
|
||||||
RegexNode::parse,
|
RegexNode::parse,
|
||||||
@@ -102,7 +92,6 @@ public final class JavaScript {
|
|||||||
ChangeNode::parsePrefixIncrease,
|
ChangeNode::parsePrefixIncrease,
|
||||||
OperationNode::parsePrefix,
|
OperationNode::parsePrefix,
|
||||||
ArrayNode::parse,
|
ArrayNode::parse,
|
||||||
(s, j) -> statement ? ParseRes.failed() : FunctionArrowNode.parse(s, j),
|
|
||||||
JavaScript::parseParens,
|
JavaScript::parseParens,
|
||||||
CallNode::parseNew,
|
CallNode::parseNew,
|
||||||
TypeofNode::parse,
|
TypeofNode::parse,
|
||||||
@@ -124,8 +113,8 @@ public final class JavaScript {
|
|||||||
if (id.result.equals("false")) return ParseRes.res(new BoolNode(loc, false), n);
|
if (id.result.equals("false")) return ParseRes.res(new BoolNode(loc, false), n);
|
||||||
if (id.result.equals("null")) return ParseRes.res(new NullNode(loc), n);
|
if (id.result.equals("null")) return ParseRes.res(new NullNode(loc), n);
|
||||||
if (id.result.equals("this")) return ParseRes.res(new ThisNode(loc), n);
|
if (id.result.equals("this")) return ParseRes.res(new ThisNode(loc), n);
|
||||||
if (id.result.equals("super")) return ParseRes.res(new SuperNode(loc), n);
|
|
||||||
if (id.result.equals("arguments")) return ParseRes.res(new ArgumentsNode(loc), n);
|
if (id.result.equals("arguments")) return ParseRes.res(new ArgumentsNode(loc), n);
|
||||||
|
if (id.result.equals("globalThis")) return ParseRes.res(new GlobalThisNode(loc), n);
|
||||||
|
|
||||||
return ParseRes.failed();
|
return ParseRes.failed();
|
||||||
}
|
}
|
||||||
@@ -188,14 +177,13 @@ public final class JavaScript {
|
|||||||
return res.addN(end.n);
|
return res.addN(end.n);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ParseRes<? extends Node> parseStatement(Source src, int i) {
|
public static ParseRes<Node> parseStatement(Source src, int i) {
|
||||||
var n = Parsing.skipEmpty(src, i);
|
var n = Parsing.skipEmpty(src, i);
|
||||||
|
|
||||||
if (src.is(i + n, ";")) return ParseRes.res(new DiscardNode(src.loc(i+ n), null), n + 1);
|
if (src.is(i + n, ";")) return ParseRes.res(new DiscardNode(src.loc(i+ n), null), n + 1);
|
||||||
if (Parsing.isIdentifier(src, i + n, "with")) return ParseRes.error(src.loc(i + n), "'with' statements are not allowed.");
|
if (Parsing.isIdentifier(src, i + n, "with")) return ParseRes.error(src.loc(i + n), "'with' statements are not allowed.");
|
||||||
|
|
||||||
ParseRes<? extends Node> res = ParseRes.first(src, i + n,
|
ParseRes<Node> res = ParseRes.first(src, i + n,
|
||||||
ClassStatementNode::parse,
|
|
||||||
VariableDeclareNode::parse,
|
VariableDeclareNode::parse,
|
||||||
ReturnNode::parse,
|
ReturnNode::parse,
|
||||||
ThrowNode::parse,
|
ThrowNode::parse,
|
||||||
@@ -207,7 +195,6 @@ public final class JavaScript {
|
|||||||
SwitchNode::parse,
|
SwitchNode::parse,
|
||||||
ForNode::parse,
|
ForNode::parse,
|
||||||
ForInNode::parse,
|
ForInNode::parse,
|
||||||
ForOfNode::parse,
|
|
||||||
DoWhileNode::parse,
|
DoWhileNode::parse,
|
||||||
TryNode::parse,
|
TryNode::parse,
|
||||||
CompoundNode::parse,
|
CompoundNode::parse,
|
||||||
@@ -231,13 +218,11 @@ public final class JavaScript {
|
|||||||
return ParseRes.failed();
|
return ParseRes.failed();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ParseRes<DeclarationType> parseDeclarationType(Source src, int i) {
|
public static ParseRes<Boolean> parseDeclarationType(Source src, int i) {
|
||||||
var res = Parsing.parseIdentifier(src, i);
|
var res = Parsing.parseIdentifier(src, i);
|
||||||
if (!res.isSuccess()) return res.chainError();
|
if (!res.isSuccess()) return res.chainError();
|
||||||
|
|
||||||
if (res.result.equals("var")) return ParseRes.res(DeclarationType.VAR, res.n);
|
if (res.result.equals("var")) return ParseRes.res(true, res.n);
|
||||||
if (res.result.equals("let")) return ParseRes.res(DeclarationType.LET, res.n);
|
|
||||||
if (res.result.equals("const")) return ParseRes.res(DeclarationType.CONST, res.n);
|
|
||||||
|
|
||||||
return ParseRes.failed();
|
return ParseRes.failed();
|
||||||
}
|
}
|
||||||
@@ -248,6 +233,7 @@ public final class JavaScript {
|
|||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
|
i += Parsing.skipEmpty(src, i);
|
||||||
if (i >= src.size()) break;
|
if (i >= src.size()) break;
|
||||||
|
|
||||||
var res = parseStatement(src, i);
|
var res = parseStatement(src, i);
|
||||||
@@ -268,22 +254,21 @@ public final class JavaScript {
|
|||||||
return !JavaScript.reserved.contains(name);
|
return !JavaScript.reserved.contains(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CompileResult compile(Environment env, Node ...statements) {
|
public static CompileResult compile(Environment env, boolean passthrough, Node ...statements) {
|
||||||
env = env.child();
|
env = env.child();
|
||||||
env.add(COMPILE_ROOT, env);
|
env.add(COMPILE_ROOT, env);
|
||||||
|
|
||||||
var func = new FunctionValueNode(null, null, new Parameters(Arrays.asList()), new CompoundNode(null, true, statements), null);
|
var func = new FunctionValueNode(null, null, Arrays.asList(), new CompoundNode(null, statements), null);
|
||||||
var res = func.compileBody(env, new FunctionScope(true), true, null, null);
|
var res = func.compileBody(env, new FunctionScope(passthrough), true, null);
|
||||||
res.buildTask.run();
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CompileResult compile(Environment env, Filename filename, String raw) {
|
public static CompileResult compile(Environment env, Filename filename, String raw, boolean passthrough) {
|
||||||
return JavaScript.compile(env, JavaScript.parse(env, filename, raw));
|
return JavaScript.compile(env, passthrough, JavaScript.parse(env, filename, raw));
|
||||||
}
|
}
|
||||||
public static CompileResult compile(Filename filename, String raw) {
|
public static CompileResult compile(Filename filename, String raw, boolean passthrough) {
|
||||||
var env = new Environment();
|
var env = new Environment();
|
||||||
return JavaScript.compile(env, JavaScript.parse(env, filename, raw));
|
return JavaScript.compile(env, passthrough, JavaScript.parse(env, filename, raw));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ParseRes<String> parseLabel(Source src, int i) {
|
public static ParseRes<String> parseLabel(Source src, int i) {
|
||||||
@@ -299,4 +284,42 @@ public final class JavaScript {
|
|||||||
|
|
||||||
return ParseRes.res(nameRes.result, n);
|
return ParseRes.res(nameRes.result, n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static ParseRes<List<VariableNode>> parseParameters(Source src, int i) {
|
||||||
|
var n = Parsing.skipEmpty(src, i);
|
||||||
|
|
||||||
|
var openParen = Parsing.parseOperator(src, i + n, "(");
|
||||||
|
if (!openParen.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a parameter list");
|
||||||
|
n += openParen.n;
|
||||||
|
|
||||||
|
var params = new ArrayList<VariableNode>();
|
||||||
|
|
||||||
|
var closeParen = Parsing.parseOperator(src, i + n, ")");
|
||||||
|
n += closeParen.n;
|
||||||
|
|
||||||
|
if (!closeParen.isSuccess()) {
|
||||||
|
while (true) {
|
||||||
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
|
||||||
|
var param = VariableNode.parse(src, i + n);
|
||||||
|
if (!param.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a parameter or a closing brace");
|
||||||
|
n += param.n;
|
||||||
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
|
||||||
|
params.add(param.result);
|
||||||
|
|
||||||
|
if (src.is(i + n, ",")) {
|
||||||
|
n++;
|
||||||
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src.is(i + n, ")")) {
|
||||||
|
n++;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ParseRes.res(params, n);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.function.IntFunction;
|
|
||||||
import java.util.function.IntSupplier;
|
import java.util.function.IntSupplier;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
import me.topchetoeu.jscript.common.Instruction;
|
||||||
@@ -12,12 +12,15 @@ import me.topchetoeu.jscript.common.environment.Key;
|
|||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
import me.topchetoeu.jscript.common.parsing.Location;
|
||||||
|
|
||||||
public class LabelContext {
|
public class LabelContext {
|
||||||
public static final Key<LabelContext> BREAK_CTX = Key.of();
|
public static final Key<LabelContext> BREAK_CTX = new Key<>();
|
||||||
public static final Key<LabelContext> CONTINUE_CTX = Key.of();
|
public static final Key<LabelContext> CONTINUE_CTX = new Key<>();
|
||||||
|
|
||||||
private final LinkedList<IntSupplier> list = new LinkedList<>();
|
private final LinkedList<IntSupplier> list = new LinkedList<>();
|
||||||
private final HashMap<String, IntSupplier> map = new HashMap<>();
|
private final HashMap<String, IntSupplier> map = new HashMap<>();
|
||||||
|
|
||||||
|
private final LinkedList<ArrayList<Runnable>> deferredList = new LinkedList<>();
|
||||||
|
private final HashMap<String, ArrayList<Runnable>> deferredMap = new HashMap<>();
|
||||||
|
|
||||||
public IntSupplier get() {
|
public IntSupplier get() {
|
||||||
return list.peekLast();
|
return list.peekLast();
|
||||||
}
|
}
|
||||||
@@ -25,15 +28,44 @@ public class LabelContext {
|
|||||||
return map.get(name);
|
return map.get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IntFunction<Instruction> getJump() {
|
public void flushAdders(String name) {
|
||||||
var res = get();
|
for (var adder : deferredList.peek()) {
|
||||||
if (res == null) return null;
|
adder.run();
|
||||||
else return i -> Instruction.jmp(res.getAsInt() - i);
|
|
||||||
}
|
}
|
||||||
public IntFunction<Instruction> getJump(String name) {
|
|
||||||
var res = get(name);
|
deferredList.pop();
|
||||||
if (res == null) return null;
|
|
||||||
else return i -> Instruction.jmp(res.getAsInt() - i);
|
if (name != null) {
|
||||||
|
var adders = deferredMap.remove(name);
|
||||||
|
if (adders != null) {
|
||||||
|
for (var adder : adders) adder.run();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean jump(CompileResult target) {
|
||||||
|
var res = get();
|
||||||
|
if (res != null) {
|
||||||
|
var tmp = target.temp();
|
||||||
|
this.deferredList.peek().add(() -> target.set(tmp, Instruction.jmp(res.getAsInt() - tmp)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
|
}
|
||||||
|
public boolean jump(CompileResult target, String name) {
|
||||||
|
var res = name == null ? get() : get(name);
|
||||||
|
if (res != null) {
|
||||||
|
var tmp = target.temp();
|
||||||
|
Runnable task = () -> target.set(tmp, Instruction.jmp(res.getAsInt() - tmp));
|
||||||
|
|
||||||
|
if (name == null) this.deferredList.peekLast().add(task);
|
||||||
|
else if (deferredMap.containsKey(name)) this.deferredMap.get(name).add(task);
|
||||||
|
else return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void push(IntSupplier jumpTarget) {
|
public void push(IntSupplier jumpTarget) {
|
||||||
@@ -48,6 +80,8 @@ public class LabelContext {
|
|||||||
public void pushLoop(Location loc, String name, IntSupplier jumpTarget) {
|
public void pushLoop(Location loc, String name, IntSupplier jumpTarget) {
|
||||||
push(jumpTarget);
|
push(jumpTarget);
|
||||||
push(loc, name, jumpTarget);
|
push(loc, name, jumpTarget);
|
||||||
|
deferredList.push(new ArrayList<>());
|
||||||
|
if (name != null) deferredMap.put(name, new ArrayList<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void pop() {
|
public void pop() {
|
||||||
@@ -61,6 +95,7 @@ public class LabelContext {
|
|||||||
public void popLoop(String name) {
|
public void popLoop(String name) {
|
||||||
pop();
|
pop();
|
||||||
pop(name);
|
pop(name);
|
||||||
|
flushAdders(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static LabelContext getBreak(Environment env) {
|
public static LabelContext getBreak(Environment env) {
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ public abstract class Node {
|
|||||||
compile(target, pollute, BreakpointType.NONE);
|
compile(target, pollute, BreakpointType.NONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract void compileFunctions(CompileResult target);
|
||||||
|
|
||||||
public Location loc() { return loc; }
|
public Location loc() { return loc; }
|
||||||
public void setLoc(Location loc) { this.loc = loc; }
|
public void setLoc(Location loc) { this.loc = loc; }
|
||||||
|
|
||||||
|
|||||||
@@ -1,84 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Source;
|
|
||||||
import me.topchetoeu.jscript.compilation.patterns.Pattern;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.operations.AssignNode;
|
|
||||||
|
|
||||||
public final class Parameters {
|
|
||||||
public final int length;
|
|
||||||
public final List<Pattern> params;
|
|
||||||
public final Pattern rest;
|
|
||||||
|
|
||||||
public Parameters(List<Pattern> params, Pattern rest) {
|
|
||||||
var len = params.size();
|
|
||||||
|
|
||||||
for (var i = params.size() - 1; i >= 0; i--) {
|
|
||||||
if (!(params.get(i) instanceof AssignNode)) break;
|
|
||||||
len--;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.params = params;
|
|
||||||
this.length = len;
|
|
||||||
this.rest = rest;
|
|
||||||
}
|
|
||||||
public Parameters(List<Pattern> params) {
|
|
||||||
this(params, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ParseRes<Parameters> parseParameters(Source src, int i) {
|
|
||||||
var n = Parsing.skipEmpty(src, i);
|
|
||||||
|
|
||||||
var openParen = Parsing.parseOperator(src, i + n, "(");
|
|
||||||
if (!openParen.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a parameter list");
|
|
||||||
n += openParen.n;
|
|
||||||
|
|
||||||
var params = new ArrayList<Pattern>();
|
|
||||||
|
|
||||||
var closeParen = Parsing.parseOperator(src, i + n, ")");
|
|
||||||
n += closeParen.n;
|
|
||||||
|
|
||||||
if (!closeParen.isSuccess()) {
|
|
||||||
while (true) {
|
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
|
||||||
|
|
||||||
if (src.is(i + n, "...")) {
|
|
||||||
n += 3;
|
|
||||||
|
|
||||||
var rest = Pattern.parse(src, i + n, true);
|
|
||||||
if (!rest.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a rest parameter");
|
|
||||||
n += rest.n;
|
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
|
||||||
|
|
||||||
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected an end of parameters list after rest parameter");
|
|
||||||
n++;
|
|
||||||
|
|
||||||
return ParseRes.res(new Parameters(params, rest.result), n);
|
|
||||||
}
|
|
||||||
|
|
||||||
var param = Pattern.parse(src, i + n, true);
|
|
||||||
if (!param.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a parameter or a closing brace");
|
|
||||||
n += param.n;
|
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
|
||||||
|
|
||||||
params.add(param.result);
|
|
||||||
|
|
||||||
if (src.is(i + n, ",")) {
|
|
||||||
n++;
|
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (src.is(i + n, ")")) {
|
|
||||||
n++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ParseRes.res(new Parameters(params), n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,58 +3,58 @@ package me.topchetoeu.jscript.compilation;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.github.bsideup.jabel.Desugar;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
import me.topchetoeu.jscript.common.Instruction;
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
import me.topchetoeu.jscript.common.parsing.Location;
|
||||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||||
import me.topchetoeu.jscript.common.parsing.Source;
|
import me.topchetoeu.jscript.common.parsing.Source;
|
||||||
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||||
import me.topchetoeu.jscript.compilation.patterns.Pattern;
|
|
||||||
|
|
||||||
public class VariableDeclareNode extends Node {
|
public class VariableDeclareNode extends Node {
|
||||||
public static class Pair {
|
@Desugar
|
||||||
public final Pattern destructor;
|
public static record Pair(VariableNode var, Node value) { }
|
||||||
public final Node value;
|
|
||||||
public final Location location;
|
|
||||||
|
|
||||||
public Pair(Pattern destr, Node value, Location location) {
|
|
||||||
this.destructor = destr;
|
|
||||||
this.value = value;
|
|
||||||
this.location = location;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final List<Pair> values;
|
public final List<Pair> values;
|
||||||
public final DeclarationType declType;
|
|
||||||
|
|
||||||
@Override public void resolve(CompileResult target) {
|
@Override public void resolve(CompileResult target) {
|
||||||
if (!declType.strict) {
|
|
||||||
for (var entry : values) {
|
for (var entry : values) {
|
||||||
entry.destructor.destructDeclResolve(target);
|
target.scope.define(entry.var.name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@Override public void compileFunctions(CompileResult target) {
|
||||||
|
for (var pair : values) {
|
||||||
|
if (pair.value != null) pair.value.compileFunctions(target);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
for (var entry : values) {
|
for (var entry : values) {
|
||||||
|
var index = target.scope.get(entry.var.name, false);
|
||||||
|
|
||||||
|
if (entry.value != null) {
|
||||||
|
entry.value.compile(target, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == null) {
|
||||||
if (entry.value == null) {
|
if (entry.value == null) {
|
||||||
if (declType == DeclarationType.VAR) entry.destructor.declare(target, null, false);
|
target.add(Instruction.globDef(entry.var.name));
|
||||||
else entry.destructor.declare(target, declType, false);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
entry.value.compile(target, true);
|
target.add(Instruction.globSet(entry.var.name, false, true));
|
||||||
|
}
|
||||||
if (declType == DeclarationType.VAR) entry.destructor.destruct(target, null, true);
|
}
|
||||||
else entry.destructor.destruct(target, declType, true);
|
else if (entry.value != null) {
|
||||||
|
target.add(index.index().toSet(false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
}
|
}
|
||||||
|
|
||||||
public VariableDeclareNode(Location loc, DeclarationType declType, List<Pair> values) {
|
public VariableDeclareNode(Location loc, List<Pair> values) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.values = values;
|
this.values = values;
|
||||||
this.declType = declType;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ParseRes<VariableDeclareNode> parse(Source src, int i) {
|
public static ParseRes<VariableDeclareNode> parse(Source src, int i) {
|
||||||
@@ -70,14 +70,14 @@ public class VariableDeclareNode extends Node {
|
|||||||
var end = JavaScript.parseStatementEnd(src, i + n);
|
var end = JavaScript.parseStatementEnd(src, i + n);
|
||||||
if (end.isSuccess()) {
|
if (end.isSuccess()) {
|
||||||
n += end.n;
|
n += end.n;
|
||||||
return ParseRes.res(new VariableDeclareNode(loc, declType.result, res), n);
|
return ParseRes.res(new VariableDeclareNode(loc, res), n);
|
||||||
}
|
}
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
var nameLoc = src.loc(i + n);
|
var nameLoc = src.loc(i + n);
|
||||||
|
|
||||||
var name = Pattern.parse(src, i + n, false);
|
var name = Parsing.parseIdentifier(src, i + n);
|
||||||
if (!name.isSuccess()) return name.chainError(nameLoc, "Expected a variable name or a destructor");
|
if (!name.isSuccess()) return name.chainError(nameLoc, "Expected a variable name");
|
||||||
n += name.n;
|
n += name.n;
|
||||||
|
|
||||||
Node val = null;
|
Node val = null;
|
||||||
@@ -96,7 +96,7 @@ public class VariableDeclareNode extends Node {
|
|||||||
val = valRes.result;
|
val = valRes.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
res.add(new Pair(name.result, val, nameLoc));
|
res.add(new Pair(new VariableNode(nameLoc, name.result), val));
|
||||||
|
|
||||||
if (src.is(i + n, ",")) {
|
if (src.is(i + n, ",")) {
|
||||||
n++;
|
n++;
|
||||||
@@ -107,7 +107,7 @@ public class VariableDeclareNode extends Node {
|
|||||||
|
|
||||||
if (end.isSuccess()) {
|
if (end.isSuccess()) {
|
||||||
n += end.n + endN - n;
|
n += end.n + endN - n;
|
||||||
return ParseRes.res(new VariableDeclareNode(loc, declType.result, res), n);
|
return ParseRes.res(new VariableDeclareNode(loc, res), n);
|
||||||
}
|
}
|
||||||
else return end.chainError(src.loc(i + n), "Expected a comma or end of statement");
|
else return end.chainError(src.loc(i + n), "Expected a comma or end of statement");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,13 +14,14 @@ import me.topchetoeu.jscript.compilation.Node;
|
|||||||
public class BreakNode extends Node {
|
public class BreakNode extends Node {
|
||||||
public final String label;
|
public final String label;
|
||||||
|
|
||||||
|
@Override public void compileFunctions(CompileResult target) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
var res = LabelContext.getBreak(target.env).getJump();
|
if (!LabelContext.getBreak(target.env).jump(target, label)) {
|
||||||
if (res == null) {
|
|
||||||
if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label));
|
if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label));
|
||||||
else throw new SyntaxException(loc(), "Illegal break statement");
|
else throw new SyntaxException(loc(), "Illegal break statement");
|
||||||
}
|
}
|
||||||
target.add(res);
|
|
||||||
|
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,13 +14,14 @@ import me.topchetoeu.jscript.compilation.Node;
|
|||||||
public class ContinueNode extends Node {
|
public class ContinueNode extends Node {
|
||||||
public final String label;
|
public final String label;
|
||||||
|
|
||||||
|
@Override public void compileFunctions(CompileResult target) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
var res = LabelContext.getCont(target.env).getJump();
|
if (!LabelContext.getCont(target.env).jump(target)) {
|
||||||
if (res == null) {
|
|
||||||
if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label));
|
if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label));
|
||||||
else throw new SyntaxException(loc(), "Illegal continue statement");
|
else throw new SyntaxException(loc(), "Illegal continue statement");
|
||||||
}
|
}
|
||||||
target.add(res);
|
|
||||||
|
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ import me.topchetoeu.jscript.compilation.JavaScript;
|
|||||||
import me.topchetoeu.jscript.compilation.Node;
|
import me.topchetoeu.jscript.compilation.Node;
|
||||||
|
|
||||||
public class DebugNode extends Node {
|
public class DebugNode extends Node {
|
||||||
|
@Override public void compileFunctions(CompileResult target) {
|
||||||
|
}
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
target.add(Instruction.debug());
|
target.add(Instruction.debug());
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ public class DeleteNode extends Node {
|
|||||||
public final Node key;
|
public final Node key;
|
||||||
public final Node value;
|
public final Node value;
|
||||||
|
|
||||||
|
@Override public void compileFunctions(CompileResult target) {
|
||||||
|
key.compileFunctions(target);
|
||||||
|
value.compileFunctions(target);
|
||||||
|
}
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
value.compile(target, true);
|
value.compile(target, true);
|
||||||
key.compile(target, true);
|
key.compile(target, true);
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ public class DoWhileNode extends Node {
|
|||||||
public final Node condition, body;
|
public final Node condition, body;
|
||||||
public final String label;
|
public final String label;
|
||||||
|
|
||||||
|
@Override public void compileFunctions(CompileResult target) {
|
||||||
|
condition.compileFunctions(target);
|
||||||
|
body.compileFunctions(target);
|
||||||
|
}
|
||||||
@Override public void resolve(CompileResult target) {
|
@Override public void resolve(CompileResult target) {
|
||||||
body.resolve(target);
|
body.resolve(target);
|
||||||
}
|
}
|
||||||
@@ -23,17 +27,16 @@ public class DoWhileNode extends Node {
|
|||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
var end = new DeferredIntSupplier();
|
var end = new DeferredIntSupplier();
|
||||||
var mid = new DeferredIntSupplier();
|
|
||||||
|
|
||||||
LabelContext.pushLoop(target.env, loc(), label, end, start);
|
LabelContext.pushLoop(target.env, loc(), label, end, start);
|
||||||
body.compile(target, false, BreakpointType.STEP_OVER);
|
body.compile(target, false, BreakpointType.STEP_OVER);
|
||||||
LabelContext.popLoop(target.env, label);
|
|
||||||
|
|
||||||
mid.set(target.size());
|
|
||||||
condition.compile(target, true, BreakpointType.STEP_OVER);
|
condition.compile(target, true, BreakpointType.STEP_OVER);
|
||||||
int endI = target.size();
|
int endI = target.size();
|
||||||
end.set(endI + 1);
|
end.set(endI + 1);
|
||||||
|
|
||||||
|
LabelContext.popLoop(target.env, label);
|
||||||
|
|
||||||
target.add(Instruction.jmpIf(start - endI));
|
target.add(Instruction.jmpIf(start - endI));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -57,6 +60,7 @@ public class DoWhileNode extends Node {
|
|||||||
var bodyRes = JavaScript.parseStatement(src, i + n);
|
var bodyRes = JavaScript.parseStatement(src, i + n);
|
||||||
if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a do-while body.");
|
if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a do-while body.");
|
||||||
n += bodyRes.n;
|
n += bodyRes.n;
|
||||||
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
|
||||||
if (!Parsing.isIdentifier(src, i + n, "while")) return ParseRes.failed();
|
if (!Parsing.isIdentifier(src, i + n, "while")) return ParseRes.failed();
|
||||||
n += 5;
|
n += 5;
|
||||||
|
|||||||
@@ -7,15 +7,15 @@ import me.topchetoeu.jscript.common.parsing.ParseRes;
|
|||||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||||
import me.topchetoeu.jscript.common.parsing.Source;
|
import me.topchetoeu.jscript.common.parsing.Source;
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
import me.topchetoeu.jscript.compilation.CompoundNode;
|
|
||||||
import me.topchetoeu.jscript.compilation.DeferredIntSupplier;
|
|
||||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
import me.topchetoeu.jscript.compilation.JavaScript;
|
||||||
import me.topchetoeu.jscript.compilation.LabelContext;
|
import me.topchetoeu.jscript.compilation.LabelContext;
|
||||||
|
import me.topchetoeu.jscript.compilation.DeferredIntSupplier;
|
||||||
import me.topchetoeu.jscript.compilation.Node;
|
import me.topchetoeu.jscript.compilation.Node;
|
||||||
import me.topchetoeu.jscript.compilation.patterns.Binding;
|
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||||
|
|
||||||
public class ForInNode extends Node {
|
public class ForInNode extends Node {
|
||||||
public final Binding binding;
|
public final boolean isDecl;
|
||||||
|
public final VariableNode binding;
|
||||||
public final Node object, body;
|
public final Node object, body;
|
||||||
public final String label;
|
public final String label;
|
||||||
|
|
||||||
@@ -24,9 +24,11 @@ public class ForInNode extends Node {
|
|||||||
binding.resolve(target);
|
binding.resolve(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override public void compileFunctions(CompileResult target) {
|
||||||
|
object.compileFunctions(target);
|
||||||
|
body.compileFunctions(target);
|
||||||
|
}
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
binding.declareLateInit(target);
|
|
||||||
|
|
||||||
object.compile(target, true, BreakpointType.STEP_OVER);
|
object.compile(target, true, BreakpointType.STEP_OVER);
|
||||||
target.add(Instruction.keys(false, true));
|
target.add(Instruction.keys(false, true));
|
||||||
|
|
||||||
@@ -35,27 +37,32 @@ public class ForInNode extends Node {
|
|||||||
int mid = target.temp();
|
int mid = target.temp();
|
||||||
|
|
||||||
target.add(Instruction.loadMember("value")).setLocation(binding.loc());
|
target.add(Instruction.loadMember("value")).setLocation(binding.loc());
|
||||||
binding.assign(target, false);
|
target.add(VariableNode.toSet(target, loc(), binding.name, false, true)).setLocation(binding.loc());
|
||||||
|
|
||||||
target.setLocationAndDebug(object.loc(), BreakpointType.STEP_OVER);
|
target.setLocationAndDebug(object.loc(), BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
var end = new DeferredIntSupplier();
|
var end = new DeferredIntSupplier();
|
||||||
|
|
||||||
LabelContext.pushLoop(target.env, loc(), label, end, start);
|
LabelContext.pushLoop(target.env, loc(), label, end, start);
|
||||||
CompoundNode.compileMultiEntry(body, target, false, BreakpointType.STEP_OVER);
|
body.compile(target, false, BreakpointType.STEP_OVER);
|
||||||
LabelContext.popLoop(target.env, label);
|
|
||||||
|
|
||||||
int endI = target.size();
|
int endI = target.size();
|
||||||
|
|
||||||
target.add(Instruction.jmp(start - endI));
|
target.add(Instruction.jmp(start - endI));
|
||||||
target.add(Instruction.discard());
|
target.add(Instruction.discard());
|
||||||
target.set(mid, Instruction.jmpIfNot(endI - mid + 1));
|
target.set(mid, Instruction.jmpIfNot(endI - mid + 1));
|
||||||
|
|
||||||
|
end.set(endI + 1);
|
||||||
|
LabelContext.popLoop(target.env, label);
|
||||||
|
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
}
|
}
|
||||||
|
|
||||||
public ForInNode(Location loc, String label, Binding binding, Node object, Node body) {
|
public ForInNode(Location loc, String label, VariableNode binding, boolean isDecl, Node object, Node body) {
|
||||||
super(loc);
|
super(loc);
|
||||||
this.label = label;
|
this.label = label;
|
||||||
this.binding = binding;
|
this.binding = binding;
|
||||||
|
this.isDecl = isDecl;
|
||||||
this.object = object;
|
this.object = object;
|
||||||
this.body = body;
|
this.body = body;
|
||||||
}
|
}
|
||||||
@@ -76,9 +83,15 @@ public class ForInNode extends Node {
|
|||||||
n++;
|
n++;
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
|
||||||
var binding = Binding.parse(src, i + n);
|
var varKw = JavaScript.parseDeclarationType(src, i + n);
|
||||||
if (!binding.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a binding in for-in loop");
|
n += varKw.n;
|
||||||
n += binding.n;
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
|
||||||
|
var bindingLoc = src.loc(i + n);
|
||||||
|
|
||||||
|
var name = Parsing.parseIdentifier(src, i + n);
|
||||||
|
if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected a variable name");
|
||||||
|
n += name.n;
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
|
||||||
if (!Parsing.isIdentifier(src, i + n, "in")) return ParseRes.error(src.loc(i + n), "Expected 'in' keyword after variable declaration");
|
if (!Parsing.isIdentifier(src, i + n, "in")) return ParseRes.error(src.loc(i + n), "Expected 'in' keyword after variable declaration");
|
||||||
@@ -96,6 +109,6 @@ public class ForInNode extends Node {
|
|||||||
if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-in body");
|
if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-in body");
|
||||||
n += bodyRes.n;
|
n += bodyRes.n;
|
||||||
|
|
||||||
return ParseRes.res(new ForInNode(loc, label.result, binding.result, obj.result, bodyRes.result), n);
|
return ParseRes.res(new ForInNode(loc, label.result, new VariableNode(bindingLoc, name.result), varKw.isSuccess(), obj.result, bodyRes.result), n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,51 +7,52 @@ import me.topchetoeu.jscript.common.parsing.ParseRes;
|
|||||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||||
import me.topchetoeu.jscript.common.parsing.Source;
|
import me.topchetoeu.jscript.common.parsing.Source;
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
import me.topchetoeu.jscript.compilation.CompoundNode;
|
|
||||||
import me.topchetoeu.jscript.compilation.DeferredIntSupplier;
|
import me.topchetoeu.jscript.compilation.DeferredIntSupplier;
|
||||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
import me.topchetoeu.jscript.compilation.JavaScript;
|
||||||
import me.topchetoeu.jscript.compilation.LabelContext;
|
import me.topchetoeu.jscript.compilation.LabelContext;
|
||||||
import me.topchetoeu.jscript.compilation.Node;
|
import me.topchetoeu.jscript.compilation.Node;
|
||||||
import me.topchetoeu.jscript.compilation.VariableDeclareNode;
|
import me.topchetoeu.jscript.compilation.VariableDeclareNode;
|
||||||
import me.topchetoeu.jscript.compilation.values.operations.DiscardNode;
|
|
||||||
|
|
||||||
public class ForNode extends Node {
|
public class ForNode extends Node {
|
||||||
public final Node declaration, assignment, condition, body;
|
public final Node declaration, assignment, condition, body;
|
||||||
public final String label;
|
public final String label;
|
||||||
|
|
||||||
@Override public void resolve(CompileResult target) {
|
@Override public void resolve(CompileResult target) {
|
||||||
declaration.resolve(target);
|
if (declaration != null) declaration.resolve(target);
|
||||||
body.resolve(target);
|
body.resolve(target);
|
||||||
}
|
}
|
||||||
|
@Override public void compileFunctions(CompileResult target) {
|
||||||
|
if (declaration != null) declaration.compileFunctions(target);
|
||||||
|
if (assignment != null) assignment.compileFunctions(target);
|
||||||
|
if (condition != null) condition.compileFunctions(target);
|
||||||
|
body.compileFunctions(target);
|
||||||
|
}
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
var subtarget = target.subtarget();
|
if (declaration != null) declaration.compile(target, false, BreakpointType.STEP_OVER);
|
||||||
subtarget.scope.singleEntry = false;
|
|
||||||
subtarget.beginScope();
|
|
||||||
|
|
||||||
declaration.compile(subtarget, false, BreakpointType.STEP_OVER);
|
var continueTarget = new DeferredIntSupplier();
|
||||||
|
int start = target.size();
|
||||||
int start = subtarget.size();
|
int mid = -1;
|
||||||
CompoundNode.compileMultiEntry(condition, subtarget, true, BreakpointType.STEP_OVER);
|
if (condition != null) {
|
||||||
int mid = subtarget.temp();
|
condition.compile(target, true, BreakpointType.STEP_OVER);
|
||||||
|
mid = target.temp();
|
||||||
|
}
|
||||||
|
|
||||||
var end = new DeferredIntSupplier();
|
var end = new DeferredIntSupplier();
|
||||||
|
|
||||||
LabelContext.pushLoop(subtarget.env, loc(), label, end, start);
|
LabelContext.pushLoop(target.env, loc(), label, end, continueTarget);
|
||||||
CompoundNode.compileMultiEntry(body, subtarget, false, BreakpointType.STEP_OVER);
|
body.compile(target, false, BreakpointType.STEP_OVER);
|
||||||
LabelContext.popLoop(subtarget.env, label);
|
|
||||||
|
|
||||||
subtarget.reallocScope();
|
continueTarget.set(target.size());
|
||||||
|
if (assignment != null) assignment.compile(target, false, BreakpointType.STEP_OVER);
|
||||||
|
int endI = target.size();
|
||||||
|
|
||||||
CompoundNode.compileMultiEntry(assignment, subtarget, false, BreakpointType.STEP_OVER);
|
end.set(endI + 1);
|
||||||
int endI = subtarget.size();
|
LabelContext.popLoop(target.env, label);
|
||||||
|
|
||||||
end.set(endI);
|
target.add(Instruction.jmp(start - endI));
|
||||||
|
if (condition != null) target.set(mid, Instruction.jmpIfNot(endI - mid + 1));
|
||||||
subtarget.add(Instruction.jmp(start - endI));
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
subtarget.set(mid, Instruction.jmpIfNot(endI - mid + 1));
|
|
||||||
if (pollute) subtarget.add(Instruction.pushUndefined());
|
|
||||||
|
|
||||||
subtarget.endScope();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ForNode(Location loc, String label, Node declaration, Node condition, Node assignment, Node body) {
|
public ForNode(Location loc, String label, Node declaration, Node condition, Node assignment, Node body) {
|
||||||
@@ -67,7 +68,7 @@ public class ForNode extends Node {
|
|||||||
var n = Parsing.skipEmpty(src, i);
|
var n = Parsing.skipEmpty(src, i);
|
||||||
|
|
||||||
if (!src.is(i + n, ";")) return ParseRes.failed();
|
if (!src.is(i + n, ";")) return ParseRes.failed();
|
||||||
else return ParseRes.res(new DiscardNode(src.loc(i), null), n + 1);
|
else return ParseRes.res(null, n + 1);
|
||||||
}
|
}
|
||||||
private static ParseRes<Node> parseCondition(Source src, int i) {
|
private static ParseRes<Node> parseCondition(Source src, int i) {
|
||||||
var n = Parsing.skipEmpty(src, i);
|
var n = Parsing.skipEmpty(src, i);
|
||||||
|
|||||||
@@ -1,109 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.control;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Source;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompoundNode;
|
|
||||||
import me.topchetoeu.jscript.compilation.DeferredIntSupplier;
|
|
||||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
|
||||||
import me.topchetoeu.jscript.compilation.LabelContext;
|
|
||||||
import me.topchetoeu.jscript.compilation.Node;
|
|
||||||
import me.topchetoeu.jscript.compilation.patterns.Binding;
|
|
||||||
|
|
||||||
public class ForOfNode extends Node {
|
|
||||||
public final Binding binding;
|
|
||||||
public final Node iterable, body;
|
|
||||||
public final String label;
|
|
||||||
|
|
||||||
@Override public void resolve(CompileResult target) {
|
|
||||||
body.resolve(target);
|
|
||||||
binding.resolve(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
|
||||||
binding.declareLateInit(target);
|
|
||||||
|
|
||||||
iterable.compile(target, true, BreakpointType.STEP_OVER);
|
|
||||||
target.add(Instruction.dup());
|
|
||||||
target.add(Instruction.loadIntrinsics("it_key"));
|
|
||||||
target.add(Instruction.loadMember()).setLocation(iterable.loc());
|
|
||||||
target.add(Instruction.call(0, true)).setLocation(iterable.loc());
|
|
||||||
|
|
||||||
int start = target.size();
|
|
||||||
target.add(Instruction.dup(2, 0));
|
|
||||||
target.add(Instruction.loadMember("next")).setLocation(iterable.loc());
|
|
||||||
target.add(Instruction.call(0, true)).setLocation(iterable.loc());
|
|
||||||
target.add(Instruction.dup());
|
|
||||||
target.add(Instruction.loadMember("done")).setLocation(iterable.loc());
|
|
||||||
int mid = target.temp();
|
|
||||||
|
|
||||||
target.add(Instruction.loadMember("value")).setLocation(binding.loc);
|
|
||||||
binding.assign(target, false);
|
|
||||||
|
|
||||||
var end = new DeferredIntSupplier();
|
|
||||||
|
|
||||||
LabelContext.pushLoop(target.env, loc(), label, end, start);
|
|
||||||
CompoundNode.compileMultiEntry(body, target, false, BreakpointType.STEP_OVER);
|
|
||||||
LabelContext.popLoop(target.env, label);
|
|
||||||
|
|
||||||
int endI = target.size();
|
|
||||||
end.set(endI);
|
|
||||||
|
|
||||||
target.add(Instruction.jmp(start - endI));
|
|
||||||
target.add(Instruction.discard());
|
|
||||||
target.add(Instruction.discard());
|
|
||||||
target.set(mid, Instruction.jmpIf(endI - mid + 1));
|
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
|
||||||
}
|
|
||||||
|
|
||||||
public ForOfNode(Location loc, String label, Binding binding, Node object, Node body) {
|
|
||||||
super(loc);
|
|
||||||
this.label = label;
|
|
||||||
this.binding = binding;
|
|
||||||
this.iterable = object;
|
|
||||||
this.body = body;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ParseRes<ForOfNode> parse(Source src, int i) {
|
|
||||||
var n = Parsing.skipEmpty(src, i);
|
|
||||||
var loc = src.loc(i + n);
|
|
||||||
|
|
||||||
var label = JavaScript.parseLabel(src, i + n);
|
|
||||||
n += label.n;
|
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
|
||||||
|
|
||||||
if (!Parsing.isIdentifier(src, i + n, "for")) return ParseRes.failed();
|
|
||||||
n += 3;
|
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
|
||||||
|
|
||||||
if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected an opening paren");
|
|
||||||
n++;
|
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
|
||||||
|
|
||||||
var binding = Binding.parse(src, i + n);
|
|
||||||
if (!binding.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a binding in for-of loop");
|
|
||||||
n += binding.n;
|
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
|
||||||
|
|
||||||
if (!Parsing.isIdentifier(src, i + n, "of")) return ParseRes.error(src.loc(i + n), "Expected 'of' keyword after variable declaration");
|
|
||||||
n += 2;
|
|
||||||
|
|
||||||
var obj = JavaScript.parseExpression(src, i + n, 0);
|
|
||||||
if (!obj.isSuccess()) return obj.chainError(src.loc(i + n), "Expected a value");
|
|
||||||
n += obj.n;
|
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
|
||||||
|
|
||||||
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren");
|
|
||||||
n++;
|
|
||||||
|
|
||||||
var bodyRes = JavaScript.parseStatement(src, i + n);
|
|
||||||
if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-of body");
|
|
||||||
n += bodyRes.n;
|
|
||||||
|
|
||||||
return ParseRes.res(new ForOfNode(loc, label.result, binding.result, obj.result, bodyRes.result), n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -20,7 +20,11 @@ public class IfNode extends Node {
|
|||||||
body.resolve(target);
|
body.resolve(target);
|
||||||
if (elseBody != null) elseBody.resolve(target);
|
if (elseBody != null) elseBody.resolve(target);
|
||||||
}
|
}
|
||||||
|
@Override public void compileFunctions(CompileResult target) {
|
||||||
|
condition.compileFunctions(target);
|
||||||
|
body.compileFunctions(target);
|
||||||
|
if (elseBody != null) elseBody.compileFunctions(target);
|
||||||
|
}
|
||||||
@Override public void compile(CompileResult target, boolean pollute, BreakpointType breakpoint) {
|
@Override public void compile(CompileResult target, boolean pollute, BreakpointType breakpoint) {
|
||||||
condition.compile(target, true, breakpoint);
|
condition.compile(target, true, breakpoint);
|
||||||
|
|
||||||
@@ -29,7 +33,7 @@ public class IfNode extends Node {
|
|||||||
var end = new DeferredIntSupplier();
|
var end = new DeferredIntSupplier();
|
||||||
|
|
||||||
LabelContext.getBreak(target.env).push(loc(), label, end);
|
LabelContext.getBreak(target.env).push(loc(), label, end);
|
||||||
body.compile(target, false, BreakpointType.STEP_OVER);
|
body.compile(target, pollute, BreakpointType.STEP_OVER);
|
||||||
LabelContext.getBreak(target.env).pop(label);
|
LabelContext.getBreak(target.env).pop(label);
|
||||||
|
|
||||||
int endI = target.size();
|
int endI = target.size();
|
||||||
@@ -42,11 +46,11 @@ public class IfNode extends Node {
|
|||||||
var end = new DeferredIntSupplier();
|
var end = new DeferredIntSupplier();
|
||||||
|
|
||||||
LabelContext.getBreak(target.env).push(loc(), label, end);
|
LabelContext.getBreak(target.env).push(loc(), label, end);
|
||||||
body.compile(target, false, BreakpointType.STEP_OVER);
|
body.compile(target, pollute, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
int mid = target.temp();
|
int mid = target.temp();
|
||||||
|
|
||||||
elseBody.compile(target, false, BreakpointType.STEP_OVER);
|
elseBody.compile(target, pollute, BreakpointType.STEP_OVER);
|
||||||
LabelContext.getBreak(target.env).pop(label);
|
LabelContext.getBreak(target.env).pop(label);
|
||||||
|
|
||||||
int endI = target.size();
|
int endI = target.size();
|
||||||
@@ -80,7 +84,7 @@ public class IfNode extends Node {
|
|||||||
var a = JavaScript.parseExpression(src, i + n, 2);
|
var a = JavaScript.parseExpression(src, i + n, 2);
|
||||||
if (!a.isSuccess()) return a.chainError(src.loc(i + n), "Expected a value after the ternary operator.");
|
if (!a.isSuccess()) return a.chainError(src.loc(i + n), "Expected a value after the ternary operator.");
|
||||||
n += a.n;
|
n += a.n;
|
||||||
n += Parsing.skipEmpty(src, i);
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
|
||||||
if (!src.is(i + n, ":")) return ParseRes.failed();
|
if (!src.is(i + n, ":")) return ParseRes.failed();
|
||||||
n++;
|
n++;
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ import me.topchetoeu.jscript.compilation.Node;
|
|||||||
public class ReturnNode extends Node {
|
public class ReturnNode extends Node {
|
||||||
public final Node value;
|
public final Node value;
|
||||||
|
|
||||||
|
@Override public void compileFunctions(CompileResult target) {
|
||||||
|
if (value != null) value.compileFunctions(target);
|
||||||
|
}
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
if (value == null) target.add(Instruction.pushUndefined());
|
if (value == null) target.add(Instruction.pushUndefined());
|
||||||
else value.compile(target, true);
|
else value.compile(target, true);
|
||||||
|
|||||||
@@ -36,48 +36,52 @@ public class SwitchNode extends Node {
|
|||||||
@Override public void resolve(CompileResult target) {
|
@Override public void resolve(CompileResult target) {
|
||||||
for (var stm : body) stm.resolve(target);
|
for (var stm : body) stm.resolve(target);
|
||||||
}
|
}
|
||||||
|
@Override public void compileFunctions(CompileResult target) {
|
||||||
|
value.compileFunctions(target);
|
||||||
|
for (var _case : cases) {
|
||||||
|
_case.value.compileFunctions(target);
|
||||||
|
}
|
||||||
|
for (var stm : body) {
|
||||||
|
stm.compileFunctions(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
var caseToStatement = new HashMap<Integer, Integer>();
|
var caseToStatement = new HashMap<Integer, Integer>();
|
||||||
var statementToIndex = new HashMap<Integer, Integer>();
|
var statementToIndex = new HashMap<Integer, Integer>();
|
||||||
|
|
||||||
value.compile(target, true, BreakpointType.STEP_OVER);
|
value.compile(target, true, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
var subtarget = target.subtarget();
|
|
||||||
subtarget.beginScope();
|
|
||||||
|
|
||||||
// TODO: create a jump map
|
// TODO: create a jump map
|
||||||
for (var ccase : cases) {
|
for (var ccase : cases) {
|
||||||
subtarget.add(Instruction.dup());
|
target.add(Instruction.dup());
|
||||||
ccase.value.compile(subtarget, true);
|
ccase.value.compile(target, true);
|
||||||
subtarget.add(Instruction.operation(Operation.EQUALS));
|
target.add(Instruction.operation(Operation.EQUALS));
|
||||||
caseToStatement.put(subtarget.temp(), ccase.statementI);
|
caseToStatement.put(target.temp(), ccase.statementI);
|
||||||
}
|
}
|
||||||
|
|
||||||
int start = subtarget.temp();
|
int start = target.temp();
|
||||||
var end = new DeferredIntSupplier();
|
var end = new DeferredIntSupplier();
|
||||||
|
|
||||||
LabelContext.getBreak(target.env).push(loc(), label, end);
|
LabelContext.getBreak(target.env).pushLoop(loc(), label, end);
|
||||||
for (var stm : body) {
|
for (var stm : body) {
|
||||||
statementToIndex.put(statementToIndex.size(), subtarget.size());
|
statementToIndex.put(statementToIndex.size(), target.size());
|
||||||
stm.compile(subtarget, false, BreakpointType.STEP_OVER);
|
stm.compile(target, false, BreakpointType.STEP_OVER);
|
||||||
}
|
}
|
||||||
LabelContext.getBreak(target.env).pop(label);
|
|
||||||
|
|
||||||
subtarget.endScope();
|
int endI = target.size();
|
||||||
|
|
||||||
int endI = subtarget.size();
|
|
||||||
end.set(endI);
|
end.set(endI);
|
||||||
subtarget.add(Instruction.discard());
|
LabelContext.getBreak(target.env).popLoop(label);
|
||||||
if (pollute) subtarget.add(Instruction.pushUndefined());
|
|
||||||
|
|
||||||
if (defaultI < 0 || defaultI >= body.length) subtarget.set(start, Instruction.jmp(endI - start));
|
target.add(Instruction.discard());
|
||||||
else subtarget.set(start, Instruction.jmp(statementToIndex.get(defaultI) - start));
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
|
|
||||||
|
if (defaultI < 0 || defaultI >= body.length) target.set(start, Instruction.jmp(endI - start));
|
||||||
|
else target.set(start, Instruction.jmp(statementToIndex.get(defaultI) - start));
|
||||||
|
|
||||||
for (var el : caseToStatement.entrySet()) {
|
for (var el : caseToStatement.entrySet()) {
|
||||||
var i = statementToIndex.get(el.getValue());
|
var i = statementToIndex.get(el.getValue());
|
||||||
if (i == null) i = endI;
|
if (i == null) i = endI;
|
||||||
subtarget.set(el.getKey(), Instruction.jmpIf(i - el.getKey()));
|
target.set(el.getKey(), Instruction.jmpIf(i - el.getKey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -100,6 +104,7 @@ public class SwitchNode extends Node {
|
|||||||
var val = JavaScript.parseExpression(src, i + n, 0);
|
var val = JavaScript.parseExpression(src, i + n, 0);
|
||||||
if (!val.isSuccess()) return val.chainError(src.loc(i + n), "Expected a value after 'case'");
|
if (!val.isSuccess()) return val.chainError(src.loc(i + n), "Expected a value after 'case'");
|
||||||
n += val.n;
|
n += val.n;
|
||||||
|
n += Parsing.skipEmpty(src, i + n);
|
||||||
|
|
||||||
if (!src.is(i + n, ":")) return ParseRes.error(src.loc(i + n), "Expected colons after 'case' value");
|
if (!src.is(i + n, ":")) return ParseRes.error(src.loc(i + n), "Expected colons after 'case' value");
|
||||||
n++;
|
n++;
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ import me.topchetoeu.jscript.compilation.Node;
|
|||||||
public class ThrowNode extends Node {
|
public class ThrowNode extends Node {
|
||||||
public final Node value;
|
public final Node value;
|
||||||
|
|
||||||
|
@Override public void compileFunctions(CompileResult target) {
|
||||||
|
value.compileFunctions(target);
|
||||||
|
}
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
value.compile(target, true);
|
value.compile(target, true);
|
||||||
target.add(Instruction.throwInstr()).setLocation(loc());
|
target.add(Instruction.throwInstr()).setLocation(loc());
|
||||||
@@ -29,17 +32,11 @@ public class ThrowNode extends Node {
|
|||||||
if (!Parsing.isIdentifier(src, i + n, "throw")) return ParseRes.failed();
|
if (!Parsing.isIdentifier(src, i + n, "throw")) return ParseRes.failed();
|
||||||
n += 5;
|
n += 5;
|
||||||
|
|
||||||
var end = JavaScript.parseStatementEnd(src, i + n);
|
|
||||||
if (end.isSuccess()) {
|
|
||||||
n += end.n;
|
|
||||||
return ParseRes.res(new ThrowNode(loc, null), n);
|
|
||||||
}
|
|
||||||
|
|
||||||
var val = JavaScript.parseExpression(src, i + n, 0);
|
var val = JavaScript.parseExpression(src, i + n, 0);
|
||||||
if (val.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a value");
|
if (val.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a throw value");
|
||||||
n += val.n;
|
n += val.n;
|
||||||
|
|
||||||
end = JavaScript.parseStatementEnd(src, i + n);
|
var end = JavaScript.parseStatementEnd(src, i + n);
|
||||||
if (end.isSuccess()) {
|
if (end.isSuccess()) {
|
||||||
n += end.n;
|
n += end.n;
|
||||||
return ParseRes.res(new ThrowNode(loc, val.result), n);
|
return ParseRes.res(new ThrowNode(loc, val.result), n);
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import me.topchetoeu.jscript.compilation.DeferredIntSupplier;
|
|||||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
import me.topchetoeu.jscript.compilation.JavaScript;
|
||||||
import me.topchetoeu.jscript.compilation.LabelContext;
|
import me.topchetoeu.jscript.compilation.LabelContext;
|
||||||
import me.topchetoeu.jscript.compilation.Node;
|
import me.topchetoeu.jscript.compilation.Node;
|
||||||
import me.topchetoeu.jscript.compilation.scope.Variable;
|
|
||||||
|
|
||||||
public class TryNode extends Node {
|
public class TryNode extends Node {
|
||||||
public final CompoundNode tryBody;
|
public final CompoundNode tryBody;
|
||||||
@@ -26,7 +25,18 @@ public class TryNode extends Node {
|
|||||||
if (catchBody != null) catchBody.resolve(target);
|
if (catchBody != null) catchBody.resolve(target);
|
||||||
if (finallyBody != null) finallyBody.resolve(target);
|
if (finallyBody != null) finallyBody.resolve(target);
|
||||||
}
|
}
|
||||||
|
@Override public void compileFunctions(CompileResult target) {
|
||||||
|
tryBody.compileFunctions(target);
|
||||||
|
if (catchBody != null) {
|
||||||
|
if (captureName != null) {
|
||||||
|
var index = target.scope.defineCatch(captureName);
|
||||||
|
target.catchBindings.put(this, index);
|
||||||
|
}
|
||||||
|
catchBody.compileFunctions(target);
|
||||||
|
if (captureName != null) target.scope.undefineCatch();
|
||||||
|
}
|
||||||
|
if (finallyBody != null) finallyBody.compileFunctions(target);
|
||||||
|
}
|
||||||
@Override public void compile(CompileResult target, boolean pollute, BreakpointType bpt) {
|
@Override public void compile(CompileResult target, boolean pollute, BreakpointType bpt) {
|
||||||
int replace = target.temp();
|
int replace = target.temp();
|
||||||
var endSuppl = new DeferredIntSupplier();
|
var endSuppl = new DeferredIntSupplier();
|
||||||
@@ -42,17 +52,12 @@ public class TryNode extends Node {
|
|||||||
catchStart = target.size() - start;
|
catchStart = target.size() - start;
|
||||||
|
|
||||||
if (captureName != null) {
|
if (captureName != null) {
|
||||||
var subtarget = target.subtarget();
|
var catchVar = target.catchBindings.get(this);
|
||||||
subtarget.beginScope();
|
target.scope.defineCatch(captureName, catchVar);
|
||||||
subtarget.scope.singleEntry = true;
|
target.add(Instruction.loadError()).setLocation(catchBody.loc());
|
||||||
|
target.add(catchVar.index().toSet(false)).setLocation(catchBody.loc());
|
||||||
var catchVar = subtarget.scope.defineStrict(new Variable(captureName, false), catchBody.loc());
|
catchBody.compile(target, false);
|
||||||
subtarget.add(Instruction.loadError());
|
target.scope.undefineCatch();
|
||||||
subtarget.add(catchVar.index().toInit());
|
|
||||||
catchBody.compile(subtarget, false);
|
|
||||||
subtarget.endScope();
|
|
||||||
|
|
||||||
subtarget.scope.end();
|
|
||||||
}
|
}
|
||||||
else catchBody.compile(target, false);
|
else catchBody.compile(target, false);
|
||||||
|
|
||||||
@@ -62,6 +67,7 @@ public class TryNode extends Node {
|
|||||||
if (finallyBody != null) {
|
if (finallyBody != null) {
|
||||||
finallyStart = target.size() - start;
|
finallyStart = target.size() - start;
|
||||||
finallyBody.compile(target, false);
|
finallyBody.compile(target, false);
|
||||||
|
target.add(Instruction.tryEnd());
|
||||||
}
|
}
|
||||||
|
|
||||||
LabelContext.getBreak(target.env).pop(label);
|
LabelContext.getBreak(target.env).pop(label);
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import me.topchetoeu.jscript.common.parsing.ParseRes;
|
|||||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||||
import me.topchetoeu.jscript.common.parsing.Source;
|
import me.topchetoeu.jscript.common.parsing.Source;
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
import me.topchetoeu.jscript.compilation.CompoundNode;
|
|
||||||
import me.topchetoeu.jscript.compilation.DeferredIntSupplier;
|
import me.topchetoeu.jscript.compilation.DeferredIntSupplier;
|
||||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
import me.topchetoeu.jscript.compilation.JavaScript;
|
||||||
import me.topchetoeu.jscript.compilation.LabelContext;
|
import me.topchetoeu.jscript.compilation.LabelContext;
|
||||||
@@ -20,6 +19,10 @@ public class WhileNode extends Node {
|
|||||||
@Override public void resolve(CompileResult target) {
|
@Override public void resolve(CompileResult target) {
|
||||||
body.resolve(target);
|
body.resolve(target);
|
||||||
}
|
}
|
||||||
|
@Override public void compileFunctions(CompileResult target) {
|
||||||
|
condition.compileFunctions(target);
|
||||||
|
body.compileFunctions(target);
|
||||||
|
}
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
int start = target.size();
|
int start = target.size();
|
||||||
condition.compile(target, true);
|
condition.compile(target, true);
|
||||||
@@ -29,14 +32,14 @@ public class WhileNode extends Node {
|
|||||||
|
|
||||||
|
|
||||||
LabelContext.pushLoop(target.env, loc(), label, end, start);
|
LabelContext.pushLoop(target.env, loc(), label, end, start);
|
||||||
CompoundNode.compileMultiEntry(body, target, false, BreakpointType.STEP_OVER);
|
body.compile(target, false, BreakpointType.STEP_OVER);
|
||||||
LabelContext.popLoop(target.env, label);
|
|
||||||
|
|
||||||
var endI = target.size();
|
var endI = target.size();
|
||||||
end.set(endI + 1);
|
end.set(endI + 1);
|
||||||
|
LabelContext.popLoop(target.env, label);
|
||||||
|
|
||||||
target.add(Instruction.jmp(start - end.getAsInt()));
|
target.add(Instruction.jmp(start - endI));
|
||||||
target.set(mid, Instruction.jmpIfNot(end.getAsInt() - mid + 1));
|
target.set(mid, Instruction.jmpIfNot(endI - mid + 1));
|
||||||
if (pollute) target.add(Instruction.pushUndefined());
|
if (pollute) target.add(Instruction.pushUndefined());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,57 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.members;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.SyntaxException;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Source;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
|
||||||
import me.topchetoeu.jscript.compilation.Node;
|
|
||||||
import me.topchetoeu.jscript.compilation.patterns.AssignTarget;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.constants.StringNode;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.operations.AssignNode;
|
|
||||||
|
|
||||||
public class AssignShorthandNode implements Member {
|
|
||||||
public final Location loc;
|
|
||||||
public final Node key;
|
|
||||||
public final AssignTarget target;
|
|
||||||
public final Node value;
|
|
||||||
|
|
||||||
@Override public Location loc() { return loc; }
|
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute, boolean enumerable) {
|
|
||||||
throw new SyntaxException(loc(), "Unexpected assign shorthand in non-destructor context");
|
|
||||||
}
|
|
||||||
|
|
||||||
public AssignShorthandNode(Location loc, Node key, AssignTarget target, Node value) {
|
|
||||||
this.loc = loc;
|
|
||||||
this.key = key;
|
|
||||||
this.target = target;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AssignTarget target() {
|
|
||||||
return new AssignNode(loc(), target, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ParseRes<AssignShorthandNode> parse(Source src, int i) {
|
|
||||||
var n = Parsing.skipEmpty(src, i);
|
|
||||||
var loc = src.loc(i + n);
|
|
||||||
|
|
||||||
var var = VariableNode.parse(src, i + n);
|
|
||||||
if (!var.isSuccess()) return var.chainError();
|
|
||||||
n += var.n;
|
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
|
||||||
|
|
||||||
if (!src.is(i + n, "=")) return ParseRes.failed();
|
|
||||||
n++;
|
|
||||||
|
|
||||||
var value = JavaScript.parseExpression(src, i + n, 2);
|
|
||||||
if (!value.isSuccess()) return value.chainError(src.loc(i + n), "Expected a shorthand initializer");
|
|
||||||
n += value.n;
|
|
||||||
|
|
||||||
return ParseRes.res(new AssignShorthandNode(loc, new StringNode(loc, var.result.name), var.result, value.result), n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -9,8 +9,6 @@ import me.topchetoeu.jscript.compilation.CompileResult;
|
|||||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
import me.topchetoeu.jscript.compilation.JavaScript;
|
||||||
import me.topchetoeu.jscript.compilation.Node;
|
import me.topchetoeu.jscript.compilation.Node;
|
||||||
import me.topchetoeu.jscript.compilation.values.ObjectNode;
|
import me.topchetoeu.jscript.compilation.values.ObjectNode;
|
||||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.constants.StringNode;
|
|
||||||
|
|
||||||
public class FieldMemberNode implements Member {
|
public class FieldMemberNode implements Member {
|
||||||
public final Location loc;
|
public final Location loc;
|
||||||
@@ -19,14 +17,19 @@ public class FieldMemberNode implements Member {
|
|||||||
|
|
||||||
@Override public Location loc() { return loc; }
|
@Override public Location loc() { return loc; }
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute, boolean enumerable) {
|
@Override public void compileFunctions(CompileResult target) {
|
||||||
|
key.compileFunctions(target);
|
||||||
|
value.compileFunctions(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
if (pollute) target.add(Instruction.dup());
|
if (pollute) target.add(Instruction.dup());
|
||||||
key.compile(target, true);
|
key.compile(target, true);
|
||||||
|
|
||||||
if (value == null) target.add(Instruction.pushUndefined());
|
if (value == null) target.add(Instruction.pushUndefined());
|
||||||
else value.compile(target, true);
|
else value.compile(target, true);
|
||||||
|
|
||||||
target.add(Instruction.defField(enumerable));
|
target.add(Instruction.defField());
|
||||||
}
|
}
|
||||||
|
|
||||||
public FieldMemberNode(Location loc, Node key, Node value) {
|
public FieldMemberNode(Location loc, Node key, Node value) {
|
||||||
@@ -35,7 +38,7 @@ public class FieldMemberNode implements Member {
|
|||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ParseRes<FieldMemberNode> parseObject(Source src, int i) {
|
public static ParseRes<FieldMemberNode> parse(Source src, int i) {
|
||||||
var n = Parsing.skipEmpty(src, i);
|
var n = Parsing.skipEmpty(src, i);
|
||||||
var loc = src.loc(i + n);
|
var loc = src.loc(i + n);
|
||||||
|
|
||||||
@@ -53,40 +56,4 @@ public class FieldMemberNode implements Member {
|
|||||||
|
|
||||||
return ParseRes.res(new FieldMemberNode(loc, name.result, value.result), n);
|
return ParseRes.res(new FieldMemberNode(loc, name.result, value.result), n);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ParseRes<FieldMemberNode> parseShorthand(Source src, int i) {
|
|
||||||
var n = Parsing.skipEmpty(src, i);
|
|
||||||
var loc = src.loc(i + n);
|
|
||||||
|
|
||||||
var var = VariableNode.parse(src, i + n);
|
|
||||||
if (!var.isSuccess()) return var.chainError();
|
|
||||||
n += var.n;
|
|
||||||
|
|
||||||
return ParseRes.res(new FieldMemberNode(loc, new StringNode(loc, var.result.name), var.result), n);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ParseRes<FieldMemberNode> parseClass(Source src, int i) {
|
|
||||||
var n = Parsing.skipEmpty(src, i);
|
|
||||||
var loc = src.loc(i + n);
|
|
||||||
|
|
||||||
var name = ObjectNode.parsePropName(src, i + n);
|
|
||||||
if (!name.isSuccess()) return name.chainError();
|
|
||||||
n += name.n;
|
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
|
||||||
|
|
||||||
if (!src.is(i + n, "=")) {
|
|
||||||
var end = JavaScript.parseStatementEnd(src, i + n);
|
|
||||||
if (!end.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected an end of statement or a field initializer");
|
|
||||||
n += end.n;
|
|
||||||
|
|
||||||
return ParseRes.res(new FieldMemberNode(loc, name.result, null), n);
|
|
||||||
}
|
|
||||||
n++;
|
|
||||||
|
|
||||||
var value = JavaScript.parseExpression(src, i + n, 2);
|
|
||||||
if (!value.isSuccess()) return value.chainError(src.loc(i + n), "Expected a value");
|
|
||||||
n += value.n;
|
|
||||||
|
|
||||||
return ParseRes.res(new FieldMemberNode(loc, name.result, value.result), n);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -6,5 +6,6 @@ import me.topchetoeu.jscript.compilation.CompileResult;
|
|||||||
public interface Member {
|
public interface Member {
|
||||||
Location loc();
|
Location loc();
|
||||||
|
|
||||||
void compile(CompileResult target, boolean pollute, boolean enumerable);
|
void compileFunctions(CompileResult target);
|
||||||
|
void compile(CompileResult target, boolean pollute);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.members;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
|
||||||
import me.topchetoeu.jscript.common.environment.Environment;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Source;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompoundNode;
|
|
||||||
import me.topchetoeu.jscript.compilation.FunctionNode;
|
|
||||||
import me.topchetoeu.jscript.compilation.Node;
|
|
||||||
import me.topchetoeu.jscript.compilation.Parameters;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.ObjectNode;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.constants.StringNode;
|
|
||||||
|
|
||||||
public class MethodMemberNode extends FunctionNode implements Member {
|
|
||||||
public final Node key;
|
|
||||||
|
|
||||||
@Override public String name() {
|
|
||||||
if (key instanceof StringNode str) return str.value;
|
|
||||||
else return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override protected Environment rootEnv(Environment env) {
|
|
||||||
return env;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
|
||||||
if (pollute) target.add(Instruction.dup());
|
|
||||||
key.compile(target, true);
|
|
||||||
|
|
||||||
var id = target.addChild(compileBody(target, name, null));
|
|
||||||
target.add(_i -> Instruction.loadFunc(id, true, false, false, false, null, captures(id, target)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute, boolean enumerable) {
|
|
||||||
compile(target, pollute);
|
|
||||||
target.add(Instruction.defField(enumerable));
|
|
||||||
}
|
|
||||||
|
|
||||||
public MethodMemberNode(Location loc, Location end, Node key, Parameters params, CompoundNode body) {
|
|
||||||
super(loc, end, params, body);
|
|
||||||
this.key = key;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ParseRes<MethodMemberNode> parse(Source src, int i) {
|
|
||||||
var n = Parsing.skipEmpty(src, i);
|
|
||||||
var loc = src.loc(i + n);
|
|
||||||
|
|
||||||
var name = ObjectNode.parsePropName(src, i + n);
|
|
||||||
if (!name.isSuccess()) return name.chainError();
|
|
||||||
n += name.n;
|
|
||||||
|
|
||||||
var params = Parameters.parseParameters(src, i + n);
|
|
||||||
if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected an argument list");
|
|
||||||
n += params.n;
|
|
||||||
|
|
||||||
var body = CompoundNode.parse(src, i + n);
|
|
||||||
if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a compound statement for property accessor.");
|
|
||||||
n += body.n;
|
|
||||||
|
|
||||||
var end = src.loc(i + n - 1);
|
|
||||||
|
|
||||||
return ParseRes.res(new MethodMemberNode(
|
|
||||||
loc, end, name.result, params.result, body.result
|
|
||||||
), n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,6 @@ import java.util.Arrays;
|
|||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
import me.topchetoeu.jscript.common.Instruction;
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.common.environment.Environment;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
import me.topchetoeu.jscript.common.parsing.Location;
|
||||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||||
@@ -12,15 +11,15 @@ import me.topchetoeu.jscript.common.parsing.Source;
|
|||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
import me.topchetoeu.jscript.compilation.CompileResult;
|
||||||
import me.topchetoeu.jscript.compilation.CompoundNode;
|
import me.topchetoeu.jscript.compilation.CompoundNode;
|
||||||
import me.topchetoeu.jscript.compilation.FunctionNode;
|
import me.topchetoeu.jscript.compilation.FunctionNode;
|
||||||
|
import me.topchetoeu.jscript.compilation.JavaScript;
|
||||||
import me.topchetoeu.jscript.compilation.Node;
|
import me.topchetoeu.jscript.compilation.Node;
|
||||||
import me.topchetoeu.jscript.compilation.Parameters;
|
|
||||||
import me.topchetoeu.jscript.compilation.patterns.Pattern;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.ObjectNode;
|
import me.topchetoeu.jscript.compilation.values.ObjectNode;
|
||||||
|
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||||
import me.topchetoeu.jscript.compilation.values.constants.StringNode;
|
import me.topchetoeu.jscript.compilation.values.constants.StringNode;
|
||||||
|
|
||||||
public class PropertyMemberNode extends FunctionNode implements Member{
|
public final class PropertyMemberNode extends FunctionNode implements Member {
|
||||||
public final Node key;
|
public final Node key;
|
||||||
public final Pattern argument;
|
public final VariableNode argument;
|
||||||
|
|
||||||
@Override public String name() {
|
@Override public String name() {
|
||||||
if (key instanceof StringNode str) {
|
if (key instanceof StringNode str) {
|
||||||
@@ -30,29 +29,24 @@ public class PropertyMemberNode extends FunctionNode implements Member{
|
|||||||
else return null;
|
else return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected Environment rootEnv(Environment env) {
|
|
||||||
return env;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isGetter() { return argument == null; }
|
public boolean isGetter() { return argument == null; }
|
||||||
public boolean isSetter() { return argument != null; }
|
public boolean isSetter() { return argument != null; }
|
||||||
|
|
||||||
|
@Override public void compileFunctions(CompileResult target) {
|
||||||
|
key.compileFunctions(target);
|
||||||
|
target.addChild(this, compileBody(target, null));
|
||||||
|
}
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
||||||
if (pollute) target.add(Instruction.dup());
|
if (pollute) target.add(Instruction.dup());
|
||||||
key.compile(target, true);
|
key.compile(target, true);
|
||||||
|
|
||||||
var id = target.addChild(compileBody(target, name, null));
|
target.add(Instruction.loadFunc(target.childrenIndices.get(this), name(name), captures(target))).setLocation(loc());
|
||||||
target.add(_i -> Instruction.loadFunc(id, true, false, false, false, null, captures(id, target)));
|
target.add(Instruction.defProp(isSetter()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PropertyMemberNode(Location loc, Location end, Node key, VariableNode argument, CompoundNode body) {
|
||||||
@Override public void compile(CompileResult target, boolean pollute, boolean enumerable) {
|
super(loc, end, argument == null ? Arrays.asList() : Arrays.asList(argument), body);
|
||||||
compile(target, pollute);
|
|
||||||
target.add(Instruction.defProp(isSetter(), enumerable));
|
|
||||||
}
|
|
||||||
|
|
||||||
public PropertyMemberNode(Location loc, Location end, Node key, Pattern argument, CompoundNode body) {
|
|
||||||
super(loc, end, argument == null ? new Parameters(Arrays.asList()) : new Parameters(Arrays.asList(argument)), body);
|
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.argument = argument;
|
this.argument = argument;
|
||||||
}
|
}
|
||||||
@@ -70,11 +64,10 @@ public class PropertyMemberNode extends FunctionNode implements Member{
|
|||||||
if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected a property name after '" + access + "'");
|
if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected a property name after '" + access + "'");
|
||||||
n += name.n;
|
n += name.n;
|
||||||
|
|
||||||
var params = Parameters.parseParameters(src, i + n);
|
var params = JavaScript.parseParameters(src, i + n);
|
||||||
if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected an argument list");
|
if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected an argument list");
|
||||||
if (access.result.equals("get") && params.result.params.size() != 0) return ParseRes.error(src.loc(i + n), "Getter must not have any parameters");
|
if (access.result.equals("get") && params.result.size() != 0) return ParseRes.error(src.loc(i + n), "Getter must not have any parameters");
|
||||||
if (access.result.equals("set") && params.result.params.size() != 1) return ParseRes.error(src.loc(i + n), "Setter must have exactly one parameter");
|
if (access.result.equals("set") && params.result.size() != 1) return ParseRes.error(src.loc(i + n), "Setter must have exactly one parameter");
|
||||||
if (params.result.rest != null) return ParseRes.error(params.result.rest.loc(), "Property members may not have rest arguments");
|
|
||||||
n += params.n;
|
n += params.n;
|
||||||
|
|
||||||
var body = CompoundNode.parse(src, i + n);
|
var body = CompoundNode.parse(src, i + n);
|
||||||
@@ -84,7 +77,7 @@ public class PropertyMemberNode extends FunctionNode implements Member{
|
|||||||
var end = src.loc(i + n - 1);
|
var end = src.loc(i + n - 1);
|
||||||
|
|
||||||
return ParseRes.res(new PropertyMemberNode(
|
return ParseRes.res(new PropertyMemberNode(
|
||||||
loc, end, name.result, access.result.equals("get") ? null : params.result.params.get(0), body.result
|
loc, end, name.result, access.result.equals("get") ? null : params.result.get(0), body.result
|
||||||
), n);
|
), n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.patterns;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Operation;
|
|
||||||
import me.topchetoeu.jscript.common.SyntaxException;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Source;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
|
||||||
import me.topchetoeu.jscript.compilation.Node;
|
|
||||||
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
|
||||||
|
|
||||||
public class AssignPattern implements Pattern {
|
|
||||||
public final Location loc;
|
|
||||||
public final AssignTarget assignable;
|
|
||||||
public final Node value;
|
|
||||||
|
|
||||||
@Override public Location loc() { return loc; }
|
|
||||||
|
|
||||||
@Override public void destructDeclResolve(CompileResult target) {
|
|
||||||
if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context");
|
|
||||||
p.destructDeclResolve(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void common(CompileResult target) {
|
|
||||||
target.add(Instruction.dup());
|
|
||||||
target.add(Instruction.pushUndefined());
|
|
||||||
target.add(Instruction.operation(Operation.EQUALS));
|
|
||||||
var start = target.temp();
|
|
||||||
target.add(Instruction.discard());
|
|
||||||
|
|
||||||
value.compile(target, true);
|
|
||||||
|
|
||||||
target.set(start, Instruction.jmpIfNot(target.size() - start));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void declare(CompileResult target, DeclarationType decl, boolean lateInitializer) {
|
|
||||||
if (lateInitializer) {
|
|
||||||
if (assignable instanceof Pattern p) p.declare(target, decl, lateInitializer);
|
|
||||||
else throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context");
|
|
||||||
}
|
|
||||||
else throw new SyntaxException(loc(), "Expected an assignment value for destructor declaration");
|
|
||||||
}
|
|
||||||
@Override public void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare) {
|
|
||||||
if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context");
|
|
||||||
common(target);
|
|
||||||
p.destruct(target, decl, shouldDeclare);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void beforeAssign(CompileResult target) {
|
|
||||||
assignable.beforeAssign(target);
|
|
||||||
}
|
|
||||||
@Override public void afterAssign(CompileResult target, boolean pollute) {
|
|
||||||
common(target);
|
|
||||||
assignable.afterAssign(target, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AssignPattern(Location loc, Pattern assignable, Node value) {
|
|
||||||
this.loc = loc;
|
|
||||||
this.assignable = assignable;
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ParseRes<AssignPattern> parse(Source src, int i) {
|
|
||||||
var n = Parsing.skipEmpty(src, i);
|
|
||||||
var loc = src.loc(i + n);
|
|
||||||
|
|
||||||
var pattern = Pattern.parse(src, i + n, false);
|
|
||||||
if (!pattern.isSuccess()) return pattern.chainError();
|
|
||||||
n += pattern.n;
|
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
|
||||||
|
|
||||||
if (!src.is(i + n, "=")) return ParseRes.failed();
|
|
||||||
n++;
|
|
||||||
|
|
||||||
var value = JavaScript.parseExpression(src, i + n, 2);
|
|
||||||
if (!value.isSuccess()) return value.chainError(src.loc(i + n), "Expected a default value");
|
|
||||||
n += value.n;
|
|
||||||
|
|
||||||
return ParseRes.res(new AssignPattern(loc, pattern.result, value.result), n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.patterns;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.SyntaxException;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Source;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
|
||||||
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
|
||||||
|
|
||||||
public class Binding implements Pattern {
|
|
||||||
public final Location loc;
|
|
||||||
public final DeclarationType type;
|
|
||||||
public final AssignTarget assignable;
|
|
||||||
|
|
||||||
@Override public Location loc() { return loc; }
|
|
||||||
|
|
||||||
@Override public void destructDeclResolve(CompileResult target) {
|
|
||||||
if (type != null && !type.strict) {
|
|
||||||
if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context");
|
|
||||||
p.destructDeclResolve(target);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare) {
|
|
||||||
if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context");
|
|
||||||
p.destruct(target, decl, shouldDeclare);
|
|
||||||
}
|
|
||||||
@Override public void declare(CompileResult target, DeclarationType decl, boolean lateInitializer) {
|
|
||||||
if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context");
|
|
||||||
p.declare(target, decl, lateInitializer);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void resolve(CompileResult target) {
|
|
||||||
if (type != null) destructDeclResolve(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void declare(CompileResult target, boolean hasInit) {
|
|
||||||
if (type != null) destructVar(target, type, hasInit);
|
|
||||||
}
|
|
||||||
public void declareLateInit(CompileResult target) {
|
|
||||||
if (type != null) declare(target, type, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void afterAssign(CompileResult target, boolean pollute) {
|
|
||||||
assignable.assign(target, pollute);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Binding(Location loc, DeclarationType type, AssignTarget assignable) {
|
|
||||||
this.loc = loc;
|
|
||||||
this.type = type;
|
|
||||||
this.assignable = assignable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ParseRes<Binding> parse(Source src, int i) {
|
|
||||||
var n = Parsing.skipEmpty(src, i);
|
|
||||||
var loc = src.loc(i + n);
|
|
||||||
|
|
||||||
var declType = JavaScript.parseDeclarationType(src, i + n);
|
|
||||||
if (!declType.isSuccess()) {
|
|
||||||
var res = JavaScript.parseExpression(src, i + n, 13);
|
|
||||||
if (res.isSuccess() && res.result instanceof AssignTargetLike target) {
|
|
||||||
n += res.n;
|
|
||||||
return ParseRes.res(new Binding(loc, null, target.toAssignTarget()), n);
|
|
||||||
}
|
|
||||||
else return ParseRes.failed();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
n += declType.n;
|
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
|
||||||
|
|
||||||
var res = Pattern.parse(src, i + n, false);
|
|
||||||
if (!res.isSuccess()) return ParseRes.failed();
|
|
||||||
n += res.n;
|
|
||||||
|
|
||||||
return ParseRes.res(new Binding(loc, declType.result, res.result), n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,161 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.patterns;
|
|
||||||
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.SyntaxException;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Source;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.Node;
|
|
||||||
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.ObjectNode;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.constants.StringNode;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.operations.IndexNode;
|
|
||||||
|
|
||||||
public class ObjectPattern extends Node implements Pattern {
|
|
||||||
public static final class Member {
|
|
||||||
public final Node key;
|
|
||||||
public final AssignTarget consumable;
|
|
||||||
|
|
||||||
public Member(Node key, AssignTarget consumer) {
|
|
||||||
this.key = key;
|
|
||||||
this.consumable = consumer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final List<Member> members;
|
|
||||||
|
|
||||||
public void compile(CompileResult target, Consumer<AssignTarget> consumer, boolean pollute) {
|
|
||||||
for (var el : members) {
|
|
||||||
target.add(Instruction.dup());
|
|
||||||
IndexNode.indexLoad(target, el.key, true);
|
|
||||||
consumer.accept(el.consumable);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!pollute) target.add(Instruction.discard());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void destructDeclResolve(CompileResult target) {
|
|
||||||
for (var t : members) {
|
|
||||||
if (t.consumable instanceof Pattern p) p.destructDeclResolve(target);
|
|
||||||
else throw new SyntaxException(t.consumable.loc(), "Unexpected non-pattern in destruct context");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare) {
|
|
||||||
compile(target, t -> {
|
|
||||||
if (t instanceof Pattern p) p.destruct(target, decl, shouldDeclare);
|
|
||||||
else throw new SyntaxException(t.loc(), "Unexpected non-pattern in destruct context");
|
|
||||||
}, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void afterAssign(CompileResult target, boolean pollute) {
|
|
||||||
compile(target, t -> t.assign(target, false), pollute);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void declare(CompileResult target, DeclarationType decl, boolean lateInitializer) {
|
|
||||||
if (lateInitializer) {
|
|
||||||
for (var t : members) {
|
|
||||||
if (t.consumable instanceof Pattern p) p.declare(target, decl, lateInitializer);
|
|
||||||
else throw new SyntaxException(t.consumable.loc(), "Unexpected non-pattern in destruct context");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else throw new SyntaxException(loc(), "Object pattern must be initialized");
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectPattern(Location loc, List<Member> members) {
|
|
||||||
super(loc);
|
|
||||||
this.members = members;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ParseRes<Member> parseShorthand(Source src, int i) {
|
|
||||||
ParseRes<Pattern> res = ParseRes.first(src, i,
|
|
||||||
AssignPattern::parse,
|
|
||||||
VariableNode::parse
|
|
||||||
);
|
|
||||||
|
|
||||||
if (res.isSuccess()) {
|
|
||||||
if (res.result instanceof AssignPattern assign) {
|
|
||||||
if (assign.assignable instanceof VariableNode var) {
|
|
||||||
return ParseRes.res(new Member(new StringNode(var.loc(), var.name), res.result), res.n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (res.result instanceof VariableNode var) {
|
|
||||||
return ParseRes.res(new Member(new StringNode(var.loc(), var.name), res.result), res.n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return res.chainError();
|
|
||||||
}
|
|
||||||
private static ParseRes<Member> parseKeyed(Source src, int i) {
|
|
||||||
var n = Parsing.skipEmpty(src, i);
|
|
||||||
|
|
||||||
var key = ObjectNode.parsePropName(src, i + n);
|
|
||||||
if (!key.isSuccess()) return key.chainError();
|
|
||||||
n += key.n;
|
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
|
||||||
|
|
||||||
if (!src.is(i + n , ":")) return ParseRes.failed();
|
|
||||||
n++;
|
|
||||||
|
|
||||||
ParseRes<Pattern> res = Pattern.parse(src, i + n, true);
|
|
||||||
if (!res.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a pattern after colon");
|
|
||||||
n += res.n;
|
|
||||||
|
|
||||||
return ParseRes.res(new Member(key.result, res.result), n);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ParseRes<ObjectPattern> parse(Source src, int i) {
|
|
||||||
var n = Parsing.skipEmpty(src, i);
|
|
||||||
var loc = src.loc(i + n);
|
|
||||||
|
|
||||||
if (!src.is(i + n, "{")) return ParseRes.failed();
|
|
||||||
n++;
|
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
|
||||||
|
|
||||||
var members = new LinkedList<Member>();
|
|
||||||
|
|
||||||
if (src.is(i + n, "}")) {
|
|
||||||
n++;
|
|
||||||
return ParseRes.res(new ObjectPattern(loc, members), n);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
|
||||||
ParseRes<Member> prop = ParseRes.first(src, i + n,
|
|
||||||
ObjectPattern::parseKeyed,
|
|
||||||
ObjectPattern::parseShorthand
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!prop.isSuccess()) return prop.chainError(src.loc(i + n), "Expected a member in object pattern");
|
|
||||||
n += prop.n;
|
|
||||||
|
|
||||||
members.add(prop.result);
|
|
||||||
|
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
|
||||||
if (src.is(i + n, ",")) {
|
|
||||||
n++;
|
|
||||||
n += Parsing.skipEmpty(src, i + n);
|
|
||||||
|
|
||||||
if (src.is(i + n, "}")) {
|
|
||||||
n++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else if (src.is(i + n, "}")) {
|
|
||||||
n++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else ParseRes.error(src.loc(i + n), "Expected a comma or a closing brace.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return ParseRes.res(new ObjectPattern(loc, members), n);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.patterns;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Source;
|
|
||||||
import me.topchetoeu.jscript.compilation.CompileResult;
|
|
||||||
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents all nodes that can be a destructors (note that all destructors are assign targets, too)
|
|
||||||
*/
|
|
||||||
public interface Pattern extends AssignTarget {
|
|
||||||
Location loc();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the destructor has to declare
|
|
||||||
* @param target
|
|
||||||
*/
|
|
||||||
void destructDeclResolve(CompileResult target);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when a declaration-like is being destructed
|
|
||||||
* @param decl The variable type the destructor must declare, if it is a named pne
|
|
||||||
*/
|
|
||||||
void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run when destructing a declaration without an initializer
|
|
||||||
*/
|
|
||||||
void declare(CompileResult target, DeclarationType decl, boolean lateInitializer);
|
|
||||||
|
|
||||||
public default void destructArg(CompileResult target, DeclarationType decl) {
|
|
||||||
destruct(target, decl, false);
|
|
||||||
}
|
|
||||||
public default void destructVar(CompileResult target, DeclarationType decl, boolean hasInitializer) {
|
|
||||||
if (hasInitializer) {
|
|
||||||
if (decl == null || !decl.strict) destruct(target, null, true);
|
|
||||||
else destruct(target, decl, true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (decl == null || !decl.strict) declare(target, null, false);
|
|
||||||
else declare(target, decl, false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ParseRes<Pattern> parse(Source src, int i, boolean withDefault) {
|
|
||||||
return withDefault ?
|
|
||||||
ParseRes.first(src, i,
|
|
||||||
AssignPattern::parse,
|
|
||||||
ObjectPattern::parse,
|
|
||||||
VariableNode::parse
|
|
||||||
) :
|
|
||||||
ParseRes.first(src, i,
|
|
||||||
ObjectPattern::parse,
|
|
||||||
VariableNode::parse
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,67 +1,103 @@
|
|||||||
package me.topchetoeu.jscript.compilation.scope;
|
package me.topchetoeu.jscript.compilation.scope;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
public final class FunctionScope {
|
||||||
|
protected final VariableList locals = new VariableList(VariableIndex.IndexType.LOCALS);
|
||||||
public class FunctionScope extends Scope {
|
protected final VariableList capturables = new VariableList(VariableIndex.IndexType.CAPTURABLES, this.locals);
|
||||||
private final VariableList captures = new VariableList(VariableIndex.IndexType.CAPTURES);
|
private final VariableList captures = new VariableList(VariableIndex.IndexType.CAPTURES);
|
||||||
|
|
||||||
private final HashMap<String, Variable> specialVarMap = new HashMap<>();
|
private final HashMap<String, Variable> localsMap = new HashMap<>();
|
||||||
private final HashMap<String, Variable> functionVarMap = new HashMap<>();
|
|
||||||
private final HashMap<String, Variable> capturesMap = new HashMap<>();
|
private final HashMap<String, Variable> capturesMap = new HashMap<>();
|
||||||
private final HashSet<String> blacklistNames = new HashSet<>();
|
private final ArrayList<Variable> catchesMap = new ArrayList<>();
|
||||||
|
|
||||||
private final HashMap<Variable, Variable> childToParent = new HashMap<>();
|
private final HashMap<Variable, Variable> childToParent = new HashMap<>();
|
||||||
private final HashMap<Variable, Variable> parentToChild = new HashMap<>();
|
private final HashMap<Variable, Variable> parentToChild = new HashMap<>();
|
||||||
|
|
||||||
private final Scope captureParent;
|
public final FunctionScope parent;
|
||||||
|
public final boolean passthrough;
|
||||||
|
|
||||||
public final boolean passtrough;
|
private Variable addCaptured(Variable var, boolean captured) {
|
||||||
|
if (captured && !this.capturables.has(var) && !this.captures.has(var)) this.capturables.add(var);
|
||||||
@Override public boolean hasNonStrict(String name) {
|
return var;
|
||||||
if (functionVarMap.containsKey(name)) return true;
|
|
||||||
if (blacklistNames.contains(name)) return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public Variable define(Variable var, Location loc) {
|
private Variable getCatchVar(String name) {
|
||||||
checkNotEnded();
|
for (var el : catchesMap) {
|
||||||
if (strictVarMap.containsKey(var.name)) throw alreadyDefinedErr(loc, var.name);
|
if (el.name.equals(name)) return el;
|
||||||
|
}
|
||||||
|
|
||||||
if (passtrough) {
|
|
||||||
blacklistNames.add(var.name);
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns If a variable with the same name exists, the old variable. Otherwise, the given variable
|
||||||
|
*/
|
||||||
|
public Variable define(Variable var) {
|
||||||
|
if (passthrough) return null;
|
||||||
else {
|
else {
|
||||||
functionVarMap.put(var.name, var);
|
var catchVar = getCatchVar(var.name);
|
||||||
|
if (catchVar != null) return catchVar;
|
||||||
|
if (localsMap.containsKey(var.name)) return localsMap.get(var.name);
|
||||||
|
if (capturesMap.containsKey(var.name)) throw new RuntimeException("HEY!");
|
||||||
|
|
||||||
|
localsMap.put(var.name, var);
|
||||||
return locals.add(var);
|
return locals.add(var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public Variable defineSpecial(Variable var, Location loc) {
|
|
||||||
checkNotEnded();
|
|
||||||
if (strictVarMap.containsKey(var.name)) throw alreadyDefinedErr(loc, var.name);
|
|
||||||
|
|
||||||
specialVarMap.put(var.name, var);
|
/**
|
||||||
return locals.add(var);
|
* @returns A variable with the given name, or null if a global variable
|
||||||
|
*/
|
||||||
|
public Variable define(String name) {
|
||||||
|
return define(new Variable(name, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public Variable get(String name, boolean capture) {
|
/**
|
||||||
var superRes = super.get(name, capture);
|
* Creates a catch variable and returns it
|
||||||
if (superRes != null) return superRes;
|
*/
|
||||||
|
public Variable defineCatch(String name) {
|
||||||
|
var var = new Variable(name, false);
|
||||||
|
this.locals.add(var);
|
||||||
|
this.catchesMap.add(var);
|
||||||
|
return var;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Creates a catch variable, using a specific variable instance
|
||||||
|
* Used in the second pass
|
||||||
|
*/
|
||||||
|
public Variable defineCatch(String name, Variable var) {
|
||||||
|
this.locals.add(var);
|
||||||
|
this.catchesMap.add(var);
|
||||||
|
return var;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Removes the last catch variable.
|
||||||
|
* NOTE: the variable is still in the internal list. It just won't be findable by its name
|
||||||
|
*/
|
||||||
|
public void undefineCatch() {
|
||||||
|
this.catchesMap.remove(this.catchesMap.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
if (specialVarMap.containsKey(name)) return addCaptured(specialVarMap.get(name), capture);
|
/**
|
||||||
if (functionVarMap.containsKey(name)) return addCaptured(functionVarMap.get(name), capture);
|
* Gets the index supplier of the given variable name, or null if it is a global
|
||||||
|
*
|
||||||
|
* @param capture If true, the variable is being captured by a function
|
||||||
|
*/
|
||||||
|
public Variable get(String name, boolean capture) {
|
||||||
|
var catchVar = getCatchVar(name);
|
||||||
|
|
||||||
|
if (catchVar != null) return addCaptured(catchVar, capture);
|
||||||
|
if (localsMap.containsKey(name)) return addCaptured(localsMap.get(name), capture);
|
||||||
if (capturesMap.containsKey(name)) return addCaptured(capturesMap.get(name), capture);
|
if (capturesMap.containsKey(name)) return addCaptured(capturesMap.get(name), capture);
|
||||||
|
|
||||||
if (captureParent == null) return null;
|
if (parent == null) return null;
|
||||||
|
|
||||||
var parentVar = captureParent.get(name, true);
|
var parentVar = parent.get(name, true);
|
||||||
if (parentVar == null) return null;
|
if (parentVar == null) return null;
|
||||||
|
|
||||||
var childVar = captures.add(parentVar.clone());
|
var childVar = captures.add(parentVar.clone().setIndexSupplier(null));
|
||||||
capturesMap.put(childVar.name, childVar);
|
capturesMap.put(childVar.name, childVar);
|
||||||
childToParent.put(childVar, parentVar);
|
childToParent.put(childVar, parentVar);
|
||||||
parentToChild.put(parentVar, childVar);
|
parentToChild.put(parentVar, childVar);
|
||||||
@@ -69,14 +105,23 @@ public class FunctionScope extends Scope {
|
|||||||
return childVar;
|
return childVar;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public Variable get(Variable var, boolean capture) {
|
/**
|
||||||
if (parentToChild.containsKey(var)) return addCaptured(parentToChild.get(var), capture);
|
* If the variable given is contained in this function, just returns the variable itself.
|
||||||
|
* However, this function is important to handle cases in which you might want to access
|
||||||
|
* a captured variable. In such cases, this function will return a capture to the given variable.
|
||||||
|
*
|
||||||
|
* @param capture Whether or not to execute this capturing logic
|
||||||
|
*/
|
||||||
|
public Variable get(Variable var, boolean capture) {
|
||||||
if (captures.has(var)) return addCaptured(var, capture);
|
if (captures.has(var)) return addCaptured(var, capture);
|
||||||
if (locals.has(var)) return addCaptured(var, capture);
|
if (locals.has(var)) return addCaptured(var, capture);
|
||||||
|
|
||||||
if (captureParent == null) return null;
|
if (capture) {
|
||||||
|
if (parentToChild.containsKey(var)) return addCaptured(parentToChild.get(var), capture);
|
||||||
|
|
||||||
var parentVar = captureParent.get(var, true);
|
if (parent == null) return null;
|
||||||
|
|
||||||
|
var parentVar = parent.get(var, true);
|
||||||
if (parentVar == null) return null;
|
if (parentVar == null) return null;
|
||||||
|
|
||||||
var childVar = captures.add(parentVar.clone());
|
var childVar = captures.add(parentVar.clone());
|
||||||
@@ -85,33 +130,40 @@ public class FunctionScope extends Scope {
|
|||||||
|
|
||||||
return childVar;
|
return childVar;
|
||||||
}
|
}
|
||||||
|
else return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override public boolean has(String name, boolean capture) {
|
/**
|
||||||
if (functionVarMap.containsKey(name)) return true;
|
* Checks if the given variable name is accessible
|
||||||
if (specialVarMap.containsKey(name)) return true;
|
*
|
||||||
|
* @param capture If true, will check beyond this function's scope
|
||||||
|
*/
|
||||||
|
public boolean has(String name, boolean capture) {
|
||||||
|
if (localsMap.containsKey(name)) return true;
|
||||||
|
|
||||||
if (capture) {
|
if (capture) {
|
||||||
if (capturesMap.containsKey(name)) return true;
|
if (capturesMap.containsKey(name)) return true;
|
||||||
if (captureParent != null) return captureParent.has(name, true);
|
if (parent != null) return parent.has(name, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override protected void onFinish() {
|
public int localsCount() {
|
||||||
captures.freeze();
|
return locals.size();
|
||||||
super.onFinish();
|
|
||||||
}
|
}
|
||||||
|
public int capturesCount() {
|
||||||
@Override public int capturesCount() {
|
|
||||||
return captures.size();
|
return captures.size();
|
||||||
}
|
}
|
||||||
|
public int capturablesCount() {
|
||||||
|
return capturables.size();
|
||||||
|
}
|
||||||
|
|
||||||
public int[] getCaptureIndices() {
|
public int[] getCaptureIndices() {
|
||||||
var res = new int[captures.size()];
|
var res = new int[captures.size()];
|
||||||
var i = 0;
|
var i = 0;
|
||||||
|
|
||||||
for (var el : captures.all()) {
|
for (var el : captures) {
|
||||||
assert childToParent.containsKey(el);
|
assert childToParent.containsKey(el);
|
||||||
res[i] = childToParent.get(el).index().toCaptureIndex();
|
res[i] = childToParent.get(el).index().toCaptureIndex();
|
||||||
i++;
|
i++;
|
||||||
@@ -120,17 +172,50 @@ public class FunctionScope extends Scope {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionScope(Scope parent) {
|
public Iterable<Variable> capturables() {
|
||||||
super();
|
return capturables;
|
||||||
if (parent.finished()) throw new RuntimeException("Parent is finished");
|
|
||||||
this.captureParent = parent;
|
|
||||||
this.passtrough = false;
|
|
||||||
this.singleEntry = false;
|
|
||||||
}
|
}
|
||||||
public FunctionScope(boolean passtrough) {
|
public Iterable<Variable> locals() {
|
||||||
super();
|
return locals;
|
||||||
this.captureParent = null;
|
}
|
||||||
this.passtrough = passtrough;
|
|
||||||
this.singleEntry = false;
|
public String[] captureNames() {
|
||||||
|
var res = new String[this.captures.size()];
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
for (var el : this.captures) {
|
||||||
|
res[i++] = el.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
public String[] localNames() {
|
||||||
|
var res = new String[this.locals.size()];
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
for (var el : this.locals) {
|
||||||
|
res[i++] = el.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
public String[] capturableNames() {
|
||||||
|
var res = new String[this.capturables.size()];
|
||||||
|
var i = 0;
|
||||||
|
|
||||||
|
for (var el : this.capturables) {
|
||||||
|
res[i++] = el.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FunctionScope(FunctionScope parent) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.passthrough = false;
|
||||||
|
}
|
||||||
|
public FunctionScope(boolean passthrough) {
|
||||||
|
this.parent = null;
|
||||||
|
this.passthrough = passthrough;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,249 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.scope;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.LinkedList;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.SyntaxException;
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
|
||||||
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
|
||||||
|
|
||||||
public class Scope {
|
|
||||||
protected final HashMap<String, Variable> strictVarMap = new HashMap<>();
|
|
||||||
|
|
||||||
protected final VariableList locals = new VariableList(VariableIndex.IndexType.LOCALS, this::variableOffset);
|
|
||||||
protected final VariableList capturables = new VariableList(VariableIndex.IndexType.CAPTURABLES, this::capturablesOffset);
|
|
||||||
|
|
||||||
private boolean ended = false;
|
|
||||||
private boolean finished = false;
|
|
||||||
private Scope child;
|
|
||||||
private LinkedList<Scope> children = new LinkedList<>();
|
|
||||||
|
|
||||||
public final Scope parent;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wether or not the scope is going to be entered multiple times.
|
|
||||||
* If set to true, captured variables will be kept as allocations, otherwise will be converted to locals
|
|
||||||
*/
|
|
||||||
public boolean singleEntry = true;
|
|
||||||
|
|
||||||
|
|
||||||
protected void transferCaptured(Variable var) {
|
|
||||||
if (!singleEntry) {
|
|
||||||
if (!this.capturables.has(var)) this.capturables.add(var);
|
|
||||||
}
|
|
||||||
else if (parent != null) {
|
|
||||||
parent.transferCaptured(var);
|
|
||||||
}
|
|
||||||
else throw new IllegalStateException("Couldn't transfer captured variable");
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final Variable addCaptured(Variable var, boolean captured) {
|
|
||||||
if (captured) transferCaptured(var);
|
|
||||||
return var;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected final SyntaxException alreadyDefinedErr(Location loc, String name) {
|
|
||||||
return new SyntaxException(loc, String.format("Identifier '%s' has already been declared", name));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Throws if the scope is ended
|
|
||||||
*/
|
|
||||||
protected final void checkNotEnded() {
|
|
||||||
if (ended) throw new IllegalStateException("Cannot define in an ended scope");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines a nameless variable for holding intermediate temporary values
|
|
||||||
*
|
|
||||||
* @throws RuntimeException If the scope is finalized or has an active child
|
|
||||||
*/
|
|
||||||
public Variable defineTemp() {
|
|
||||||
checkNotEnded();
|
|
||||||
return this.locals.add(new Variable("<temp>", false));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines an ES5-style variable
|
|
||||||
*
|
|
||||||
* @returns The index supplier of the variable if it is a local, or null if it is a global
|
|
||||||
* @throws SyntaxException If an ES2015-style variable with the same name exists anywhere from the current function to the current scope
|
|
||||||
* @throws RuntimeException If the scope is finalized or has an active child
|
|
||||||
*/
|
|
||||||
public Variable define(Variable var, Location loc) {
|
|
||||||
checkNotEnded();
|
|
||||||
if (strictVarMap.containsKey(var.name)) throw alreadyDefinedErr(loc, var.name);
|
|
||||||
if (parent != null) return parent.define(var, loc);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if this scope's function parent has a non-strict variable of the given name
|
|
||||||
*/
|
|
||||||
public boolean hasNonStrict(String name) { return false; }
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines an ES2015-style variable
|
|
||||||
* @param readonly True if const, false if let
|
|
||||||
* @return The index supplier of the variable
|
|
||||||
* @throws SyntaxException If any variable with the same name exists in the current scope
|
|
||||||
* @throws RuntimeException If the scope is finalized or has an active child
|
|
||||||
*/
|
|
||||||
public Variable defineStrict(Variable var, Location loc) {
|
|
||||||
checkNotEnded();
|
|
||||||
if (strictVarMap.containsKey(var.name)) throw alreadyDefinedErr(loc, var.name);
|
|
||||||
if (hasNonStrict(var.name)) throw alreadyDefinedErr(loc, var.name);
|
|
||||||
|
|
||||||
strictVarMap.put(var.name, var);
|
|
||||||
return locals.add(var);
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Gets the index supplier of the given variable name, or null if it is a global
|
|
||||||
*
|
|
||||||
* @param capture If true, the variable is being captured by a function
|
|
||||||
*/
|
|
||||||
public Variable get(String name, boolean capture) {
|
|
||||||
var res = strictVarMap.get(name);
|
|
||||||
|
|
||||||
if (res != null) return addCaptured(res, capture);
|
|
||||||
if (parent != null) return parent.get(name, capture);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Gets the index supplier of the given variable, or null if it isn't in this scope chain.
|
|
||||||
* This will also execute capturing logic.
|
|
||||||
*
|
|
||||||
* @param capture If true, the variable is being captured by a function
|
|
||||||
*/
|
|
||||||
public Variable get(Variable var, boolean capture) {
|
|
||||||
if (locals.has(var)) return addCaptured(var, capture);
|
|
||||||
if (parent != null) return parent.get(var, capture);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Checks if the given variable name is accessible
|
|
||||||
*
|
|
||||||
* @param capture If true, will check beyond this function's scope
|
|
||||||
*/
|
|
||||||
public boolean has(String name, boolean capture) {
|
|
||||||
if (strictVarMap.containsKey(name)) return true;
|
|
||||||
if (parent != null) return parent.has(name, capture);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* Gets the index offset from this scope to its children
|
|
||||||
*/
|
|
||||||
public final int variableOffset() {
|
|
||||||
var res = 0;
|
|
||||||
|
|
||||||
for (var curr = parent; curr != null; curr = curr.parent) {
|
|
||||||
res += curr.locals.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
public final int capturablesOffset() {
|
|
||||||
var res = 0;
|
|
||||||
|
|
||||||
for (var curr = this; curr != null; curr = curr.parent) {
|
|
||||||
if (curr != this) res += curr.capturables.size();
|
|
||||||
if (curr.parent == null) res += curr.localsCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final Variable define(DeclarationType type, String name, Location loc) {
|
|
||||||
if (type.strict) return defineStrict(new Variable(name, type.readonly), loc);
|
|
||||||
else return define(new Variable(name, type.readonly), loc);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int localsCount() {
|
|
||||||
var res = 0;
|
|
||||||
for (var child : children) {
|
|
||||||
var childN = child.localsCount();
|
|
||||||
if (res < childN) res = childN;
|
|
||||||
}
|
|
||||||
|
|
||||||
return res + locals.size();
|
|
||||||
}
|
|
||||||
public int capturesCount() { return 0; }
|
|
||||||
public int allocCount() {
|
|
||||||
var res = capturables.size();
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
public int capturablesCount() {
|
|
||||||
var res = capturables.size();
|
|
||||||
for (var child : children) res += child.capturablesCount();
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Iterable<Variable> capturables() {
|
|
||||||
return capturables.all();
|
|
||||||
}
|
|
||||||
public Iterable<Variable> locals() {
|
|
||||||
return locals.all();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ends this scope. This will make it possible for another child to take its place
|
|
||||||
*/
|
|
||||||
public boolean end() {
|
|
||||||
if (ended) return false;
|
|
||||||
|
|
||||||
this.ended = true;
|
|
||||||
|
|
||||||
if (this.parent != null) {
|
|
||||||
assert this.parent.child == this;
|
|
||||||
this.parent.child = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void onFinish() {
|
|
||||||
this.locals.freeze();
|
|
||||||
this.capturables.freeze();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Finalizes this scope. The scope will become immutable after this call
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public final boolean finish() {
|
|
||||||
if (finished) return false;
|
|
||||||
|
|
||||||
if (parent != null && parent.finished) throw new IllegalStateException("Tried to finish a child after the parent was finished");
|
|
||||||
this.onFinish();
|
|
||||||
|
|
||||||
for (var child : children) child.finish();
|
|
||||||
|
|
||||||
this.finished = true;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final boolean ended() { return ended; }
|
|
||||||
public final boolean finished() { return finished; }
|
|
||||||
public final Scope child() { return child; }
|
|
||||||
|
|
||||||
public Scope() {
|
|
||||||
this(null);
|
|
||||||
}
|
|
||||||
public Scope(Scope parent) {
|
|
||||||
if (parent != null) {
|
|
||||||
if (parent.ended) throw new RuntimeException("Parent is not active");
|
|
||||||
if (parent.finished) throw new RuntimeException("Parent is finished");
|
|
||||||
if (parent.child != null) throw new RuntimeException("Parent has an active child");
|
|
||||||
|
|
||||||
this.parent = parent;
|
|
||||||
this.parent.child = this;
|
|
||||||
this.parent.children.add(this);
|
|
||||||
}
|
|
||||||
else this.parent = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -4,20 +4,14 @@ import java.util.function.Supplier;
|
|||||||
|
|
||||||
public final class Variable {
|
public final class Variable {
|
||||||
private Supplier<VariableIndex> indexSupplier;
|
private Supplier<VariableIndex> indexSupplier;
|
||||||
private boolean frozen;
|
|
||||||
|
|
||||||
public final boolean readonly;
|
public final boolean readonly;
|
||||||
public final String name;
|
public final String name;
|
||||||
|
|
||||||
public final VariableIndex index() {
|
public final VariableIndex index() {
|
||||||
if (!frozen) throw new IllegalStateException("Tried to access the index of a variable before it was finalized");
|
|
||||||
return indexSupplier.get();
|
return indexSupplier.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void freeze() {
|
|
||||||
this.frozen = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final Variable setIndexSupplier(Supplier<VariableIndex> index) {
|
public final Variable setIndexSupplier(Supplier<VariableIndex> index) {
|
||||||
this.indexSupplier = index;
|
this.indexSupplier = index;
|
||||||
return this;
|
return this;
|
||||||
|
|||||||
@@ -4,12 +4,21 @@ import me.topchetoeu.jscript.common.Instruction;
|
|||||||
|
|
||||||
public final class VariableIndex {
|
public final class VariableIndex {
|
||||||
public static enum IndexType {
|
public static enum IndexType {
|
||||||
|
/**
|
||||||
|
* A simple variable that is only ever used within the function
|
||||||
|
*/
|
||||||
LOCALS,
|
LOCALS,
|
||||||
|
/**
|
||||||
|
* A variable that has the ability to be captured by children functions
|
||||||
|
*/
|
||||||
CAPTURABLES,
|
CAPTURABLES,
|
||||||
|
/**
|
||||||
|
* A variable that has been captured from the parent function
|
||||||
|
*/
|
||||||
CAPTURES,
|
CAPTURES,
|
||||||
}
|
}
|
||||||
|
|
||||||
public final VariableIndex.IndexType type;
|
public final IndexType type;
|
||||||
public final int index;
|
public final int index;
|
||||||
|
|
||||||
public final int toCaptureIndex() {
|
public final int toCaptureIndex() {
|
||||||
@@ -30,28 +39,12 @@ public final class VariableIndex {
|
|||||||
}
|
}
|
||||||
public final Instruction toSet(boolean keep) {
|
public final Instruction toSet(boolean keep) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case CAPTURES: return Instruction.storeVar(index, keep, false);
|
case CAPTURES: return Instruction.storeVar(~index, keep, false);
|
||||||
case CAPTURABLES: return Instruction.storeVar(index, keep, false);
|
case CAPTURABLES: return Instruction.storeVar(index, keep, false);
|
||||||
case LOCALS: return Instruction.storeVar(index, keep, false);
|
case LOCALS: return Instruction.storeVar(index, keep, false);
|
||||||
default: throw new UnsupportedOperationException("Unknown index type " + type);
|
default: throw new UnsupportedOperationException("Unknown index type " + type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public final Instruction toInit() {
|
|
||||||
switch (type) {
|
|
||||||
case CAPTURES: throw new UnsupportedOperationException("Unknown index type " + type);
|
|
||||||
case CAPTURABLES: return Instruction.storeVar(index, false, true);
|
|
||||||
case LOCALS: return Instruction.storeVar(index, false, true);
|
|
||||||
default: throw new UnsupportedOperationException("Unknown index type " + type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public final Instruction toUndefinedInit(boolean force) {
|
|
||||||
switch (type) {
|
|
||||||
case CAPTURES: throw new UnsupportedOperationException("Unknown index type " + type);
|
|
||||||
case CAPTURABLES: return Instruction.varInit(index, force);
|
|
||||||
case LOCALS: return Instruction.varInit(index, force);
|
|
||||||
default: throw new UnsupportedOperationException("Unknown index type " + type);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public VariableIndex(VariableIndex.IndexType type, int index) {
|
public VariableIndex(VariableIndex.IndexType type, int index) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
|
|||||||
@@ -1,48 +1,36 @@
|
|||||||
package me.topchetoeu.jscript.compilation.scope;
|
package me.topchetoeu.jscript.compilation.scope;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.function.IntSupplier;
|
import java.util.function.IntSupplier;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public final class VariableList {
|
import me.topchetoeu.jscript.compilation.scope.VariableIndex.IndexType;
|
||||||
|
|
||||||
|
public final class VariableList implements Iterable<Variable> {
|
||||||
private final class VariableNode implements Supplier<VariableIndex> {
|
private final class VariableNode implements Supplier<VariableIndex> {
|
||||||
public Variable var;
|
public Variable var;
|
||||||
public VariableNode next;
|
public VariableNode next;
|
||||||
public VariableNode prev;
|
public VariableNode prev;
|
||||||
public boolean frozen;
|
|
||||||
public int index;
|
public int index;
|
||||||
|
public int indexIteration = -1;
|
||||||
|
|
||||||
public VariableList list() { return VariableList.this; }
|
public VariableList list() { return VariableList.this; }
|
||||||
|
|
||||||
|
private int getIndex() {
|
||||||
|
if (this.indexIteration != VariableList.this.indexIteration) {
|
||||||
|
this.indexIteration = VariableList.this.indexIteration;
|
||||||
|
|
||||||
|
if (prev == null) this.index = 0;
|
||||||
|
else this.index = prev.getIndex() + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.index;
|
||||||
|
}
|
||||||
|
|
||||||
@Override public VariableIndex get() {
|
@Override public VariableIndex get() {
|
||||||
if (frozen) {
|
if (offset == null) return new VariableIndex(indexType, this.getIndex());
|
||||||
if (offset == null) return new VariableIndex(indexType, index);
|
else return new VariableIndex(indexType, offset.getAsInt() + this.getIndex());
|
||||||
else new VariableIndex(indexType, index + offset.getAsInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
var res = 0;
|
|
||||||
if (offset != null) res = offset.getAsInt();
|
|
||||||
|
|
||||||
for (var it = prev; it != null; it = it.prev) {
|
|
||||||
res++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return new VariableIndex(indexType, res);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void freeze() {
|
|
||||||
if (frozen) return;
|
|
||||||
this.frozen = true;
|
|
||||||
this.next = null;
|
|
||||||
this.var.freeze();
|
|
||||||
if (prev == null) return;
|
|
||||||
|
|
||||||
this.index = prev.index + 1;
|
|
||||||
this.next = null;
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public VariableNode(Variable var, VariableNode next, VariableNode prev) {
|
public VariableNode(Variable var, VariableNode next, VariableNode prev) {
|
||||||
@@ -54,32 +42,25 @@ public final class VariableList {
|
|||||||
|
|
||||||
private VariableNode first, last;
|
private VariableNode first, last;
|
||||||
|
|
||||||
private ArrayList<VariableNode> frozenList = null;
|
|
||||||
private HashMap<Variable, VariableNode> varMap = new HashMap<>();
|
private HashMap<Variable, VariableNode> varMap = new HashMap<>();
|
||||||
|
|
||||||
private final IntSupplier offset;
|
private final IntSupplier offset;
|
||||||
|
/**
|
||||||
|
* Increased when indices need recalculation. VariableNode will check if
|
||||||
|
* its internal indexIteration is up to date with this, and if not, will
|
||||||
|
* recalculate its index
|
||||||
|
*/
|
||||||
|
private int indexIteration = 0;
|
||||||
|
|
||||||
public final VariableIndex.IndexType indexType;
|
public final VariableIndex.IndexType indexType;
|
||||||
|
|
||||||
public boolean frozen() {
|
/**
|
||||||
if (frozenList != null) {
|
* Adds the given variable to this list. If it already exists, does nothing
|
||||||
assert frozenList != null;
|
* @return val
|
||||||
assert varMap != null;
|
*/
|
||||||
assert first == null;
|
|
||||||
assert last == null;
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
assert frozenList == null;
|
|
||||||
assert varMap != null;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Variable add(Variable val) {
|
public Variable add(Variable val) {
|
||||||
if (frozen()) throw new RuntimeException("The scope has been frozen");
|
if (this.varMap.containsKey(val)) return val;
|
||||||
|
this.indexIteration++;
|
||||||
|
|
||||||
if (val.indexSupplier() instanceof VariableNode prevNode) {
|
if (val.indexSupplier() instanceof VariableNode prevNode) {
|
||||||
prevNode.list().remove(val);
|
prevNode.list().remove(val);
|
||||||
@@ -105,14 +86,18 @@ public final class VariableList {
|
|||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If the variable is not in the list, does nothing. Otherwise, removes the variable from the list
|
||||||
|
* @return null if nothing was done, else the deleted variable (should be var)
|
||||||
|
*/
|
||||||
public Variable remove(Variable var) {
|
public Variable remove(Variable var) {
|
||||||
if (frozen()) throw new RuntimeException("The scope has been frozen");
|
|
||||||
|
|
||||||
if (var == null) return null;
|
if (var == null) return null;
|
||||||
|
|
||||||
var node = varMap.get(var);
|
var node = varMap.get(var);
|
||||||
if (node == null) return null;
|
if (node == null) return null;
|
||||||
|
|
||||||
|
this.indexIteration++;
|
||||||
|
|
||||||
if (node.prev != null) {
|
if (node.prev != null) {
|
||||||
assert node != first;
|
assert node != first;
|
||||||
node.prev.next = node.next;
|
node.prev.next = node.next;
|
||||||
@@ -135,42 +120,31 @@ public final class VariableList {
|
|||||||
node.prev = null;
|
node.prev = null;
|
||||||
|
|
||||||
varMap.remove(node.var);
|
varMap.remove(node.var);
|
||||||
|
node.var.setIndexSupplier(null);
|
||||||
|
|
||||||
return node.var;
|
return node.var;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if the list has the given variable
|
||||||
|
*/
|
||||||
public boolean has(Variable var) {
|
public boolean has(Variable var) {
|
||||||
return varMap.containsKey(var);
|
return varMap.containsKey(var);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an indexer for the given variable
|
||||||
|
*/
|
||||||
public Supplier<VariableIndex> indexer(Variable var) {
|
public Supplier<VariableIndex> indexer(Variable var) {
|
||||||
return varMap.get(var);
|
return varMap.get(var);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int size() {
|
public int size() {
|
||||||
if (frozen()) return frozenList.size();
|
return varMap.size();
|
||||||
else return varMap.size();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void freeze() {
|
public Iterator<Variable> iterator() {
|
||||||
if (frozen()) return;
|
return new Iterator<Variable>() {
|
||||||
|
|
||||||
frozenList = new ArrayList<>();
|
|
||||||
|
|
||||||
for (var node = first; node != null; ) {
|
|
||||||
frozenList.add(node);
|
|
||||||
|
|
||||||
var tmp = node;
|
|
||||||
node = node.next;
|
|
||||||
tmp.freeze();
|
|
||||||
}
|
|
||||||
|
|
||||||
first = last = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Iterable<Variable> all() {
|
|
||||||
if (frozen()) return () -> frozenList.stream().map(v -> v.var).iterator();
|
|
||||||
else return () -> new Iterator<Variable>() {
|
|
||||||
private VariableNode curr = first;
|
private VariableNode curr = first;
|
||||||
|
|
||||||
@Override public boolean hasNext() {
|
@Override public boolean hasNext() {
|
||||||
@@ -186,19 +160,31 @@ public final class VariableList {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public VariableList(VariableIndex.IndexType type, IntSupplier offset) {
|
/**
|
||||||
|
* @param offset Will offset the indices by the given amount from the supplier
|
||||||
|
*/
|
||||||
|
public VariableList(IndexType type, IntSupplier offset) {
|
||||||
this.indexType = type;
|
this.indexType = type;
|
||||||
this.offset = offset;
|
this.offset = offset;
|
||||||
}
|
}
|
||||||
public VariableList(VariableIndex.IndexType type, int offset) {
|
/**
|
||||||
|
* @param offset Will offset the indices by the given amount
|
||||||
|
*/
|
||||||
|
public VariableList(IndexType type, int offset) {
|
||||||
this.indexType = type;
|
this.indexType = type;
|
||||||
this.offset = () -> offset;
|
this.offset = () -> offset;
|
||||||
}
|
}
|
||||||
public VariableList(VariableIndex.IndexType type, VariableList prev) {
|
/**
|
||||||
|
* @param offset Will offset the indices by the size of the given list
|
||||||
|
*/
|
||||||
|
public VariableList(IndexType type, VariableList prev) {
|
||||||
this.indexType = type;
|
this.indexType = type;
|
||||||
this.offset = prev::size;
|
this.offset = () -> {
|
||||||
|
if (prev.offset != null) return prev.offset.getAsInt() + prev.size();
|
||||||
|
else return prev.size();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
public VariableList(VariableIndex.IndexType type) {
|
public VariableList(IndexType type) {
|
||||||
this.indexType = type;
|
this.indexType = type;
|
||||||
this.offset = null;
|
this.offset = null;
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user