Compare commits

..

2 Commits

Author SHA1 Message Date
b791f3277e refactor: split up code in 4 modules 2024-01-10 11:21:09 +02:00
5e6e6fea61 feat: some API improvements 2024-01-07 13:40:01 +02:00
371 changed files with 16598 additions and 17227 deletions

View File

@@ -3,7 +3,7 @@ name: "tagged-release"
on:
push:
tags:
- "*"
- "v*"
jobs:
tagged-release:
@@ -11,24 +11,25 @@ jobs:
runs-on: "ubuntu-latest"
steps:
- name: Clone repository
uses: actions/checkout@v3
- name: Setup Java
uses: actions/setup-java@v3
with:
distribution: 'adopt'
java-version: '17'
- name: Setup Gradle
uses: gradle/gradle-build-action@v2
java-version: '11'
- name: Clone repository
uses: GuillaumeFalourd/clone-github-repo-action@main
with:
cache-disabled: true
gradle-version: "8.10"
- name: Build
run: gradle build
- name: Create release
uses: "https://gitea.com/actions/gitea-release-action@main"
branch: 'master' # fuck this political bullshitshit, took me an hour to fix this
owner: 'TopchetoEU'
repository: 'java-jscript'
- name: "Build"
run: |
cd java-jscript; node ./build.js release ${{ github.ref }}
- uses: "marvinpinto/action-automatic-releases@latest"
with:
# api_key: "${{secrets.TOKEN}}"
repo_token: "${{ secrets.GITHUB_TOKEN }}"
prerelease: false
files: |
LICENSE
build/libs/*.jar
java-jscript/LICENSE
java-jscript/dst/*.jar

20
.gitignore vendored
View File

@@ -1,19 +1,21 @@
/*
*
!/src
!/src/**/*
/src/assets/js/ts.js
!/doc
!/doc/**/*
!/tests
!/tests/**/*
!/.github
!/.github/**/*
!/.gitignore
!/.gitattributes
!/build.js
!/LICENSE
!/README.md
!/settings.gradle
!/build.gradle
!/gradle.properties
!/package.json
!/rollup.config.js
!/tsconfig.json

View File

@@ -1,15 +1,17 @@
# J2S (Java-JavaScript or Java to JavaScript)
# JScript
**NOTE: This had nothing to do with Microsoft's dialect of EcmaScript**
**WARNING: Currently, this code is undocumented. Proceed with caution and a psychiatrist.**
J2S is an engine, capable of running EcmaScript 5, written entirely in Java. This engine has been developed with the goal of being easy to integrate with your preexisting codebase, **THE GOAL OF THIS ENGINE IS NOT PERFORMANCE**. My crude experiments show that this engine is 50x-100x slower than V8, which, although bad, is acceptable for most simple scripting purposes. Note that although the codebase has a Main class, this isn't meant to be a standalone program, but instead a library for running JavaScript code.
JScript is an engine, capable of running EcmaScript 5, written entirely in Java. This engine has been developed with the goal of being easy to integrate with your preexisting codebase, **THE GOAL OF THIS ENGINE IS NOT PERFORMANCE**. My crude experiments show that this engine is 50x-100x slower than V8, which, although bad, is acceptable for most simple scripting purposes. Note that although the codebase has a Main class, this isn't meant to be a standalone program, but instead a library for running JavaScript code.
## Example
The following is going to execute a simple javascript statement:
```java
var engine = new Engine();
var engine = new Engine(false);
// Initialize a standard environment, with implementations of most basic standard libraries (Object, Array, Symbol, etc.)
var env = Internals.apply(new Environment());
@@ -21,3 +23,7 @@ engine.run(true);
// Get our result
System.out.println(awaitable.await());
```
## NOTE:
To setup the typescript bundle in your sources, run `node build.js init-ts`. This will download the latest version of typescript, minify it, and add it to your src/assets folder. If you are going to work with the `node build.js debug|release` command, this is not a necessary step.

View File

@@ -1,128 +0,0 @@
import java.text.SimpleDateFormat
plugins {
id 'application';
id 'com.github.node-gradle.node' version '5.0.0';
id 'net.nemerosa.versioning' version '2.15.0';
id 'org.ajoberstar.grgit' version '5.0.0-rc.3'; // required by gradle
// TODO: figure out how to integrate proguard
// id "com.github.xaverkapeller.proguard-annotations"
}
base.archivesName = project.project_name;
version = project.project_version;
group = project.project_group;
description = 'ES5-compliant JavaScript interpreter';
node {
version = '20.0.0';
npmVersion = '8.0.0';
download = true;
}
task compileEnv(type: NpmTask) {
dependsOn npmInstall;
inputs.files('rollup.config.js');
inputs.dir('src/lib/libs');
outputs.files("build/js/env.js");
// group = 'build'
args = ['run', 'build-env'];
}
task compileTypescript(type: NpmTask) {
dependsOn npmInstall;
inputs.files('rollup.config.js');
inputs.dir('src/lib/transpiler');
outputs.files("build/js/ts.js");
// nom nom tasty ram
environment.put("NODE_OPTIONS", "--max-old-space-size=4096");
// group = 'build'
args = ['run', 'build-ts'];
}
repositories {
mavenCentral();
}
dependencies {
annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2';
compileOnly 'com.github.bsideup.jabel:jabel-javac-plugin:0.4.2';
testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2';
testRuntimeOnly 'org.junit.platform:junit-platform-launcher';
}
java {
sourceCompatibility = JavaVersion.VERSION_17;
targetCompatibility = JavaVersion.VERSION_17;
toolchain {
languageVersion = JavaLanguageVersion.of(17);
}
}
configure([tasks.compileJava]) {
options.release = 8;
}
jar {
manifest {
attributes(
'Main-Class': project.main_class,
'Build-Timestamp': new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").format(new Date()),
'Build-Branch': versioning.info.branch,
'Build-Revision': versioning.info.commit,
'Build-Jdk': "${System.properties['java.version']} (${System.properties['java.vendor']} ${System.properties['java.vm.version']})",
'Build-Author': 'TopchetoEU',
);
}
}
application {
mainClass = project.main_class;
applicationDefaultJvmArgs = ['-Xmx2G', '-Xms2G', '-server', '-Dfile.encoding=UTF-8'];
}
distZip {
eachFile { file ->
if (file.path.contains('bin')) {
file.exclude();
}
}
}
distTar {
eachFile { file ->
if (file.path.contains('bin')) {
file.exclude();
}
}
}
processResources {
dependsOn compileEnv;
dependsOn compileTypescript;
from("build/js") {
into "lib";
}
filesMatching "metadata.json", {
expand(
version: project.project_version,
name: project.project_name,
);
}
}
test {
useJUnitPlatform();
}
wrapper {
gradleVersion = '8.10';
}

193
build.js Normal file
View File

@@ -0,0 +1,193 @@
const { spawn } = require('child_process');
const fs = require('fs/promises');
const pt = require('path');
const { argv, exit } = require('process');
const { Readable } = require('stream');
async function* find(src, dst, wildcard) {
const stat = await fs.stat(src);
if (stat.isDirectory()) {
for (const el of await fs.readdir(src)) {
for await (const res of find(pt.join(src, el), dst ? pt.join(dst, el) : undefined, wildcard)) yield res;
}
}
else if (stat.isFile() && wildcard(src)) yield dst ? { src, dst } : src;
}
async function copy(src, dst, wildcard) {
const promises = [];
for await (const el of find(src, dst, wildcard)) {
promises.push((async () => {
await fs.mkdir(pt.dirname(el.dst), { recursive: true });
await fs.copyFile(el.src, el.dst);
})());
}
await Promise.all(promises);
}
function run(suppressOutput, cmd, ...args) {
return new Promise((res, rej) => {
const proc = spawn(cmd, args, { stdio: suppressOutput ? 'ignore' : 'inherit' });
proc.once('exit', code => {
if (code === 0) res(code);
else rej(new Error(`Process ${cmd} exited with code ${code}.`));
});
})
}
async function downloadTypescript(outFile) {
try {
// Import the required libraries, without the need of a package.json
console.log('Importing modules...');
await run(true, 'npm', 'i', 'tar', 'zlib', 'uglify-js');
await fs.mkdir(pt.dirname(outFile), { recursive: true });
await fs.mkdir('tmp', { recursive: true });
const tar = require('tar');
const zlib = require('zlib');
const { minify } = await import('uglify-js');
// Download the package.json file of typescript
const packageDesc = await (await fetch('https://registry.npmjs.org/typescript/latest')).json();
const url = packageDesc.dist.tarball;
console.log('Extracting typescript...');
await new Promise(async (res, rej) => Readable.fromWeb((await fetch(url)).body)
.pipe(zlib.createGunzip())
.pipe(tar.x({ cwd: 'tmp', filter: v => v === 'package/lib/typescript.js' }))
.on('end', res)
.on('error', rej)
);
console.log('Compiling typescript to ES5...');
const ts = require('./tmp/package/lib/typescript');
const program = ts.createProgram([ 'tmp/package/lib/typescript.js' ], {
outFile: "tmp/typescript-es5.js",
target: ts.ScriptTarget.ES5,
module: ts.ModuleKind.None,
downlevelIteration: true,
allowJs: true,
});
program.emit();
console.log('Minifying typescript...');
const minified = minify((await fs.readFile('tmp/typescript-es5.js')).toString());
// const minified = { code: (await fs.readFile('tmp/typescript-es5.js')).toString() };
if (minified.error) throw minified.error;
// Patch unsupported regex syntax
minified.code = minified.code.replaceAll('[-/\\\\^$*+?.()|[\\]{}]', '[-/\\\\^$*+?.()|\\[\\]{}]');
const stream = await fs.open(outFile, 'w');
// Write typescript's license
await stream.write(new TextEncoder().encode(`
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
The following is a minified version of the unmodified Typescript 5.2
***************************************************************************** */
`));
await stream.write(minified.code);
console.log('Typescript bundling done!');
}
finally {
// Clean up all stuff left from typescript bundling
await fs.rm('tmp', { recursive: true, force: true });
await fs.rm('package.json');
await fs.rm('package-lock.json');
await fs.rm('node_modules', { recursive: true });
}
}
async function compileJava(conf) {
try {
await fs.writeFile('Metadata.java', (await fs.readFile('src/me/topchetoeu/jscript/common/Metadata.java')).toString()
.replace('${VERSION}', conf.version)
.replace('${NAME}', conf.name)
.replace('${AUTHOR}', conf.author)
);
const args = ['--release', '11', ];
if (argv[2] === 'debug') args.push('-g');
args.push('-d', 'dst/classes', 'Metadata.java');
console.log('Compiling java project...');
for await (const path of find('src', undefined, v => v.endsWith('.java') && !v.endsWith('Metadata.java'))) args.push(path);
await run(false, conf.javahome + 'javac', ...args);
console.log('Compiled java project!');
}
finally {
await fs.rm('Metadata.java');
}
}
async function jar(conf, project, mainClass) {
const args = [
'jar', '-c',
'-f', `dst/${project}-v${conf.version}.jar`,
];
if (mainClass) args.push('-e', mainClass);
args.push('-C', 'dst/classes', project.replaceAll('.', '/'));
console.log(args.join(' '));
await run(true, ...args);
}
(async () => {
try {
if (argv[2] === 'init-ts') {
await downloadTypescript('src/me/topchetoeu/jscript/utils/assets/js/ts.js');
}
else {
const conf = {
name: "java-jscript",
author: "TopchetoEU",
javahome: "",
version: argv[3]
};
if (conf.version.startsWith('refs/tags/')) conf.version = conf.version.substring(10);
if (conf.version.startsWith('v')) conf.version = conf.version.substring(1);
try { await fs.rm('dst', { recursive: true }); } catch {}
await Promise.all([
(async () => {
await copy('src', 'dst/classes', v => !v.endsWith('.java'));
// await downloadTypescript('dst/classes/me/topchetoeu/jscript/utils/assets/js/ts.js');
})(),
compileJava(conf),
]);
await Promise.all([
jar(conf, 'me.topchetoeu.jscript.common'),
jar(conf, 'me.topchetoeu.jscript.core'),
jar(conf, 'me.topchetoeu.jscript.lib'),
jar(conf, 'me.topchetoeu.jscript.utils'),
jar(conf, 'me.topchetoeu.jscript', 'me.topchetoeu.jscript.utils.JScriptRepl'),
]);
console.log('Done!');
}
}
catch (e) {
if (argv[2] === 'debug') throw e;
console.log(e.toString());
exit(-1);
}
})();

View File

@@ -1,4 +0,0 @@
project_group = me.topchetoeu
project_name = j2s
project_version = 0.10.3-beta
main_class = me.topchetoeu.j2s.repl.SimpleRepl

View File

@@ -1,31 +0,0 @@
{
"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"
}
}

View File

@@ -1,131 +0,0 @@
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 = () => true;
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,
keep_fnames: 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);

View File

@@ -1,12 +0,0 @@
pluginManagement {
repositories {
mavenCentral();
gradlePluginPortal();
}
}
plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0';
}
rootProject.name = properties.project_name;

View File

@@ -1,91 +0,0 @@
import { object, setGlobalPrototypes, target } from "./primordials.ts";
import { Error, RangeError, SyntaxError, TypeError } from "./values/errors.ts";
import { Boolean } from "./values/boolean.ts";
import { Function } from "./values/function.ts";
import { Number } from "./values/number.ts";
import { Object } from "./values/object.ts";
import { String } from "./values/string.ts";
import { Symbol } from "./values/symbol.ts";
import { Array } from "./values/array.ts";
import { Map, WeakMap } from "./classes/map.ts";
import { RegExp } from "./values/regex.ts";
import { Date } from "./classes/date.ts";
import { Math as _Math } from "./namespaces/math.ts";
import { Set, WeakSet } from "./classes/set.ts";
import { JSON } from "./namespaces/json.ts";
import { console } from "./namespaces/console.ts";
import { encodeURI, encodeURIComponent } from "./url.ts";
import { Promise } from "./classes/promise.ts";
import { ArrayBuffer } from "./arrays/ArrayBuffer.ts";
import { Uint8Array } from "./arrays/Uint8Array.ts";
import { Int32Array } from "./arrays/Int32Array.ts";
import { TypedArray } from "./arrays/TypedArray.ts";
declare global {
function print(...args: any[]): void;
function measure(func: Function): void;
}
function fixup<T extends Function>(clazz: T) {
object.setPrototype(clazz, Function.prototype);
object.setPrototype(clazz.prototype as any, Object.prototype);
return clazz;
}
object.setPrototype(target, Object.prototype);
object.defineField(target, "undefined", { e: false, c: false, w: false, v: void 0 });
target.Symbol = fixup(Symbol);
target.Number = fixup(Number);
target.String = fixup(String);
target.Boolean = fixup(Boolean);
target.Object = Object;
target.Function = fixup(Function);
target.Array = fixup(Array);
target.Error = fixup(Error);
target.RangeError = RangeError;
target.SyntaxError = SyntaxError;
target.TypeError = TypeError;
fixup(TypedArray);
target.ArrayBuffer = fixup(ArrayBuffer);
target.Uint8Array = Uint8Array;
target.Int32Array = Int32Array;
target.Map = fixup(Map);
target.WeakMap = fixup(WeakMap);
target.Set = fixup(Set);
target.WeakSet = fixup(WeakSet);
target.RegExp = fixup(RegExp);
target.Date = fixup(Date);
target.Promise = fixup(Promise);
target.Math = object.setPrototype(_Math, Object.prototype);
target.JSON = object.setPrototype(JSON, Object.prototype);
target.console = object.setPrototype(console, Object.prototype);
target.parseInt = Number.parseInt;
target.parseFloat = Number.parseFloat;
target.NaN = Number.NaN;
target.Infinity = Number.POSITIVE_INFINITY;
target.encodeURI = encodeURI;
target.encodeURIComponent = encodeURIComponent;
setGlobalPrototypes({
string: String.prototype,
number: Number.prototype,
boolean: Boolean.prototype,
symbol: Symbol.prototype,
object: Object.prototype,
array: Array.prototype,
function: Function.prototype,
error: Error.prototype,
syntax: SyntaxError.prototype,
range: RangeError.prototype,
type: TypeError.prototype,
uint8: Uint8Array.prototype,
int32: Int32Array.prototype,
regex: RegExp,
});

View File

@@ -1,30 +0,0 @@
import { buffer, type InternalBuffer, map, symbol } from "../primordials.ts";
export const abs = new map(true);
export const abKey: unique symbol = symbol.getSymbol("ArrayBuffer.impl") as any;
export class ArrayBuffer {
public [abKey]!: InternalBuffer;
public get byteLength() {
return this[abKey].length;
}
public get byteOffset() {
return 0;
}
public constructor(val: unknown) {
if (buffer.isBuff(val)) this[abKey] = val;
else this[abKey] = buffer.buff(Number(val));
}
}
export function getAB(buff: InternalBuffer): ArrayBuffer {
let res = abs.get(buff);
if (res == null) {
res = new ArrayBuffer(buff);
abs.set(buff, res);
}
return res;
}

View File

@@ -1,25 +0,0 @@
import { buffer } from "../primordials.ts";
import { abstractIgnore, TypedArray, typedArrayFuncs } from "./TypedArray.ts";
const factory = buffer.int32;
const funcs = typedArrayFuncs(4, factory);
export class Int32Array extends TypedArray {
public subarray(this: number[], start?: number, end?: number) {
return funcs.subarray(this, start, end);
}
public slice(this: any[], start?: number, end?: number) {
return funcs.slice(this, start, end);
}
public map(this: any[], cb: (val: number, i: number, self: any) => number, self?: any) {
return funcs.map(this, cb, self);
}
public filter(this: any[], cb: (val: number, i: number, self: any) => boolean, self?: any) {
return funcs.filter(this, cb, self);
}
public constructor(obj: any, start?: number, end?: number) {
super(abstractIgnore);
return funcs.construct(obj, start, end) as any;
}
}

View File

@@ -1,220 +0,0 @@
import { buffer, func, type InternalBuffer, object, string, symbol } from "../primordials.ts";
import { symbols, wrapI } from "../utils.ts";
import { Error, TypeError } from "../values/errors.ts";
import { abKey, ArrayBuffer, getAB } from "./ArrayBuffer.ts";
export const abstractIgnore = symbol.getSymbol("TypedArray.abstractIgnore");
export function typedArrayFuncs(perEl: number, constructor: (buff: InternalBuffer, start: number, end: number) => number[]) {
return {
map(self: number[], cb: (val: number, i: number, self: number[]) => number, fnSelf: any) {
const res = constructor(buffer.buff(self.length * perEl), 0, self.length);
for (let i = 0; i < self.length; i++) {
res[i] = func.invoke(cb, fnSelf, [self[i], i, self]);
}
return res;
},
filter(self: number[], cb: (val: number, i: number, self: number[]) => boolean, fnSelf: any) {
const bigger = constructor(buffer.buff(self.length * perEl), 0, self.length);
let j = 0;
for (let i = 0; i < self.length; i++) {
if (func.invoke(cb, self, [self[i], i, fnSelf])) bigger[j++] = self[i];
}
const res = constructor(buffer.buff(j * perEl), 0, j);
for (let i = 0; i < j; i++) res[i] = bigger[i];
return res;
},
slice(self: number[], start = 0, end = self.length) {
start = wrapI(start, self.length);
end = wrapI(end, self.length);
if (end <= start) return constructor(buffer.buff(0), 0, 0);
const res = constructor(buffer.buff((end - start) * perEl), 0, end - start);
for (let i = 0; i < end - start; i++) {
res[i] = self[start + i];
}
return res;
},
subarray(self: number[], start = 0, end = self.length) {
start = wrapI(start, self.length);
end = wrapI(end, self.length);
if (end <= start) return constructor(buffer.buff(0), 0, 0);
const offset = buffer.start(self);
return constructor(buffer.backer(self), offset + start, offset + end);
},
construct(self: ArrayBuffer | number[] | number | Iterable<number>, start?: number, end?: number) {
if (typeof self === "number") {
return constructor(buffer.buff(self * perEl), 0, self);
}
if (self instanceof ArrayBuffer) {
const internal = self[abKey];
if (start === undefined) start = 0;
if (end === undefined) end = (internal.length / perEl) | 0;
return constructor(internal, start, end);
}
if (symbols.iterator in self && typeof self[symbols.iterator] === "function") {
const arr: number[] = [];
let i = 0;
const gen: Iterator<number> = self[symbols.iterator]();
for (let it = gen.next(); !it.done; it = gen.next()) {
arr[i++] = Number(it.value);
}
const res = constructor(buffer.buff(i * perEl), 0, i);
for (let j = 0; j < i; j++) res[j] = arr[j];
return res;
}
const res = constructor(buffer.buff((self as number[]).length * perEl), 0, (self as number[]).length);
for (let i = 0; i < (self as number[]).length; i++) res[i] = (self as number[])[i];
return res;
},
byteOffset(self: number[]) {
return buffer.start(self) * perEl;
},
byteLength(self: number[]) {
return (buffer.end(self) - buffer.start(self)) * perEl;
},
};
}
export class TypedArray {
public get buffer() {
return getAB(buffer.backer(this as any));
}
public get byteOffset(): number {
throw new Error("abstract");
}
public get byteLength(): number {
throw new Error("abstract");
}
public forEach(this: number[], cb: (val: number, i: number, self: this) => void, self?: any) {
for (let i = 0; i < this.length; i++) {
if (i in this) func.invoke(cb, self, [this[i], i, this]);
}
}
public join(this: number[], delim = ",") {
delim = String(delim);
const parts = [];
if (delim) {
for (let i = 0; i < this.length; i++) {
if (i) parts[parts.length] = delim;
parts[parts.length] = (i in this) ? String(this[i]) : "";
}
}
else {
for (let i = 0; i < this.length; i++) {
parts[i] = (i in this) ? String(this[i]) : "";
}
}
return string.stringBuild(parts);
}
public subarray(this: number[], start = 0, end = this.length) {
throw new Error("'slice' is an abstract method");
}
public slice(this: number[], start = 0, end = this.length) {
throw new Error("'slice' is an abstract method");
}
public map(this: number[], cb: (val: number, i: number, self: this) => number, self?: any) {
throw new Error("'map' is an abstract method");
}
public filter(this: number[], cb: (val: number, i: number, self: this) => boolean, self?: any) {
throw new Error("'filter' is an abstract method");
}
public reduce(this: number[], cb: (a: number, b: number, i: number, self: number[]) => number, initial?: number) {
let i = 0;
if (arguments.length <= 1) initial = this[i++];
for (; i < this.length; i++) {
initial = cb(initial!, this[i], i, this);
}
return initial;
}
public some(this: number[], cb: (val: number, i: number, self: number[]) => boolean, self?: any) {
for (let i = 0; i < this.length; i++) {
if (func.invoke(cb, self, [this[i], i, this])) return true;
}
return false;
}
public every(this: number[], cb: (val: number, i: number, self: number[]) => boolean, self?: any) {
for (let i = 0; i < this.length; i++) {
if (!func.invoke(cb, self, [this[i], i, this])) return false;
}
return true;
}
public find(this: number[], cb: (val: number, i: number, self: number[]) => boolean, self?: any) {
for (let i = 0; i < this.length; i++) {
if (func.invoke(cb, self, [this[i], i, this])) return this[i];
}
return undefined;
}
public indexOf(this: number[], val: number, start = 0) {
start |= 0;
if (start < 0) start = 0;
for (let i = start; i < this.length; i++) {
if (this[i] === val) return i;
}
return -1;
}
public lastIndexOf(this: number[], val: number, start = 0) {
start |= 0;
if (start < 0) start = 0;
for (let i = this.length - 1; i >= start; i--) {
if (this[i] === val) return i;
}
return -1;
}
public includes(this: number[], val: number) {
for (let i = 0; i < this.length; i++) {
if (this[i] === val) return i;
}
return false;
}
public sort(this: number[], cb?: (a: number, b: number) => number) {
cb ||= (a, b) => a - b;
return object.sort(this, cb);
}
public reverse(this: number[]) {
const mid = this.length >> 1;
const end = this.length - 1;
for (let i = 0; i < mid; i++) {
const tmp = this[i];
this[i] = this[end - i];
this[end - i] = tmp;
}
return this;
}
public constructor(token?: typeof abstractIgnore) {
if (token !== abstractIgnore) {
throw new TypeError("TypedArray constructor can't be called");
}
}
}

