feat: Create filesystem interface and a physical filesystem

This commit is contained in:
TopchetoEU 2023-09-18 10:31:50 +03:00
parent 8c049ac08f
commit da4b35f506
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
12 changed files with 340 additions and 27 deletions

View File

@ -37,6 +37,7 @@ interface Internals {
var env: Environment = arguments[0], internals: Internals = arguments[1]; var env: Environment = arguments[0], internals: Internals = arguments[1];
globalThis.log = internals.constructor.log; globalThis.log = internals.constructor.log;
var i = 0.0;
try { try {
run('values/object'); run('values/object');

View File

@ -2,11 +2,12 @@ define("values/errors", () => {
var Error = env.global.Error = function Error(msg: string) { var Error = env.global.Error = function Error(msg: string) {
if (msg === undefined) msg = ''; if (msg === undefined) msg = '';
else msg += ''; else msg += '';
return Object.setPrototypeOf({ return {
message: msg, message: msg,
stack: [] as string[], stack: [] as string[],
}, Error.prototype); __proto__: Error.prototype,
} as any;
} as ErrorConstructor; } as ErrorConstructor;
setConstr(Error.prototype, Error); setConstr(Error.prototype, Error);

View File

@ -44,8 +44,9 @@ public class EngineException extends RuntimeException {
return ss.toString(); return ss.toString();
} }
private static Object err(String msg, PlaceholderProto proto) { private static Object err(String name, String msg, PlaceholderProto proto) {
var res = new ObjectValue(proto); var res = new ObjectValue(proto);
if (name != null) res.defineProperty(null, "name", name);
res.defineProperty(null, "message", msg); res.defineProperty(null, "message", msg);
return res; return res;
} }
@ -57,19 +58,22 @@ public class EngineException extends RuntimeException {
this.cause = null; this.cause = null;
} }
public static EngineException ofError(String name, String msg) {
return new EngineException(err(name, msg, PlaceholderProto.ERROR));
}
public static EngineException ofError(String msg) { public static EngineException ofError(String msg) {
return new EngineException(err(msg, PlaceholderProto.ERROR)); return new EngineException(err(null, msg, PlaceholderProto.ERROR));
} }
public static EngineException ofSyntax(SyntaxException e) { public static EngineException ofSyntax(SyntaxException e) {
return new EngineException(err(e.msg, PlaceholderProto.SYNTAX_ERROR)).add(null, e.loc); return new EngineException(err(null, e.msg, PlaceholderProto.SYNTAX_ERROR)).add(null, e.loc);
} }
public static EngineException ofSyntax(String msg) { public static EngineException ofSyntax(String msg) {
return new EngineException(err(msg, PlaceholderProto.SYNTAX_ERROR)); return new EngineException(err(null, msg, PlaceholderProto.SYNTAX_ERROR));
} }
public static EngineException ofType(String msg) { public static EngineException ofType(String msg) {
return new EngineException(err(msg, PlaceholderProto.TYPE_ERROR)); return new EngineException(err(null, msg, PlaceholderProto.TYPE_ERROR));
} }
public static EngineException ofRange(String msg) { public static EngineException ofRange(String msg) {
return new EngineException(err(msg, PlaceholderProto.RANGE_ERROR)); return new EngineException(err(null, msg, PlaceholderProto.RANGE_ERROR));
} }
} }

View File

@ -0,0 +1,7 @@
package me.topchetoeu.jscript.filesystem;
public enum EntryType {
NONE,
FILE,
FOLDER,
}

View File

@ -0,0 +1,27 @@
package me.topchetoeu.jscript.filesystem;
import java.io.IOException;
public interface File {
int read() throws IOException, InterruptedException;
boolean write(byte val) throws IOException, InterruptedException;
long tell() throws IOException, InterruptedException;
void seek(long offset, int pos) throws IOException, InterruptedException;
void close() throws IOException, InterruptedException;
Permissions perms();
default String readToString() throws IOException, InterruptedException {
seek(0, 2);
long len = tell();
if (len < 0) return null;
seek(0, 0);
byte[] res = new byte[(int)len];
for (var i = 0; i < len; i++) {
res[i] = (byte)read();
}
return new String(res);
}
}

View File

@ -0,0 +1,10 @@
package me.topchetoeu.jscript.filesystem;
import java.io.IOException;
public interface Filesystem {
File open(String path) throws IOException, InterruptedException;
boolean mkdir(String path) throws IOException, InterruptedException;
EntryType type(String path) throws IOException, InterruptedException;
boolean rm(String path) throws IOException, InterruptedException;
}

View File

@ -0,0 +1,35 @@
package me.topchetoeu.jscript.filesystem;
import java.io.IOException;
public class InaccessibleFile implements File {
public static final InaccessibleFile INSTANCE = new InaccessibleFile();
@Override
public int read() throws IOException, InterruptedException {
return -1;
}
@Override
public boolean write(byte val) throws IOException, InterruptedException {
return false;
}
@Override
public long tell() throws IOException, InterruptedException {
return 0;
}
@Override
public void seek(long offset, int pos) throws IOException, InterruptedException { }
@Override
public void close() throws IOException, InterruptedException { }
@Override
public Permissions perms() {
return Permissions.NONE;
}
private InaccessibleFile() { }
}

View File

@ -0,0 +1,53 @@
package me.topchetoeu.jscript.filesystem;
import java.io.IOException;
public class MemoryFile implements File {
private int ptr;
private Permissions mode;
public final byte[] data;
@Override
public int read() throws IOException, InterruptedException {
if (data == null || !mode.readable || ptr >= data.length) return -1;
return data[ptr++];
}
@Override
public boolean write(byte val) throws IOException, InterruptedException {
if (data == null || !mode.writable || ptr >= data.length) return false;
data[ptr++] = val;
return true;
}
@Override
public long tell() throws IOException, InterruptedException {
return ptr;
}
@Override
public void seek(long offset, int pos) throws IOException, InterruptedException {
if (data == null) return;
if (pos == 0) ptr = (int)offset;
else if (pos == 1) ptr += (int)offset;
else if (pos == 2) ptr = data.length - (int)offset;
}
@Override
public void close() throws IOException, InterruptedException {
mode = null;
ptr = 0;
}
@Override
public Permissions perms() {
if (data == null) return Permissions.NONE;
return mode;
}
public MemoryFile(byte[] buff, Permissions mode) {
this.data = buff;
this.mode = mode;
}
}

View File

@ -0,0 +1,17 @@
package me.topchetoeu.jscript.filesystem;
public enum Permissions {
NONE("", false, false),
READ("r", true, false),
READ_WRITE("rw", true, true);
public final String readMode;
public final boolean readable;
public final boolean writable;
private Permissions(String mode, boolean r, boolean w) {
this.readMode = mode;
this.readable = r;
this.writable = w;
}
}

View File

@ -0,0 +1,51 @@
package me.topchetoeu.jscript.filesystem;
import java.io.IOException;
import java.io.RandomAccessFile;
public class PhysicalFile implements File {
private RandomAccessFile file;
private Permissions perms;
@Override
public int read() throws IOException, InterruptedException {
if (file == null || !perms.readable) return -1;
else return file.read();
}
@Override
public boolean write(byte val) throws IOException, InterruptedException {
if (file == null || !perms.writable) return false;
file.write(val);
return true;
}
@Override
public long tell() throws IOException, InterruptedException {
if (file == null) return 0;
return file.getFilePointer();
}
@Override
public void seek(long offset, int pos) throws IOException, InterruptedException {
if (file == null) return;
if (pos == 0) file.seek(pos);
else if (pos == 1) file.seek(file.getFilePointer() + pos);
else if (pos == 2) file.seek(file.length() + pos);
}
@Override
public void close() throws IOException, InterruptedException {
if (file == null) return;
file.close();
}
@Override
public Permissions perms() { return perms; }
public PhysicalFile(String path, Permissions mode) throws IOException {
if (mode == Permissions.NONE) file = null;
else file = new RandomAccessFile(path, mode.readMode);
perms = mode;
}
}

View File

@ -0,0 +1,74 @@
package me.topchetoeu.jscript.filesystem;
import java.io.IOException;
import java.nio.file.Path;
public class PhysicalFilesystem implements Filesystem {
public final Path root;
private Permissions getPerms(Path path) {
var file = path.toFile();
if (!path.startsWith(root)) return Permissions.NONE;
if (file.canRead() && file.canWrite()) return Permissions.READ_WRITE;
if (file.canRead()) return Permissions.READ;
return Permissions.NONE;
}
private Path getPath(String name) {
return root.resolve(name);
}
@Override
public File open(String path) throws IOException, InterruptedException {
var _path = root.resolve(path);
var perms = getPerms(_path);
if (perms == Permissions.NONE) return InaccessibleFile.INSTANCE;
var f = _path.toFile();
if (f.isDirectory()) {
var res = new StringBuilder();
for (var child : f.listFiles()) res.append(child.toString()).append('\n');
return new MemoryFile(res.toString().getBytes(), Permissions.READ);
}
else return new PhysicalFile(path, perms);
}
@Override
public boolean mkdir(String path) throws IOException, InterruptedException {
var _path = getPath(path);
var perms = getPerms(_path);
var f = _path.toFile();
if (!perms.writable) return false;
else return f.mkdir();
}
@Override
public EntryType type(String path) throws IOException, InterruptedException {
var _path = getPath(path);
var perms = getPerms(_path);
var f = _path.toFile();
if (perms == Permissions.NONE) return EntryType.NONE;
else if (f.isFile()) return EntryType.FILE;
else return EntryType.FOLDER;
}
@Override
public boolean rm(String path) throws IOException, InterruptedException {
var _path = getPath(path);
var perms = getPerms(_path);
var f = _path.toFile();
if (!perms.writable) return false;
else return f.delete();
}
public PhysicalFilesystem(Path root) {
this.root = root;
}
}

View File

@ -516,12 +516,59 @@ public class Parsing {
for (int i = 2; i < literal.length(); i++) { for (int i = 2; i < literal.length(); i++) {
res *= 16; res *= 16;
res += fromHex(literal.charAt(i)); int dig = fromHex(literal.charAt(i));
res += dig;
} }
return res; return res;
} }
public static Double parseNumber(boolean octals, String value) {
if (value.startsWith("0x") || value.startsWith("0X")) {
if (value.length() == 2) return null;
return parseHex(value);
}
if (value.endsWith("e") || value.endsWith("E") || value.endsWith("-")) return null;
int i = 0;
double res = 0, dotDivisor = 1;
boolean e = false, dot = false;
int exponent = 0;
for (; i < value.length(); i++) {
char c = value.charAt(i);
if (c == '.') { dot = true; break; }
if (c == 'e') { e = true; break; }
if (!isDigit(c)) break;
res = res * 10 + c - '0';
}
if (dot) for (i++; i < value.length(); i++) {
char c = value.charAt(i);
if (c == 'e') { e = true; break; }
if (!isDigit(c)) break;
res += (c - '0') / (dotDivisor *= 10);
}
if (e) for (i++; i < value.length(); i++) {
char c = value.charAt(i);
if (!isDigit(c)) break;
exponent = 10 * exponent + c - '0';
}
if (exponent < 0) for (int j = 0; j < -exponent; j++) res /= 10;
else for (int j = 0; j < exponent; j++) res *= 10;
return res;
}
private static double parseNumber(Location loc, String value) {
var res = parseNumber(false, value);
if (res == null) throw new SyntaxException(loc, "Invalid number format.");
else return res;
}
private static List<Token> parseTokens(String filename, Collection<RawToken> tokens) { private static List<Token> parseTokens(String filename, Collection<RawToken> tokens) {
var res = new ArrayList<Token>(); var res = new ArrayList<Token>();
@ -529,28 +576,14 @@ public class Parsing {
var loc = new Location(el.line, el.start, filename); var loc = new Location(el.line, el.start, filename);
switch (el.type) { switch (el.type) {
case LITERAL: res.add(Token.identifier(el.line, el.start, el.value)); break; case LITERAL: res.add(Token.identifier(el.line, el.start, el.value)); break;
case NUMBER: case NUMBER: res.add(Token.number(el.line, el.start, parseNumber(loc, el.value))); break;
if (el.value.startsWith("0x") || el.value.startsWith("0X")) { case STRING: res.add(Token.string(el.line, el.start, parseString(loc, el.value))); break;
if (el.value.endsWith("x") || el.value.endsWith("X")) { case REGEX: res.add(Token.regex(el.line, el.start, parseRegex(loc, el.value))); break;
throw new SyntaxException(loc, "Invalid number format.");
}
res.add(Token.number(el.line, el.start, parseHex(el.value))); break;
}
if (
el.value.endsWith("e") || el.value.endsWith("E") || el.value.endsWith("-")
) throw new SyntaxException(loc, "Invalid number format.");
else res.add(Token.number(el.line, el.start, Double.parseDouble(el.value))); break;
case OPERATOR: case OPERATOR:
Operator op = Operator.parse(el.value); Operator op = Operator.parse(el.value);
if (op == null) throw new SyntaxException(loc, String.format("Unrecognized operator '%s'.", el.value)); if (op == null) throw new SyntaxException(loc, String.format("Unrecognized operator '%s'.", el.value));
res.add(Token.operator(el.line, el.start, op)); res.add(Token.operator(el.line, el.start, op));
break; break;
case STRING:
res.add(Token.string(el.line, el.start, parseString(loc, el.value)));
break;
case REGEX:
res.add(Token.regex(el.line, el.start, parseRegex(loc, el.value)));
break;
} }
} }