Module support #11
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
@ -0,0 +1 @@
|
||||
* -text
|
27
.gitignore
vendored
27
.gitignore
vendored
@ -1,11 +1,18 @@
|
||||
.vscode
|
||||
.gradle
|
||||
.ignore
|
||||
/out
|
||||
/build
|
||||
/bin
|
||||
/dst
|
||||
/*.js
|
||||
*
|
||||
|
||||
!/src
|
||||
!/src/**/*
|
||||
|
||||
/src/assets/js/ts.js
|
||||
|
||||
!/tests
|
||||
!/tests/**/*
|
||||
|
||||
!/.github
|
||||
!/.github/**/*
|
||||
|
||||
!/.gitignore
|
||||
!/.gitattributes
|
||||
!/build.js
|
||||
/dead-code
|
||||
/Metadata.java
|
||||
!/LICENSE
|
||||
!/README.md
|
||||
|
44
README.md
44
README.md
@ -2,44 +2,28 @@
|
||||
|
||||
**NOTE: This had nothing to do with Microsoft's dialect of EcmaScript**
|
||||
|
||||
**WARNING: Currently, this code is mostly undocumented. Proceed with caution and a psychiatrist.**
|
||||
**WARNING: Currently, this code is undocumented. Proceed with caution and a psychiatrist.**
|
||||
|
||||
JScript is an engine, capable of running EcmaScript 5, written entirely in Java. This engine has been developed with the goal of being easy to integrate with your preexisting codebase, **THE GOAL OF THIS ENGINE IS NOT PERFORMANCE**. My crude experiments show that this engine is 50x-100x slower than V8, which, although bad, is acceptable for most simple scripting purposes. Note that although the codebase has a Main class, this isn't meant to be a standalone program, but instead a library for running JavaScript code.
|
||||
|
||||
## Example
|
||||
|
||||
The following will create a REPL using the engine as a backend. Not that this won't properly log errors. I recommend checking out the implementation in `Main.main`:
|
||||
The following is going to execute a simple javascript statement:
|
||||
|
||||
```java
|
||||
var engine = new Engine(true /* false if you dont want debugging */);
|
||||
var env = new Environment(null, null, null);
|
||||
var debugger = new DebugServer();
|
||||
|
||||
// Create one target for the engine and start debugging server
|
||||
debugger.targets.put("target", (socket, req) -> new SimpleDebugger(socket, engine));
|
||||
debugger.start(new InetSocketAddress("127.0.0.1", 9229), true);
|
||||
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());
|
||||
|
||||
// Queue code to load internal libraries and start engine
|
||||
engine.pushMsg(false, null, new Internals().getApplier(env));
|
||||
engine.start();
|
||||
var awaitable = engine.pushMsg(false, env, new Filename("tmp", "eval"), "10 + Math.sqrt(5 / 3)", null);
|
||||
// Run the engine on the same thread, until the event loop runs empty
|
||||
engine.run(true);
|
||||
|
||||
while (true) {
|
||||
try {
|
||||
var raw = Reading.read();
|
||||
if (raw == null) break;
|
||||
|
||||
// Push a message to the engine with the raw REPL code
|
||||
var res = engine.pushMsg(
|
||||
false, new Context(engine).pushEnv(env),
|
||||
new Filename("jscript", "repl.js"), raw, null
|
||||
).await();
|
||||
|
||||
Values.printValue(null, res);
|
||||
}
|
||||
catch (EngineException e) { Values.printError(e, ""); }
|
||||
catch (SyntaxException ex) {
|
||||
System.out.println("Syntax error:" + ex.msg);
|
||||
}
|
||||
catch (IOException e) { }
|
||||
}
|
||||
// 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.
|
||||
|
126
build.js
126
build.js
@ -2,16 +2,8 @@ const { spawn } = require('child_process');
|
||||
const fs = require('fs/promises');
|
||||
const pt = require('path');
|
||||
const { argv, exit } = require('process');
|
||||
const { Readable } = require('stream');
|
||||
|
||||
const conf = {
|
||||
name: "java-jscript",
|
||||
author: "TopchetoEU",
|
||||
javahome: "",
|
||||
version: argv[3]
|
||||
};
|
||||
|
||||
if (conf.version.startsWith('refs/tags/')) conf.version = conf.version.substring(10);
|
||||
if (conf.version.startsWith('v')) conf.version = conf.version.substring(1);
|
||||
|
||||
async function* find(src, dst, wildcard) {
|
||||
const stat = await fs.stat(src);
|
||||
@ -36,9 +28,9 @@ async function copy(src, dst, wildcard) {
|
||||
await Promise.all(promises);
|
||||
}
|
||||
|
||||
function run(cmd, ...args) {
|
||||
function run(suppressOutput, cmd, ...args) {
|
||||
return new Promise((res, rej) => {
|
||||
const proc = spawn(cmd, args, { stdio: 'inherit' });
|
||||
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}.`));
|
||||
@ -46,7 +38,84 @@ function run(cmd, ...args) {
|
||||
})
|
||||
}
|
||||
|
||||
async function compileJava() {
|
||||
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 = { 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/Metadata.java')).toString()
|
||||
.replace('${VERSION}', conf.version)
|
||||
@ -57,8 +126,10 @@ async function compileJava() {
|
||||
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(conf.javahome + 'javac', ...args);
|
||||
await run(false, conf.javahome + 'javac', ...args);
|
||||
console.log('Compiled java project!');
|
||||
}
|
||||
finally {
|
||||
await fs.rm('Metadata.java');
|
||||
@ -67,10 +138,31 @@ async function compileJava() {
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
try { await fs.rm('dst', { recursive: true }); } catch {}
|
||||
await copy('src', 'dst/classes', v => !v.endsWith('.java'));
|
||||
await compileJava();
|
||||
await run('jar', '-c', '-f', 'dst/jscript.jar', '-e', 'me.topchetoeu.jscript.Main', '-C', 'dst/classes', '.');
|
||||
if (argv[2] === 'init-ts') {
|
||||
await downloadTypescript('src/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([
|
||||
downloadTypescript('dst/classes/assets/js/ts.js'),
|
||||
copy('src', 'dst/classes', v => !v.endsWith('.java')),
|
||||
compileJava(conf),
|
||||
]);
|
||||
|
||||
await run(true, 'jar', '-c', '-f', 'dst/jscript.jar', '-e', 'me.topchetoeu.jscript.Main', '-C', 'dst/classes', '.');
|
||||
console.log('Done!');
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
if (argv[2] === 'debug') throw e;
|
||||
|
4
src/assets/js/bootstrap.js
vendored
4
src/assets/js/bootstrap.js
vendored
@ -63,7 +63,7 @@
|
||||
version++;
|
||||
|
||||
if (!environments[env.id]) environments[env.id] = []
|
||||
declSnapshots = environments[env.id];
|
||||
var decls = declSnapshots = environments[env.id];
|
||||
var emit = service.getEmitOutput("/src.ts");
|
||||
|
||||
var diagnostics = []
|
||||
@ -94,7 +94,7 @@
|
||||
return {
|
||||
function: function () {
|
||||
var val = compiled.function.apply(this, arguments);
|
||||
if (declaration !== '') declSnapshots.push(ts.ScriptSnapshot.fromString(declaration));
|
||||
if (declaration !== '') decls.push(ts.ScriptSnapshot.fromString(declaration));
|
||||
return val;
|
||||
},
|
||||
breakpoints: compiled.breakpoints,
|
||||
|
13
src/assets/js/lib.d.ts
vendored
13
src/assets/js/lib.d.ts
vendored
@ -105,7 +105,7 @@ interface AsyncIterableIterator<T> extends AsyncIterator<T> {
|
||||
[Symbol.asyncIterator](): AsyncIterableIterator<T>;
|
||||
}
|
||||
|
||||
interface Generator<T = unknown, TReturn = unknown, TNext = unknown> extends Iterator<T, TReturn, TNext> {
|
||||
interface Generator<T = unknown, TReturn = void, TNext = unknown> extends Iterator<T, TReturn, TNext> {
|
||||
[Symbol.iterator](): Generator<T, TReturn, TNext>;
|
||||
return(value: TReturn): IteratorResult<T, TReturn>;
|
||||
throw(e: any): IteratorResult<T, TReturn>;
|
||||
@ -118,7 +118,7 @@ interface GeneratorFunction {
|
||||
readonly prototype: Generator;
|
||||
}
|
||||
|
||||
interface AsyncGenerator<T = unknown, TReturn = unknown, TNext = unknown> extends AsyncIterator<T, TReturn, TNext> {
|
||||
interface AsyncGenerator<T = unknown, TReturn = void, TNext = unknown> extends AsyncIterator<T, TReturn, TNext> {
|
||||
return(value: TReturn | Thenable<TReturn>): Promise<IteratorResult<T, TReturn>>;
|
||||
throw(e: any): Promise<IteratorResult<T, TReturn>>;
|
||||
[Symbol.asyncIterator](): AsyncGenerator<T, TReturn, TNext>;
|
||||
@ -488,14 +488,17 @@ interface FileStat {
|
||||
interface File {
|
||||
readonly pointer: Promise<number>;
|
||||
readonly length: Promise<number>;
|
||||
readonly mode: Promise<'' | 'r' | 'rw'>;
|
||||
|
||||
read(n: number): Promise<number[]>;
|
||||
write(buff: number[]): Promise<void>;
|
||||
close(): Promise<void>;
|
||||
setPointer(val: number): Promise<void>;
|
||||
seek(offset: number, whence: number): Promise<void>;
|
||||
}
|
||||
interface Filesystem {
|
||||
readonly SEEK_SET: 0;
|
||||
readonly SEEK_CUR: 1;
|
||||
readonly SEEK_END: 2;
|
||||
|
||||
open(path: string, mode: 'r' | 'rw'): Promise<File>;
|
||||
ls(path: string): AsyncIterableIterator<string>;
|
||||
mkdir(path: string): Promise<void>;
|
||||
@ -503,6 +506,7 @@ interface Filesystem {
|
||||
rm(path: string, recursive?: boolean): Promise<void>;
|
||||
stat(path: string): Promise<FileStat>;
|
||||
exists(path: string): Promise<boolean>;
|
||||
normalize(...paths: string[]): string;
|
||||
}
|
||||
|
||||
interface Encoding {
|
||||
@ -526,6 +530,7 @@ declare var parseInt: typeof Number.parseInt;
|
||||
declare var parseFloat: typeof Number.parseFloat;
|
||||
|
||||
declare function log(...vals: any[]): void;
|
||||
declare function require(name: string): any;
|
||||
|
||||
declare var Array: ArrayConstructor;
|
||||
declare var Boolean: BooleanConstructor;
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,6 +1,7 @@
|
||||
package me.topchetoeu.jscript;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class Filename {
|
||||
public final String protocol;
|
||||
@ -40,9 +41,7 @@ public class Filename {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Filename fromFile(File file) {
|
||||
return new Filename("file", file.getAbsolutePath());
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Filename(String protocol, String path) {
|
||||
@ -57,4 +56,10 @@ public class Filename {
|
||||
if (i >= 0) return new Filename(val.substring(0, i).trim(), val.substring(i + 3).trim());
|
||||
else return new Filename("file", val.trim());
|
||||
}
|
||||
public static Path normalize(String path) {
|
||||
return Path.of(Path.of("/" + path.trim().replace("\\", "/")).normalize().toString().substring(1));
|
||||
}
|
||||
public static Filename fromFile(File file) {
|
||||
return new Filename("file", file.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import me.topchetoeu.jscript.filesystem.MemoryFilesystem;
|
||||
import me.topchetoeu.jscript.filesystem.Mode;
|
||||
import me.topchetoeu.jscript.filesystem.PhysicalFilesystem;
|
||||
import me.topchetoeu.jscript.lib.Internals;
|
||||
import me.topchetoeu.jscript.modules.ModuleRepo;
|
||||
|
||||
public class Main {
|
||||
public static class Printer implements Observer<Object> {
|
||||
@ -57,7 +58,7 @@ public class Main {
|
||||
var file = Path.of(arg);
|
||||
var raw = Files.readString(file);
|
||||
var res = engine.pushMsg(
|
||||
false, new Context(engine, environment),
|
||||
false, environment,
|
||||
Filename.fromFile(file.toFile()),
|
||||
raw, null
|
||||
).await();
|
||||
@ -73,7 +74,7 @@ public class Main {
|
||||
|
||||
if (raw == null) break;
|
||||
var res = engine.pushMsg(
|
||||
false, new Context(engine, environment),
|
||||
false, environment,
|
||||
new Filename("jscript", "repl/" + i + ".js"),
|
||||
raw, null
|
||||
).await();
|
||||
@ -119,7 +120,8 @@ public class Main {
|
||||
}));
|
||||
|
||||
environment.filesystem.protocols.put("temp", new MemoryFilesystem(Mode.READ_WRITE));
|
||||
environment.filesystem.protocols.put("file", new PhysicalFilesystem(Path.of(".").toAbsolutePath()));
|
||||
environment.filesystem.protocols.put("file", new PhysicalFilesystem("."));
|
||||
environment.modules.repos.put("file", ModuleRepo.ofFilesystem(environment.filesystem));
|
||||
}
|
||||
private static void initEngine() {
|
||||
debugServer.targets.put("target", (ws, req) -> new SimpleDebugger(ws, engine));
|
||||
@ -135,18 +137,16 @@ public class Main {
|
||||
bsEnv.stackVisible = false;
|
||||
|
||||
engine.pushMsg(
|
||||
false, new Context(engine, tsEnv),
|
||||
false, tsEnv,
|
||||
new Filename("jscript", "ts.js"),
|
||||
Reading.resourceToString("js/ts.js"), null
|
||||
).await();
|
||||
System.out.println("Loaded typescript!");
|
||||
|
||||
var ctx = new Context(engine, bsEnv);
|
||||
|
||||
engine.pushMsg(
|
||||
false, ctx,
|
||||
false, bsEnv,
|
||||
new Filename("jscript", "bootstrap.js"), Reading.resourceToString("js/bootstrap.js"), null,
|
||||
tsEnv.global.get(ctx, "ts"), environment, new ArrayValue(null, Reading.resourceToString("js/lib.d.ts"))
|
||||
tsEnv.global.get(new Context(engine, bsEnv), "ts"), environment, new ArrayValue(null, Reading.resourceToString("js/lib.d.ts"))
|
||||
).await();
|
||||
}
|
||||
catch (EngineException e) {
|
||||
|
@ -26,11 +26,11 @@ public class Context {
|
||||
return env.empty() ? null : env.peek();
|
||||
}
|
||||
|
||||
public Context pushEnv(Environment env) {
|
||||
private Context pushEnv(Environment env) {
|
||||
this.env.push(env);
|
||||
return this;
|
||||
}
|
||||
public void popEnv() {
|
||||
private void popEnv() {
|
||||
if (!env.empty()) this.env.pop();
|
||||
}
|
||||
|
||||
@ -119,7 +119,6 @@ public class Context {
|
||||
}
|
||||
public Context(Engine engine, Environment env) {
|
||||
this(engine);
|
||||
this.pushEnv(env);
|
||||
|
||||
if (env != null) this.pushEnv(env);
|
||||
}
|
||||
}
|
||||
|
@ -141,13 +141,13 @@ public class Engine implements DebugController {
|
||||
return this.thread != null;
|
||||
}
|
||||
|
||||
public Awaitable<Object> pushMsg(boolean micro, Context ctx, FunctionValue func, Object thisArg, Object ...args) {
|
||||
var msg = new Task(ctx == null ? new Context(this) : ctx, func, thisArg, args, micro);
|
||||
public Awaitable<Object> pushMsg(boolean micro, Environment env, FunctionValue func, Object thisArg, Object ...args) {
|
||||
var msg = new Task(new Context(this, env), func, thisArg, args, micro);
|
||||
tasks.add(msg);
|
||||
return msg.notifier;
|
||||
}
|
||||
public Awaitable<Object> pushMsg(boolean micro, Context ctx, Filename filename, String raw, Object thisArg, Object ...args) {
|
||||
return pushMsg(micro, ctx, new UncompiledFunction(filename, raw), thisArg, args);
|
||||
public Awaitable<Object> pushMsg(boolean micro, Environment env, Filename filename, String raw, Object thisArg, Object ...args) {
|
||||
return pushMsg(micro, env, new UncompiledFunction(filename, raw), thisArg, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -18,10 +18,12 @@ import me.topchetoeu.jscript.interop.Native;
|
||||
import me.topchetoeu.jscript.interop.NativeGetter;
|
||||
import me.topchetoeu.jscript.interop.NativeSetter;
|
||||
import me.topchetoeu.jscript.interop.NativeWrapperProvider;
|
||||
import me.topchetoeu.jscript.modules.RootModuleRepo;
|
||||
import me.topchetoeu.jscript.parsing.Parsing;
|
||||
import me.topchetoeu.jscript.permissions.Permission;
|
||||
import me.topchetoeu.jscript.permissions.PermissionsProvider;
|
||||
|
||||
// TODO: Remove hardcoded extensions form environment
|
||||
public class Environment implements PermissionsProvider {
|
||||
private HashMap<String, ObjectValue> prototypes = new HashMap<>();
|
||||
|
||||
@ -30,8 +32,11 @@ public class Environment implements PermissionsProvider {
|
||||
|
||||
public GlobalScope global;
|
||||
public WrappersProvider wrappers;
|
||||
|
||||
public PermissionsProvider permissions = null;
|
||||
public final RootFilesystem filesystem = new RootFilesystem(this);
|
||||
public final RootModuleRepo modules = new RootModuleRepo();
|
||||
public String moduleCwd = "/";
|
||||
|
||||
private static int nextId = 0;
|
||||
|
||||
@ -53,7 +58,6 @@ public class Environment implements PermissionsProvider {
|
||||
res.defineProperty(ctx, "function", target.func(env));
|
||||
res.defineProperty(ctx, "mapChain", new ArrayValue());
|
||||
|
||||
|
||||
if (isDebug) {
|
||||
res.defineProperty(ctx, "breakpoints", ArrayValue.of(ctx, target.breakpoints.stream().map(Location::toString).collect(Collectors.toList())));
|
||||
}
|
||||
@ -64,11 +68,6 @@ public class Environment implements PermissionsProvider {
|
||||
throw EngineException.ofError("Regular expressions not supported.").setCtx(ctx.environment(), ctx.engine);
|
||||
});
|
||||
|
||||
public Environment addData(Data data) {
|
||||
this.data.addAll(data);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Native public ObjectValue proto(String name) {
|
||||
return prototypes.get(name);
|
||||
}
|
||||
@ -97,6 +96,9 @@ public class Environment implements PermissionsProvider {
|
||||
@Native public Environment child() {
|
||||
var res = fork();
|
||||
res.global = res.global.globalChild();
|
||||
res.permissions = this.permissions;
|
||||
res.filesystem.protocols.putAll(this.filesystem.protocols);
|
||||
res.modules.repos.putAll(this.modules.repos);
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -108,7 +110,7 @@ public class Environment implements PermissionsProvider {
|
||||
}
|
||||
|
||||
public Context context(Engine engine) {
|
||||
return new Context(engine).pushEnv(this);
|
||||
return new Context(engine, this);
|
||||
}
|
||||
|
||||
public static Symbol getSymbol(String name) {
|
||||
@ -128,4 +130,7 @@ public class Environment implements PermissionsProvider {
|
||||
this.wrappers = nativeConverter;
|
||||
this.global = global;
|
||||
}
|
||||
public Environment() {
|
||||
this(null, null, null);
|
||||
}
|
||||
}
|
||||
|
@ -482,8 +482,8 @@ public class SimpleDebugger implements Debugger {
|
||||
|
||||
env.global = new GlobalScope(codeFrame.local);
|
||||
|
||||
var ctx = new Context(engine).pushEnv(env);
|
||||
var awaiter = engine.pushMsg(false, ctx, new Filename("jscript", "eval"), code, codeFrame.frame.thisArg, codeFrame.frame.args);
|
||||
var ctx = new Context(engine, env);
|
||||
var awaiter = engine.pushMsg(false, ctx.environment(), new Filename("jscript", "eval"), code, codeFrame.frame.thisArg, codeFrame.frame.args);
|
||||
|
||||
engine.run(true);
|
||||
|
||||
|
@ -254,6 +254,7 @@ public class CodeFrame {
|
||||
break;
|
||||
}
|
||||
else {
|
||||
popTryFlag = false;
|
||||
if (tryCtx.state == TryState.CATCH) scope.catchVars.remove(scope.catchVars.size() - 1);
|
||||
|
||||
if (tryCtx.state != TryState.FINALLY && tryCtx.hasFinally()) {
|
||||
|
@ -741,7 +741,7 @@ public class Values {
|
||||
try {
|
||||
if (err instanceof EngineException) {
|
||||
var ee = ((EngineException)err);
|
||||
System.out.println(prefix + " " + ee.toString(new Context(ee.engine).pushEnv(ee.env)));
|
||||
System.out.println(prefix + " " + ee.toString(new Context(ee.engine, ee.env)));
|
||||
}
|
||||
else if (err instanceof SyntaxException) {
|
||||
System.out.println("Syntax error:" + ((SyntaxException)err).msg);
|
||||
|
@ -37,7 +37,7 @@ public class EngineException extends RuntimeException {
|
||||
if (function.equals("")) function = null;
|
||||
|
||||
if (ctx == null) this.ctx = null;
|
||||
else this.ctx = new Context(ctx.engine).pushEnv(ctx.environment());
|
||||
else this.ctx = new Context(ctx.engine, ctx.environment());
|
||||
this.location = location;
|
||||
this.function = function;
|
||||
}
|
||||
|
@ -5,20 +5,16 @@ import me.topchetoeu.jscript.Buffer;
|
||||
public interface File {
|
||||
int read(byte[] buff);
|
||||
void write(byte[] buff);
|
||||
long getPtr();
|
||||
void setPtr(long offset, int pos);
|
||||
long seek(long offset, int pos);
|
||||
void close();
|
||||
Mode mode();
|
||||
|
||||
default String readToString() {
|
||||
setPtr(0, 2);
|
||||
long len = getPtr();
|
||||
long len = seek(0, 2);
|
||||
if (len < 0) return null;
|
||||
|
||||
setPtr(0, 0);
|
||||
seek(0, 0);
|
||||
|
||||
byte[] res = new byte[(int)len];
|
||||
read(res);
|
||||
len = read(res);
|
||||
|
||||
return new String(res);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package me.topchetoeu.jscript.filesystem;
|
||||
|
||||
public interface Filesystem {
|
||||
String normalize(String... path);
|
||||
File open(String path, Mode mode) throws FilesystemException;
|
||||
void create(String path, EntryType type) throws FilesystemException;
|
||||
FileStat stat(String path) throws FilesystemException;
|
||||
|
@ -12,7 +12,8 @@ public class FilesystemException extends RuntimeException {
|
||||
NO_PERMISSIONS_RW(0x5),
|
||||
FOLDER_NOT_EMPTY(0x6),
|
||||
ALREADY_EXISTS(0x7),
|
||||
FOLDER_EXISTS(0x8);
|
||||
FOLDER_EXISTS(0x8),
|
||||
UNSUPPORTED_OPERATION(0x9);
|
||||
|
||||
public final int code;
|
||||
|
||||
@ -27,7 +28,8 @@ public class FilesystemException extends RuntimeException {
|
||||
"No permissions to read '%s'",
|
||||
"No permissions to write '%s'",
|
||||
"Can't delete '%s', since it is a full folder.",
|
||||
"'%s' already exists."
|
||||
"'%s' already exists.",
|
||||
"An unsupported operation was performed on the file '%s'."
|
||||
};
|
||||
|
||||
public final String message, filename;
|
||||
|
73
src/me/topchetoeu/jscript/filesystem/ListFile.java
Normal file
73
src/me/topchetoeu/jscript/filesystem/ListFile.java
Normal file
@ -0,0 +1,73 @@
|
||||
package me.topchetoeu.jscript.filesystem;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Iterator;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
|
||||
|
||||
public class ListFile implements File {
|
||||
private Iterator<String> it;
|
||||
private String filename;
|
||||
private byte[] currFile;
|
||||
private long ptr = 0, start = 0, end = 0;
|
||||
|
||||
private void next() {
|
||||
if (it != null && it.hasNext()) {
|
||||
start = end;
|
||||
currFile = (it.next() + "\n").getBytes();
|
||||
end = start + currFile.length;
|
||||
}
|
||||
else {
|
||||
it = null;
|
||||
currFile = null;
|
||||
end = -1;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
it = null;
|
||||
currFile = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] buff) {
|
||||
if (ptr < start) return 0;
|
||||
if (it == null) return 0;
|
||||
|
||||
var i = 0;
|
||||
|
||||
while (i < buff.length) {
|
||||
while (i + ptr >= end) {
|
||||
next();
|
||||
if (it == null) return 0;
|
||||
}
|
||||
|
||||
int cpyN = Math.min(currFile.length, buff.length - i);
|
||||
System.arraycopy(currFile, (int)(ptr + i - start), buff, i, cpyN);
|
||||
|
||||
i += cpyN;
|
||||
}
|
||||
|
||||
ptr += i;
|
||||
return i;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long seek(long offset, int pos) {
|
||||
if (pos == 2) throw new FilesystemException(filename, FSCode.UNSUPPORTED_OPERATION);
|
||||
if (pos == 1) offset += ptr;
|
||||
return ptr = offset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(byte[] buff) {
|
||||
throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_RW);
|
||||
}
|
||||
|
||||
public ListFile(String filename, Stream<String> stream) throws IOException {
|
||||
this.it = stream.iterator();
|
||||
this.filename = filename;
|
||||
}
|
||||
}
|
@ -27,17 +27,17 @@ public class MemoryFile implements File {
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPtr() {
|
||||
if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
||||
return ptr;
|
||||
}
|
||||
@Override
|
||||
public void setPtr(long offset, int pos) {
|
||||
public long seek(long offset, int pos) {
|
||||
if (data == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
||||
|
||||
if (pos == 0) ptr = (int)offset;
|
||||
else if (pos == 1) ptr += (int)offset;
|
||||
else if (pos == 2) ptr = data.length() - (int)offset;
|
||||
|
||||
if (ptr < 0) ptr = 0;
|
||||
if (ptr > data.length()) ptr = data.length();
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -45,11 +45,6 @@ public class MemoryFile implements File {
|
||||
mode = Mode.NONE;
|
||||
ptr = 0;
|
||||
}
|
||||
@Override
|
||||
public Mode mode() {
|
||||
if (data == null) return Mode.NONE;
|
||||
return mode;
|
||||
}
|
||||
|
||||
public MemoryFile(String filename, Buffer buff, Mode mode) {
|
||||
this.filename = filename;
|
||||
|
@ -5,6 +5,7 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
import me.topchetoeu.jscript.Buffer;
|
||||
import me.topchetoeu.jscript.Filename;
|
||||
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
|
||||
|
||||
public class MemoryFilesystem implements Filesystem {
|
||||
@ -12,66 +13,71 @@ public class MemoryFilesystem implements Filesystem {
|
||||
private HashMap<Path, Buffer> files = new HashMap<>();
|
||||
private HashSet<Path> folders = new HashSet<>();
|
||||
|
||||
private Path getPath(String name) {
|
||||
return Path.of("/" + name.replace("\\", "/")).normalize();
|
||||
private Path realPath(String path) {
|
||||
return Filename.normalize(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(String path, EntryType type) {
|
||||
var _path = getPath(path);
|
||||
public String normalize(String... path) {
|
||||
return Paths.normalize(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(String _path, EntryType type) {
|
||||
var path = realPath(_path);
|
||||
|
||||
switch (type) {
|
||||
case FILE:
|
||||
if (!folders.contains(_path.getParent())) throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
||||
if (folders.contains(_path) || files.containsKey(_path)) throw new FilesystemException(path, FSCode.ALREADY_EXISTS);
|
||||
if (folders.contains(_path)) throw new FilesystemException(path, FSCode.ALREADY_EXISTS);
|
||||
files.put(_path, new Buffer());
|
||||
if (!folders.contains(path.getParent())) throw new FilesystemException(path.toString(), FSCode.DOESNT_EXIST);
|
||||
if (folders.contains(path) || files.containsKey(path)) throw new FilesystemException(path.toString(), FSCode.ALREADY_EXISTS);
|
||||
if (folders.contains(path)) throw new FilesystemException(path.toString(), FSCode.ALREADY_EXISTS);
|
||||
files.put(path, new Buffer());
|
||||
break;
|
||||
case FOLDER:
|
||||
if (!folders.contains(_path.getParent())) throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
||||
if (folders.contains(_path) || files.containsKey(_path)) throw new FilesystemException(path, FSCode.ALREADY_EXISTS);
|
||||
folders.add(_path);
|
||||
if (!folders.contains(path.getParent())) throw new FilesystemException(_path, FSCode.DOESNT_EXIST);
|
||||
if (folders.contains(path) || files.containsKey(path)) throw new FilesystemException(path.toString(), FSCode.ALREADY_EXISTS);
|
||||
folders.add(path);
|
||||
break;
|
||||
default:
|
||||
case NONE:
|
||||
if (!folders.remove(_path) && files.remove(_path) == null) throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
||||
if (!folders.remove(path) && files.remove(path) == null) throw new FilesystemException(path.toString(), FSCode.DOESNT_EXIST);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public File open(String path, Mode perms) {
|
||||
var _path = getPath(path);
|
||||
var pcount = _path.getNameCount();
|
||||
public File open(String _path, Mode perms) {
|
||||
var path = realPath(_path);
|
||||
var pcount = path.getNameCount();
|
||||
|
||||
if (files.containsKey(_path)) return new MemoryFile(path, files.get(_path), perms);
|
||||
else if (folders.contains(_path)) {
|
||||
if (files.containsKey(path)) return new MemoryFile(path.toString(), files.get(path), perms);
|
||||
else if (folders.contains(path)) {
|
||||
var res = new StringBuilder();
|
||||
for (var folder : folders) {
|
||||
if (pcount + 1 != folder.getNameCount()) continue;
|
||||
if (!folder.startsWith(_path)) continue;
|
||||
if (!folder.startsWith(path)) continue;
|
||||
res.append(folder.toFile().getName()).append('\n');
|
||||
}
|
||||
for (var file : files.keySet()) {
|
||||
if (pcount + 1 != file.getNameCount()) continue;
|
||||
if (!file.startsWith(_path)) continue;
|
||||
if (!file.startsWith(path)) continue;
|
||||
res.append(file.toFile().getName()).append('\n');
|
||||
}
|
||||
return new MemoryFile(path, new Buffer(res.toString().getBytes()), perms.intersect(Mode.READ));
|
||||
return new MemoryFile(path.toString(), new Buffer(res.toString().getBytes()), perms.intersect(Mode.READ));
|
||||
}
|
||||
else throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
||||
else throw new FilesystemException(path.toString(), FSCode.DOESNT_EXIST);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileStat stat(String path) {
|
||||
var _path = getPath(path);
|
||||
public FileStat stat(String _path) {
|
||||
var path = realPath(_path);
|
||||
|
||||
if (files.containsKey(_path)) return new FileStat(mode, EntryType.FILE);
|
||||
else if (folders.contains(_path)) return new FileStat(mode, EntryType.FOLDER);
|
||||
else throw new FilesystemException(path, FSCode.DOESNT_EXIST);
|
||||
if (files.containsKey(path)) return new FileStat(mode, EntryType.FILE);
|
||||
else if (folders.contains(path)) return new FileStat(mode, EntryType.FOLDER);
|
||||
else return new FileStat(Mode.NONE, EntryType.NONE);
|
||||
}
|
||||
|
||||
public MemoryFilesystem put(String path, byte[] data) {
|
||||
var _path = getPath(path);
|
||||
var _path = realPath(path);
|
||||
var _curr = "/";
|
||||
|
||||
for (var seg : _path) {
|
||||
|
52
src/me/topchetoeu/jscript/filesystem/Paths.java
Normal file
52
src/me/topchetoeu/jscript/filesystem/Paths.java
Normal file
@ -0,0 +1,52 @@
|
||||
package me.topchetoeu.jscript.filesystem;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
public class Paths {
|
||||
public static String normalize(String... path) {
|
||||
var parts = String.join("/", path).split("[\\\\/]");
|
||||
var res = new ArrayList<String>();
|
||||
|
||||
for (var part : parts) {
|
||||
if (part.equals("...")) res.clear();
|
||||
else if (part.equals("..")) {
|
||||
if (res.size() > 0) res.remove(res.size() - 1);
|
||||
}
|
||||
else if (!part.equals(".") && !part.isEmpty()) res.add(part);
|
||||
}
|
||||
|
||||
var sb = new StringBuilder();
|
||||
|
||||
for (var el : res) sb.append("/").append(el);
|
||||
|
||||
if (sb.isEmpty()) return "/";
|
||||
else return sb.toString();
|
||||
}
|
||||
|
||||
public static String chroot(String root, String path) {
|
||||
return normalize(root) + normalize(path);
|
||||
}
|
||||
|
||||
public static String cwd(String cwd, String path) {
|
||||
return normalize(cwd + "/" + path);
|
||||
}
|
||||
|
||||
public static String filename(String path) {
|
||||
var i = path.lastIndexOf('/');
|
||||
if (i < 0) i = path.lastIndexOf('\\');
|
||||
|
||||
if (i < 0) return path;
|
||||
else return path.substring(i + 1);
|
||||
}
|
||||
|
||||
public static String extension(String path) {
|
||||
var i = path.lastIndexOf('.');
|
||||
|
||||
if (i < 0) return "";
|
||||
else return path.substring(i + 1);
|
||||
}
|
||||
|
||||
public static String dir(String path) {
|
||||
return normalize(path + "/..");
|
||||
}
|
||||
}
|
@ -9,35 +9,30 @@ import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
|
||||
public class PhysicalFile implements File {
|
||||
private String filename;
|
||||
private RandomAccessFile file;
|
||||
private Mode perms;
|
||||
private Mode mode;
|
||||
|
||||
@Override
|
||||
public int read(byte[] buff) {
|
||||
if (file == null || !perms.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
||||
if (file == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
||||
else try { return file.read(buff); }
|
||||
catch (IOException e) { throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R); }
|
||||
}
|
||||
@Override
|
||||
public void write(byte[] buff) {
|
||||
if (file == null || !perms.writable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_RW);
|
||||
if (file == null || !mode.writable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_RW);
|
||||
else try { file.write(buff); }
|
||||
catch (IOException e) { throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_RW); }
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getPtr() {
|
||||
if (file == null || !perms.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
||||
else try { return file.getFilePointer(); }
|
||||
catch (IOException e) { throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R); }
|
||||
}
|
||||
@Override
|
||||
public void setPtr(long offset, int pos) {
|
||||
if (file == null || !perms.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
||||
public long seek(long offset, int pos) {
|
||||
if (file == null || !mode.readable) throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R);
|
||||
|
||||
try {
|
||||
if (pos == 1) pos += file.getFilePointer();
|
||||
else if (pos == 2) pos += file.length();
|
||||
file.seek(pos);
|
||||
if (pos == 1) offset += file.getFilePointer();
|
||||
else if (pos == 2) offset += file.length();
|
||||
file.seek(offset);
|
||||
return offset;
|
||||
}
|
||||
catch (IOException e) { throw new FilesystemException(filename, FSCode.NO_PERMISSIONS_R); }
|
||||
}
|
||||
@ -48,16 +43,15 @@ public class PhysicalFile implements File {
|
||||
try { file.close(); }
|
||||
catch (IOException e) {} // SHUT
|
||||
file = null;
|
||||
perms = Mode.NONE;
|
||||
mode = Mode.NONE;
|
||||
}
|
||||
@Override
|
||||
public Mode mode() { return perms; }
|
||||
|
||||
public PhysicalFile(String path, Mode mode) throws FileNotFoundException {
|
||||
public PhysicalFile(String name, String path, Mode mode) throws FileNotFoundException {
|
||||
this.filename = name;
|
||||
this.mode = mode;
|
||||
|
||||
if (mode == Mode.NONE) file = null;
|
||||
else try { file = new RandomAccessFile(path, mode.name); }
|
||||
catch (FileNotFoundException e) { throw new FilesystemException(filename, FSCode.DOESNT_EXIST); }
|
||||
|
||||
perms = mode;
|
||||
}
|
||||
}
|
||||
|
@ -1,74 +1,88 @@
|
||||
package me.topchetoeu.jscript.filesystem;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import me.topchetoeu.jscript.filesystem.FilesystemException.FSCode;
|
||||
|
||||
public class PhysicalFilesystem implements Filesystem {
|
||||
public final Path root;
|
||||
|
||||
private Path getPath(String name) {
|
||||
return root.resolve(name.replace("\\", "/")).normalize();
|
||||
}
|
||||
public final String root;
|
||||
|
||||
private void checkMode(Path path, Mode mode) {
|
||||
if (!path.startsWith(root)) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_R);
|
||||
if (mode.readable && !path.toFile().canRead()) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_R);
|
||||
if (mode.writable && !path.toFile().canWrite()) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_RW);
|
||||
|
||||
if (mode.readable && !Files.isReadable(path)) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_R);
|
||||
if (mode.writable && !Files.isWritable(path)) throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_RW);
|
||||
}
|
||||
|
||||
private Path realPath(String path) {
|
||||
return Path.of(Paths.chroot(root, path));
|
||||
}
|
||||
|
||||
@Override
|
||||
public File open(String path, Mode perms) {
|
||||
var _path = getPath(path);
|
||||
var f = _path.toFile();
|
||||
|
||||
checkMode(_path, perms);
|
||||
|
||||
|
||||
if (f.isDirectory()) return MemoryFile.fromFileList(path, f.listFiles());
|
||||
else try { return new PhysicalFile(path, perms); }
|
||||
catch (FileNotFoundException e) { throw new FilesystemException(_path.toString(), FSCode.DOESNT_EXIST); }
|
||||
public String normalize(String... paths) {
|
||||
return Paths.normalize(paths);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void create(String path, EntryType type) {
|
||||
var _path = getPath(path);
|
||||
var f = _path.toFile();
|
||||
public File open(String _path, Mode perms) {
|
||||
_path = normalize(_path);
|
||||
var path = realPath(_path);
|
||||
|
||||
switch (type) {
|
||||
case FILE:
|
||||
try {
|
||||
if (!f.createNewFile()) throw new FilesystemException(_path.toString(), FSCode.ALREADY_EXISTS);
|
||||
else break;
|
||||
}
|
||||
catch (IOException e) { throw new FilesystemException(_path.toString(), FSCode.NO_PERMISSIONS_RW); }
|
||||
case FOLDER:
|
||||
if (!f.mkdir()) throw new FilesystemException(_path.toString(), FSCode.ALREADY_EXISTS);
|
||||
else break;
|
||||
case NONE:
|
||||
default:
|
||||
if (!f.delete()) throw new FilesystemException(_path.toString(), FSCode.DOESNT_EXIST);
|
||||
else break;
|
||||
checkMode(path, perms);
|
||||
|
||||
try {
|
||||
if (Files.isDirectory(path)) return new ListFile(_path, Files.list(path).map((v -> v.getFileName().toString())));
|
||||
else return new PhysicalFile(_path, path.toString(), perms);
|
||||
}
|
||||
catch (IOException e) { throw new FilesystemException(path.toString(), FSCode.DOESNT_EXIST); }
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileStat stat(String path) {
|
||||
var _path = getPath(path);
|
||||
var f = _path.toFile();
|
||||
public void create(String _path, EntryType type) {
|
||||
var path = realPath(_path);
|
||||
|
||||
if (!f.exists()) throw new FilesystemException(_path.toString(), FSCode.DOESNT_EXIST);
|
||||
checkMode(_path, Mode.READ);
|
||||
if (type == EntryType.NONE != Files.exists(path)) throw new FilesystemException(path.toString(), FSCode.ALREADY_EXISTS);
|
||||
|
||||
try {
|
||||
switch (type) {
|
||||
case FILE:
|
||||
Files.createFile(path);
|
||||
break;
|
||||
case FOLDER:
|
||||
Files.createDirectories(path);
|
||||
break;
|
||||
case NONE:
|
||||
default:
|
||||
Files.delete(path);
|
||||
}
|
||||
}
|
||||
catch (IOException e) { throw new FilesystemException(path.toString(), FSCode.NO_PERMISSIONS_RW); }
|
||||
}
|
||||
|
||||
@Override
|
||||
public FileStat stat(String _path) {
|
||||
var path = realPath(_path);
|
||||
|
||||
if (!Files.exists(path)) return new FileStat(Mode.NONE, EntryType.NONE);
|
||||
|
||||
var perms = Mode.NONE;
|
||||
|
||||
if (Files.isReadable(path)) {
|
||||
if (Files.isWritable(path)) perms = Mode.READ_WRITE;
|
||||
else perms = Mode.READ;
|
||||
}
|
||||
|
||||
if (perms == Mode.NONE) return new FileStat(Mode.NONE, EntryType.NONE);
|
||||
|
||||
return new FileStat(
|
||||
f.canWrite() ? Mode.READ_WRITE : Mode.READ,
|
||||
f.isFile() ? EntryType.FILE : EntryType.FOLDER
|
||||
perms,
|
||||
Files.isDirectory(path) ? EntryType.FOLDER : EntryType.FILE
|
||||
);
|
||||
}
|
||||
|
||||
public PhysicalFilesystem(Path root) {
|
||||
this.root = root.toAbsolutePath().normalize();
|
||||
public PhysicalFilesystem(String root) {
|
||||
this.root = Paths.normalize(Path.of(root).toAbsolutePath().toString());
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,18 @@ public class RootFilesystem implements Filesystem {
|
||||
if (mode.writable && perms != null && !canWrite(_path)) throw new FilesystemException(_path, FSCode.NO_PERMISSIONS_RW);
|
||||
}
|
||||
|
||||
@Override public String normalize(String... paths) {
|
||||
if (paths.length == 0) return "file://";
|
||||
else {
|
||||
var filename = Filename.parse(paths[0]);
|
||||
var protocol = protocols.get(filename.protocol);
|
||||
paths[0] = filename.path;
|
||||
|
||||
|
||||
if (protocol == null) return Paths.normalize(paths);
|
||||
else return filename.protocol + "://" + protocol.normalize(paths);
|
||||
}
|
||||
}
|
||||
@Override public File open(String path, Mode perms) throws FilesystemException {
|
||||
var filename = Filename.parse(path);
|
||||
var protocol = protocols.get(filename.protocol);
|
||||
@ -45,9 +57,8 @@ public class RootFilesystem implements Filesystem {
|
||||
var filename = Filename.parse(path);
|
||||
var protocol = protocols.get(filename.protocol);
|
||||
if (protocol == null) throw new FilesystemException(filename.toString(), FSCode.DOESNT_EXIST);
|
||||
modeAllowed(filename.toString(), Mode.READ);
|
||||
|
||||
try { return protocol.stat(path); }
|
||||
try { return protocol.stat(filename.path); }
|
||||
catch (FilesystemException e) { throw new FilesystemException(filename.toString(), e.code); }
|
||||
}
|
||||
|
||||
|
@ -267,14 +267,12 @@ public class NativeWrapperProvider implements WrappersProvider {
|
||||
else return null;
|
||||
}));
|
||||
proto.defineProperty(null, "name", new NativeFunction("name", (ctx, thisArg, args) -> getName(thisArg.getClass())));
|
||||
proto.defineProperty(null, "toString", new NativeFunction("toString", (ctx, thisArg, args) -> thisArg.toString()));
|
||||
|
||||
var constr = makeConstructor(null, Throwable.class);
|
||||
proto.defineProperty(null, "constructor", constr, true, false, false);
|
||||
constr.defineProperty(null, "prototype", proto, true, false, false);
|
||||
|
||||
proto.setPrototype(null, getProto(Object.class));
|
||||
constr.setPrototype(null, getConstr(Object.class));
|
||||
|
||||
setProto(Throwable.class, proto);
|
||||
setConstr(Throwable.class, constr);
|
||||
}
|
||||
|
@ -21,7 +21,6 @@ import me.topchetoeu.jscript.interop.Native;
|
||||
private void next(Context ctx, Object inducedValue, Object inducedError) {
|
||||
Object res = null;
|
||||
ctx.pushFrame(frame);
|
||||
ctx.pushEnv(frame.function.environment);
|
||||
|
||||
awaiting = false;
|
||||
while (!awaiting) {
|
||||
|
@ -15,7 +15,7 @@ public class FileLib {
|
||||
@NativeGetter public PromiseLib pointer(Context ctx) {
|
||||
return PromiseLib.await(ctx, () -> {
|
||||
try {
|
||||
return file.getPtr();
|
||||
return file.seek(0, 1);
|
||||
}
|
||||
catch (FilesystemException e) { throw e.toEngineException(); }
|
||||
});
|
||||
@ -23,23 +23,14 @@ public class FileLib {
|
||||
@NativeGetter public PromiseLib length(Context ctx) {
|
||||
return PromiseLib.await(ctx, () -> {
|
||||
try {
|
||||
long curr = file.getPtr();
|
||||
file.setPtr(0, 2);
|
||||
long res = file.getPtr();
|
||||
file.setPtr(curr, 0);
|
||||
long curr = file.seek(0, 1);
|
||||
long res = file.seek(0, 2);
|
||||
file.seek(curr, 0);
|
||||
return res;
|
||||
}
|
||||
catch (FilesystemException e) { throw e.toEngineException(); }
|
||||
});
|
||||
}
|
||||
@NativeGetter public PromiseLib getMode(Context ctx) {
|
||||
return PromiseLib.await(ctx, () -> {
|
||||
try {
|
||||
return file.mode().name;
|
||||
}
|
||||
catch (FilesystemException e) { throw e.toEngineException(); }
|
||||
});
|
||||
}
|
||||
|
||||
@Native public PromiseLib read(Context ctx, int n) {
|
||||
return PromiseLib.await(ctx, () -> {
|
||||
@ -73,11 +64,10 @@ public class FileLib {
|
||||
return null;
|
||||
});
|
||||
}
|
||||
@Native public PromiseLib setPointer(Context ctx, long ptr) {
|
||||
@Native public PromiseLib seek(Context ctx, long ptr, int whence) {
|
||||
return PromiseLib.await(ctx, () -> {
|
||||
try {
|
||||
file.setPtr(ptr, 0);
|
||||
return null;
|
||||
return file.seek(ptr, whence);
|
||||
}
|
||||
catch (FilesystemException e) { throw e.toEngineException(); }
|
||||
});
|
||||
|
@ -20,6 +20,10 @@ import me.topchetoeu.jscript.interop.Native;
|
||||
|
||||
@Native("Filesystem")
|
||||
public class FilesystemLib {
|
||||
@Native public static final int SEEK_SET = 0;
|
||||
@Native public static final int SEEK_CUR = 1;
|
||||
@Native public static final int SEEK_END = 2;
|
||||
|
||||
private static Filesystem fs(Context ctx) {
|
||||
var env = ctx.environment();
|
||||
if (env != null) {
|
||||
@ -29,24 +33,28 @@ public class FilesystemLib {
|
||||
throw EngineException.ofError("Current environment doesn't have a file system.");
|
||||
}
|
||||
|
||||
@Native public static String normalize(Context ctx, String... paths) {
|
||||
return fs(ctx).normalize(paths);
|
||||
}
|
||||
|
||||
@Native public static PromiseLib open(Context ctx, String _path, String mode) {
|
||||
var filename = Filename.parse(_path);
|
||||
var path = fs(ctx).normalize(_path);
|
||||
var _mode = Mode.parse(mode);
|
||||
|
||||
return PromiseLib.await(ctx, () -> {
|
||||
try {
|
||||
if (fs(ctx).stat(filename.path).type != EntryType.FILE) {
|
||||
throw new FilesystemException(filename.toString(), FSCode.NOT_FILE);
|
||||
if (fs(ctx).stat(path).type != EntryType.FILE) {
|
||||
throw new FilesystemException(path, FSCode.NOT_FILE);
|
||||
}
|
||||
|
||||
var file = fs(ctx).open(filename.path, _mode);
|
||||
var file = fs(ctx).open(path, _mode);
|
||||
return new FileLib(file);
|
||||
}
|
||||
catch (FilesystemException e) { throw e.toEngineException(); }
|
||||
});
|
||||
}
|
||||
@Native public static ObjectValue ls(Context ctx, String _path) throws IOException {
|
||||
var filename = Filename.parse(_path);
|
||||
var path = fs(ctx).normalize(_path);
|
||||
|
||||
return Values.toJSAsyncIterator(ctx, new Iterator<>() {
|
||||
private boolean failed, done;
|
||||
@ -57,11 +65,11 @@ public class FilesystemLib {
|
||||
if (done) return;
|
||||
if (!failed) {
|
||||
if (file == null) {
|
||||
if (fs(ctx).stat(filename.path).type != EntryType.FOLDER) {
|
||||
throw new FilesystemException(filename.toString(), FSCode.NOT_FOLDER);
|
||||
if (fs(ctx).stat(path).type != EntryType.FOLDER) {
|
||||
throw new FilesystemException(path, FSCode.NOT_FOLDER);
|
||||
}
|
||||
|
||||
file = fs(ctx).open(filename.path, Mode.READ);
|
||||
file = fs(ctx).open(path, Mode.READ);
|
||||
}
|
||||
|
||||
if (nextLine == null) {
|
||||
@ -108,34 +116,34 @@ public class FilesystemLib {
|
||||
});
|
||||
|
||||
}
|
||||
@Native public static PromiseLib mkfile(Context ctx, String _path) throws IOException {
|
||||
@Native public static PromiseLib mkfile(Context ctx, String path) throws IOException {
|
||||
return PromiseLib.await(ctx, () -> {
|
||||
try {
|
||||
fs(ctx).create(Filename.parse(_path).toString(), EntryType.FILE);
|
||||
fs(ctx).create(path, EntryType.FILE);
|
||||
return null;
|
||||
}
|
||||
catch (FilesystemException e) { throw e.toEngineException(); }
|
||||
});
|
||||
}
|
||||
@Native public static PromiseLib rm(Context ctx, String _path, boolean recursive) throws IOException {
|
||||
@Native public static PromiseLib rm(Context ctx, String path, boolean recursive) throws IOException {
|
||||
return PromiseLib.await(ctx, () -> {
|
||||
try {
|
||||
if (!recursive) fs(ctx).create(Filename.parse(_path).toString(), EntryType.NONE);
|
||||
if (!recursive) fs(ctx).create(path, EntryType.NONE);
|
||||
else {
|
||||
var stack = new Stack<String>();
|
||||
stack.push(_path);
|
||||
stack.push(path);
|
||||
|
||||
while (!stack.empty()) {
|
||||
var path = Filename.parse(stack.pop()).toString();
|
||||
var currPath = stack.pop();
|
||||
FileStat stat;
|
||||
|
||||
try { stat = fs(ctx).stat(path); }
|
||||
try { stat = fs(ctx).stat(currPath); }
|
||||
catch (FilesystemException e) { continue; }
|
||||
|
||||
if (stat.type == EntryType.FOLDER) {
|
||||
for (var el : fs(ctx).open(path, Mode.READ).readToString().split("\n")) stack.push(el);
|
||||
for (var el : fs(ctx).open(currPath, Mode.READ).readToString().split("\n")) stack.push(el);
|
||||
}
|
||||
else fs(ctx).create(path, EntryType.NONE);
|
||||
else fs(ctx).create(currPath, EntryType.NONE);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@ -143,10 +151,10 @@ public class FilesystemLib {
|
||||
catch (FilesystemException e) { throw e.toEngineException(); }
|
||||
});
|
||||
}
|
||||
@Native public static PromiseLib stat(Context ctx, String _path) throws IOException {
|
||||
@Native public static PromiseLib stat(Context ctx, String path) throws IOException {
|
||||
return PromiseLib.await(ctx, () -> {
|
||||
try {
|
||||
var stat = fs(ctx).stat(_path);
|
||||
var stat = fs(ctx).stat(path);
|
||||
var res = new ObjectValue();
|
||||
|
||||
res.defineProperty(ctx, "type", stat.type.name);
|
||||
|
@ -20,6 +20,12 @@ public class Internals {
|
||||
private static final DataKey<HashMap<Integer, Thread>> THREADS = new DataKey<>();
|
||||
private static final DataKey<Integer> I = new DataKey<>();
|
||||
|
||||
@Native public static Object require(Context ctx, String name) {
|
||||
var env = ctx.environment();
|
||||
var res = env.modules.getModule(ctx, env.moduleCwd, name);
|
||||
res.load(ctx);
|
||||
return res.value();
|
||||
}
|
||||
|
||||
@Native public static Object log(Context ctx, Object ...args) {
|
||||
for (var arg : args) {
|
||||
@ -51,7 +57,7 @@ public class Internals {
|
||||
}
|
||||
catch (InterruptedException e) { return; }
|
||||
|
||||
ctx.engine.pushMsg(false, ctx, func, null, args);
|
||||
ctx.engine.pushMsg(false, ctx.environment(), func, null, args);
|
||||
});
|
||||
thread.start();
|
||||
|
||||
@ -71,7 +77,7 @@ public class Internals {
|
||||
}
|
||||
catch (InterruptedException e) { return; }
|
||||
|
||||
ctx.engine.pushMsg(false, ctx, func, null, args);
|
||||
ctx.engine.pushMsg(false, ctx.environment(), func, null, args);
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
|
@ -253,7 +253,7 @@ import me.topchetoeu.jscript.interop.Native;
|
||||
this.val = val;
|
||||
this.state = STATE_FULFILLED;
|
||||
|
||||
ctx.engine.pushMsg(true, ctx, new NativeFunction((_ctx, _thisArg, _args) -> {
|
||||
ctx.engine.pushMsg(true, ctx.environment(), new NativeFunction((_ctx, _thisArg, _args) -> {
|
||||
for (var handle : handles) {
|
||||
handle.fulfilled.call(handle.ctx, null, val);
|
||||
}
|
||||
@ -288,7 +288,7 @@ import me.topchetoeu.jscript.interop.Native;
|
||||
this.val = val;
|
||||
this.state = STATE_REJECTED;
|
||||
|
||||
ctx.engine.pushMsg(true, ctx, new NativeFunction((_ctx, _thisArg, _args) -> {
|
||||
ctx.engine.pushMsg(true, ctx.environment(), new NativeFunction((_ctx, _thisArg, _args) -> {
|
||||
for (var handle : handles) handle.rejected.call(handle.ctx, null, val);
|
||||
if (!handled) {
|
||||
Values.printError(new EngineException(val).setCtx(ctx.environment(), ctx.engine), "(in promise)");
|
||||
@ -305,9 +305,9 @@ import me.topchetoeu.jscript.interop.Native;
|
||||
}
|
||||
|
||||
private void handle(Context ctx, FunctionValue fulfill, FunctionValue reject) {
|
||||
if (state == STATE_FULFILLED) ctx.engine.pushMsg(true, ctx, fulfill, null, val);
|
||||
if (state == STATE_FULFILLED) ctx.engine.pushMsg(true, ctx.environment(), fulfill, null, val);
|
||||
else if (state == STATE_REJECTED) {
|
||||
ctx.engine.pushMsg(true, ctx, reject, null, val);
|
||||
ctx.engine.pushMsg(true, ctx.environment(), reject, null, val);
|
||||
handled = true;
|
||||
}
|
||||
else handles.add(new Handle(ctx, fulfill, reject));
|
||||
|
@ -57,19 +57,16 @@ import me.topchetoeu.jscript.interop.NativeGetter;
|
||||
@Native(thisArg = true) public static String charAt(Context ctx, Object thisArg, int i) {
|
||||
return passThis(ctx, "charAt", thisArg).charAt(i) + "";
|
||||
}
|
||||
// @Native(thisArg = true) public static int charCodeAt(Context ctx, Object thisArg, int i) {
|
||||
// return passThis(ctx, "charCodeAt", thisArg).charAt(i);
|
||||
// }
|
||||
// @Native(thisArg = true) public static String charAt(Context ctx, Object thisArg, int i) {
|
||||
// var str = passThis(ctx, "charAt", thisArg);
|
||||
// if (i < 0 || i >= str.length()) return "";
|
||||
// else return str.charAt(i) + "";
|
||||
// }
|
||||
@Native(thisArg = true) public static double charCodeAt(Context ctx, Object thisArg, int i) {
|
||||
var str = passThis(ctx, "charCodeAt", thisArg);
|
||||
if (i < 0 || i >= str.length()) return Double.NaN;
|
||||
else return str.charAt(i);
|
||||
}
|
||||
@Native(thisArg = true) public static double codePointAt(Context ctx, Object thisArg, int i) {
|
||||
var str = passThis(ctx, "codePointAt", thisArg);
|
||||
if (i < 0 || i >= str.length()) return Double.NaN;
|
||||
else return str.codePointAt(i);
|
||||
}
|
||||
|
||||
@Native(thisArg = true) public static boolean startsWith(Context ctx, Object thisArg, String term, int pos) {
|
||||
return passThis(ctx, "startsWith", thisArg).startsWith(term, pos);
|
||||
@ -105,7 +102,7 @@ import me.topchetoeu.jscript.interop.NativeGetter;
|
||||
}
|
||||
|
||||
@Native(thisArg = true) public static boolean includes(Context ctx, Object thisArg, Object term, int pos) {
|
||||
return lastIndexOf(ctx, passThis(ctx, "includes", thisArg), term, pos) >= 0;
|
||||
return indexOf(ctx, passThis(ctx, "includes", thisArg), term, pos) >= 0;
|
||||
}
|
||||
|
||||
@Native(thisArg = true) public static String replace(Context ctx, Object thisArg, Object term, Object replacement) {
|
||||
@ -238,6 +235,12 @@ import me.topchetoeu.jscript.interop.NativeGetter;
|
||||
@Native(thisArg = true) public static String trim(Context ctx, Object thisArg) {
|
||||
return passThis(ctx, "trim", thisArg).trim();
|
||||
}
|
||||
@Native(thisArg = true) public static String trimStart(Context ctx, Object thisArg) {
|
||||
return passThis(ctx, "trimStart", thisArg).replaceAll("^\\s+", "");
|
||||
}
|
||||
@Native(thisArg = true) public static String trimEnd(Context ctx, Object thisArg) {
|
||||
return passThis(ctx, "trimEnd", thisArg).replaceAll("\\s+$", "");
|
||||
}
|
||||
|
||||
@NativeConstructor(thisArg = true) public static Object constructor(Context ctx, Object thisArg, Object val) {
|
||||
val = Values.toString(ctx, val);
|
||||
|
20
src/me/topchetoeu/jscript/modules/Module.java
Normal file
20
src/me/topchetoeu/jscript/modules/Module.java
Normal file
@ -0,0 +1,20 @@
|
||||
package me.topchetoeu.jscript.modules;
|
||||
|
||||
import me.topchetoeu.jscript.engine.Context;
|
||||
|
||||
public abstract class Module {
|
||||
private Object value;
|
||||
private boolean loaded;
|
||||
|
||||
public Object value() { return value; }
|
||||
public boolean loaded() { return loaded; }
|
||||
|
||||
protected abstract Object onLoad(Context ctx);
|
||||
|
||||
public void load(Context ctx) {
|
||||
if (loaded) return;
|
||||
this.value = onLoad(ctx);
|
||||
this.loaded = true;
|
||||
}
|
||||
}
|
||||
|
32
src/me/topchetoeu/jscript/modules/ModuleRepo.java
Normal file
32
src/me/topchetoeu/jscript/modules/ModuleRepo.java
Normal file
@ -0,0 +1,32 @@
|
||||
package me.topchetoeu.jscript.modules;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import me.topchetoeu.jscript.Filename;
|
||||
import me.topchetoeu.jscript.engine.Context;
|
||||
import me.topchetoeu.jscript.filesystem.Filesystem;
|
||||
import me.topchetoeu.jscript.filesystem.Mode;
|
||||
|
||||
public interface ModuleRepo {
|
||||
public Module getModule(Context ctx, String cwd, String name);
|
||||
|
||||
public static ModuleRepo ofFilesystem(Filesystem fs) {
|
||||
var modules = new HashMap<String, Module>();
|
||||
|
||||
return (ctx, cwd, name) -> {
|
||||
name = fs.normalize(cwd, name);
|
||||
var filename = Filename.parse(name);
|
||||
var src = fs.open(name, Mode.READ).readToString();
|
||||
|
||||
if (modules.containsKey(name)) return modules.get(name);
|
||||
|
||||
var env = ctx.environment().child();
|
||||
env.moduleCwd = fs.normalize(name, "..");
|
||||
|
||||
var mod = new SourceModule(filename, src, env);
|
||||
modules.put(name, mod);
|
||||
|
||||
return mod;
|
||||
};
|
||||
}
|
||||
}
|
30
src/me/topchetoeu/jscript/modules/RootModuleRepo.java
Normal file
30
src/me/topchetoeu/jscript/modules/RootModuleRepo.java
Normal file
@ -0,0 +1,30 @@
|
||||
package me.topchetoeu.jscript.modules;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import me.topchetoeu.jscript.engine.Context;
|
||||
import me.topchetoeu.jscript.exceptions.EngineException;
|
||||
|
||||
public class RootModuleRepo implements ModuleRepo {
|
||||
public final HashMap<String, ModuleRepo> repos = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public Module getModule(Context ctx, String cwd, String name) {
|
||||
var i = name.indexOf(":");
|
||||
String repoName, modName;
|
||||
|
||||
if (i < 0) {
|
||||
repoName = "file";
|
||||
modName = name;
|
||||
}
|
||||
else {
|
||||
repoName = name.substring(0, i);
|
||||
modName = name.substring(i + 1);
|
||||
}
|
||||
|
||||
var repo = repos.get(repoName);
|
||||
if (repo == null) throw EngineException.ofError("ModuleError", "Couldn't find module repo '" + repoName + "'.");
|
||||
|
||||
return repo.getModule(ctx, cwd, modName);
|
||||
}
|
||||
}
|
23
src/me/topchetoeu/jscript/modules/SourceModule.java
Normal file
23
src/me/topchetoeu/jscript/modules/SourceModule.java
Normal file
@ -0,0 +1,23 @@
|
||||
package me.topchetoeu.jscript.modules;
|
||||
|
||||
import me.topchetoeu.jscript.Filename;
|
||||
import me.topchetoeu.jscript.engine.Context;
|
||||
import me.topchetoeu.jscript.engine.Environment;
|
||||
|
||||
public class SourceModule extends Module {
|
||||
public final Filename filename;
|
||||
public final String source;
|
||||
public final Environment env;
|
||||
|
||||
@Override
|
||||
protected Object onLoad(Context ctx) {
|
||||
var res = new Context(ctx.engine, env).compile(filename, source);
|
||||
return res.call(ctx);
|
||||
}
|
||||
|
||||
public SourceModule(Filename filename, String source, Environment env) {
|
||||
this.filename = filename;
|
||||
this.source = source;
|
||||
this.env = env;
|
||||
}
|
||||
}
|
@ -69,6 +69,9 @@ public class ParseRes<T> {
|
||||
|
||||
@SafeVarargs
|
||||
public static <T> ParseRes<? extends T> any(ParseRes<? extends T> ...parsers) {
|
||||
return any(List.of(parsers));
|
||||
}
|
||||
public static <T> ParseRes<? extends T> any(List<ParseRes<? extends T>> parsers) {
|
||||
ParseRes<? extends T> best = null;
|
||||
ParseRes<? extends T> error = ParseRes.failed();
|
||||
|
||||
|
@ -216,8 +216,10 @@ public class Parsing {
|
||||
currToken.append(c);
|
||||
continue;
|
||||
case CURR_SCIENTIFIC_NOT:
|
||||
if (c == '-') currStage = CURR_NEG_SCIENTIFIC_NOT;
|
||||
else if (!isDigit(c)) {
|
||||
if (c == '-') {
|
||||
if (currToken.toString().endsWith("e")) currStage = CURR_NEG_SCIENTIFIC_NOT;
|
||||
}
|
||||
if (currStage == CURR_SCIENTIFIC_NOT && !isDigit(c)) {
|
||||
i--; start--;
|
||||
break;
|
||||
}
|
||||
@ -565,7 +567,8 @@ public class Parsing {
|
||||
}
|
||||
private static double parseNumber(Location loc, String value) {
|
||||
var res = parseNumber(false, value);
|
||||
if (res == null) throw new SyntaxException(loc, "Invalid number format.");
|
||||
if (res == null)
|
||||
throw new SyntaxException(loc, "Invalid number format.");
|
||||
else return res;
|
||||
}
|
||||
|
||||
@ -1022,9 +1025,8 @@ public class Parsing {
|
||||
|
||||
return ParseRes.res(res.result, n);
|
||||
}
|
||||
@SuppressWarnings("all")
|
||||
public static ParseRes<? extends Statement> parseSimple(Filename filename, List<Token> tokens, int i, boolean statement) {
|
||||
var res = new ArrayList<>();
|
||||
var res = new ArrayList<ParseRes<? extends Statement>>();
|
||||
|
||||
if (!statement) {
|
||||
res.add(parseObject(filename, tokens, i));
|
||||
@ -1047,7 +1049,7 @@ public class Parsing {
|
||||
parseDelete(filename, tokens, i)
|
||||
));
|
||||
|
||||
return ParseRes.any(res.toArray(ParseRes[]::new));
|
||||
return ParseRes.any(res);
|
||||
}
|
||||
|
||||
public static ParseRes<VariableStatement> parseVariable(Filename filename, List<Token> tokens, int i) {
|
||||
|
Loading…
Reference in New Issue
Block a user