View File

@@ -1,32 +0,0 @@
import { buffer } from "../primordials.ts";
import { abstractIgnore, TypedArray, typedArrayFuncs } from "./TypedArray.ts";
const factory = buffer.uint8;
const funcs = typedArrayFuncs(1, factory);
export class Uint8Array extends TypedArray {
public override get byteOffset() {
return funcs.byteOffset(this as any);
}
public override get byteLength() {
return funcs.byteLength(this as any);
}
public override subarray(this: number[], start?: number, end?: number) {
return funcs.subarray(this, start, end);
}
public override slice(this: any[], start?: number, end?: number) {
return funcs.slice(this, start, end);
}
public override map(this: any[], cb: (val: number, i: number, self: any) => number, self?: any) {
return funcs.map(this, cb, self);
}
public override filter(this: any[], cb: (val: number, i: number, self: any) => boolean, self?: any) {
return funcs.filter(this, cb, self);
}
public constructor(obj: any, start?: number, end?: number) {
super(abstractIgnore);
return funcs.construct(obj, start, end) as any;
}
}

View File

@@ -1,20 +0,0 @@
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>;

View File

@@ -1,128 +0,0 @@
import { Array } from "../values/array.ts";
import { func, map, symbol } from "../primordials.ts";
import { symbols } from "../utils.ts";
const mapKey: unique symbol = symbol.makeSymbol("Map.impl") as any;
export class Map<K, V> {
private [mapKey]: InstanceType<typeof map>;
public get size() {
return this[mapKey].size();
}
public get(key: K): V {
return this[mapKey].get(key);
}
public has(key: K): boolean {
return this[mapKey].has(key);
}
public set(key: K, val: V) {
this[mapKey].set(key, val);
return this;
}
public delete(key: K): boolean {
if (!this[mapKey].has(key)) return false;
else {
this[mapKey].delete(key);
return true;
}
}
public clear() {
this[mapKey].clear();
}
public keys(): K[] {
return this[mapKey].keys();
}
public values(): V[] {
const res = this[mapKey].keys();
for (let i = 0; i < res.length; i++) {
res[i] = this[mapKey].get(res[i]);
}
return res;
}
public entries(): [K, V][] {
const res = this[mapKey].keys();
for (let i = 0; i < res.length; i++) {
res[i] = [res[i], this[mapKey].get(res[i])];
}
return res;
}
public forEach(cb: Function, self?: any) {
const entries = this.entries();
for (let i = 0; i < entries.length; i++) {
func.invoke(cb, self, [entries[i][1], entries[i][0], this]);
}
}
public [symbols.iterator](): Iterator<[K, V]> {
return func.invoke(Array.prototype[symbols.iterator], this.entries(), []) as any;
}
public constructor(iterable?: Iterable<[K, V]>) {
const _map = this[mapKey] = new map();
if (iterable != null) {
if (Array.isArray(iterable)) {
for (let i = 0; i < iterable.length; i++) {
if (!(i in iterable)) continue;
_map.set(iterable[i][0], iterable[i][1]);
}
}
else {
const it = (iterable as any)[symbols.iterator]();
for (let val = it.next(); !val.done; val = it.next()) {
_map.set(val.value[0], val.value[1]);
}
}
}
}
}
export class WeakMap<K, V> {
private [mapKey]: InstanceType<typeof map>;
public get(key: K): V {
return this[mapKey].get(key);
}
public has(key: K): boolean {
return this[mapKey].has(key);
}
public set(key: K, val: V) {
this[mapKey].set(key, val);
return this;
}
public delete(key: K): boolean {
if (!this[mapKey].has(key)) return false;
else {
this[mapKey].delete(key);
return true;
}
}
public clear() {
this[mapKey].clear();
}
public constructor(iterable?: Iterable<[K, V]>) {
const _map = this[mapKey] = new map(true);
if (iterable != null) {
if (Array.isArray(iterable)) {
for (let i = 0; i < iterable.length; i++) {
if (!(i in iterable)) continue;
_map.set(iterable[i][0], iterable[i][1]);
}
}
else {
const it = (iterable as any)[symbols.iterator]();
for (let val = it.next(); !val.done; val = it.next()) {
_map.set(val.value[0], val.value[1]);
}
}
}
}
}
func.setCallable(Map, false);
func.setCallable(WeakMap, false);

View File

@@ -1,154 +0,0 @@
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);

View File

@@ -1,121 +0,0 @@
import { Array } from "../values/array.ts";
import { func, map, symbol } from "../primordials.ts";
import { symbols } from "../utils.ts";
const mapKey: unique symbol = symbol.makeSymbol("Set.impl") as any;
export class Set<T> {
private [mapKey]: InstanceType<typeof map>;
public get size() {
return this[mapKey].size();
}
public has(key: T): boolean {
return this[mapKey].has(key);
}
public add(val: T) {
this[mapKey].set(val, true);
return this;
}
public delete(val: T): boolean {
if (!this[mapKey].has(val)) return false;
else {
this[mapKey].delete(val);
return true;
}
}
public clear() {
this[mapKey].clear();
}
public keys(): T[] {
return this[mapKey].keys();
}
public values(): T[] {
return this[mapKey].keys();
}
public entries(): [T, T][] {
const res = this[mapKey].keys();
for (let i = 0; i < res.length; i++) {
res[i] = [res[i], res[i]];
}
return res;
}
public forEach(cb: Function, self?: any) {
const vals = this.values();
for (let i = 0; i < vals.length; i++) {
func.invoke(cb, self, [vals[i], vals[i], this]);
}
}
public [symbols.iterator](): Iterator<T> {
return func.invoke(Array.prototype[symbols.iterator], this.values(), []) as any;
}
public constructor(iterable?: Iterable<T>) {
const _map = this[mapKey] = new map();
if (iterable != null) {
if (Array.isArray(iterable)) {
for (let i = 0; i < iterable.length; i++) {
if (!(i in iterable)) continue;
_map.set(iterable[i], true);
}
}
else {
const it = (iterable as any)[symbols.iterator]();
for (let val = it.next(); !val.done; val = it.next()) {
_map.set(val.value, true);
}
}
}
}
}
export class WeakSet<T> {
private [mapKey]: InstanceType<typeof map>;
public has(key: T): boolean {
return this[mapKey].has(key);
}
public add(val: T) {
this[mapKey].set(val, true);
return this;
}
public delete(val: T): boolean {
if (!this[mapKey].has(val)) return false;
else {
this[mapKey].delete(val);
return true;
}
}
public clear() {
this[mapKey].clear();
}
public constructor(iterable?: Iterable<T>) {
const _map = this[mapKey] = new map(true);
if (iterable != null) {
if (Array.isArray(iterable)) {
for (let i = 0; i < iterable.length; i++) {
if (!(i in iterable)) continue;
_map.set(iterable[i], true);
}
}
else {
const it = (iterable as any)[symbols.iterator]();
for (let val = it.next(); !val.done; val = it.next()) {
_map.set(val.value, true);
}
}
}
}
}
func.setCallable(Set, false);
func.setCallable(WeakSet, false);

View File

@@ -1,11 +0,0 @@
import { func, json, object } from "../primordials.ts";
export const console = {};
function method(name: string, func: Function) {
object.defineField(console, name, { c: true, e: false, w: true, v: func });
}
method("log", function log() {
func.invoke(print, null, arguments as any);
});

View File

@@ -1,15 +0,0 @@
import { json, object } from "../primordials.ts";
export const JSON = {};
function method(name: string, func: Function) {
object.defineField(JSON, name, { c: true, e: false, w: true, v: func });
}
method("parse", function parse(val: string) {
return json.parse(val);
});
method("stringify", function stringify(val: string) {
return json.stringify(val);
});

View File

@@ -1,60 +0,0 @@
import { number, object } from "../primordials.ts";
export const Math = {};
function method(name: string, func: Function) {
object.defineField(Math, name, { c: true, e: false, w: true, v: func });
}
method("max", function max() {
let res = -number.Infinity;
for (let i = 0; i < arguments.length; i++) {
if (res < arguments[i]) res = arguments[i];
}
return res;
});
method("min", function min() {
let res = +number.Infinity;
for (let i = 0; i < arguments.length; i++) {
if (res > arguments[i]) res = arguments[i];
}
return res;
});
method("abs", function abs(val: number) {
val = +val;
if (val < 0) return -val;
else return val;
});
method("floor", function floor(val: number) {
val = val - 0;
if (number.isNaN(val)) return number.NaN;
let rem = val % 1;
if (rem < 0) rem += 1;
return val - rem;
});
method("ceil", function floor(val: number) {
val = val - 0;
if (number.isNaN(val)) return number.NaN;
let rem = val % 1;
if (rem === 0) return val;
if (rem < 0) rem += 1;
return val + (1 - rem);
});
method("pow", function pow(a: number, b: number) {
return number.pow(a, b);
});
method("log", function log(val: number) {
return number.log(val);
});

View File

@@ -1,5 +0,0 @@
import { func, object } from "../primordials.ts";
export default function _callSuper(self, constr, args) {
return func.construct(object.getPrototype(constr), func.target(1), args || []);
}

View File

@@ -1,5 +0,0 @@
import { func } from "../primordials.ts";
export default function _classCallCheck() {
if (func.invokeTypeInfer() !== "new") throw new TypeError("Cannot call a class as a function");
}

View File

@@ -1,35 +0,0 @@
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;
}

View File

@@ -1,7 +0,0 @@
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;
}

View File

@@ -1,5 +0,0 @@
import { object } from "../primordials.ts";
export default function _getPrototypeOf(obj) {
return object.getPrototype(obj) || null;
}

View File

@@ -1,11 +0,0 @@
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);
}
}

View File

@@ -1 +0,0 @@
export default function _possibleConstructorReturn(_, res) { return res; }

View File

@@ -1,3 +0,0 @@
export default function _readOnlyError(name) {
throw name;
}

View File

@@ -1,5 +0,0 @@
import { object } from "../primordials";
export default function _setPrototypeOf(obj, proto) {
object.setPrototype(obj, proto);
}

View File

@@ -1,3 +0,0 @@
export default function _typeof(val) {
return typeof val;
}

View File

@@ -1,137 +0,0 @@
const buffSymbol: unique symbol = undefined as any;
export interface InternalBuffer {
length: number;
[buffSymbol]: "buffer";
}
export interface SymbolPrimordials {
makeSymbol(name: string): symbol;
getSymbol(name: string): symbol;
getSymbolKey(symbol: symbol): string | undefined;
getSymbolDescription(symbol: symbol): string;
}
export interface NumberPrimordials {
NaN: number;
Infinity: number;
PI: number;
E: number;
parseInt(raw: string | number, radix?: number): number;
parseFloat(raw: string | number): number;
isNaN(num: number): boolean;
pow(a: number, b: number): number;
log(val: number): number;
}
export interface StringPrimordials {
stringBuild(parts: string[]): string;
fromCharCode(char: number): string;
fromCodePoint(char: number): string;
toCharCode(char: string): number;
toCodePoint(char: string, i: number): number;
indexOf(str: string, search: string, start: number, reverse?: boolean): number;
substring(str: string, start: number, end: number): string;
lower(str: string): string;
upper(str: string): string;
}
export interface ObjectPrimordials {
defineProperty(obj: object, key: string | number | symbol, conf: { g?: Function, s?: Function, e?: boolean, c?: boolean }): boolean;
defineField(obj: object, key: string | number | symbol, conf: { v?: any, e?: boolean, c?: boolean, w?: boolean }): boolean;
getOwnMember(obj: object, key: any): PropertyDescriptor | undefined;
getOwnMembers(obj: object, onlyEnumerable: boolean): string[];
getOwnSymbolMembers(obj: object, onlyEnumerable: boolean): symbol[];
getPrototype(obj: object): object | undefined;
setPrototype(obj: object, proto?: object): object;
preventExt(obj: object): void;
seal(obj: object): void;
freeze(obj: object): void;
isArray(obj: any): obj is any[];
subarray(arr: any[], start: number, end: number): any[];
memcpy(src: any[], dst: any[], srcI: number, dstI: number, n: number): void;
sort(arr: any[], cb: Function): any[];
}
export interface BufferPrimordials {
buff(n: number): InternalBuffer;
backer(arr: number[]): InternalBuffer;
start(arr: number[]): number;
end(arr: number[]): number;
uint8(buff: InternalBuffer, start: number, end: number): number[];
int8(buff: InternalBuffer, start: number, end: number): number[];
int32(buff: InternalBuffer, start: number, end: number): number[];
isUint8(val: any): val is number[];
isInt8(val: any): val is number[];
isInt32(val: any): val is number[];
is(val: any): val is number[];
isBuff(val: any): val is InternalBuffer;
}
export interface FunctionPrimordials {
invokeType(args: IArguments, self: any): "new" | "call";
invokeTypeInfer(): "new" | "call";
target(): Function | null | undefined;
setConstructable(func: Function, flag: boolean): void;
setCallable(func: Function, flag: boolean): void;
invoke(func: Function, self: any, args: any[]): any;
construct(func: Function, self: any, args: any[]): any;
}
export interface JSONPrimordials {
parse(data: string): any;
stringify(data: any): string;
}
export interface Primordials {
symbol: SymbolPrimordials;
number: NumberPrimordials;
string: StringPrimordials;
object: ObjectPrimordials;
function: FunctionPrimordials;
json: JSONPrimordials;
buffer: BufferPrimordials;
map: new (weak?: boolean) => {
get(key: any): any;
has(key: any): boolean;
set(key: any, val: any): void;
delete(key: any): void;
keys(): any[];
clear(): void;
size(): number;
};
regex: new (source: string, multiline?: boolean, noCase?: boolean, dotall?: boolean, unicode?: boolean, unicodeClass?: boolean) => {
exec(target: string, offset: number, indices: boolean): { matches: RegExpMatchArray, end: number } | null;
groupCount(): number;
};
compile(src: string): Function;
setGlobalPrototypes(prototype: Record<string, any>): void;
now(): number;
next(func: () => void): void;
schedule(func: () => void, delay: number): () => void;
}
// prevent optimization to "undefined", which doesn't exist yet
globalThis.undefined = ({} as any).bogus;
export const target = (globalThis as any).target;
export const primordials: Primordials = (globalThis as any).primordials;
export const {
symbol,
number,
string,
object,
buffer,
function: func,
json,
map,
regex,
setGlobalPrototypes,
compile,
now,
next,
schedule,
} = primordials;
export type regex = InstanceType<typeof regex>;

View File

@@ -1,26 +0,0 @@
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\\-+.!~*'();/?:@&=+$,#]"));

View File

@@ -1,218 +0,0 @@
import { func, string, symbol } from "./primordials.ts";
export const valueKey: unique symbol = symbol.makeSymbol("Primitive.value") as any;
export namespace symbols {
export const asyncIterator: unique symbol = symbol.makeSymbol("Symbol.asyncIterator") as any;
export const iterator: unique symbol = symbol.makeSymbol("Symbol.iterator") as any;
export const match: unique symbol = symbol.makeSymbol("Symbol.match") as any;
export const matchAll: unique symbol = symbol.makeSymbol("Symbol.matchAll") as any;
export const replace: unique symbol = symbol.makeSymbol("Symbol.replace") as any;
export const search: unique symbol = symbol.makeSymbol("Symbol.search") as any;
export const split: unique symbol = symbol.makeSymbol("Symbol.split") as any;
export const toStringTag: unique symbol = symbol.makeSymbol("Symbol.toStringTag") as any;
export const isConcatSpreadable: unique symbol = symbol.makeSymbol("Symbol.isConcatSpreadable") as any;
}
export interface TypeMap {
undefined: undefined;
boolean: boolean;
string: string;
number: number;
symbol: symbol;
object: null | object;
function: Function;
}
export function unwrapThis<T extends keyof TypeMap>(self: any, type: T, constr: Function, name: string, arg = "this", defaultVal?: TypeMap[T]): TypeMap[T] {
if (typeof self === type) return self;
if (self instanceof constr && valueKey in self) self = self[valueKey];
if (typeof self === type) return self;
if (defaultVal !== undefined) return defaultVal;
throw new TypeError(name + " requires that '" + arg + "' be a " + constr.name);
}
export function wrapI(i: number, length: number) {
if (i < 0) return (i + length) | 0;
else return i | 0;
}
export function limitI(i: number, max: number) {
i |= 0;
if (i < 0) return 0;
else if (i > max) return max;
else return i;
}
export type ReplaceRange = { start: number; end: number; matches: string[]; groups?: Record<string, string>; };
type ReplaceLiteral = (string | ((_: { groups: string[]; prev: () => string; next: () => string; }) => string))[];
function parseReplacer(replacer: string, groupN: number) {
const parts: ReplaceLiteral = [];
let lastI = 0;
let lastSlice = 0;
while (true) {
const i = string.indexOf(replacer, "$", lastI);
if (i < 0 || i + 1 >= replacer.length) break;
lastI = i + 1;
switch (replacer[i + 1]) {
case "$":
parts[parts.length] = string.substring(replacer, lastSlice, i);
parts[parts.length] = "$";
lastSlice = i + 2;
continue;
case "&":
parts[parts.length] = string.substring(replacer, lastSlice, i);
parts[parts.length] = ({ groups }) => groups[0];
lastSlice = i + 2;
continue;
case "`":
parts[parts.length] = string.substring(replacer, lastSlice, i);
parts[parts.length] = ({ prev }) => prev();
lastSlice = i + 2;
continue;
case "'":
parts[parts.length] = string.substring(replacer, lastSlice, i);
parts[parts.length] = ({ next }) => next();
lastSlice = i + 2;
continue;
}
let groupI = 0;
let hasGroup = false;
let consumedN = 1;
while (i + consumedN < replacer.length) {
const code = string.toCharCode(replacer[i + consumedN]);
if (code >= 48 && code <= 57) {
const newGroupI = groupI * 10 + code - 48;
if (newGroupI < 1 || newGroupI >= groupN) break;
groupI = newGroupI;
hasGroup = true;
}
consumedN++;
}
if (hasGroup) {
parts[parts.length] = string.substring(replacer, lastSlice, i);
parts[parts.length] = ({ groups }) => groups[groupI];
lastSlice = i + consumedN;
continue;
}
}
if (lastSlice === 0) return [replacer];
else parts[parts.length] = string.substring(replacer, lastSlice, replacer.length);
return parts;
}
function executeReplacer(text: string, match: ReplaceRange, literal: ReplaceLiteral, prevEnd?: number, nextStart?: number) {
const res = [];
for (let i = 0; i < literal.length; i++) {
const curr = literal[i];
if (typeof curr === "function") res[i] = curr({
groups: match.matches,
next: () => string.substring(text, prevEnd ?? 0, match.start),
prev: () => string.substring(text, match.end, nextStart ?? 0),
});
else res[i] = curr;
}
return string.stringBuild(res);
}
export function applyReplaces(text: string, ranges: ReplaceRange[], replace: any, groupN?: number | false) {
if (ranges.length === 0) return text;
const res: string[] = [];
let offset = 0;
if (groupN !== false && typeof replace === "string") {
if (groupN == null) {
for (let i = 0; i < ranges.length; i++) {
const prevEnd = i - 1 >= 0 ? ranges[i - 1].end : undefined;
const nextStart = i + 1 < ranges.length ? ranges[i + 1].start : undefined;
const range = ranges[i];
res[res.length] = string.substring(text, offset, range.start);
res[res.length] = executeReplacer(text, range, parseReplacer(replace, range.matches.length), prevEnd, nextStart);
offset = range.end;
}
res[res.length] = string.substring(text, offset, text.length);
}
else {
const literal = parseReplacer(replace, groupN);
for (let i = 0; i < ranges.length; i++) {
const prevEnd = i - 1 >= 0 ? ranges[i - 1].end : undefined;
const nextStart = i + 1 < ranges.length ? ranges[i + 1].start : undefined;
const range = ranges[i];
res[res.length] = string.substring(text, offset, range.start);
res[res.length] = executeReplacer(text, range, literal, prevEnd, nextStart);
offset = range.end;
}
res[res.length] = string.substring(text, offset, text.length);
}
return string.stringBuild(res);
}
if (typeof replace === "string") {
for (let i = 0; i < ranges.length; i++) {
const range = ranges[i];
res[res.length] = string.substring(text, offset, range.start);
res[res.length] = replace;
offset = range.end;
}
res[res.length] = string.substring(text, offset, text.length);
}
else {
for (let i = 0; i < ranges.length; i++) {
const range = ranges[i];
const args: any[] = range.matches;
args[args.length] = range.start;
args[args.length] = text;
args[args.length] = range.groups;
res[res.length] = string.substring(text, offset, range.start);
res[res.length] = func.invoke(replace, undefined, args);
offset = range.end;
}
res[res.length] = string.substring(text, offset, text.length);
}
return string.stringBuild(res);
}
export function applySplits(text: string, limit: number | undefined, next: (offset: number) => { start: number; end: number; } | undefined) {
let lastEnd = 0;
let lastEmpty = true;
let offset = 0;
const res: string[] = [];
while (true) {
if (limit != null && limit >= 0 && res.length >= limit) break;
const curr = next(offset);
if (curr == null) {
if (!lastEmpty || !res.length) res[res.length] = string.substring(text, lastEnd, text.length);
break;
}
const { start, end } = curr;
const empty = start === end;
if (offset > 0 || !empty) res[res.length] = string.substring(text, lastEnd, start);
lastEnd = end;
offset = empty ? end + 1 : end;
lastEmpty = empty;
}
return res;
}

View File

@@ -1,330 +0,0 @@
import { Error } from "../values/errors.ts";
import { func, object, string } from "../primordials.ts";
import { String } from "../values/string.ts";
import { limitI, symbols, wrapI } from "../utils.ts";
export const Array = (() => {
class Array {
public forEach(this: any[], cb: (val: any, i: number, self: this) => void, self?: any) {
for (let i = 0; i < this.length; i++) {
if (i in this) func.invoke(cb, self, [this[i], i, this]);
}
}
public join(this: any[], delim = ",") {
delim = String(delim);
const parts = [];
if (delim) {
for (let i = 0; i < this.length; i++) {
if (i) parts[parts.length] = delim;
parts[parts.length] = (i in this) ? String(this[i]) : "";
}
}
else {
for (let i = 0; i < this.length; i++) {
parts[i] = (i in this) ? String(this[i]) : "";
}
}
return string.stringBuild(parts);
}
public push(this: any[]) {
const start = this.length;
for (let i = arguments.length - 1; i >= 0; i--) {
this[start + i] = arguments[i];
}
return this.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 this.length;
}
public shift(this: any[]) {
if (this.length === 0) return undefined;
const tmp = this[0];
for (let i = 1; i < this.length; i++) {
this[i - 1] = this[i];
}
this.length--;
return tmp;
}
public concat(this: any[]) {
const res: any[] = [];
function add(arr: any) {
if (Array.isArray(arr) || arr != null && typeof arr === "object" && symbols.isConcatSpreadable in arr) {
const start = res.length;
res.length += arr.length;
for (let i = 0; i < res.length; i++) {
if (i in arr) res[start + i] = arr[i];
}
}
else res[res.length] = arr;
}
add(this);
for (let i = 0; i < arguments.length; i++) {
add(arguments[i]);
}
return res;
}
public slice(this: any[], start = 0, end = this.length) {
start = wrapI(start, this.length);
end = wrapI(end, this.length);
if (end <= start) return [];
const res: any[] = [];
res.length = end - start;
for (let i = 0; i < end - start; i++) {
res[i] = this[start + i];
}
return res;
}
public splice(this: any[], start = 0, count = this.length - start) {
const vals: any[] = []
for (let i = 0; i < arguments.length - 2; i++) vals[i] = arguments[i + 2];
start = limitI(wrapI(start, this.length), this.length);
count = limitI(wrapI(count, this.length), this.length - start);
const res: any[] = [];
const change = vals.length - count;
for (let i = start; i < start + count; i++) {
res[i - start] = this[i];
}
if (change < 0) {
for (let i = start - change; i < this.length; i++) {
this[i + change] = this[i];
}
this.length = this.length + change;
}
else {
for (let i = this.length - 1; i >= start - change; i--) {
this[i + change] = this[i];
}
}
for (let i = 0; i < vals.length; i++) {
this[i + start] = vals[i];
}
return res;
}
public map(this: any[], cb: Function, self?: any) {
const res = [];
res.length = this.length;
for (let i = 0; i < this.length; i++) {
if (i in this) res[i] = func.invoke(cb, self, [this[i], i, this]);
}
return res;
}
public filter(this: any[], cb: Function, self?: any) {
const res = [];
for (let i = 0; i < this.length; i++) {
if (i in this && func.invoke(cb, self, [this[i], i, this])) res[res.length] = this[i];
}
return res;
}
public reduce(this: any[], cb: Function, initial: any) {
let i = 0;
if (arguments.length <= 1) initial = this[i++];
for (; i < this.length; i++) {
initial = cb(initial, this[i], i, this);
}
return initial;
}
public some(this: any[], cb: Function, self?: any) {
for (let i = 0; i < this.length; i++) {
if (i in this && func.invoke(cb, self, [this[i], i, this])) return true;
}
return false;
}
public every(this: any[], cb: Function, self?: any) {
for (let i = 0; i < this.length; i++) {
if (i in this && !func.invoke(cb, self, [this[i], i, this])) return false;
}
return true;
}
public find(this: any[], cb: Function, self?: any) {
for (let i = 0; i < this.length; i++) {
if (i in this && func.invoke(cb, self, [this[i], i, this])) return this[i];
}
return undefined;
}
public indexOf(this: any[], val: any, start = 0) {
start |= 0;
if (start < 0) start = 0;
for (let i = start; i < this.length; i++) {
if (i in this && this[i] === val) return i;
}
return -1;
}
public lastIndexOf(this: any[], val: any, start = 0) {
start |= 0;
if (start < 0) start = 0;
for (let i = this.length - 1; i >= start; i--) {
if (i in this && this[i] === val) return i;
}
return -1;
}
public includes(this: any[], val: any) {
for (let i = 0; i < this.length; i++) {
if (i in this && this[i] === val) return true;
}
return false;
}
public sort(this: any[], cb?: Function) {
cb ||= (a: any, b: any) => {
if (String(a) < String(b)) return -1;
if (String(a) === String(b)) return 0;
return 1;
};
return object.sort(this, cb);
}
public reverse(this: any[]) {
const mid = this.length >> 1;
const end = this.length - 1;
for (let i = 0; i < mid; i++) {
const tmp = this[i];
this[i] = this[end - i];
this[end - i] = tmp;
}
return this;
}
public [symbols.iterator](this: any[]) {
let i = 0;
let arr: any[] | undefined = func.invoke(Array.prototype.slice, this, []);
return {
next() {
if (arr == null) return { done: true, value: undefined };
if (i >= arr.length) {
arr = undefined;
return { done: true, value: undefined };
}
while (true) {
const res = arr![i];
if (i in arr!) {
i++;
return { done: false, value: res };
}
else i++;
}
},
[symbols.iterator]() { return this; }
};
}
public constructor (len: unknown) {
if (arguments.length === 1 && typeof len === "number") {
const res: any[] = [];
res.length = len;
return res as any;
}
else {
const res: any[] = [];
res.length = arguments.length;
for (let i = 0; i < arguments.length; i++) {
res[i] = arguments[i];
}
return res as any;
}
}
public static isArray(val: any): val is any[] {
return object.isArray(val);
}
public static from(val: any, cb?: Function, self?: any): any[] {
if (symbols.iterator in val) {
const res = [];
const it = val[symbols.iterator]();
if (cb) {
for (let val = it.next(); !val.done; val = it.next()) {
res[res.length] = func.invoke(cb, self, [val.value]);
}
}
else {
for (let val = it.next(); !val.done; val = it.next()) {
res[res.length] = val.value;
}
}
return res;
}
else if ("length" in val) {
const res = [];
if (cb) {
for (let i = 0; i < val.length; i++) {
if (i in val) res[i] = func.invoke(cb, self, [val[i]]);
}
}
else {
for (let i = 0; i < val.length; i++) {
if (i in val) res[i] = val[i];
}
}
return res;
}
else if (val == null) throw new Error("Illegal argument");
else return [];
}
}
func.setCallable(Array, true);
func.setConstructable(Array, true);
return Array as any as typeof Array & ((value?: unknown) => object);
})();
export type Array = InstanceType<typeof Array>;

View File

@@ -1,29 +0,0 @@
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>;

View File

@@ -1,39 +0,0 @@
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);

View File

@@ -1,82 +0,0 @@
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);
})();

View File

@@ -1,77 +0,0 @@
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>;

View File

@@ -1,208 +0,0 @@
import { Boolean } from "./boolean.ts";
import { TypeError } from "./errors.ts";
import { Number } from "./number.ts";
import { func, object } from "../primordials.ts";
import { String } from "./string.ts";
import { symbols, valueKey } from "../utils.ts";
import { Symbol } from "../values/symbol.ts";
export const Object = (() => {
class Object {
public toString(this: unknown) {
if (this === undefined) return "[object Undefined]";
else if (this === null) return "[object Null]";
else if (typeof this === "object") {
if (symbols.toStringTag in this) return "[object " + (this as any)[symbols.toStringTag] + "]";
else if (object.isArray(this)) return "[object Array]";
else return "[object Object]";
}
else if (typeof this === "number" || this instanceof Number) return "[object Number]";
else if (typeof this === "symbol" || this instanceof Symbol) return "[object Symbol]";
else if (typeof this === "string" || this instanceof String) return "[object String]";
else if (typeof this === "boolean" || this instanceof Boolean) return "[object Boolean]";
else if (typeof this === "function") return "[object Function]";
}
public valueOf() {
return this;
}
public hasOwnProperty(key: string) {
return object.getOwnMember(this, key) != null;
}
public get __proto__() {
return object.getPrototype(this);
}
public set __proto__(val) {
object.setPrototype(this, val);
}
public constructor (value?: unknown) {
if (typeof value === 'object' && value !== null) return value as any;
if (typeof value === 'string') return new String(value) as any;
if (typeof value === 'number') return new Number(value) as any;
if (typeof value === 'boolean') return new Boolean(value) as any;
if (typeof value === 'symbol') {
const res: Symbol = {} as any;
object.setPrototype(res, Symbol.prototype);
res[valueKey] = value;
return res as any;
}
return {} as any;
}
public static getOwnPropertyDescriptor(obj: object, key: any) {
return object.getOwnMember(obj, key);
}
public static getOwnPropertyNames(obj: object): string[] {
return object.getOwnMembers(obj, false);
}
public static getOwnPropertySymbols(obj: object): symbol[] {
return object.getOwnSymbolMembers(obj, false);
}
public static defineProperty(obj: object, key: string | symbol, desc: PropertyDescriptor) {
if (obj === null || typeof obj !== "function" && typeof obj !== "object") {
throw new TypeError("Object.defineProperty called on non-object");
}
if (desc === null || typeof desc !== "function" && typeof desc !== "object") {
throw new TypeError("Property description must be an object: " + desc);
}
const res: any = {};
if ("get" in desc || "set" in desc) {
if ("get" in desc) {
const get = desc.get;
if (get !== undefined && typeof get !== "function") throw new TypeError("Getter must be a function: " + get);
res.g = get;
}
if ("set" in desc) {
const set = desc.set;
if (set !== undefined && typeof set !== "function") throw new TypeError("Setter must be a function: " + set);
res.s = set;
}
if ("enumerable" in desc) res.e = !!desc.enumerable;
if ("configurable" in desc) res.c = !!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.c = !!desc.configurable;
if ("writable" in desc) res.w = !!desc.writable;
if ("value" in desc) res.v = desc.value;
if (!object.defineField(obj, key, res)) throw new TypeError("Cannot redefine property: " + String(key));
}
return obj;
}
public static defineProperties(obj: object, desc: PropertyDescriptorMap) {
const keys = object.getOwnMembers(desc, true) as ((keyof typeof obj) & string)[];
const symbols = object.getOwnSymbolMembers(desc, true) as ((keyof typeof obj) & symbol)[];
for (let i = 0; i < keys.length; i++) {
Object.defineProperty(obj, keys[i], desc[keys[i]]);
}
for (let i = 0; i < symbols.length; i++) {
Object.defineProperty(obj, symbols[i], desc[symbols[i]]);
}
return obj;
}
public static create(proto: object, desc?: PropertyDescriptorMap) {
let res = object.setPrototype({}, proto);
if (desc != null) this.defineProperties(res, desc);
return res;
}
public static assign(target: any) {
for (let i = 1; i < arguments.length; i++) {
const obj = arguments[i];
const keys = object.getOwnMembers(obj, false);
const symbols = object.getOwnSymbolMembers(obj, false);
for (let j = 0; j < keys.length; j++) {
target[keys[j]] = obj[keys[j]];
}
for (let j = 0; j < symbols.length; j++) {
target[symbols[j]] = obj[symbols[j]];
}
}
return target;
}
public static setPrototypeOf(obj: object, proto: object | null) {
object.setPrototype(obj, proto!);
}
public static getPrototypeOf(obj: object) {
return object.getPrototype(obj) || null;
}
public static keys(obj: any) {
const res: any[] = [];
const keys = object.getOwnMembers(obj, true);
const symbols = object.getOwnSymbolMembers(obj, true);
for (let i = 0; i < keys.length; i++) {
res[res.length] = keys[i];
}
for (let i = 0; i < symbols.length; i++) {
res[res.length] = symbols[i];
}
return res;
}
public static values(obj: any) {
const res: any[] = [];
const keys = object.getOwnMembers(obj, true);
const symbols = object.getOwnSymbolMembers(obj, true);
for (let i = 0; i < keys.length; i++) {
res[res.length] = obj[keys[i]];
}
for (let i = 0; i < symbols.length; i++) {
res[res.length] = obj[symbols[i]];
}
return res;
}
public static entries(obj: any) {
const res: [any, any][] = [];
const keys = object.getOwnMembers(obj, true);
const symbols = object.getOwnSymbolMembers(obj, true);
for (let i = 0; i < keys.length; i++) {
res[res.length] = [keys[i], obj[keys[i]]];
}
for (let i = 0; i < symbols.length; i++) {
res[res.length] = [symbols[i], obj[symbols[i]]];
}
return res;
}
public static preventExtensions(obj: object) {
object.preventExt(obj);
return obj;
}
public static seal(obj: object) {
object.seal(obj);
return obj;
}
public static freeze(obj: object) {
object.freeze(obj);
return obj;
}
}
object.defineProperty(Object.prototype, "__proto__", { e: false });
object.setPrototype(Object.prototype, undefined);
func.setCallable(Object, true);
func.setConstructable(Object, true);
return Object as any as typeof Object & ((value?: unknown) => object);
})();
export type Object = InstanceType<typeof Object>;

View File

@@ -1,138 +0,0 @@
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);
}
}

View File

@@ -1,277 +0,0 @@
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?: number) {
const self = unwrapThis(this, "string", String, "String.prototype.lastIndexOf");
if (offset == null) offset = self.length;
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>;

View File

@@ -1,53 +0,0 @@
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>;

View File

@@ -1,4 +0,0 @@
import coffeescript from "./coffeescript.ts";
import babel from "./babel.ts";
register(v => coffeescript(babel(v)));

View File

@@ -1,24 +0,0 @@
import { SourceMap } from "./map.ts";
import { transform, availablePresets } from "@babel/standalone";
export default function babel(next: Compiler): Compiler {
print("Loaded babel!");
return (filename, code, prevMap) => {
const res = transform(code, {
filename,
sourceMaps: true,
presets: [availablePresets.env],
});
const map = SourceMap.parse({
file: "babel-internal://" + filename,
mappings: res.map!.mappings,
sources: [filename],
});
registerSource(filename, code);
return next("babel-internal://" + filename, res.code!, SourceMap.chain(map, prevMap));
};
}

View File

@@ -1,27 +0,0 @@
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],
});
registerSource(filename, code);
return next("coffee-internal://" + filename, result, SourceMap.chain(map, prevMap));
};
}

View File

@@ -1,190 +0,0 @@
const map: number[] = [];
let j = 0;
for (let i = 65; i <= 90; i++) map[i] = j++;
for (let i = 97; i <= 122; i++) map[i] = j++;
map[43] = j++;
map[47] = j++;
export function decodeVLQ(val: string): number[][][] {
const lines: number[][][] = [];
const fileParts = val.split(";", -1);
for (let i = 0; i < fileParts.length; i++) {
const line = fileParts[i];
if (line.length === 0) {
lines.push([]);
continue;
}
const elements: number[][] = [];
const lineParts = line.split(",", -1);
for (let i = 0; i < lineParts.length; i++) {
const el = lineParts[i];
if (el.length === 0) elements.push([]);
else {
const list: number[] = [];
for (let i = 0; i < el.length;) {
let sign = 1;
let curr = map[el.charCodeAt(i++)];
let cont = (curr & 0x20) === 0x20;
if ((curr & 1) === 1) sign = -1;
let res = (curr & 0b11110) >> 1;
let n = 4;
for (; i < el.length && cont;) {
curr = map[el.charCodeAt(i++)];
cont = (curr & 0x20) == 0x20;
res |= (curr & 0b11111) << n;
n += 5;
if (!cont) break;
}
list.push(res * sign);
}
elements.push(list);
}
}
lines.push(elements);
}
return lines;
}
export namespace Location {
export function compare(a: Location, b: Location) {
const { 0: filenameA, 1: lineA, 2: startA } = a;
const { 0: filenameB, 1: lineB, 2: startB } = b;
if (filenameA < filenameB) return -1;
if (filenameA > filenameB) return 1;
const lineI = lineA - lineB;
if (lineI !== 0) return lineI;
return startA - startB;
}
export function comparePoints(a: Location, b: Location) {
const { 1: lineA, 2: startA } = a;
const { 1: lineB, 2: startB } = b;
const lineI = lineA - lineB;
if (lineI !== 0) return lineI;
return startA - startB;
}
}
export class VLQSourceMap {
public constructor(
public readonly array: Map<string, [start: number, dst: Location][][]>,
) { }
public converter() {
return (src: Location) => {
const file = this.array.get(src[0]);
if (file == null) return src;
const line = file[src[1]];
if (line == null || line.length === 0) return undefined;
let a = 0;
let b = line.length;
while (true) {
const done = b - a <= 1;
const mid = (a + b) >> 1;
const el = line[mid];
const cmp = el[0] - src[2];
if (cmp < 0) {
if (done) {
if (b >= line.length) return undefined;
break;
}
a = mid;
}
else if (cmp > 0) {
if (done) {
if (a <= 0) return undefined;
break;
}
b = mid;
}
else return el[1];
}
return line[b][1];
};
}
public static parseVLQ(compiled: string, filenames: string[], raw: string): VLQSourceMap {
const mapping = decodeVLQ(raw);
const file: [start: number, dst: Location][][] = [];
const res = new Map<string, [start: number, dst: Location][][]>([[compiled, file]]);
let originalRow = 0;
let originalCol = 0;
let originalFile = 0;
const lastCols = new Set<number>();
for (let compiledRow = 0; compiledRow < mapping.length; compiledRow++) {
const line: [start: number, dst: Location][] = file[compiledRow] = [];
let compiledCol = 0;
const rowData = mapping[compiledRow];
for (let i = 0; i < rowData.length; i++) {
const rawSeg = rowData[i];
compiledCol += rawSeg.length > 0 ? rawSeg[0] : 0;
originalFile += rawSeg.length > 1 ? rawSeg[1] : 0;
originalRow += rawSeg.length > 2 ? rawSeg[2] : 0;
originalCol += rawSeg.length > 3 ? rawSeg[3] : 0;
if (!lastCols.has(compiledCol)) {
line[line.length] = [compiledCol, [filenames[originalFile], originalRow, originalCol]];
}
lastCols.add(compiledCol);
}
line.sort((a, b) => a[0] - b[0]);
}
return new VLQSourceMap(res);
}
public static parse(raw: string | { file: string, mappings: string, sources: string[] }) {
if (typeof raw === "string") raw = JSON.parse(raw) as { file: string, mappings: string, sources: string[] };
const compiled = raw.file;
const mapping = raw.mappings;
let filenames = raw.sources;
if (filenames.length === 0 || filenames.length === 1 && filenames[0] === "") filenames = [compiled];
return this.parseVLQ(compiled, filenames, mapping);
}
}
export namespace SourceMap {
export function parse(raw: string | { file: string, mappings: string, sources: string[] }) {
return VLQSourceMap.parse(raw).converter();
}
export function chain(...maps: SourceMap[]): SourceMap {
return loc => {
for (const el of maps) {
const tmp = el(loc);
if (tmp == null) return undefined;
else loc = tmp;
}
return loc;
};
}
}

View File

@@ -1,10 +0,0 @@
type Location = [file: string, line: number, start: number];
type SourceMap = (loc: Location) => Location | undefined;
type CompilerFactory = (next: Compiler) => Compiler;
type Compiler = (filename: string, src: string, mapper: SourceMap) => (this: any, ...args: any[]) => any;
declare function getResource(name: string): string | undefined;
declare function print(...args: any[]): void;
declare function register(factory: CompilerFactory): void;
declare function registerSource(filename: string, src: string): void;

View File

@@ -1,118 +0,0 @@
import { createDocumentRegistry, createLanguageService, ModuleKind, ScriptSnapshot, ScriptTarget, type Diagnostic, type CompilerOptions, type IScriptSnapshot, flattenDiagnosticMessageText, CompilerHost, LanguageService } from "typescript";
import { SourceMap } from "./map.ts";
const resources: Record<string, string | undefined> = {};
function resource(name: string) {
if (name in resources) return resources[name];
else return resources[name] = getResource(name);
}
export default function typescript(next: Compiler): Compiler {
const files: Record<string, IScriptSnapshot> = {};
const versions: Record<string, number> = {};
let declI = 0;
const settings: CompilerOptions = {
target: ScriptTarget.ES5,
module: ModuleKind.Preserve,
allowImportingTsExtensions: true,
verbatimModuleSyntax: true,
strict: false,
skipLibCheck: true,
forceConsistentCasingInFileNames: true,
declaration: true,
sourceMap: true,
downlevelIteration: true,
};
let service: LanguageService;
measure(() => {
service = createLanguageService({
getCurrentDirectory: () => "/",
getDefaultLibFileName: () => "/lib.d.ts",
getScriptFileNames: () => {
const res = ["/src.ts", "/lib.d.ts"];
for (let i = 0; i < declI; i++) res.push("/src." + i + ".d.ts");
return res;
},
getCompilationSettings: () => settings,
log: print,
fileExists: filename => filename in files || resource(filename) != null,
getScriptSnapshot: (filename) => {
if (filename in files) return files[filename];
else {
const src = resource(filename);
if (src == null) return undefined;
return files[filename] = ScriptSnapshot.fromString(src);
}
},
getScriptVersion: (filename) => String(versions[filename] || 0),
readFile: () => { throw "no"; },
writeFile: () => { throw "no"; },
}, createDocumentRegistry());
});
measure(() => {
service.getEmitOutput("/lib.d.ts");
});
print("Loaded typescript!");
return (filename, code, prevMap) => {
files["/src.ts"] = ScriptSnapshot.fromString(code);
versions["/src.ts"] ??= 0;
versions["/src.ts"]++;
const emit = service.getEmitOutput("/src.ts");
const diagnostics = new Array<Diagnostic>()
.concat(service.getSyntacticDiagnostics("/src.ts"))
.concat(service.getSemanticDiagnostics("/src.ts"))
.map(diagnostic => {
const message = flattenDiagnosticMessageText(diagnostic.messageText, "\n");
if (diagnostic.file != null) {
let file = diagnostic.file.fileName.substring(1);
if (file === "src.ts") file = filename;
if (diagnostic.start == null) return file;
const pos = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
return file + ":" + (pos.line + 1) + ":" + (pos.character + 1) + ": " + message;
}
else return message;
});
if (diagnostics.length > 0) {
throw new SyntaxError(diagnostics.join("\n"));
}
const outputs: Record<string, string> = {};
for (const el of emit.outputFiles) {
outputs[el.name] = el.text;
}
const rawMap = JSON.parse(outputs["/src.js.map"]);
const map = SourceMap.parse({
file: "ts-internal://" + filename,
mappings: rawMap.mappings,
sources: [filename],
});
const result = outputs["/src.js"];
const declaration = outputs["/src.d.ts"];
registerSource(filename, code);
const compiled = next("ts-internal://" + filename, result, SourceMap.chain(map, prevMap));
return function (this: any) {
const res = compiled.apply(this, arguments);
if (declaration !== '') files["/src." + declI++ + ".d.ts"] = ScriptSnapshot.fromString(declaration);
return res;
};
};
}

View File

@@ -1,16 +0,0 @@
package me.topchetoeu.j2s.common;
public class FunctionBody {
public final FunctionBody[] children;
public final Instruction[] instructions;
public final int localsN, capturablesN, capturesN, length;
public FunctionBody(int localsN, int capturablesN, int capturesN, int length, Instruction[] instructions, FunctionBody[] children) {
this.children = children;
this.length = length;
this.localsN = localsN;
this.capturablesN = capturablesN;
this.capturesN = capturesN;
this.instructions = instructions;
}
}

View File

@@ -1,376 +0,0 @@
package me.topchetoeu.j2s.common;
import java.util.HashMap;
import java.util.function.IntFunction;
import java.util.function.IntSupplier;
import me.topchetoeu.j2s.common.parsing.Location;
public class Instruction {
public static enum Type {
RETURN(0x00),
NOP(0x01),
THROW(0x02),
THROW_SYNTAX(0x03),
DELETE(0x04),
TRY_START(0x05),
TRY_END(0x06),
CALL(0x10),
CALL_NEW(0x12),
JMP_IF(0x18),
JMP_IFN(0x19),
JMP(0x1A),
PUSH_UNDEFINED(0x20),
PUSH_NULL(0x21),
PUSH_BOOL(0x22),
PUSH_NUMBER(0x23),
PUSH_STRING(0x24),
DUP(0x25),
DISCARD(0x26),
LOAD_FUNC(0x30),
LOAD_ARR(0x31),
LOAD_OBJ(0x32),
LOAD_REGEX(0x33),
LOAD_GLOB(0x38),
LOAD_INTRINSICS(0x39),
LOAD_ARG(0x3A),
LOAD_ARGS_N(0x3B),
LOAD_ARGS(0x3C),
LOAD_CALLED(0x3D),
LOAD_THIS(0x3E),
LOAD_ERROR(0x3F),
LOAD_VAR(0x40),
LOAD_MEMBER(0x41),
LOAD_MEMBER_INT(0x42),
LOAD_MEMBER_STR(0x43),
STORE_VAR(0x48),
STORE_MEMBER(0x49),
STORE_MEMBER_INT(0x4A),
STORE_MEMBER_STR(0x4B),
DEF_PROP(0x50),
DEF_FIELD(0x51),
KEYS(0x52),
TYPEOF(0x53),
OPERATION(0x54),
GLOB_GET(0x60),
GLOB_SET(0x61),
GLOB_DEF(0x62);
private static final HashMap<Integer, Type> types = new HashMap<>();
public final int numeric;
static {
for (var val : Type.values()) types.put(val.numeric, val);
}
private Type(int numeric) {
this.numeric = numeric;
}
public static Type fromNumeric(int i) {
return types.get(i);
}
}
public static enum BreakpointType {
/**
* A debugger should never stop at such instruction, unless a breakpoint has been set on it
*/
NONE,
/**
* Debuggers should pause at instructions marked with this breakpoint type
* after any step command
*/
STEP_OVER,
/**
* Debuggers should pause at instructions marked with this breakpoint type
* only after a step-in command
*/
STEP_IN;
public boolean shouldStepIn() {
return this != NONE;
}
public boolean shouldStepOver() {
return this == STEP_OVER;
}
}
public final Type type;
public final Object[] params;
@SuppressWarnings("unchecked")
public <T> T get(int i) {
if (i >= params.length || i < 0) return null;
return (T)params[i];
}
private Instruction(Type type, Object ...params) {
this.type = type;
this.params = params;
}
/**
* Signals the start of a protected context
* @param catchStart The point to witch to jump if an error has been caught
* @param finallyStart The point to witch to jump after either the try or catch bodies have exited
* @param end The point to which to jump after exiting the whole protected context
*/
public static Instruction tryStart(int catchStart, int finallyStart, int end) {
return new Instruction(Type.TRY_START, catchStart, finallyStart, end);
}
/**
* Signifies that the current protected section (try, catch or finally) has ended
*/
public static Instruction tryEnd() {
return new Instruction(Type.TRY_END);
}
/**
* Throws the top stack value
*/
public static Instruction throwInstr() {
return new Instruction(Type.THROW);
}
/**
* Converts the given exception to a runtime syntax error and throws it
*/
public static Instruction throwSyntax(SyntaxException err) {
return new Instruction(Type.THROW_SYNTAX, err.getMessage());
}
/**
* Converts the given exception to a runtime syntax error and throws it
*/
public static Instruction throwSyntax(String err) {
return new Instruction(Type.THROW_SYNTAX, err);
}
/**
* Converts the given exception to a runtime syntax error and throws it
*/
public static Instruction throwSyntax(Location loc, String err) {
return new Instruction(Type.THROW_SYNTAX, new SyntaxException(loc, err).getMessage());
}
/**
* Performs a JS object property deletion.
* Operands:
* 1. Object to manipulate
* 2. Key to delete
*/
public static Instruction delete() {
return new Instruction(Type.DELETE);
}
/**
* Returns the top stack value
*/
public static Instruction ret() {
return new Instruction(Type.RETURN);
}
/**
* A special NOP instruction telling any debugger to pause
*/
public static Instruction debug() {
return new Instruction(Type.NOP, "debug");
}
/**
* Does nothing. May be used for metadata or implementation-specific instructions that don't alter the behavior
*/
public static Instruction nop(Object ...params) {
return new Instruction(Type.NOP, params);
}
public static Instruction call(int argn, boolean hasSelf) {
return new Instruction(Type.CALL, argn, hasSelf);
}
public static Instruction callNew(int argn) {
return new Instruction(Type.CALL_NEW, argn);
}
public static Instruction jmp(int offset) {
return new Instruction(Type.JMP, offset);
}
public static Instruction jmpIf(int offset) {
return new Instruction(Type.JMP_IF, offset);
}
public static Instruction jmpIfNot(int offset) {
return new Instruction(Type.JMP_IFN, offset);
}
public static IntFunction<Instruction> jmp(IntSupplier pos) {
return i -> new Instruction(Type.JMP, pos.getAsInt() - i);
}
public static IntFunction<Instruction> jmpIf(IntSupplier pos) {
return i -> new Instruction(Type.JMP_IF, pos.getAsInt() - i);
}
public static IntFunction<Instruction> jmpIfNot(IntSupplier pos) {
return i -> new Instruction(Type.JMP_IFN, pos.getAsInt() - i);
}
public static Instruction pushUndefined() {
return new Instruction(Type.PUSH_UNDEFINED);
}
public static Instruction pushNull() {
return new Instruction(Type.PUSH_NULL);
}
public static Instruction pushValue(boolean val) {
return new Instruction(Type.PUSH_BOOL, val);
}
public static Instruction pushValue(double val) {
return new Instruction(Type.PUSH_NUMBER, val);
}
public static Instruction pushValue(String val) {
return new Instruction(Type.PUSH_STRING, val);
}
public static Instruction globDef(String name) {
return new Instruction(Type.GLOB_DEF, name);
}
public static Instruction globGet(String name, boolean force) {
return new Instruction(Type.GLOB_GET, name, force);
}
public static Instruction globSet(String name, boolean keep, boolean define) {
return new Instruction(Type.GLOB_SET, name, keep, define);
}
public static Instruction loadVar(int i) {
return new Instruction(Type.LOAD_VAR, i);
}
public static Instruction loadThis() {
return new Instruction(Type.LOAD_THIS);
}
/**
* Loads the given argument
* @param i The index of the argument to load. If -1, will get the index from the stack instead
*/
public static Instruction loadArg(int i) {
return new Instruction(Type.LOAD_ARG, i);
}
/**
* Pushes the amount of arguments to the stack
*/
public static Instruction loadArgsN() {
return new Instruction(Type.LOAD_ARGS_N);
}
/**
* Pushes the arguments object to the stack
*/
public static Instruction loadArgs() {
return new Instruction(Type.LOAD_ARGS);
}
/**
* Loads a reference to the function being called
*/
public static Instruction loadCalled() {
return new Instruction(Type.LOAD_CALLED);
}
public static Instruction loadGlob() {
return new Instruction(Type.LOAD_GLOB);
}
public static Instruction loadIntrinsics(String key) {
return new Instruction(Type.LOAD_INTRINSICS, key);
}
public static Instruction loadError() {
return new Instruction(Type.LOAD_ERROR);
}
public static Instruction loadMember() {
return new Instruction(Type.LOAD_MEMBER);
}
public static Instruction loadMember(int member) {
return new Instruction(Type.LOAD_MEMBER_INT, member);
}
public static Instruction loadMember(String member) {
return new Instruction(Type.LOAD_MEMBER_STR, member);
}
public static Instruction loadRegex(String pattern, String flags) {
return new Instruction(Type.LOAD_REGEX, pattern, flags);
}
// TODO: make this capturing a concern of the compiler
public static Instruction loadFunc(int id, String name, int[] captures) {
var args = new Object[2 + captures.length];
args[0] = id;
args[1] = name;
for (var i = 0; i < captures.length; i++) args[i + 2] = captures[i];
return new Instruction(Type.LOAD_FUNC, args);
}
public static Instruction loadObj() {
return new Instruction(Type.LOAD_OBJ);
}
public static Instruction loadArr(int count) {
return new Instruction(Type.LOAD_ARR, count);
}
public static Instruction dup() {
return new Instruction(Type.DUP, 1, 0);
}
public static Instruction dup(int count, int offset) {
return new Instruction(Type.DUP, count, offset);
}
public static Instruction storeVar(int i, boolean keep, boolean initialize) {
return new Instruction(Type.STORE_VAR, i, keep, initialize);
}
public static Instruction storeMember() {
return new Instruction(Type.STORE_MEMBER, false);
}
public static Instruction storeMember(boolean keep) {
return new Instruction(Type.STORE_MEMBER, keep);
}
public static Instruction storeMember(String key) {
return new Instruction(Type.STORE_MEMBER_STR, key, false);
}
public static Instruction storeMember(String key, boolean keep) {
return new Instruction(Type.STORE_MEMBER_STR, key, keep);
}
public static Instruction storeMember(int key) {
return new Instruction(Type.STORE_MEMBER_INT, key, false);
}
public static Instruction storeMember(int key, boolean keep) {
return new Instruction(Type.STORE_MEMBER_INT, key, keep);
}
public static Instruction discard() {
return new Instruction(Type.DISCARD);
}
public static Instruction typeof() {
return new Instruction(Type.TYPEOF);
}
public static Instruction typeof(String varName) {
return new Instruction(Type.TYPEOF, varName);
}
public static Instruction keys(boolean own, boolean onlyEnumerable) {
return new Instruction(Type.KEYS, own, onlyEnumerable);
}
public static Instruction defProp(boolean setter) {
return new Instruction(Type.DEF_PROP, setter);
}
public static Instruction defField() {
return new Instruction(Type.DEF_FIELD);
}
public static Instruction operation(Operation op) {
return new Instruction(Type.OPERATION, op);
}
@Override public String toString() {
var res = type.toString();
for (int i = 0; i < params.length; i++) {
res += " " + params[i];
}
return res;
}
}

View File

@@ -1,30 +0,0 @@
package me.topchetoeu.j2s.common;
import me.topchetoeu.j2s.common.json.JSON;
import me.topchetoeu.j2s.common.parsing.Filename;
public class Metadata {
private static final String VERSION;
private static final String AUTHOR;
private static final String NAME;
static {
var data = JSON.parse(new Filename("internal", "metadata.json"), Reading.resourceToString("metadata.json")).map();
VERSION = data.string("version");
AUTHOR = data.string("author");
NAME = data.string("name");
}
public static String version() {
if (VERSION.equals("$" + "{VERSION}")) return "1337-devel";
else return VERSION;
}
public static String author() {
if (AUTHOR.equals("$" + "{AUTHOR}")) return "anonymous";
else return AUTHOR;
}
public static String name() {
if (NAME.equals("$" + "{NAME}")) return "some-product";
else return NAME;
}
}

View File

@@ -1,54 +0,0 @@
package me.topchetoeu.j2s.common;
import java.util.HashMap;
public enum Operation {
INSTANCEOF(1, 2),
IN(2, 2),
MULTIPLY(3, 2),
DIVIDE(4, 2),
MODULO(5, 2),
ADD(6, 2),
SUBTRACT(7, 2),
USHIFT_RIGHT(8, 2),
SHIFT_RIGHT(9, 2),
SHIFT_LEFT(10, 2),
GREATER(11, 2),
LESS(12, 2),
GREATER_EQUALS(13, 2),
LESS_EQUALS(14, 2),
LOOSE_EQUALS(15, 2),
LOOSE_NOT_EQUALS(16, 2),
EQUALS(17, 2),
NOT_EQUALS(18, 2),
AND(19, 2),
OR(20, 2),
XOR(21, 2),
NEG(23, 1),
POS(24, 1),
NOT(25, 1),
INVERSE(26, 1);
private static final HashMap<Integer, Operation> operations = new HashMap<>();
static {
for (var val : Operation.values()) operations.put(val.numeric, val);
}
public final int numeric;
public final int operands;
private Operation(int numeric, int n) {
this.numeric = numeric;
this.operands = n;
}
public static Operation fromNumeric(int i) {
return operations.get(i);
}
}

View File

@@ -1,92 +0,0 @@
package me.topchetoeu.j2s.common;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UncheckedIOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Reading {
private static final BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
public static synchronized String readline() throws IOException {
return reader.readLine();
}
public static byte[] streamToBytes(InputStream in) {
if (in == null) return null;
try {
List<byte[]> bufs = null;
byte[] result = null;
int total = 0;
int n;
do {
var buf = new byte[8192];
int nread = 0;
// read to EOF which may read more or less than buffer size
while ((n = in.read(buf, nread, buf.length - nread)) > 0) {
nread += n;
}
if (nread > 0) {
if (Integer.MAX_VALUE - 8 - total < nread) throw new OutOfMemoryError("Required array size too large");
if (nread < buf.length) buf = Arrays.copyOfRange(buf, 0, nread);
total += nread;
if (result == null) result = buf;
else {
if (bufs == null) {
bufs = new ArrayList<>();
bufs.add(result);
}
bufs.add(buf);
}
}
// if the last call to read returned -1 or the number of bytes
// requested have been read then break
} while (n >= 0);
if (bufs == null) {
if (result == null) return new byte[0];
return result.length == total ? result : Arrays.copyOf(result, total);
}
result = new byte[total];
int offset = 0;
int remaining = total;
for (byte[] b : bufs) {
int count = Math.min(b.length, remaining);
System.arraycopy(b, 0, result, offset, count);
offset += count;
remaining -= count;
}
return result;
}
catch (IOException e) { throw new UncheckedIOException(e); }
}
public static String streamToString(InputStream in) {
var bytes = streamToBytes(in);
if (bytes == null) return null;
else return new String(bytes);
}
public static InputStream resourceToStream(String name) {
return Reading.class.getResourceAsStream("/" + name);
}
public static String resourceToString(String name) {
return streamToString(resourceToStream(name));
}
public static byte[] resourceToBytes(String name) {
return streamToBytes(resourceToStream(name));
}
}

View File

@@ -1,14 +0,0 @@
package me.topchetoeu.j2s.common;
import me.topchetoeu.j2s.common.parsing.Location;
public class SyntaxException extends RuntimeException {
public final Location loc;
public final String msg;
public SyntaxException(Location loc, String msg) {
super(String.format("Syntax error %s: %s", loc, msg));
this.loc = loc;
this.msg = msg;
}
}

View File

@@ -1,97 +0,0 @@
package me.topchetoeu.j2s.common.environment;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
public class Environment {
public final Environment parent;
private final Map<Key<Object>, Object> map = new HashMap<>();
private final Set<Key<Object>> hidden = new HashSet<>();
@SuppressWarnings("unchecked")
public <T> T get(Key<T> key) {
if (map.containsKey(key)) return (T)map.get(key);
else if (!hidden.contains(key) && parent != null) return parent.get(key);
else return null;
}
public boolean has(Key<?> key) {
if (map.containsKey(key)) return true;
else if (!hidden.contains(key) && parent != null) return parent.has(key);
else return false;
}
public boolean hasNotNull(Key<?> key) {
return get(key) != null;
}
public <T> T get(Key<T> key, T defaultVal) {
if (has(key)) return get(key);
else return defaultVal;
}
public <T> T getWith(Key<T> key, Supplier<T> defaultVal) {
if (has(key)) return get(key);
else return defaultVal.get();
}
@SuppressWarnings("unchecked")
public <T> Environment add(Key<T> key, T val) {
map.put((Key<Object>)key, val);
hidden.remove(key);
return this;
}
public Environment add(Key<Void> key) {
return add(key, null);
}
@SuppressWarnings("all")
public Environment addAll(Map<Key<?>, ?> map, boolean iterableAsMulti) {
map.putAll((Map)map);
hidden.removeAll(map.keySet());
return this;
}
public Environment addAll(Map<Key<?>, ?> map) {
return addAll(map, true);
}
@SuppressWarnings("unchecked")
public Environment remove(Key<?> key) {
map.remove(key);
hidden.add((Key<Object>)key);
return this;
}
public <T> T init(Key<T> key, T val) {
if (!has(key)) this.add(key, val);
return val;
}
public <T> T initFrom(Key<T> key, Supplier<T> val) {
if (!has(key)) {
var res = val.get();
this.add(key, res);
return res;
}
else return get(key);
}
public Environment child() {
return new Environment(this);
}
public Environment(Environment parent) {
this.parent = parent;
}
public Environment() {
this.parent = null;
}
public static Environment wrap(Environment env) {
if (env == null) return empty();
else return env;
}
public static Environment empty() {
return new Environment();
}
}

View File

@@ -1,3 +0,0 @@
package me.topchetoeu.j2s.common.environment;
public final class Key<T> { }

View File

@@ -1,174 +0,0 @@
package me.topchetoeu.j2s.common.json;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.stream.Collectors;
import me.topchetoeu.j2s.common.Metadata;
import me.topchetoeu.j2s.common.SyntaxException;
import me.topchetoeu.j2s.common.parsing.Filename;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.parsing.ParseRes;
import me.topchetoeu.j2s.common.parsing.Parsing;
import me.topchetoeu.j2s.common.parsing.Source;
public class JSON {
public static ParseRes<JSONElement> parseString(Source src, int i) {
var res = Parsing.parseString(src, i);
if (!res.isSuccess()) return res.chainError();
return ParseRes.res(JSONElement.string(res.result), res.n);
}
public static ParseRes<JSONElement> parseNumber(Source src, int i) {
var res = Parsing.parseNumber(src, i, true);
if (!res.isSuccess()) return res.chainError();
else return ParseRes.res(JSONElement.number(res.result), res.n);
}
public static ParseRes<JSONElement> parseLiteral(Source src, int i) {
var id = Parsing.parseIdentifier(src, i);
if (!id.isSuccess()) return ParseRes.failed();
else if (id.result.equals("true")) return ParseRes.res(JSONElement.bool(true), id.n);
else if (id.result.equals("false")) return ParseRes.res(JSONElement.bool(false), id.n);
else if (id.result.equals("null")) return ParseRes.res(JSONElement.NULL, id.n);
else return ParseRes.failed();
}
public static ParseRes<JSONElement> parseValue(Source src, int i) {
return ParseRes.first(src, i,
JSON::parseString,
JSON::parseNumber,
JSON::parseLiteral,
JSON::parseMap,
JSON::parseList
);
}
public static ParseRes<JSONElement> parseMap(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
if (!src.is(i + n, "{")) return ParseRes.failed();
n++;
var values = new JSONMap();
if (src.is(i + n, "}")) return ParseRes.res(JSONElement.map(new JSONMap(new HashMap<>())), n + 1);
while (true) {
var name = parseString(src, i + n);
if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected an index");
n += name.n;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, ":")) return name.chainError(src.loc(i + n), "Expected a colon");
n++;
var res = parseValue(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a list element");
values.put(name.result.toString(), res.result);
n += res.n;
n += Parsing.skipEmpty(src, i + n);
if (src.is(i + n, ",")) n++;
else if (src.is(i + n, "}")) {
n++;
break;
}
}
return ParseRes.res(JSONElement.map(values), n);
}
public static ParseRes<JSONElement> parseList(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
if (!src.is(i + n++, "[")) return ParseRes.failed();
var values = new JSONList();
if (src.is(i + n, "]")) return ParseRes.res(JSONElement.list(new JSONList()), n + 1);
while (true) {
var res = parseValue(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a list element");
values.add(res.result);
n += res.n;
n += Parsing.skipEmpty(src, i + n);
if (src.is(i + n, ",")) n++;
else if (src.is(i + n, "]")) {
n++;
break;
}
}
return ParseRes.res(JSONElement.list(values), n);
}
public static JSONElement parse(Filename filename, String raw) {
if (filename == null) filename = new Filename(Metadata.name(), "json");
var res = parseValue(new Source(null, filename, raw), 0);
if (res.isFailed()) throw new SyntaxException(Location.of(filename, 0, 0), "Invalid JSON given");
else if (res.isError()) throw new SyntaxException(res.errorLocation, res.error);
else return JSONElement.of(res.result);
}
public static String stringify(JSONElement el) {
if (el.isNumber()) {
var d = el.number();
if (d == Double.NEGATIVE_INFINITY) return "-Infinity";
if (d == Double.POSITIVE_INFINITY) return "Infinity";
if (Double.isNaN(d)) return "NaN";
return BigDecimal.valueOf(d).stripTrailingZeros().toPlainString();
}
if (el.isBoolean()) return el.bool() ? "true" : "false";
if (el.isNull()) return "null";
if (el.isString()) {
var res = new StringBuilder("\"");
var alphabet = "0123456789ABCDEF".toCharArray();
for (var c : el.string().toCharArray()) {
if (c < 32 || c >= 127) {
res
.append("\\u")
.append(alphabet[(c >> 12) & 0xF])
.append(alphabet[(c >> 8) & 0xF])
.append(alphabet[(c >> 4) & 0xF])
.append(alphabet[(c >> 0) & 0xF]);
}
else if (c == '\\')
res.append("\\\\");
else if (c == '"')
res.append("\\\"");
else res.append(c);
}
return res.append('"').toString();
}
if (el.isList()) {
var res = new StringBuilder().append("[");
for (int i = 0; i < el.list().size(); i++) {
if (i != 0) res.append(",");
res.append(stringify(el.list().get(i)));
}
res.append("]");
return res.toString();
}
if (el.isMap()) {
var res = new StringBuilder().append("{");
var entries = el.map().entrySet().stream().collect(Collectors.toList());
for (int i = 0; i < entries.size(); i++) {
if (i != 0) res.append(",");
res.append(stringify(JSONElement.string(entries.get(i).getKey())));
res.append(":");
res.append(stringify(entries.get(i).getValue()));
}
res.append("}");
return res.toString();
}
return null;
}
public static String stringify(JSONMap map) {
return stringify(JSONElement.of(map));
}
public static String stringify(JSONList list) {
return stringify(JSONElement.of(list));
}
}

View File

@@ -1,88 +0,0 @@
package me.topchetoeu.j2s.common.json;
public class JSONElement {
public static enum Type {
STRING,
NUMBER,
BOOLEAN,
NULL,
LIST,
MAP,
}
public static final JSONElement NULL = new JSONElement(Type.NULL, null);
public static JSONElement map(JSONMap val) {
return new JSONElement(Type.MAP, val);
}
public static JSONElement list(JSONList val) {
return new JSONElement(Type.LIST, val);
}
public static JSONElement string(String val) {
return new JSONElement(Type.STRING, val);
}
public static JSONElement number(double val) {
return new JSONElement(Type.NUMBER, val);
}
public static JSONElement bool(boolean val) {
return new JSONElement(Type.BOOLEAN, val);
}
public static JSONElement of(Object val) {
if (val instanceof JSONElement el) return el;
else if (val instanceof JSONMap map) return map(map);
else if (val instanceof JSONList list) return list(list);
else if (val instanceof String str) return string(str);
else if (val instanceof Boolean bool) return bool(bool);
else if (val instanceof Number num) return number(num.doubleValue());
else if (val == null) return NULL;
else throw new IllegalArgumentException("val must be: String, Boolean, Number, JSONList or JSONMap");
}
public final Type type;
private final Object value;
public boolean isMap() { return type == Type.MAP; }
public boolean isList() { return type == Type.LIST; }
public boolean isString() { return type == Type.STRING; }
public boolean isNumber() { return type == Type.NUMBER; }
public boolean isBoolean() { return type == Type.BOOLEAN; }
public boolean isNull() { return type == Type.NULL; }
public JSONMap map() {
if (!isMap()) throw new IllegalStateException("Element is not a map");
return (JSONMap)value;
}
public JSONList list() {
if (!isList()) throw new IllegalStateException("Element is not a map");
return (JSONList)value;
}
public String string() {
if (!isString()) throw new IllegalStateException("Element is not a string");
return (String)value;
}
public double number() {
if (!isNumber()) throw new IllegalStateException("Element is not a number");
return (double)value;
}
public boolean bool() {
if (!isBoolean()) throw new IllegalStateException("Element is not a boolean");
return (boolean)value;
}
@Override public String toString() {
if (isMap()) return "{...}";
if (isList()) return "[...]";
if (isString()) return (String)value;
if (isString()) return (String)value;
if (isNumber()) return (double)value + "";
if (isBoolean()) return (boolean)value + "";
if (isNull()) return "null";
return "";
}
private JSONElement(Type type, Object val) {
this.type = type;
this.value = val;
}
}

View File

@@ -1,26 +0,0 @@
package me.topchetoeu.j2s.common.json;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
public class JSONList extends ArrayList<JSONElement> {
public JSONList() {}
public JSONList(JSONElement ...els) {
super(Arrays.asList(els));
}
public JSONList(Collection<JSONElement> els) {
super(els);
}
public JSONList addNull() { this.add(JSONElement.NULL); return this; }
public JSONList add(String val) { this.add(JSONElement.of(val)); return this; }
public JSONList add(double val) { this.add(JSONElement.of(val)); return this; }
public JSONList add(boolean val) { this.add(JSONElement.of(val)); return this; }
public JSONList add(Map<String, JSONElement> val) { this.add(JSONElement.of(val)); return this; }
public JSONList add(Collection<JSONElement> val) { this.add(JSONElement.of(val)); return this; }
public JSONList add(JSONMap val) { this.add(JSONElement.of(val)); return this; }
public JSONList add(JSONList val) { this.add(JSONElement.of(val)); return this; }
}

View File

@@ -1,136 +0,0 @@
package me.topchetoeu.j2s.common.json;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class JSONMap implements Map<String, JSONElement> {
private Map<String, JSONElement> elements = new HashMap<>();
public JSONElement get(String path) {
JSONElement curr = JSONElement.map(this);
for (var seg : path.split("\\.")) {
if (!curr.isMap()) return null;
curr = curr.map().elements.get(seg);
}
return curr;
}
public boolean isMap(String path) {
var el = get(path);
return el != null && el.isMap();
}
public boolean isList(String path) {
var el = get(path);
return el != null && el.isList();
}
public boolean isString(String path) {
var el = get(path);
return el != null && el.isString();
}
public boolean isNumber(String path) {
var el = get(path);
return el != null && el.isNumber();
}
public boolean isBoolean(String path) {
var el = get(path);
return el != null && el.isBoolean();
}
public boolean isNull(String path) {
var el = get(path);
return el != null && el.isNull();
}
public boolean contains(String path) {
return get(path) != null;
}
public JSONMap map(String path) {
var el = get(path);
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path));
return el.map();
}
public JSONMap map(String path, JSONMap defaultVal) {
var el = get(path);
if (el == null) return defaultVal;
if (el.isMap()) return el.map();
return defaultVal;
}
public JSONList list(String path) {
var el = get(path);
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path));
return el.list();
}
public JSONList list(String path, JSONList defaultVal) {
var el = get(path);
if (el == null) return defaultVal;
if (el.isList()) return el.list();
return defaultVal;
}
public String string(String path) {
var el = get(path);
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path));
return el.string();
}
public String string(String path, String defaultVal) {
var el = get(path);
if (el == null) return defaultVal;
if (el.isString()) return el.string();
return defaultVal;
}
public double number(String path) {
var el = get(path);
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path));
return el.number();
}
public double number(String path, double defaultVal) {
var el = get(path);
if (el == null) return defaultVal;
if (el.isNumber()) return el.number();
return defaultVal;
}
public boolean bool(String path) {
var el = get(path);
if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path));
return el.bool();
}
public boolean bool(String path, boolean defaultVal) {
var el = get(path);
if (el == null) return defaultVal;
if (el.isBoolean()) return el.bool();
return defaultVal;
}
public JSONMap setNull(String key) { elements.put(key, JSONElement.NULL); return this; }
public JSONMap set(String key, String val) { elements.put(key, JSONElement.of(val)); return this; }
public JSONMap set(String key, double val) { elements.put(key, JSONElement.of(val)); return this; }
public JSONMap set(String key, boolean val) { elements.put(key, JSONElement.of(val)); return this; }
public JSONMap set(String key, Map<String, JSONElement> val) { elements.put(key, JSONElement.of(val)); return this; }
public JSONMap set(String key, Collection<JSONElement> val) { elements.put(key, JSONElement.of(val)); return this; }
@Override public int size() { return elements.size(); }
@Override public boolean isEmpty() { return elements.isEmpty(); }
@Override public boolean containsKey(Object key) { return elements.containsKey(key); }
@Override public boolean containsValue(Object value) { return elements.containsValue(value); }
@Override public JSONElement get(Object key) { return elements.get(key); }
@Override public JSONElement put(String key, JSONElement value) { return elements.put(key, value); }
@Override public JSONElement remove(Object key) { return elements.remove(key); }
@Override public void putAll(Map<? extends String, ? extends JSONElement> m) { elements.putAll(m); }
@Override public void clear() { elements.clear(); }
@Override public Set<String> keySet() { return elements.keySet(); }
@Override public Collection<JSONElement> values() { return elements.values(); }
@Override public Set<Entry<String, JSONElement>> entrySet() { return elements.entrySet(); }
public JSONMap() { }
public JSONMap(Map<String, JSONElement> els) {
this.elements = new HashMap<>(els);
}
}

View File

@@ -1,193 +0,0 @@
package me.topchetoeu.j2s.common.mapping;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import java.util.Objects;
import java.util.TreeMap;
import java.util.TreeSet;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
import me.topchetoeu.j2s.common.parsing.Filename;
import me.topchetoeu.j2s.common.parsing.Location;
public class FunctionMap {
public static class FunctionMapBuilder {
private final TreeMap<Integer, Location> sourceMap = new TreeMap<>();
private final HashMap<Location, BreakpointType> breakpoints = new HashMap<>();
public Location toLocation(int pc) {
return sourceMap.headMap(pc, true).firstEntry().getValue();
}
public FunctionMapBuilder setDebug(Location loc, BreakpointType type) {
if (loc == null || type == null || type == BreakpointType.NONE) return this;
breakpoints.put(loc, type);
return this;
}
public FunctionMapBuilder setLocation(int i, Location loc) {
if (loc == null || i < 0) return this;
sourceMap.put(i, loc);
return this;
}
public FunctionMapBuilder setLocationAndDebug(int i, Location loc, BreakpointType type) {
setDebug(loc, type);
setLocation(i, loc);
return this;
}
public Location first() {
if (sourceMap.size() == 0) return null;
return sourceMap.firstEntry().getValue();
}
public Location last() {
if (sourceMap.size() == 0) return null;
return sourceMap.lastEntry().getValue();
}
public FunctionMapBuilder map(Function<Location, Location> mapper) {
var newSourceMaps = new HashMap<Integer, Location>();
var newBreakpoints = new HashMap<Location, BreakpointType>();
for (var key : sourceMap.keySet()) {
var mapped = mapper.apply(sourceMap.get(key));
if (mapped == null) continue;
newSourceMaps.put(key, mapped);
}
for (var key : breakpoints.keySet()) {
var mapped = mapper.apply(key);
if (mapped == null) continue;
newBreakpoints.put(mapped, breakpoints.get(key));
}
sourceMap.clear();
sourceMap.putAll(newSourceMaps);
breakpoints.clear();
breakpoints.putAll(newBreakpoints);
return this;
}
public FunctionMap build(String[] localNames, String[] capturableNames, String[] captureNames) {
return new FunctionMap(sourceMap, breakpoints, localNames, capturableNames, captureNames);
}
public FunctionMap build(Function<Location, Location> mapper) {
return new FunctionMap(sourceMap, breakpoints, new String[0], new String[0], new String[0]);
}
private FunctionMapBuilder() { }
}
public static final FunctionMap EMPTY = new FunctionMap();
private final HashMap<Integer, BreakpointType> bps = new HashMap<>();
private final HashMap<Filename, TreeSet<Location>> bpLocs = new HashMap<>();
private final TreeMap<Integer, Location> pcToLoc = new TreeMap<>();
public final String[] localNames, capturableNames, captureNames;
public Location toLocation(int pc, boolean approxiamte) {
if (pcToLoc.size() == 0 || pc < 0 || pc > pcToLoc.lastKey()) return null;
var res = pcToLoc.get(pc);
if (!approxiamte || res != null) return res;
var entry = pcToLoc.headMap(pc, true).lastEntry();
if (entry == null) return null;
else return entry.getValue();
}
public Location toLocation(int pc) {
return toLocation(pc, false);
}
public BreakpointType getBreakpoint(int pc) {
return bps.getOrDefault(pc, BreakpointType.NONE);
}
public Location correctBreakpoint(Location loc) {
var set = bpLocs.get(loc.filename());
if (set == null) return null;
else return set.ceiling(loc);
}
public List<Location> correctBreakpoint(Pattern filename, int line, int column) {
var candidates = new HashMap<Filename, TreeSet<Location>>();
for (var name : bpLocs.keySet()) {
if (filename.matcher(name.toString()).matches()) {
candidates.put(name, bpLocs.get(name));
}
}
var res = new ArrayList<Location>(candidates.size());
for (var candidate : candidates.entrySet()) {
var val = correctBreakpoint(Location.of(candidate.getKey(), line, column));
if (val == null) continue;
res.add(val);
}
return res;
}
public List<Location> breakpoints(Location start, Location end) {
if (!Objects.equals(start.filename(), end.filename())) return Arrays.asList();
NavigableSet<Location> set = bpLocs.get(start.filename());
if (set == null) return Arrays.asList();
if (start != null) set = set.tailSet(start, true);
if (end != null) set = set.headSet(end, true);
return set.stream().collect(Collectors.toList());
}
public Location start() {
if (pcToLoc.size() == 0) return null;
return pcToLoc.firstEntry().getValue();
}
public Location end() {
if (pcToLoc.size() == 0) return null;
return pcToLoc.lastEntry().getValue();
}
public FunctionMap clone() {
var res = new FunctionMap(new HashMap<>(), new HashMap<>(), localNames, capturableNames, captureNames);
res.pcToLoc.putAll(this.pcToLoc);
res.bps.putAll(bps);
res.bpLocs.putAll(bpLocs);
res.pcToLoc.putAll(pcToLoc);
return res;
}
public FunctionMap(Map<Integer, Location> map, Map<Location, BreakpointType> breakpoints, String[] localNames, String[] capturableNames, String[] captureNames) {
var locToPc = new HashMap<Location, Integer>();
for (var el : map.entrySet()) {
pcToLoc.put(el.getKey(), el.getValue());
locToPc.putIfAbsent(el.getValue(), el.getKey());
}
for (var el : breakpoints.entrySet()) {
if (el.getValue() == null || el.getValue() == BreakpointType.NONE) continue;
bps.put(locToPc.get(el.getKey()), el.getValue());
if (!bpLocs.containsKey(el.getKey().filename())) bpLocs.put(el.getKey().filename(), new TreeSet<>());
bpLocs.get(el.getKey().filename()).add(el.getKey());
}
this.localNames = localNames;
this.captureNames = captureNames;
this.capturableNames = capturableNames;
}
private FunctionMap() {
localNames = new String[0];
captureNames = new String[0];
capturableNames = new String[0];
}
public static FunctionMapBuilder builder() {
return new FunctionMapBuilder();
}
}

View File

@@ -1,53 +0,0 @@
package me.topchetoeu.j2s.common.parsing;
import java.io.File;
public class Filename {
public final String protocol;
public final String path;
@Override public String toString() {
return protocol + "://" + path;
}
@Override public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + protocol.hashCode();
result = prime * result + path.hashCode();
return result;
}
@Override public boolean equals(Object obj) {
if (this == obj) return true;
if (obj == null) return false;
if (getClass() != obj.getClass()) return false;
var other = (Filename) obj;
if (protocol == null) {
if (other.protocol != null) return false;
}
else if (!protocol.equals(other.protocol)) return false;
if (path == null) {
if (other.path != null) return false;
}
else if (!path.equals(other.path)) return false;
return true;
}
public Filename(String protocol, String path) {
path = path.trim();
protocol = protocol.trim();
this.protocol = protocol;
this.path = path;
}
public static Filename parse(String val) {
var i = val.indexOf("://");
if (i >= 0) return new Filename(val.substring(0, i).trim(), val.substring(i + 3).trim());
else return new Filename("file", val.trim());
}
public static Filename fromFile(File file) {
return new Filename("file", file.getAbsolutePath());
}
}

View File

@@ -1,113 +0,0 @@
package me.topchetoeu.j2s.common.parsing;
import java.util.ArrayList;
import java.util.Objects;
import me.topchetoeu.j2s.common.Metadata;
public abstract class Location implements Comparable<Location> {
public static final Location INTERNAL = Location.of(new Filename(Metadata.name(), "native"), -1, -1);
public abstract int line();
public abstract int start();
public abstract Filename filename();
public final String toString() {
var res = new ArrayList<String>();
if (filename() != null) res.add(filename().toString());
if (line() >= 0) res.add(line() + 1 + "");
if (start() >= 0) res.add(start() + 1 + "");
return String.join(":", res);
}
public final Location add(int n) {
var self = this;
return new Location() {
@Override public Filename filename() { return self.filename(); }
@Override public int start() { return self.start() + n; }
@Override public int line() { return self.line(); }
};
}
public final Location nextLine() {
return changeLine(1);
}
public final Location changeLine(int offset) {
var self = this;
return new Location() {
@Override public Filename filename() { return self.filename(); }
@Override public int start() { return 0; }
@Override public int line() { return self.line() + offset; }
};
}
@Override public int hashCode() {
return Objects.hash(line(), start(), filename());
}
@Override public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof Location)) return false;
var other = (Location)obj;
if (!Objects.equals(this.start(), other.start())) return false;
if (!Objects.equals(this.line(), other.line())) return false;
if (!Objects.equals(this.filename(), other.filename())) return false;
return true;
}
@Override public int compareTo(Location other) {
int a = filename().toString().compareTo(other.filename().toString());
int b = Integer.compare(line(), other.line());
int c = Integer.compare(start(), other.start());
if (a != 0) return a;
if (b != 0) return b;
return c;
}
public static Location of(Filename filename, int line, int start) {
return new Location() {
@Override public Filename filename() { return filename; }
@Override public int start() { return start; }
@Override public int line() { return line; }
};
}
public static Location of(String raw) {
var i0 = raw.lastIndexOf(':');
if (i0 < 0) return Location.of(Filename.parse(raw), -1, -1);
var i1 = raw.lastIndexOf(':', i0);
if (i0 < 0) {
try {
return Location.of(Filename.parse(raw.substring(0, i0)), Integer.parseInt(raw.substring(i0 + 1)), -1);
}
catch (NumberFormatException e) {
return Location.of(Filename.parse(raw), -1, -1);
}
}
int start, line;
try {
start = Integer.parseInt(raw.substring(i1 + 1));
}
catch (NumberFormatException e) {
return Location.of(Filename.parse(raw), -1, -1);
}
try {
line = Integer.parseInt(raw.substring(i0 + 1, i1));
}
catch (NumberFormatException e) {
return Location.of(Filename.parse(raw.substring(i1 + 1)), start, -1);
}
return Location.of(Filename.parse(raw.substring(0, i0)), start, line);
}
}

View File

@@ -1,80 +0,0 @@
package me.topchetoeu.j2s.common.parsing;
public class ParseRes<T> {
public static enum State {
SUCCESS,
FAILED,
ERROR;
public boolean isSuccess() { return this == SUCCESS; }
public boolean isFailed() { return this == FAILED; }
public boolean isError() { return this == ERROR; }
}
public final ParseRes.State state;
public final Location errorLocation;
public final String error;
public final T result;
public final int n;
private ParseRes(ParseRes.State state, Location errorLocation, String error, T result, int readN) {
this.result = result;
this.n = readN;
this.state = state;
this.error = error;
this.errorLocation = errorLocation;
}
public ParseRes<T> setN(int i) {
if (!state.isSuccess()) return this;
return new ParseRes<>(state, null, null, result, i);
}
public ParseRes<T> addN(int n) {
if (!state.isSuccess()) return this;
return new ParseRes<>(state, null, null, result, this.n + n);
}
public <T2> ParseRes<T2> chainError() {
if (isSuccess()) throw new RuntimeException("Can't transform a ParseRes that hasn't failed");
return new ParseRes<>(state, errorLocation, error, null, 0);
}
@SuppressWarnings("unchecked")
public <T2> ParseRes<T2> chainError(ParseRes<?> other) {
if (!this.isError()) return other.chainError();
return (ParseRes<T2>) this;
}
@SuppressWarnings("unchecked")
public <T2> ParseRes<T2> chainError(Location loc, String error) {
if (!this.isError()) return new ParseRes<>(State.ERROR, loc, error, null, 0);
return (ParseRes<T2>) this;
}
public boolean isSuccess() { return state.isSuccess(); }
public boolean isFailed() { return state.isFailed(); }
public boolean isError() { return state.isError(); }
public static <T> ParseRes<T> failed() {
return new ParseRes<T>(State.FAILED, null, null, null, 0);
}
public static <T> ParseRes<T> error(Location loc, String error) {
// TODO: differentiate definitive and probable errors
return new ParseRes<>(State.ERROR, loc, error, null, 0);
}
public static <T> ParseRes<T> res(T val, int i) {
return new ParseRes<>(State.SUCCESS, null, null, val, i);
}
@SafeVarargs
@SuppressWarnings("all")
public static <T> ParseRes<T> first(Source src, int i, Parser ...parsers) {
int n = Parsing.skipEmpty(src, i);
ParseRes<T> error = ParseRes.failed();
for (var parser : parsers) {
var res = parser.parse(src, i + n);
if (res.isSuccess()) return res.addN(n);
if (res.isError() && error.isFailed()) error = res.chainError();
}
return error;
}
}

View File

@@ -1,5 +0,0 @@
package me.topchetoeu.j2s.common.parsing;
public interface Parser<T> {
public ParseRes<T> parse(Source src, int i);
}

View File

@@ -1,452 +0,0 @@
package me.topchetoeu.j2s.common.parsing;
import me.topchetoeu.j2s.common.SyntaxException;
import me.topchetoeu.j2s.compilation.values.constants.NumberNode;
public class Parsing {
public static boolean isDigit(Character c) {
return c != null && c >= '0' && c <= '9';
}
public static boolean isAny(char c, String alphabet) {
return alphabet.contains(Character.toString(c));
}
public static int fromHex(char c) {
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
if (c >= '0' && c <= '9') return c - '0';
return -1;
}
public static int skipEmpty(Source src, int i) {
return skipEmpty(src, i, true);
}
public static int skipEmpty(Source src, int i, boolean noComments) {
int n = 0;
if (i == 0 && src.is(0, "#!")) {
while (!src.is(n, '\n')) n++;
n++;
}
var isSingle = false;
var isMulti = false;
while (i + n < src.size()) {
if (isSingle) {
if (src.is(i + n, '\n')) {
n++;
isSingle = false;
}
else n++;
}
else if (isMulti) {
if (src.is(i + n, "*/")) {
n += 2;
isMulti = false;
}
else n++;
}
else if (src.is(i + n, "//")) {
n += 2;
isSingle = true;
}
else if (src.is(i + n, "/*")) {
n += 2;
isMulti = true;
}
else if (src.is(i + n, Character::isWhitespace)) {
n++;
}
else break;
}
return n;
}
public static ParseRes<Character> parseChar(Source src, int i) {
int n = 0;
if (src.is(i + n, '\\')) {
n++;
char c = src.at(i + n++);
if (c == 'b') return ParseRes.res('\b', n);
else if (c == 't') return ParseRes.res('\t', n);
else if (c == 'n') return ParseRes.res('\n', n);
else if (c == 'f') return ParseRes.res('\f', n);
else if (c == 'r') return ParseRes.res('\r', n);
else if (c == '0') {
if (src.is(i + n, Parsing::isDigit)) return ParseRes.error(src.loc(i), "Octal escape sequences are not allowed");
else return ParseRes.res('\0', n);
}
else if (c >= '1' && c <= '9') return ParseRes.error(src.loc(i), "Octal escape sequences are not allowed");
else if (c == 'x') {
var newC = 0;
for (var j = 0; j < 2; j++) {
if (i + n >= src.size()) return ParseRes.error(src.loc(i), "Invalid hexadecimal escape sequence");
int val = fromHex(src.at(i + n));
if (val == -1) throw new SyntaxException(src.loc(i + n), "Invalid hexadecimal escape sequence");
n++;
newC = (newC << 4) | val;
}
return ParseRes.res((char)newC, n);
}
else if (c == 'u') {
var newC = 0;
for (var j = 0; j < 4; j++) {
if (i + n >= src.size()) return ParseRes.error(src.loc(i), "Invalid Unicode escape sequence");
int val = fromHex(src.at(i + n));
if (val == -1) throw new SyntaxException(src.loc(i + n), "Invalid Unicode escape sequence");
n++;
newC = (newC << 4) | val;
}
return ParseRes.res((char)newC, n);
}
else if (c == '\n') return ParseRes.res(null, n);
else n--;
}
return ParseRes.res(src.at(i + n), n + 1);
}
public static ParseRes<String> parseIdentifier(Source src, int i) {
var n = skipEmpty(src, i);
var res = new StringBuilder();
var first = true;
while (true) {
if (i + n > src.size()) break;
char c = src.at(i + n, '\0');
if (first && Parsing.isDigit(c)) break;
if (!Character.isLetterOrDigit(c) && c != '_' && c != '$') break;
res.append(c);
n++;
first = false;
}
if (res.length() <= 0) return ParseRes.failed();
else return ParseRes.res(res.toString(), n);
}
public static ParseRes<String> parseIdentifier(Source src, int i, String test) {
var n = skipEmpty(src, i);
var res = new StringBuilder();
var first = true;
while (true) {
if (i + n > src.size()) break;
char c = src.at(i + n, '\0');
if (first && Parsing.isDigit(c)) break;
if (!Character.isLetterOrDigit(c) && c != '_' && c != '$') break;
res.append(c);
n++;
first = false;
}
if (res.length() <= 0) return ParseRes.failed();
else if (test == null || res.toString().equals(test)) return ParseRes.res(res.toString(), n);
else return ParseRes.failed();
}
public static boolean isIdentifier(Source src, int i, String test) {
return parseIdentifier(src, i, test).isSuccess();
}
public static ParseRes<String> parseOperator(Source src, int i, String op) {
var n = skipEmpty(src, i);
if (src.is(i + n, op)) return ParseRes.res(op, n + op.length());
else return ParseRes.failed();
}
private static ParseRes<Double> parseHex(Source src, int i) {
int n = 0;
double res = 0;
while (true) {
int digit = Parsing.fromHex(src.at(i + n, '\0'));
if (digit < 0) {
if (n <= 0) return ParseRes.failed();
else return ParseRes.res(res, n);
}
n++;
res *= 16;
res += digit;
}
}
private static ParseRes<Double> parseOct(Source src, int i) {
int n = 0;
double res = 0;
while (true) {
int digit = src.at(i + n, '\0') - '0';
if (digit < 0 || digit > 9) break;
if (digit > 7) return ParseRes.error(src.loc(i + n), "Digits in octal literals must be from 0 to 7, encountered " + digit);
if (digit < 0) {
if (n <= 0) return ParseRes.failed();
else return ParseRes.res(res, n);
}
n++;
res *= 8;
res += digit;
}
return ParseRes.res(res, n);
}
private static ParseRes<Double> parseBin(Source src, int i) {
int n = 0;
double res = 0;
while (true) {
int digit = src.at(i + n, '\0') - '0';
if (digit < 0 || digit > 9) break;
if (digit > 1) return ParseRes.error(src.loc(i + n), "Digits in binary literals must be from 0 to 1, encountered " + digit);
if (digit < 0) {
if (n <= 0) return ParseRes.failed();
else return ParseRes.res(res, n);
}
n++;
res *= 2;
res += digit;
}
return ParseRes.res(res, n);
}
public static ParseRes<String> parseString(Source src, int i) {
var n = skipEmpty(src, i);
char quote;
if (src.is(i + n, '\'')) quote = '\'';
else if (src.is(i + n, '"')) quote = '"';
else return ParseRes.failed();
n++;
var res = new StringBuilder();
while (true) {
if (i + n >= src.size()) return ParseRes.error(src.loc(i + n), "Unterminated string literal");
if (src.is(i + n, quote)) {
n++;
return ParseRes.res(res.toString(), n);
}
var charRes = parseChar(src, i + n);
if (!charRes.isSuccess()) return charRes.chainError(src.loc(i + n), "Invalid character");
n += charRes.n;
if (charRes.result != null) res.append(charRes.result);
}
}
public static ParseRes<Double> parseNumber(Source src, int i, boolean withMinus) {
var n = skipEmpty(src, i);
double whole = 0;
double fract = 0;
long exponent = 0;
boolean parsedAny = false;
boolean negative = false;
if (withMinus && src.is(i + n, "-")) {
negative = true;
n++;
}
if (src.is(i + n, "0x") || src.is(i + n, "0X")) {
n += 2;
var res = parseHex(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete hexadecimal literal");
n += res.n;
if (negative) return ParseRes.res(-res.result, n);
else return ParseRes.res(res.result, n);
}
else if (src.is(i + n, "0o") || src.is(i + n, "0O")) {
n += 2;
var res = parseOct(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete octal literal");
n += res.n;
if (negative) return ParseRes.res(-res.result, n);
else return ParseRes.res(res.result, n);
}
else if (src.is(i + n, "0b") || src.is(i + n, "0B")) {
n += 2;
var res = parseBin(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete binary literal");
n += res.n;
if (negative) return ParseRes.res(-res.result, n);
else return ParseRes.res(res.result, n);
}
else if (src.is(i + n, '0')) {
n++;
parsedAny = true;
if (src.is(i + n, Parsing::isDigit)) return ParseRes.error(src.loc(i + n), "Decimals with leading zeroes are not allowed");
}
while (src.is(i + n, Parsing::isDigit)) {
parsedAny = true;
whole *= 10;
whole += src.at(i + n++) - '0';
}
if (src.is(i + n, '.')) {
parsedAny = true;
n++;
while (src.is(i + n, Parsing::isDigit)) {
fract += src.at(i + n++) - '0';
fract /= 10;
}
}
if (parsedAny && src.is(i + n, 'e') || src.is(i + n, 'E')) {
n++;
boolean expNegative = false;
boolean parsedE = false;
if (src.is(i + n, '-')) {
expNegative = true;
n++;
}
else if (src.is(i + n, '+')) n++;
while (src.is(i + n, Parsing::isDigit)) {
parsedE = true;
exponent *= 10;
if (expNegative) exponent -= src.at(i + n++) - '0';
else exponent += src.at(i + n++) - '0';
}
if (!parsedE) return ParseRes.error(src.loc(i + n), "Incomplete number exponent");
}
if (!parsedAny) {
if (negative) return ParseRes.error(src.loc(i + n), "Expected number immediatly after minus");
return ParseRes.failed();
}
else if (negative) return ParseRes.res(-(whole + fract) * NumberNode.power(10, exponent), n);
else return ParseRes.res((whole + fract) * NumberNode.power(10, exponent), n);
}
public static ParseRes<Double> parseFloat(Source src, int i, boolean withMinus) {
var n = skipEmpty(src, i);
double whole = 0;
double fract = 0;
long exponent = 0;
boolean parsedAny = false;
boolean negative = false;
if (withMinus && src.is(i + n, "-")) {
negative = true;
n++;
}
while (src.is(i + n, Parsing::isDigit)) {
parsedAny = true;
whole *= 10;
whole += src.at(i + n++) - '0';
}
if (src.is(i + n, '.')) {
parsedAny = true;
n++;
while (src.is(i + n, Parsing::isDigit)) {
fract += src.at(i + n++) - '0';
fract /= 10;
}
}
if (src.is(i + n, 'e') || src.is(i + n, 'E')) {
n++;
parsedAny = true;
boolean expNegative = false;
boolean parsedE = false;
if (src.is(i + n, '-')) {
expNegative = true;
n++;
}
else if (src.is(i + n, '+')) n++;
while (src.is(i + n, Parsing::isDigit)) {
parsedE = true;
exponent *= 10;
if (expNegative) exponent -= src.at(i + n++) - '0';
else exponent += src.at(i + n++) - '0';
}
if (!parsedE) return ParseRes.error(src.loc(i + n), "Incomplete number exponent");
}
if (!parsedAny) {
if (negative) return ParseRes.error(src.loc(i + n), "Expected number immediatly after minus");
return ParseRes.failed();
}
else if (negative) return ParseRes.res(-(whole + fract) * NumberNode.power(10, exponent), n);
else return ParseRes.res((whole + fract) * NumberNode.power(10, exponent), n);
}
public static ParseRes<Double> parseInt(Source src, int i, String alphabet, boolean withMinus) {
var n = skipEmpty(src, i);
double result = 0;
boolean parsedAny = false;
boolean negative = false;
if (withMinus && src.is(i + n, "-")) {
negative = true;
n++;
}
if (alphabet == null && src.is(i + n, "0x") || src.is(i + n, "0X")) {
n += 2;
var res = parseHex(src, i);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Incomplete hexadecimal literal");
n += res.n;
if (negative) return ParseRes.res(-res.result, n);
else return ParseRes.res(res.result, n);
}
while (true) {
var digit = alphabet.indexOf(Character.toLowerCase(src.at(i + n, '\0')));
if (digit < 0) break;
parsedAny = true;
result *= alphabet.length();
result += digit;
n++;
}
if (!parsedAny) {
if (negative) return ParseRes.error(src.loc(i + n), "Expected number immediatly after minus");
return ParseRes.failed();
}
else if (negative) return ParseRes.res(-result, n);
else return ParseRes.res(result, n);
}
}

View File

@@ -1,75 +0,0 @@
package me.topchetoeu.j2s.common.parsing;
import java.util.function.Predicate;
import me.topchetoeu.j2s.common.environment.Environment;
public class Source {
public final Environment env;
public final Filename filename;
public final String src;
private int[] lineStarts;
public Location loc(int offset) {
return new SourceLocation(filename, lineStarts, offset);
}
public boolean is(int i, char c) {
return i >= 0 && i < src.length() && src.charAt(i) == c;
}
public boolean is(int i, String src) {
if (i < 0 || i + src.length() > size()) return false;
for (int j = 0; j < src.length(); j++) {
if (at(i + j) != src.charAt(j)) return false;
}
return true;
}
public boolean is(int i, Predicate<Character> predicate) {
if (i < 0 || i >= src.length()) return false;
return predicate.test(at(i));
}
public char at(int i) {
return src.charAt(i);
}
public char at(int i, char defaultVal) {
if (i < 0 || i >= src.length()) return defaultVal;
else return src.charAt(i);
}
public int size() {
return src.length();
}
public String slice(int start, int end) {
return src.substring(start, end);
}
public Source(Environment env, Filename filename, String src) {
if (env == null) this.env = new Environment();
else this.env = env;
this.filename = filename;
this.src = src;
int n = 1;
lineStarts = new int[16];
lineStarts[0] = 0;
for (int i = src.indexOf("\n"); i > 0; i = src.indexOf("\n", i + 1)) {
if (n >= lineStarts.length) {
var newArr = new int[lineStarts.length * 2];
System.arraycopy(lineStarts, 0, newArr, 0, n);
lineStarts = newArr;
}
lineStarts[n++] = i + 1;
}
var newArr = new int[n];
System.arraycopy(lineStarts, 0, newArr, 0, n);
lineStarts = newArr;
}
public Source(String src) {
this(null, null, src);
}
}

View File

@@ -1,66 +0,0 @@
package me.topchetoeu.j2s.common.parsing;
import java.util.Objects;
public class SourceLocation extends Location {
private int[] lineStarts;
private int line;
private int start;
private final Filename filename;
private final int offset;
private void update() {
if (lineStarts == null) return;
int a = 0;
int b = lineStarts.length;
while (true) {
if (a + 1 >= b) break;
var mid = -((-a - b) >> 1);
var el = lineStarts[mid];
if (el < offset) a = mid;
else if (el > offset) b = mid;
else {
this.line = mid;
this.start = 0;
this.lineStarts = null;
return;
}
}
this.line = a;
this.start = offset - lineStarts[a];
this.lineStarts = null;
return;
}
@Override public Filename filename() { return filename; }
@Override public int line() {
update();
return line;
}
@Override public int start() {
update();
return start;
}
@Override public int hashCode() {
return Objects.hash(offset);
}
@Override public int compareTo(Location other) {
if (other instanceof SourceLocation srcLoc) return Integer.compare(offset, srcLoc.offset);
else return super.compareTo(other);
}
@Override public boolean equals(Object obj) {
if (obj instanceof SourceLocation other) return this.offset == other.offset;
else return super.equals(obj);
}
public SourceLocation(Filename filename, int[] lineStarts, int offset) {
this.filename = filename;
this.lineStarts = lineStarts;
this.offset = offset;
}
}

View File

@@ -1,142 +0,0 @@
package me.topchetoeu.j2s.compilation;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import me.topchetoeu.j2s.common.FunctionBody;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
import me.topchetoeu.j2s.common.environment.Environment;
import me.topchetoeu.j2s.common.environment.Key;
import me.topchetoeu.j2s.common.mapping.FunctionMap;
import me.topchetoeu.j2s.common.mapping.FunctionMap.FunctionMapBuilder;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.compilation.control.TryNode;
import me.topchetoeu.j2s.compilation.scope.FunctionScope;
import me.topchetoeu.j2s.compilation.scope.Variable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
public final class CompileResult {
public static final Key<Void> DEBUG_LOG = new Key<>();
public final List<Instruction> instructions;
public final List<CompileResult> children;
public final Map<FunctionNode, CompileResult> childrenMap = new HashMap<>();
public final Map<FunctionNode, Integer> childrenIndices = new HashMap<>();
public final FunctionMapBuilder map;
public final Environment env;
public int length;
public final FunctionScope scope;
public final Map<TryNode, Variable> catchBindings = new HashMap<>();
public int temp() {
instructions.add(null);
return instructions.size() - 1;
}
public CompileResult add(Instruction instr) {
instructions.add(instr);
return this;
}
public CompileResult set(int i, Instruction instr) {
instructions.set(i, instr);
return this;
}
public int size() { return instructions.size(); }
public void setDebug(Location loc, BreakpointType type) {
map.setDebug(loc, type);
}
public void setLocation(int i, Location loc) {
map.setLocation(i, loc);
}
public void setLocationAndDebug(int i, Location loc, BreakpointType type) {
map.setLocationAndDebug(i, loc, type);
}
public void setDebug(BreakpointType type) {
setDebug(map.last(), type);
}
public void setLocation(Location type) {
setLocation(instructions.size() - 1, type);
}
public void setLocationAndDebug(Location loc, BreakpointType type) {
setLocationAndDebug(instructions.size() - 1, loc, type);
}
public CompileResult addChild(FunctionNode node, CompileResult res) {
this.children.add(res);
this.childrenMap.put(node, res);
this.childrenIndices.put(node, this.children.size() - 1);
return res;
}
public Instruction[] instructions() {
return instructions.toArray(new Instruction[0]);
}
public FunctionMap map(Function<Location, Location> mapper) {
return map.map(mapper).build(scope.localNames(), scope.capturableNames(), scope.captureNames());
}
public FunctionMap map() {
return map.build(scope.localNames(), scope.capturableNames(), scope.captureNames());
}
public FunctionBody body() {
var builtChildren = new FunctionBody[children.size()];
for (var i = 0; i < children.size(); i++) builtChildren[i] = children.get(i).body();
var instrRes = instructions();
if (env.has(DEBUG_LOG)) {
System.out.println("================= BODY =================");
System.out.println("LOCALS: " + scope.localsCount());
System.out.println("CAPTURABLES: " + scope.capturablesCount());
System.out.println("CAPTURES: " + scope.capturesCount());
for (var instr : instrRes) System.out.println(instr);
}
return new FunctionBody(
scope.localsCount(), scope.capturablesCount(), scope.capturesCount(),
length, instrRes, builtChildren
);
}
public CompileResult subtarget() {
return new CompileResult(env, new FunctionScope(scope), this);
}
public CompileResult setEnvironment(Environment env) {
return new CompileResult(env, scope, this);
}
/**
* Returns a compile result with a child of the environment that relates to the given key.
* In essence, this is used to create a compile result which is back at the root environment of the compilation
*/
public CompileResult rootEnvironment(Key<Environment> env) {
return new CompileResult(this.env.get(env).child(), scope, this);
}
public CompileResult subEnvironment() {
return new CompileResult(env.child(), scope, this);
}
public CompileResult(Environment env, FunctionScope scope, int length) {
this.scope = scope;
this.instructions = new ArrayList<>();
this.children = new LinkedList<>();
this.map = FunctionMap.builder();
this.env = env;
this.length = length;
}
private CompileResult(Environment env, FunctionScope scope, CompileResult parent) {
this.scope = scope;
this.instructions = parent.instructions;
this.children = parent.children;
this.map = parent.map;
this.env = env;
}
}

View File

@@ -1,112 +0,0 @@
package me.topchetoeu.j2s.compilation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.parsing.ParseRes;
import me.topchetoeu.j2s.common.parsing.Parsing;
import me.topchetoeu.j2s.common.parsing.Source;
public class CompoundNode extends Node {
public final Node[] statements;
public Location end;
@Override public void resolve(CompileResult target) {
for (var stm : statements) stm.resolve(target);
}
@Override public void compileFunctions(CompileResult target) {
for (var stm : statements) stm.compileFunctions(target);
}
public void compile(CompileResult target, boolean pollute, BreakpointType type) {
List<Node> statements = new ArrayList<Node>();
for (var stm : this.statements) {
if (stm instanceof FunctionStatementNode func) {
func.compile(target, false);
}
else statements.add(stm);
}
var polluted = false;
for (var i = 0; i < statements.size(); i++) {
var stm = statements.get(i);
if (i != statements.size() - 1) stm.compile(target, false, BreakpointType.STEP_OVER);
else stm.compile(target, polluted = pollute, BreakpointType.STEP_OVER);
}
if (!polluted && pollute) {
target.add(Instruction.pushUndefined());
}
}
public CompoundNode setEnd(Location loc) {
this.end = loc;
return this;
}
public CompoundNode(Location loc, Node ...statements) {
super(loc);
this.statements = statements;
}
public static ParseRes<CompoundNode> parseComma(Source src, int i, Node prev, int precedence) {
if (precedence > 1) return ParseRes.failed();
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!src.is(i + n, ",")) return ParseRes.failed();
n++;
var curr = JavaScript.parseExpression(src, i + n, 2);
if (!curr.isSuccess()) return curr.chainError(src.loc(i + n), "Expected a value after the comma");
n += curr.n;
if (prev instanceof CompoundNode comp) {
var children = new ArrayList<Node>();
children.addAll(Arrays.asList(comp.statements));
children.add(curr.result);
return ParseRes.res(new CompoundNode(loc, children.toArray(new Node[0])), n);
}
else return ParseRes.res(new CompoundNode(loc, prev, curr.result), n);
}
public static ParseRes<CompoundNode> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!src.is(i + n, "{")) return ParseRes.failed();
n++;
var statements = new ArrayList<Node>();
while (true) {
n += Parsing.skipEmpty(src, i + n);
if (src.is(i + n, "}")) {
n++;
break;
}
if (src.is(i + n, ";")) {
n++;
continue;
}
var res = JavaScript.parseStatement(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected a statement");
n += res.n;
statements.add(res.result);
}
return ParseRes.res(new CompoundNode(loc, statements.toArray(new Node[0])).setEnd(src.loc(i + n - 1)), n);
}
}

View File

@@ -1,19 +0,0 @@
package me.topchetoeu.j2s.compilation;
import java.util.function.IntSupplier;
public final class DeferredIntSupplier implements IntSupplier {
private int value;
private boolean set;
public void set(int val) {
if (set) throw new RuntimeException("A deferred int supplier may be set only once");
value = val;
set = true;
}
@Override public int getAsInt() {
if (!set) throw new RuntimeException("Deferred int supplier accessed too early");
return value;
}
}

View File

@@ -1,125 +0,0 @@
package me.topchetoeu.j2s.compilation;
import java.util.List;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
import me.topchetoeu.j2s.common.environment.Environment;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.parsing.ParseRes;
import me.topchetoeu.j2s.common.parsing.Parsing;
import me.topchetoeu.j2s.common.parsing.Source;
import me.topchetoeu.j2s.compilation.scope.FunctionScope;
import me.topchetoeu.j2s.compilation.values.VariableNode;
public abstract class FunctionNode extends Node {
public final CompoundNode body;
public final List<VariableNode> params;
public final Location end;
public abstract String name();
public final String name(String fallback) {
return this.name() != null ? this.name() : fallback;
}
protected final int[] captures(CompileResult target) {
return target.childrenMap.get(this).scope.getCaptureIndices();
}
protected final Environment rootEnv(Environment env) {
return env.get(JavaScript.COMPILE_ROOT);
}
@Override public void resolve(CompileResult target) { }
public final CompileResult compileBody(Environment env, FunctionScope scope, boolean lastReturn, String selfName) {
var target = new CompileResult(env, scope, params.size());
var i = 0;
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);
return target;
}
public final CompileResult compileBody(CompileResult parent, String 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 void compile(CompileResult target, boolean pollute, String name) {
compile(target, pollute, name, BreakpointType.NONE);
}
@Override public void compile(CompileResult target, boolean pollute, BreakpointType bp) {
compile(target, pollute, (String)null, bp);
}
@Override public void compile(CompileResult target, boolean pollute) {
compile(target, pollute, (String)null, BreakpointType.NONE);
}
public FunctionNode(Location loc, Location end, List<VariableNode> params, CompoundNode body) {
super(loc);
this.end = end;
this.params = params;
this.body = body;
}
public static void compileWithName(Node stm, CompileResult target, boolean pollute, String name) {
if (stm instanceof FunctionNode) ((FunctionNode)stm).compile(target, pollute, name);
else stm.compile(target, pollute);
}
public static void compileWithName(Node stm, CompileResult target, boolean pollute, String name, BreakpointType bp) {
if (stm instanceof FunctionNode) ((FunctionNode)stm).compile(target, pollute, name, bp);
else stm.compile(target, pollute, bp);
}
public static ParseRes<FunctionNode> parseFunction(Source src, int i, boolean statement) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!Parsing.isIdentifier(src, i + n, "function")) return ParseRes.failed();
n += 8;
var name = Parsing.parseIdentifier(src, i + n);
if (!name.isSuccess() && statement) return ParseRes.error(src.loc(i + n), "A statement function requires a name");
n += name.n;
n += Parsing.skipEmpty(src, i + n);
var params = JavaScript.parseParameters(src, i + n);
if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected a parameter 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 function");
n += body.n;
if (statement) return ParseRes.res(new FunctionStatementNode(
loc, src.loc(i + n - 1),
params.result, body.result, name.result
), n);
else return ParseRes.res(new FunctionValueNode(
loc, src.loc(i + n - 1),
params.result, body.result, name.result
), n);
}
}

View File

@@ -1,33 +0,0 @@
package me.topchetoeu.j2s.compilation;
import java.util.List;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.compilation.scope.Variable;
import me.topchetoeu.j2s.compilation.values.VariableNode;
public class FunctionStatementNode extends FunctionNode {
public final String name;
@Override public String name() { return name; }
@Override public void resolve(CompileResult target) {
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) {
target.add(Instruction.loadFunc(target.childrenIndices.get(this), name(name), captures(target))).setLocation(loc());
target.add(VariableNode.toSet(target, end, this.name, false, true)).setLocation(loc());
if (pollute) target.add(Instruction.pushUndefined());
}
public FunctionStatementNode(Location loc, Location end, List<VariableNode> params, CompoundNode body, String name) {
super(loc, end, params, body);
this.name = name;
}
}

View File

@@ -1,28 +0,0 @@
package me.topchetoeu.j2s.compilation;
import java.util.List;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.compilation.values.VariableNode;
public class FunctionValueNode extends FunctionNode {
public final String name;
@Override public String name() { return name; }
@Override public void compileFunctions(CompileResult target) {
target.addChild(this, compileBody(target, 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);
this.name = name;
}
}

View File

@@ -1,325 +0,0 @@
package me.topchetoeu.j2s.compilation;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import me.topchetoeu.j2s.common.SyntaxException;
import me.topchetoeu.j2s.common.environment.Environment;
import me.topchetoeu.j2s.common.environment.Key;
import me.topchetoeu.j2s.common.parsing.Filename;
import me.topchetoeu.j2s.common.parsing.ParseRes;
import me.topchetoeu.j2s.common.parsing.Parsing;
import me.topchetoeu.j2s.common.parsing.Source;
import me.topchetoeu.j2s.compilation.control.BreakNode;
import me.topchetoeu.j2s.compilation.control.ContinueNode;
import me.topchetoeu.j2s.compilation.control.DebugNode;
import me.topchetoeu.j2s.compilation.control.DeleteNode;
import me.topchetoeu.j2s.compilation.control.DoWhileNode;
import me.topchetoeu.j2s.compilation.control.ForInNode;
import me.topchetoeu.j2s.compilation.control.ForNode;
import me.topchetoeu.j2s.compilation.control.IfNode;
import me.topchetoeu.j2s.compilation.control.ReturnNode;
import me.topchetoeu.j2s.compilation.control.SwitchNode;
import me.topchetoeu.j2s.compilation.control.ThrowNode;
import me.topchetoeu.j2s.compilation.control.TryNode;
import me.topchetoeu.j2s.compilation.control.WhileNode;
import me.topchetoeu.j2s.compilation.scope.FunctionScope;
import me.topchetoeu.j2s.compilation.values.ArgumentsNode;
import me.topchetoeu.j2s.compilation.values.ArrayNode;
import me.topchetoeu.j2s.compilation.values.GlobalThisNode;
import me.topchetoeu.j2s.compilation.values.ObjectNode;
import me.topchetoeu.j2s.compilation.values.RegexNode;
import me.topchetoeu.j2s.compilation.values.ThisNode;
import me.topchetoeu.j2s.compilation.values.VariableNode;
import me.topchetoeu.j2s.compilation.values.constants.BoolNode;
import me.topchetoeu.j2s.compilation.values.constants.NullNode;
import me.topchetoeu.j2s.compilation.values.constants.NumberNode;
import me.topchetoeu.j2s.compilation.values.constants.StringNode;
import me.topchetoeu.j2s.compilation.values.operations.CallNode;
import me.topchetoeu.j2s.compilation.values.operations.ChangeNode;
import me.topchetoeu.j2s.compilation.values.operations.DiscardNode;
import me.topchetoeu.j2s.compilation.values.operations.IndexNode;
import me.topchetoeu.j2s.compilation.values.operations.OperationNode;
import me.topchetoeu.j2s.compilation.values.operations.PostfixNode;
import me.topchetoeu.j2s.compilation.values.operations.TypeofNode;
public final class JavaScript {
public static enum DeclarationType {
@Deprecated
VAR;
}
public static final Key<Environment> COMPILE_ROOT = new Key<>();
static final Set<String> reserved = new HashSet<>(Arrays.asList(
"true", "false", "void", "null", "this", "if", "else", "try", "catch",
"finally", "for", "do", "while", "switch", "case", "default", "new",
"function", "var", "return", "throw", "typeof", "delete", "break",
"continue", "debugger", "implements", "interface", "package", "private",
"protected", "public", "static", "arguments", "class", "extends"
));
public static ParseRes<? extends Node> parseParens(Source src, int i) {
int n = 0;
var openParen = Parsing.parseOperator(src, i + n, "(");
if (!openParen.isSuccess()) return openParen.chainError();
n += openParen.n;
var res = JavaScript.parseExpression(src, i + n, 0);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected an expression in parens");
n += res.n;
var closeParen = Parsing.parseOperator(src, i + n, ")");
if (!closeParen.isSuccess()) return closeParen.chainError(src.loc(i + n), "Expected a closing paren");
n += closeParen.n;
return ParseRes.res(res.result, n);
}
public static ParseRes<? extends Node> parseSimple(Source src, int i, boolean statement) {
return ParseRes.first(src, i,
(s, j) -> statement ? ParseRes.failed() : ObjectNode.parse(s, j),
(s, j) -> statement ? ParseRes.failed() : FunctionNode.parseFunction(s, j, false),
JavaScript::parseLiteral,
StringNode::parse,
RegexNode::parse,
NumberNode::parse,
ChangeNode::parsePrefixDecrease,
ChangeNode::parsePrefixIncrease,
OperationNode::parsePrefix,
ArrayNode::parse,
JavaScript::parseParens,
CallNode::parseNew,
TypeofNode::parse,
DiscardNode::parse,
DeleteNode::parse,
VariableNode::parse
);
}
public static ParseRes<? extends Node> parseLiteral(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var id = Parsing.parseIdentifier(src, i);
if (!id.isSuccess()) return id.chainError();
n += id.n;
if (id.result.equals("true")) return ParseRes.res(new BoolNode(loc, true), 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("this")) return ParseRes.res(new ThisNode(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();
}
public static ParseRes<Node> parseExpression(Source src, int i, int precedence, boolean statement) {
var n = Parsing.skipEmpty(src, i);
Node prev = null;
while (true) {
if (prev == null) {
var res = parseSimple(src, i + n, statement);
if (res.isSuccess()) {
n += res.n;
prev = res.result;
}
else if (res.isError()) return res.chainError();
else break;
}
else {
var _prev = prev;
ParseRes<Node> res = ParseRes.first(src, i + n,
(s, j) -> OperationNode.parseInstanceof(s, j, _prev, precedence),
(s, j) -> OperationNode.parseIn(s, j, _prev, precedence),
(s, j) -> PostfixNode.parsePostfixIncrease(s, j, _prev, precedence),
(s, j) -> PostfixNode.parsePostfixDecrease(s, j, _prev, precedence),
(s, j) -> OperationNode.parseOperator(s, j, _prev, precedence),
(s, j) -> IfNode.parseTernary(s, j, _prev, precedence),
(s, j) -> IndexNode.parseMember(s, j, _prev, precedence),
(s, j) -> IndexNode.parseIndex(s, j, _prev, precedence),
(s, j) -> CallNode.parseCall(s, j, _prev, precedence),
(s, j) -> CompoundNode.parseComma(s, j, _prev, precedence)
);
if (res.isSuccess()) {
n += res.n;
prev = res.result;
continue;
}
else if (res.isError()) return res.chainError();
break;
}
}
if (prev == null) return ParseRes.failed();
else return ParseRes.res(prev, n);
}
public static ParseRes<Node> parseExpression(Source src, int i, int precedence) {
return parseExpression(src, i, precedence, false);
}
public static ParseRes<Node> parseExpressionStatement(Source src, int i) {
var res = parseExpression(src, i, 0, true);
if (!res.isSuccess()) return res.chainError();
var end = JavaScript.parseStatementEnd(src, i + res.n);
if (!end.isSuccess()) return ParseRes.error(src.loc(i + res.n), "Expected an end of statement");
return res.addN(end.n);
}
public static ParseRes<Node> parseStatement(Source src, int 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 (Parsing.isIdentifier(src, i + n, "with")) return ParseRes.error(src.loc(i + n), "'with' statements are not allowed.");
ParseRes<Node> res = ParseRes.first(src, i + n,
VariableDeclareNode::parse,
ReturnNode::parse,
ThrowNode::parse,
ContinueNode::parse,
BreakNode::parse,
DebugNode::parse,
IfNode::parse,
WhileNode::parse,
SwitchNode::parse,
ForNode::parse,
ForInNode::parse,
DoWhileNode::parse,
TryNode::parse,
CompoundNode::parse,
(s, j) -> FunctionNode.parseFunction(s, j, true),
JavaScript::parseExpressionStatement
);
return res.addN(n);
}
public static ParseRes<Boolean> parseStatementEnd(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
if (i + n >= src.size()) return ParseRes.res(true, n);
for (var j = i; j < i + n; j++) {
if (src.is(j, '\n')) return ParseRes.res(true, n);
}
if (src.is(i + n, ';')) return ParseRes.res(true, n + 1);
if (src.is(i + n, '}')) return ParseRes.res(true, n);
return ParseRes.failed();
}
public static ParseRes<Boolean> parseDeclarationType(Source src, int i) {
var res = Parsing.parseIdentifier(src, i);
if (!res.isSuccess()) return res.chainError();
if (res.result.equals("var")) return ParseRes.res(true, res.n);
return ParseRes.failed();
}
public static Node[] parse(Environment env, Filename filename, String raw) {
var src = new Source(env, filename, raw);
var list = new ArrayList<Node>();
int i = 0;
while (true) {
i += Parsing.skipEmpty(src, i);
if (i >= src.size()) break;
var res = parseStatement(src, i);
if (res.isError()) throw new SyntaxException(res.errorLocation, res.error);
else if (res.isFailed()) throw new SyntaxException(src.loc(i), "Unexpected syntax");
i += res.n;
i += Parsing.skipEmpty(src, i);
list.add(res.result);
}
return list.toArray(new Node[0]);
}
public static boolean checkVarName(String name) {
return !JavaScript.reserved.contains(name);
}
public static CompileResult compile(Environment env, boolean passthrough, Node ...statements) {
env = env.child();
env.add(COMPILE_ROOT, env);
var func = new FunctionValueNode(null, null, Arrays.asList(), new CompoundNode(null, statements), null);
var res = func.compileBody(env, new FunctionScope(passthrough), true, null);
return res;
}
public static CompileResult compile(Environment env, Filename filename, String raw, boolean passthrough) {
return JavaScript.compile(env, passthrough, JavaScript.parse(env, filename, raw));
}
public static CompileResult compile(Filename filename, String raw, boolean passthrough) {
var env = new Environment();
return JavaScript.compile(env, passthrough, JavaScript.parse(env, filename, raw));
}
public static ParseRes<String> parseLabel(Source src, int i) {
int n = Parsing.skipEmpty(src, i);
var nameRes = Parsing.parseIdentifier(src, i + n);
if (!nameRes.isSuccess()) return nameRes.chainError();
n += nameRes.n;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, ":")) return ParseRes.failed();
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);
}
}

View File

@@ -1,120 +0,0 @@
package me.topchetoeu.j2s.compilation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.function.IntSupplier;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.SyntaxException;
import me.topchetoeu.j2s.common.environment.Environment;
import me.topchetoeu.j2s.common.environment.Key;
import me.topchetoeu.j2s.common.parsing.Location;
public class LabelContext {
public static final Key<LabelContext> BREAK_CTX = new Key<>();
public static final Key<LabelContext> CONTINUE_CTX = new Key<>();
private final LinkedList<IntSupplier> list = new LinkedList<>();
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() {
return list.peekLast();
}
public IntSupplier get(String name) {
return map.get(name);
}
public void flushAdders(String name) {
for (var adder : deferredList.peek()) {
adder.run();
}
deferredList.pop();
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) {
list.add(jumpTarget);
}
public void push(Location loc, String name, IntSupplier jumpTarget) {
if (name == null) return;
if (map.containsKey(name)) throw new SyntaxException(loc, String.format("Label '%s' has already been declared", name));
map.put(name, jumpTarget);
}
public void pushLoop(Location loc, String name, IntSupplier jumpTarget) {
push(jumpTarget);
push(loc, name, jumpTarget);
deferredList.push(new ArrayList<>());
if (name != null) deferredMap.put(name, new ArrayList<>());
}
public void pop() {
list.removeLast();
}
public void pop(String name) {
if (name == null) return;
map.remove(name);
}
public void popLoop(String name) {
pop();
pop(name);
flushAdders(name);
}
public static LabelContext getBreak(Environment env) {
return env.initFrom(BREAK_CTX, () -> new LabelContext());
}
public static LabelContext getCont(Environment env) {
return env.initFrom(CONTINUE_CTX, () -> new LabelContext());
}
public static void pushLoop(Environment env, Location loc, String name, IntSupplier breakTarget, int contTarget) {
LabelContext.getBreak(env).pushLoop(loc, name, breakTarget);
LabelContext.getCont(env).pushLoop(loc, name, () -> contTarget);
}
public static void pushLoop(Environment env, Location loc, String name, IntSupplier breakTarget, IntSupplier contTarget) {
LabelContext.getBreak(env).pushLoop(loc, name, breakTarget);
LabelContext.getCont(env).pushLoop(loc, name, contTarget);
}
public static void popLoop(Environment env, String name) {
LabelContext.getBreak(env).popLoop(name);
LabelContext.getCont(env).popLoop(name);
}
}

View File

@@ -1,28 +0,0 @@
package me.topchetoeu.j2s.compilation;
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
import me.topchetoeu.j2s.common.parsing.Location;
public abstract class Node {
private Location loc;
public void resolve(CompileResult target) {}
public void compile(CompileResult target, boolean pollute, BreakpointType type) {
int start = target.size();
compile(target, pollute);
if (target.size() != start) target.setLocationAndDebug(start, loc(), type);
}
public void compile(CompileResult target, boolean pollute) {
compile(target, pollute, BreakpointType.NONE);
}
public abstract void compileFunctions(CompileResult target);
public Location loc() { return loc; }
public void setLoc(Location loc) { this.loc = loc; }
protected Node(Location loc) {
this.loc = loc;
}
}

View File

@@ -1,93 +0,0 @@
package me.topchetoeu.j2s.compilation;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.function.Function;
public final class NodeChildren implements Iterable<Node> {
public static final class Slot {
private Node node;
private final Function<Node, Node> replacer;
public final void replace(Node node) {
this.node = this.replacer.apply(node);
}
public Slot(Node nodes, Function<Node, Node> replacer) {
this.node = nodes;
this.replacer = replacer;
}
}
private final Slot[] slots;
private NodeChildren(Slot[] slots) {
this.slots = slots;
}
@Override public Iterator<Node> iterator() {
return new Iterator<Node>() {
private int i = 0;
private Slot[] arr = slots;
@Override public boolean hasNext() {
if (arr == null) return false;
else if (i >= arr.length) {
arr = null;
return false;
}
else return true;
}
@Override public Node next() {
if (!hasNext()) return null;
return arr[i++].node;
}
};
}
public Iterable<Slot> slots() {
return () -> new Iterator<Slot>() {
private int i = 0;
private Slot[] arr = slots;
@Override public boolean hasNext() {
if (arr == null) return false;
else if (i >= arr.length) {
arr = null;
return false;
}
else return true;
}
@Override public Slot next() {
if (!hasNext()) return null;
return arr[i++];
}
};
}
public static final class Builder {
private final ArrayList<Slot> slots = new ArrayList<>();
public final Builder add(Slot ...children) {
for (var child : children) {
this.slots.add(child);
}
return this;
}
public final Builder add(Iterable<Slot> children) {
for (var child : children) {
this.slots.add(child);
}
return this;
}
public final Builder add(Node child, Function<Node, Node> replacer) {
slots.add(new Slot(child, replacer));
return this;
}
public final NodeChildren build() {
return new NodeChildren(slots.toArray(new Slot[0]));
}
}
}

View File

@@ -1,15 +0,0 @@
package me.topchetoeu.j2s.compilation;
import me.topchetoeu.j2s.common.parsing.Location;
public final class Parameter {
public final Location loc;
public final String name;
public final Node node;
public Parameter(Location loc, String name, Node node) {
this.name = name;
this.node = node;
this.loc = loc;
}
}

View File

@@ -1,115 +0,0 @@
package me.topchetoeu.j2s.compilation;
import java.util.ArrayList;
import java.util.List;
import com.github.bsideup.jabel.Desugar;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.parsing.ParseRes;
import me.topchetoeu.j2s.common.parsing.Parsing;
import me.topchetoeu.j2s.common.parsing.Source;
import me.topchetoeu.j2s.compilation.values.VariableNode;
public class VariableDeclareNode extends Node {
@Desugar
public static record Pair(VariableNode var, Node value) { }
public final List<Pair> values;
@Override public void resolve(CompileResult target) {
for (var entry : values) {
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) {
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) {
target.add(Instruction.globDef(entry.var.name));
}
else {
target.add(Instruction.globSet(entry.var.name, false, true));
}
}
else if (entry.value != null) {
target.add(index.index().toSet(false));
}
}
if (pollute) target.add(Instruction.pushUndefined());
}
public VariableDeclareNode(Location loc, List<Pair> values) {
super(loc);
this.values = values;
}
public static ParseRes<VariableDeclareNode> 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()) return declType.chainError();
n += declType.n;
var res = new ArrayList<Pair>();
var end = JavaScript.parseStatementEnd(src, i + n);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new VariableDeclareNode(loc, res), n);
}
while (true) {
var nameLoc = src.loc(i + n);
var name = Parsing.parseIdentifier(src, i + n);
if (!name.isSuccess()) return name.chainError(nameLoc, "Expected a variable name");
n += name.n;
Node val = null;
var endN = n;
n += Parsing.skipEmpty(src, i + n);
if (src.is(i + n, "=")) {
n++;
var valRes = JavaScript.parseExpression(src, i + n, 2);
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after '='");
n += valRes.n;
endN = n;
n += Parsing.skipEmpty(src, i + n);
val = valRes.result;
}
res.add(new Pair(new VariableNode(nameLoc, name.result), val));
if (src.is(i + n, ",")) {
n++;
continue;
}
end = JavaScript.parseStatementEnd(src, i + endN);
if (end.isSuccess()) {
n += end.n + endN - n;
return ParseRes.res(new VariableDeclareNode(loc, res), n);
}
else return end.chainError(src.loc(i + n), "Expected a comma or end of statement");
}
}
}

View File

@@ -1,58 +0,0 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.SyntaxException;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.parsing.ParseRes;
import me.topchetoeu.j2s.common.parsing.Parsing;
import me.topchetoeu.j2s.common.parsing.Source;
import me.topchetoeu.j2s.compilation.CompileResult;
import me.topchetoeu.j2s.compilation.JavaScript;
import me.topchetoeu.j2s.compilation.LabelContext;
import me.topchetoeu.j2s.compilation.Node;
public class BreakNode extends Node {
public final String label;
@Override public void compileFunctions(CompileResult target) {
}
@Override public void compile(CompileResult target, boolean pollute) {
if (!LabelContext.getBreak(target.env).jump(target, label)) {
if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label));
else throw new SyntaxException(loc(), "Illegal break statement");
}
if (pollute) target.add(Instruction.pushUndefined());
}
public BreakNode(Location loc, String label) {
super(loc);
this.label = label;
}
public static ParseRes<BreakNode> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!Parsing.isIdentifier(src, i + n, "break")) return ParseRes.failed();
n += 5;
var end = JavaScript.parseStatementEnd(src, i + n);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new BreakNode(loc, null), n);
}
var label = Parsing.parseIdentifier(src, i + n);
if (label.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a label name or an end of statement");
n += label.n;
end = JavaScript.parseStatementEnd(src, i + n);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new BreakNode(loc, label.result), n);
}
else return end.chainError(src.loc(i + n), "Expected end of statement");
}
}

View File

@@ -1,58 +0,0 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.SyntaxException;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.parsing.ParseRes;
import me.topchetoeu.j2s.common.parsing.Parsing;
import me.topchetoeu.j2s.common.parsing.Source;
import me.topchetoeu.j2s.compilation.CompileResult;
import me.topchetoeu.j2s.compilation.JavaScript;
import me.topchetoeu.j2s.compilation.LabelContext;
import me.topchetoeu.j2s.compilation.Node;
public class ContinueNode extends Node {
public final String label;
@Override public void compileFunctions(CompileResult target) {
}
@Override public void compile(CompileResult target, boolean pollute) {
if (!LabelContext.getCont(target.env).jump(target)) {
if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label));
else throw new SyntaxException(loc(), "Illegal continue statement");
}
if (pollute) target.add(Instruction.pushUndefined());
}
public ContinueNode(Location loc, String label) {
super(loc);
this.label = label;
}
public static ParseRes<ContinueNode> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!Parsing.isIdentifier(src, i + n, "continue")) return ParseRes.failed();
n += 8;
var end = JavaScript.parseStatementEnd(src, i + n);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new ContinueNode(loc, null), n);
}
var label = Parsing.parseIdentifier(src, i + n);
if (label.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a label name or an end of statement");
n += label.n;
end = JavaScript.parseStatementEnd(src, i + n);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new ContinueNode(loc, label.result), n);
}
else return end.chainError(src.loc(i + n), "Expected end of statement");
}
}

View File

@@ -1,40 +0,0 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.parsing.ParseRes;
import me.topchetoeu.j2s.common.parsing.Parsing;
import me.topchetoeu.j2s.common.parsing.Source;
import me.topchetoeu.j2s.compilation.CompileResult;
import me.topchetoeu.j2s.compilation.JavaScript;
import me.topchetoeu.j2s.compilation.Node;
public class DebugNode extends Node {
@Override public void compileFunctions(CompileResult target) {
}
@Override public void compile(CompileResult target, boolean pollute) {
target.add(Instruction.debug());
if (pollute) target.add(Instruction.pushUndefined());
}
public DebugNode(Location loc) {
super(loc);
}
public static ParseRes<DebugNode> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!Parsing.isIdentifier(src, i + n, "debugger")) return ParseRes.failed();
n += 8;
var end = JavaScript.parseStatementEnd(src, i + n);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new DebugNode(loc), n);
}
else return end.chainError(src.loc(i + n), "Expected end of statement");
}
}

View File

@@ -1,57 +0,0 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.parsing.ParseRes;
import me.topchetoeu.j2s.common.parsing.Parsing;
import me.topchetoeu.j2s.common.parsing.Source;
import me.topchetoeu.j2s.compilation.CompileResult;
import me.topchetoeu.j2s.compilation.JavaScript;
import me.topchetoeu.j2s.compilation.Node;
import me.topchetoeu.j2s.compilation.values.VariableNode;
import me.topchetoeu.j2s.compilation.values.constants.BoolNode;
import me.topchetoeu.j2s.compilation.values.operations.IndexNode;
public class DeleteNode extends Node {
public final Node key;
public final Node value;
@Override public void compileFunctions(CompileResult target) {
key.compileFunctions(target);
value.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute) {
value.compile(target, true);
key.compile(target, true);
target.add(Instruction.delete());
if (pollute) target.add(Instruction.pushValue(true));
}
public static ParseRes<? extends Node> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!Parsing.isIdentifier(src, i + n, "delete")) return ParseRes.failed();
n += 6;
var valRes = JavaScript.parseExpression(src, i + n, 15);
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after 'delete'");
n += valRes.n;
if (valRes.result instanceof IndexNode) {
var index = (IndexNode)valRes.result;
return ParseRes.res(new DeleteNode(loc, index.index, index.object), n);
}
else if (valRes.result instanceof VariableNode) {
return ParseRes.error(src.loc(i + n), "A variable may not be deleted");
}
else return ParseRes.res(new BoolNode(loc, true), n);
}
public DeleteNode(Location loc, Node key, Node value) {
super(loc);
this.key = key;
this.value = value;
}
}

View File

@@ -1,88 +0,0 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.parsing.ParseRes;
import me.topchetoeu.j2s.common.parsing.Parsing;
import me.topchetoeu.j2s.common.parsing.Source;
import me.topchetoeu.j2s.compilation.CompileResult;
import me.topchetoeu.j2s.compilation.DeferredIntSupplier;
import me.topchetoeu.j2s.compilation.JavaScript;
import me.topchetoeu.j2s.compilation.LabelContext;
import me.topchetoeu.j2s.compilation.Node;
public class DoWhileNode extends Node {
public final Node condition, body;
public final String label;
@Override public void compileFunctions(CompileResult target) {
condition.compileFunctions(target);
body.compileFunctions(target);
}
@Override public void resolve(CompileResult target) {
body.resolve(target);
}
@Override public void compile(CompileResult target, boolean pollute) {
int start = target.size();
var end = new DeferredIntSupplier();
LabelContext.pushLoop(target.env, loc(), label, end, start);
body.compile(target, false, BreakpointType.STEP_OVER);
condition.compile(target, true, BreakpointType.STEP_OVER);
int endI = target.size();
end.set(endI + 1);
LabelContext.popLoop(target.env, label);
target.add(Instruction.jmpIf(start - endI));
}
public DoWhileNode(Location loc, String label, Node condition, Node body) {
super(loc);
this.label = label;
this.condition = condition;
this.body = body;
}
public static ParseRes<DoWhileNode> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var labelRes = JavaScript.parseLabel(src, i + n);
n += labelRes.n;
if (!Parsing.isIdentifier(src, i + n, "do")) return ParseRes.failed();
n += 2;
var bodyRes = JavaScript.parseStatement(src, i + n);
if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a do-while body.");
n += bodyRes.n;
n += Parsing.skipEmpty(src, i + n);
if (!Parsing.isIdentifier(src, i + n, "while")) return ParseRes.failed();
n += 5;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'while'.");
n++;
var condRes = JavaScript.parseExpression(src, i + n, 0);
if (!condRes.isSuccess()) return condRes.chainError(src.loc(i + n), "Expected a do-while condition.");
n += condRes.n;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after do-while condition.");
n++;
var end = JavaScript.parseStatementEnd(src, i + n);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new DoWhileNode(loc, labelRes.result, condRes.result, bodyRes.result), n);
}
else return end.chainError(src.loc(i + n), "Expected end of statement");
}
}

View File

@@ -1,116 +0,0 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.parsing.ParseRes;
import me.topchetoeu.j2s.common.parsing.Parsing;
import me.topchetoeu.j2s.common.parsing.Source;
import me.topchetoeu.j2s.compilation.CompileResult;
import me.topchetoeu.j2s.compilation.DeferredIntSupplier;
import me.topchetoeu.j2s.compilation.JavaScript;
import me.topchetoeu.j2s.compilation.LabelContext;
import me.topchetoeu.j2s.compilation.Node;
import me.topchetoeu.j2s.compilation.values.VariableNode;
public class ForInNode extends Node {
public final boolean isDecl;
public final VariableNode binding;
public final Node object, body;
public final String label;
@Override public void resolve(CompileResult target) {
body.resolve(target);
if (isDecl) {
target.scope.define(binding.name);
}
}
@Override public void compileFunctions(CompileResult target) {
object.compileFunctions(target);
body.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute) {
object.compile(target, true, BreakpointType.STEP_OVER);
target.add(Instruction.keys(false, true));
int start = target.size();
target.add(Instruction.dup());
int mid = target.temp();
target.add(Instruction.loadMember("value")).setLocation(binding.loc());
target.add(VariableNode.toSet(target, loc(), binding.name, false, true)).setLocation(binding.loc());
target.setLocationAndDebug(object.loc(), BreakpointType.STEP_OVER);
var end = new DeferredIntSupplier();
LabelContext.pushLoop(target.env, loc(), label, end, start);
body.compile(target, false, BreakpointType.STEP_OVER);
int endI = target.size();
target.add(Instruction.jmp(start - endI));
target.add(Instruction.discard());
target.set(mid, Instruction.jmpIfNot(endI - mid + 1));
end.set(endI + 1);
LabelContext.popLoop(target.env, label);
if (pollute) target.add(Instruction.pushUndefined());
}
public ForInNode(Location loc, String label, VariableNode binding, boolean isDecl, Node object, Node body) {
super(loc);
this.label = label;
this.binding = binding;
this.isDecl = isDecl;
this.object = object;
this.body = body;
}
public static ParseRes<ForInNode> 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 varKw = JavaScript.parseDeclarationType(src, i + n);
n += varKw.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);
if (!Parsing.isIdentifier(src, i + n, "in")) return ParseRes.error(src.loc(i + n), "Expected 'in' 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-in body");
n += bodyRes.n;
return ParseRes.res(new ForInNode(loc, label.result, new VariableNode(bindingLoc, name.result), varKw.isSuccess(), obj.result, bodyRes.result), n);
}
}

View File

@@ -1,132 +0,0 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.parsing.ParseRes;
import me.topchetoeu.j2s.common.parsing.Parsing;
import me.topchetoeu.j2s.common.parsing.Source;
import me.topchetoeu.j2s.compilation.CompileResult;
import me.topchetoeu.j2s.compilation.DeferredIntSupplier;
import me.topchetoeu.j2s.compilation.JavaScript;
import me.topchetoeu.j2s.compilation.LabelContext;
import me.topchetoeu.j2s.compilation.Node;
import me.topchetoeu.j2s.compilation.VariableDeclareNode;
public class ForNode extends Node {
public final Node declaration, assignment, condition, body;
public final String label;
@Override public void resolve(CompileResult target) {
if (declaration != null) declaration.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) {
if (declaration != null) declaration.compile(target, false, BreakpointType.STEP_OVER);
var continueTarget = new DeferredIntSupplier();
int start = target.size();
int mid = -1;
if (condition != null) {
condition.compile(target, true, BreakpointType.STEP_OVER);
mid = target.temp();
}
var end = new DeferredIntSupplier();
LabelContext.pushLoop(target.env, loc(), label, end, continueTarget);
body.compile(target, false, BreakpointType.STEP_OVER);
continueTarget.set(target.size());
if (assignment != null) assignment.compile(target, false, BreakpointType.STEP_OVER);
int endI = target.size();
end.set(endI + 1);
LabelContext.popLoop(target.env, label);
target.add(Instruction.jmp(start - endI));
if (condition != null) target.set(mid, Instruction.jmpIfNot(endI - mid + 1));
if (pollute) target.add(Instruction.pushUndefined());
}
public ForNode(Location loc, String label, Node declaration, Node condition, Node assignment, Node body) {
super(loc);
this.label = label;
this.declaration = declaration;
this.condition = condition;
this.assignment = assignment;
this.body = body;
}
private static ParseRes<Node> parseSemicolon(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
if (!src.is(i + n, ";")) return ParseRes.failed();
else return ParseRes.res(null, n + 1);
}
private static ParseRes<Node> parseCondition(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var res = JavaScript.parseExpression(src, i + n, 0);
if (!res.isSuccess()) return res.chainError();
n += res.n;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, ";")) return ParseRes.error(src.loc(i + n), "Expected a semicolon");
else return ParseRes.res(res.result, n + 1);
}
private static ParseRes<? extends Node> parseUpdater(Source src, int i) {
return JavaScript.parseExpression(src, i, 0);
}
public static ParseRes<ForNode> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var labelRes = JavaScript.parseLabel(src, i + n);
n += labelRes.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 a open paren after 'for'");
n++;
ParseRes<Node> decl = ParseRes.first(src, i + n,
ForNode::parseSemicolon,
VariableDeclareNode::parse,
ForNode::parseCondition
);
if (!decl.isSuccess()) return decl.chainError(src.loc(i + n), "Expected a declaration or an expression");
n += decl.n;
ParseRes<Node> cond = ParseRes.first(src, i + n,
ForNode::parseSemicolon,
ForNode::parseCondition
);
if (!cond.isSuccess()) return cond.chainError(src.loc(i + n), "Expected a condition");
n += cond.n;
var update = parseUpdater(src, i + n);
if (update.isError()) return update.chainError();
n += update.n;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a close paren after for updater");
n++;
var body = JavaScript.parseStatement(src, i + n);
if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a for body.");
n += body.n;
return ParseRes.res(new ForNode(loc, labelRes.result, decl.result, cond.result, update.result, body.result), n);
}
}

View File

@@ -1,135 +0,0 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.parsing.ParseRes;
import me.topchetoeu.j2s.common.parsing.Parsing;
import me.topchetoeu.j2s.common.parsing.Source;
import me.topchetoeu.j2s.compilation.CompileResult;
import me.topchetoeu.j2s.compilation.DeferredIntSupplier;
import me.topchetoeu.j2s.compilation.JavaScript;
import me.topchetoeu.j2s.compilation.LabelContext;
import me.topchetoeu.j2s.compilation.Node;
public class IfNode extends Node {
public final Node condition, body, elseBody;
public final String label;
@Override public void resolve(CompileResult target) {
body.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) {
condition.compile(target, true, breakpoint);
if (elseBody == null) {
int start = target.temp();
var end = new DeferredIntSupplier();
LabelContext.getBreak(target.env).push(loc(), label, end);
body.compile(target, pollute, BreakpointType.STEP_OVER);
LabelContext.getBreak(target.env).pop(label);
int endI = target.size();
end.set(endI);
target.set(start, Instruction.jmpIfNot(endI - start));
}
else {
int start = target.temp();
var end = new DeferredIntSupplier();
LabelContext.getBreak(target.env).push(loc(), label, end);
body.compile(target, pollute, BreakpointType.STEP_OVER);
int mid = target.temp();
elseBody.compile(target, pollute, BreakpointType.STEP_OVER);
LabelContext.getBreak(target.env).pop(label);
int endI = target.size();
end.set(endI);
target.set(start, Instruction.jmpIfNot(mid - start + 1));
target.set(mid, Instruction.jmp(endI - mid));
}
}
@Override public void compile(CompileResult target, boolean pollute) {
compile(target, pollute, BreakpointType.STEP_IN);
}
public IfNode(Location loc, Node condition, Node body, Node elseBody, String label) {
super(loc);
this.condition = condition;
this.body = body;
this.elseBody = elseBody;
this.label = label;
}
public static ParseRes<IfNode> parseTernary(Source src, int i, Node prev, int precedence) {
if (precedence > 2) return ParseRes.failed();
var n = Parsing.skipEmpty(src, i);
if (!src.is(i + n, "?")) return ParseRes.failed();
var loc = src.loc(i + n);
n++;
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.");
n += a.n;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, ":")) return ParseRes.failed();
n++;
var b = JavaScript.parseExpression(src, i + n, 2);
if (!b.isSuccess()) return b.chainError(src.loc(i + n), "Expected a second value after the ternary operator.");
n += b.n;
return ParseRes.res(new IfNode(loc, prev, a.result, b.result, null), n);
}
public static ParseRes<IfNode> 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, "if")) return ParseRes.failed();
n += 2;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'if'.");
n++;
var condRes = JavaScript.parseExpression(src, i + n, 0);
if (!condRes.isSuccess()) return condRes.chainError(src.loc(i + n), "Expected an if condition.");
n += condRes.n;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after if condition.");
n++;
var res = JavaScript.parseStatement(src, i + n);
if (!res.isSuccess()) return res.chainError(src.loc(i + n), "Expected an if body.");
n += res.n;
var elseKw = Parsing.parseIdentifier(src, i + n, "else");
if (!elseKw.isSuccess()) return ParseRes.res(new IfNode(loc, condRes.result, res.result, null, label.result), n);
n += elseKw.n;
var elseRes = JavaScript.parseStatement(src, i + n);
if (!elseRes.isSuccess()) return elseRes.chainError(src.loc(i + n), "Expected an else body.");
n += elseRes.n;
return ParseRes.res(new IfNode(loc, condRes.result, res.result, elseRes.result, label.result), n);
}
}

View File

@@ -1,53 +0,0 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.parsing.ParseRes;
import me.topchetoeu.j2s.common.parsing.Parsing;
import me.topchetoeu.j2s.common.parsing.Source;
import me.topchetoeu.j2s.compilation.CompileResult;
import me.topchetoeu.j2s.compilation.JavaScript;
import me.topchetoeu.j2s.compilation.Node;
public class ReturnNode extends Node {
public final Node value;
@Override public void compileFunctions(CompileResult target) {
if (value != null) value.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute) {
if (value == null) target.add(Instruction.pushUndefined());
else value.compile(target, true);
target.add(Instruction.ret()).setLocation(loc());
}
public ReturnNode(Location loc, Node value) {
super(loc);
this.value = value;
}
public static ParseRes<ReturnNode> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!Parsing.isIdentifier(src, i + n, "return")) return ParseRes.failed();
n += 6;
var end = JavaScript.parseStatementEnd(src, i + n);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new ReturnNode(loc, null), n);
}
var val = JavaScript.parseExpression(src, i + n, 0);
if (val.isError()) return val.chainError();
n += val.n;
end = JavaScript.parseStatementEnd(src, i + n);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new ReturnNode(loc, val.result), n);
}
else return end.chainError(src.loc(i + n), "Expected end of statement or a return value");
}
}

View File

@@ -1,198 +0,0 @@
package me.topchetoeu.j2s.compilation.control;
import java.util.ArrayList;
import java.util.HashMap;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Operation;
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.parsing.ParseRes;
import me.topchetoeu.j2s.common.parsing.Parsing;
import me.topchetoeu.j2s.common.parsing.Source;
import me.topchetoeu.j2s.compilation.CompileResult;
import me.topchetoeu.j2s.compilation.DeferredIntSupplier;
import me.topchetoeu.j2s.compilation.JavaScript;
import me.topchetoeu.j2s.compilation.LabelContext;
import me.topchetoeu.j2s.compilation.Node;
public class SwitchNode extends Node {
public static class SwitchCase {
public final Node value;
public final int statementI;
public SwitchCase(Node value, int statementI) {
this.value = value;
this.statementI = statementI;
}
}
public final Node value;
public final SwitchCase[] cases;
public final Node[] body;
public final int defaultI;
public final String label;
@Override public void resolve(CompileResult 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) {
var caseToStatement = new HashMap<Integer, Integer>();
var statementToIndex = new HashMap<Integer, Integer>();
value.compile(target, true, BreakpointType.STEP_OVER);
// TODO: create a jump map
for (var ccase : cases) {
target.add(Instruction.dup());
ccase.value.compile(target, true);
target.add(Instruction.operation(Operation.EQUALS));
caseToStatement.put(target.temp(), ccase.statementI);
}
int start = target.temp();
var end = new DeferredIntSupplier();
LabelContext.getBreak(target.env).pushLoop(loc(), label, end);
for (var stm : body) {
statementToIndex.put(statementToIndex.size(), target.size());
stm.compile(target, false, BreakpointType.STEP_OVER);
}
int endI = target.size();
end.set(endI);
LabelContext.getBreak(target.env).popLoop(label);
target.add(Instruction.discard());
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()) {
var i = statementToIndex.get(el.getValue());
if (i == null) i = endI;
target.set(el.getKey(), Instruction.jmpIf(i - el.getKey()));
}
}
public SwitchNode(Location loc, String label, Node value, int defaultI, SwitchCase[] cases, Node[] body) {
super(loc);
this.label = label;
this.value = value;
this.defaultI = defaultI;
this.cases = cases;
this.body = body;
}
private static ParseRes<Node> parseSwitchCase(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
if (!Parsing.isIdentifier(src, i + n, "case")) return ParseRes.failed();
n += 4;
var val = JavaScript.parseExpression(src, i + n, 0);
if (!val.isSuccess()) return val.chainError(src.loc(i + n), "Expected a value after 'case'");
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");
n++;
return ParseRes.res(val.result, n);
}
private static ParseRes<Void> parseDefaultCase(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
if (!Parsing.isIdentifier(src, i + n, "default")) return ParseRes.failed();
n += 7;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, ":")) return ParseRes.error(src.loc(i + n), "Expected colons after 'default'");
n++;
return ParseRes.res(null, n);
}
public static ParseRes<SwitchNode> 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, "switch")) return ParseRes.failed();
n += 6;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'switch'");
n++;
var val = JavaScript.parseExpression(src, i + n, 0);
if (!val.isSuccess()) return val.chainError(src.loc(i + n), "Expected a switch value");
n += val.n;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after switch value");
n++;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, "{")) return ParseRes.error(src.loc(i + n), "Expected an opening brace after switch value");
n++;
n += Parsing.skipEmpty(src, i + n);
var statements = new ArrayList<Node>();
var cases = new ArrayList<SwitchCase>();
var defaultI = -1;
while (true) {
n += Parsing.skipEmpty(src, i + n);
if (src.is(i + n, "}")) {
n++;
break;
}
if (src.is(i + n, ";")) {
n++;
continue;
}
ParseRes<Node> caseRes = ParseRes.first(src, i + n,
SwitchNode::parseDefaultCase,
SwitchNode::parseSwitchCase
);
if (caseRes.isSuccess()) {
n += caseRes.n;
if (caseRes.result == null) defaultI = statements.size();
else cases.add(new SwitchCase(caseRes.result, statements.size()));
continue;
}
if (caseRes.isError()) return caseRes.chainError();
var stm = JavaScript.parseStatement(src, i + n);
if (stm.isSuccess()) {
n += stm.n;
statements.add(stm.result);
continue;
}
else stm.chainError(src.loc(i + n), "Expected a statement, 'case' or 'default'");
}
return ParseRes.res(new SwitchNode(
loc, label.result, val.result, defaultI,
cases.toArray(new SwitchCase[0]),
statements.toArray(new Node[0])
), n);
}
}

View File

@@ -1,46 +0,0 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.parsing.ParseRes;
import me.topchetoeu.j2s.common.parsing.Parsing;
import me.topchetoeu.j2s.common.parsing.Source;
import me.topchetoeu.j2s.compilation.CompileResult;
import me.topchetoeu.j2s.compilation.JavaScript;
import me.topchetoeu.j2s.compilation.Node;
public class ThrowNode extends Node {
public final Node value;
@Override public void compileFunctions(CompileResult target) {
value.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute) {
value.compile(target, true);
target.add(Instruction.throwInstr()).setLocation(loc());
}
public ThrowNode(Location loc, Node value) {
super(loc);
this.value = value;
}
public static ParseRes<ThrowNode> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!Parsing.isIdentifier(src, i + n, "throw")) return ParseRes.failed();
n += 5;
var val = JavaScript.parseExpression(src, i + n, 0);
if (val.isFailed()) return ParseRes.error(src.loc(i + n), "Expected a throw value");
n += val.n;
var end = JavaScript.parseStatementEnd(src, i + n);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new ThrowNode(loc, val.result), n);
}
else return end.chainError(src.loc(i + n), "Expected end of statement");
}
}

View File

@@ -1,148 +0,0 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.parsing.ParseRes;
import me.topchetoeu.j2s.common.parsing.Parsing;
import me.topchetoeu.j2s.common.parsing.Source;
import me.topchetoeu.j2s.compilation.CompileResult;
import me.topchetoeu.j2s.compilation.CompoundNode;
import me.topchetoeu.j2s.compilation.DeferredIntSupplier;
import me.topchetoeu.j2s.compilation.JavaScript;
import me.topchetoeu.j2s.compilation.LabelContext;
import me.topchetoeu.j2s.compilation.Node;
public class TryNode extends Node {
public final CompoundNode tryBody;
public final CompoundNode catchBody;
public final CompoundNode finallyBody;
public final String captureName;
public final String label;
@Override public void resolve(CompileResult target) {
tryBody.resolve(target);
if (catchBody != null) catchBody.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) {
int replace = target.temp();
var endSuppl = new DeferredIntSupplier();
int start = replace + 1, catchStart = -1, finallyStart = -1;
LabelContext.getBreak(target.env).push(loc(), label, endSuppl);
tryBody.compile(target, false);
target.add(Instruction.tryEnd());
if (catchBody != null) {
catchStart = target.size() - start;
if (captureName != null) {
var catchVar = target.catchBindings.get(this);
target.scope.defineCatch(captureName, catchVar);
target.add(Instruction.loadError()).setLocation(catchBody.loc());
target.add(catchVar.index().toSet(false)).setLocation(catchBody.loc());
catchBody.compile(target, false);
target.scope.undefineCatch();
}
else catchBody.compile(target, false);
target.add(Instruction.tryEnd());
}
if (finallyBody != null) {
finallyStart = target.size() - start;
finallyBody.compile(target, false);
target.add(Instruction.tryEnd());
}
LabelContext.getBreak(target.env).pop(label);
endSuppl.set(target.size());
target.set(replace, Instruction.tryStart(catchStart, finallyStart, target.size() - start));
target.setLocationAndDebug(replace, loc(), BreakpointType.STEP_OVER);
if (pollute) target.add(Instruction.pushUndefined());
}
public TryNode(Location loc, String label, CompoundNode tryBody, CompoundNode catchBody, CompoundNode finallyBody, String captureName) {
super(loc);
this.tryBody = tryBody;
this.catchBody = catchBody;
this.finallyBody = finallyBody;
this.captureName = captureName;
this.label = label;
}
public static ParseRes<TryNode> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var labelRes = JavaScript.parseLabel(src, i + n);
n += labelRes.n;
n += Parsing.skipEmpty(src, i + n);
if (!Parsing.isIdentifier(src, i + n, "try")) return ParseRes.failed();
n += 3;
var tryBody = CompoundNode.parse(src, i + n);
if (!tryBody.isSuccess()) return tryBody.chainError(src.loc(i + n), "Expected a try body");
n += tryBody.n;
n += Parsing.skipEmpty(src, i + n);
String capture = null;
CompoundNode catchBody = null, finallyBody = null;
if (Parsing.isIdentifier(src, i + n, "catch")) {
n += 5;
n += Parsing.skipEmpty(src, i + n);
if (src.is(i + n, "(")) {
n++;
var nameRes = Parsing.parseIdentifier(src, i + n);
if (!nameRes.isSuccess()) return nameRes.chainError(src.loc(i + n), "xpected a catch variable name");
capture = nameRes.result;
n += nameRes.n;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after catch variable name");
n++;
}
var bodyRes = CompoundNode.parse(src, i + n);
if (!bodyRes.isSuccess()) return tryBody.chainError(src.loc(i + n), "Expected a catch body");
n += bodyRes.n;
n += Parsing.skipEmpty(src, i + n);
catchBody = bodyRes.result;
}
if (Parsing.isIdentifier(src, i + n, "finally")) {
n += 7;
var bodyRes = CompoundNode.parse(src, i + n);
if (!bodyRes.isSuccess()) return tryBody.chainError(src.loc(i + n), "Expected a finally body");
n += bodyRes.n;
n += Parsing.skipEmpty(src, i + n);
finallyBody = bodyRes.result;
}
if (finallyBody == null && catchBody == null) ParseRes.error(src.loc(i + n), "Expected catch or finally");
return ParseRes.res(new TryNode(loc, labelRes.result, tryBody.result, catchBody, finallyBody, capture), n);
}
}

View File

@@ -1,82 +0,0 @@
package me.topchetoeu.j2s.compilation.control;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.parsing.ParseRes;
import me.topchetoeu.j2s.common.parsing.Parsing;
import me.topchetoeu.j2s.common.parsing.Source;
import me.topchetoeu.j2s.compilation.CompileResult;
import me.topchetoeu.j2s.compilation.DeferredIntSupplier;
import me.topchetoeu.j2s.compilation.JavaScript;
import me.topchetoeu.j2s.compilation.LabelContext;
import me.topchetoeu.j2s.compilation.Node;
public class WhileNode extends Node {
public final Node condition, body;
public final String label;
@Override public void resolve(CompileResult target) {
body.resolve(target);
}
@Override public void compileFunctions(CompileResult target) {
condition.compileFunctions(target);
body.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute) {
int start = target.size();
condition.compile(target, true);
int mid = target.temp();
var end = new DeferredIntSupplier();
LabelContext.pushLoop(target.env, loc(), label, end, start);
body.compile(target, false, BreakpointType.STEP_OVER);
var endI = target.size();
end.set(endI + 1);
LabelContext.popLoop(target.env, label);
target.add(Instruction.jmp(start - endI));
target.set(mid, Instruction.jmpIfNot(endI - mid + 1));
if (pollute) target.add(Instruction.pushUndefined());
}
public WhileNode(Location loc, String label, Node condition, Node body) {
super(loc);
this.label = label;
this.condition = condition;
this.body = body;
}
public static ParseRes<WhileNode> 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, "while")) return ParseRes.failed();
n += 5;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected a open paren after 'while'.");
n++;
var cond = JavaScript.parseExpression(src, i + n, 0);
if (!cond.isSuccess()) return cond.chainError(src.loc(i + n), "Expected a while condition.");
n += cond.n;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren after while condition.");
n++;
var body = JavaScript.parseStatement(src, i + n);
if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a while body.");
n += body.n;
return ParseRes.res(new WhileNode(loc, label.result, cond.result, body.result), n);
}
}

View File

@@ -1,59 +0,0 @@
package me.topchetoeu.j2s.compilation.members;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.parsing.ParseRes;
import me.topchetoeu.j2s.common.parsing.Parsing;
import me.topchetoeu.j2s.common.parsing.Source;
import me.topchetoeu.j2s.compilation.CompileResult;
import me.topchetoeu.j2s.compilation.JavaScript;
import me.topchetoeu.j2s.compilation.Node;
import me.topchetoeu.j2s.compilation.values.ObjectNode;
public class FieldMemberNode implements Member {
public final Location loc;
public final Node key;
public final Node value;
@Override public Location loc() { return loc; }
@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());
key.compile(target, true);
if (value == null) target.add(Instruction.pushUndefined());
else value.compile(target, true);
target.add(Instruction.storeMember());
}
public FieldMemberNode(Location loc, Node key, Node value) {
this.loc = loc;
this.key = key;
this.value = value;
}
public static ParseRes<FieldMemberNode> 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;
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 value");
n += value.n;
return ParseRes.res(new FieldMemberNode(loc, name.result, value.result), n);
}
}

View File

@@ -1,11 +0,0 @@
package me.topchetoeu.j2s.compilation.members;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.compilation.CompileResult;
public interface Member {
Location loc();
void compileFunctions(CompileResult target);
void compile(CompileResult target, boolean pollute);
}

View File

@@ -1,83 +0,0 @@
package me.topchetoeu.j2s.compilation.members;
import java.util.Arrays;
import me.topchetoeu.j2s.common.Instruction;
import me.topchetoeu.j2s.common.Instruction.BreakpointType;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.common.parsing.ParseRes;
import me.topchetoeu.j2s.common.parsing.Parsing;
import me.topchetoeu.j2s.common.parsing.Source;
import me.topchetoeu.j2s.compilation.CompileResult;
import me.topchetoeu.j2s.compilation.CompoundNode;
import me.topchetoeu.j2s.compilation.FunctionNode;
import me.topchetoeu.j2s.compilation.JavaScript;
import me.topchetoeu.j2s.compilation.Node;
import me.topchetoeu.j2s.compilation.values.ObjectNode;
import me.topchetoeu.j2s.compilation.values.VariableNode;
import me.topchetoeu.j2s.compilation.values.constants.StringNode;
public final class PropertyMemberNode extends FunctionNode implements Member {
public final Node key;
public final VariableNode argument;
@Override public String name() {
if (key instanceof StringNode str) {
if (isGetter()) return "get " + str.value;
else return "set " + str.value;
}
else return null;
}
public boolean isGetter() { 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) {
if (pollute) target.add(Instruction.dup());
key.compile(target, true);
target.add(Instruction.loadFunc(target.childrenIndices.get(this), name(name), captures(target))).setLocation(loc());
target.add(Instruction.defProp(isSetter()));
}
public PropertyMemberNode(Location loc, Location end, Node key, VariableNode argument, CompoundNode body) {
super(loc, end, argument == null ? Arrays.asList() : Arrays.asList(argument), body);
this.key = key;
this.argument = argument;
}
public static ParseRes<PropertyMemberNode> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
var access = Parsing.parseIdentifier(src, i + n);
if (!access.isSuccess()) return ParseRes.failed();
if (!access.result.equals("get") && !access.result.equals("set")) return ParseRes.failed();
n += access.n;
var name = ObjectNode.parsePropName(src, i + n);
if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected a property name after '" + access + "'");
n += name.n;
var params = JavaScript.parseParameters(src, i + n);
if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected an argument list");
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.size() != 1) return ParseRes.error(src.loc(i + n), "Setter must have exactly one parameter");
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 PropertyMemberNode(
loc, end, name.result, access.result.equals("get") ? null : params.result.get(0), body.result
), n);
}
}

View File

@@ -1,27 +0,0 @@
package me.topchetoeu.j2s.compilation.patterns;
import me.topchetoeu.j2s.common.parsing.Location;
import me.topchetoeu.j2s.compilation.CompileResult;
/**
* Represents all nodes that can be assign targets
*/
public interface AssignTarget extends AssignTargetLike {
Location loc();
/**
* Called to perform calculations before the assigned value is calculated
*/
default void beforeAssign(CompileResult target) {}
/**
* Called to perform the actual assignemnt. Between the `beforeAssign` and this call a single value will have been pushed to the stack
* @param pollute Whether or not to leave the original value on the stack
*/
void afterAssign(CompileResult target, boolean pollute);
default void assign(CompileResult target, boolean pollute) {
afterAssign(target, pollute);
}
@Override default AssignTarget toAssignTarget() { return this; }
}

View File

@@ -1,8 +0,0 @@
package me.topchetoeu.j2s.compilation.patterns;
/**
* Represents all nodes that can be converted to assign targets
*/
public interface AssignTargetLike {
AssignTarget toAssignTarget();
}

View File

@@ -1,7 +0,0 @@
package me.topchetoeu.j2s.compilation.patterns;
import me.topchetoeu.j2s.compilation.CompileResult;
public interface ChangeTarget extends AssignTarget {
void beforeChange(CompileResult target);
}

Some files were not shown because too many files have changed in this diff Show More